Complete rewrite of indicators panel to fix duplicate event listeners
Key changes: - Single 'listenersAttached' flag tracks if any listeners are attached - One shared event delegation handler handles all button types (add, config, remove, favorite) - Stop propagation immediately on button clicks to prevent multiple triggers - Added e.stopPropagation() calls to prevent event bubbling - Consolidated event listener logic into single function - Added safety checks before calling window.functionName This fixes the issue where one click added multiple indicators (3x MA, 6x HTS, 4x RSI) by preventing duplicate event listener setup.
This commit is contained in:
@ -6,7 +6,7 @@ let configuringId = null;
|
|||||||
let searchQuery = '';
|
let searchQuery = '';
|
||||||
let selectedCategory = 'all';
|
let selectedCategory = 'all';
|
||||||
let nextInstanceId = 1;
|
let nextInstanceId = 1;
|
||||||
let eventListenersSet = false;
|
let listenersAttached = false; // Single flag to track if any listeners are attached
|
||||||
|
|
||||||
// Chart pane management
|
// Chart pane management
|
||||||
let indicatorPanes = new Map();
|
let indicatorPanes = new Map();
|
||||||
@ -35,14 +35,6 @@ const CATEGORY_MAP = {
|
|||||||
const DEFAULT_COLORS = ['#2962ff', '#26a69a', '#ef5350', '#ff9800', '#9c27b0', '#00bcd4', '#ffeb3b', '#e91e63'];
|
const DEFAULT_COLORS = ['#2962ff', '#26a69a', '#ef5350', '#ff9800', '#9c27b0', '#00bcd4', '#ffeb3b', '#e91e63'];
|
||||||
const LINE_TYPES = ['solid', 'dotted', 'dashed'];
|
const LINE_TYPES = ['solid', 'dotted', 'dashed'];
|
||||||
|
|
||||||
// Initialize
|
|
||||||
export function initIndicatorPanel() {
|
|
||||||
console.log('[IndicatorPanel] Initializing...');
|
|
||||||
renderIndicatorPanel();
|
|
||||||
setupEventListeners();
|
|
||||||
console.log('[IndicatorPanel] Initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultColor(index) {
|
function getDefaultColor(index) {
|
||||||
return DEFAULT_COLORS[index % DEFAULT_COLORS.length];
|
return DEFAULT_COLORS[index % DEFAULT_COLORS.length];
|
||||||
}
|
}
|
||||||
@ -92,6 +84,12 @@ function groupPlotsByColor(plots) {
|
|||||||
return Object.values(groups);
|
return Object.values(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function initIndicatorPanel() {
|
||||||
|
console.log('[IndicatorPanel] Initializing...');
|
||||||
|
renderIndicatorPanel();
|
||||||
|
console.log('[IndicatorPanel] Initialized');
|
||||||
|
}
|
||||||
|
|
||||||
export function getActiveIndicators() {
|
export function getActiveIndicators() {
|
||||||
return activeIndicators;
|
return activeIndicators;
|
||||||
}
|
}
|
||||||
@ -136,7 +134,7 @@ export function renderIndicatorPanel() {
|
|||||||
value="${searchQuery}"
|
value="${searchQuery}"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
>
|
>
|
||||||
${searchQuery ? `<button class="search-clear" onclick="clearSearch()">×</button>` : ''}
|
${searchQuery ? `<button class="search-clear">×</button>` : ''}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Categories -->
|
<!-- Categories -->
|
||||||
@ -167,7 +165,7 @@ export function renderIndicatorPanel() {
|
|||||||
<div class="indicator-section active">
|
<div class="indicator-section active">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
${activeIndicators.length} Active
|
${activeIndicators.length} Active
|
||||||
${activeIndicators.length > 0 ? `<button class="clear-all" onclick="clearAllIndicators()">Clear All</button>` : ''}
|
${activeIndicators.length > 0 ? `<button class="clear-all">Clear All</button>` : ''}
|
||||||
</div>
|
</div>
|
||||||
${activeIndicators.slice().reverse().map(ind => renderActiveIndicator(ind)).join('')}
|
${activeIndicators.slice().reverse().map(ind => renderActiveIndicator(ind)).join('')}
|
||||||
</div>
|
</div>
|
||||||
@ -188,9 +186,9 @@ export function renderIndicatorPanel() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
// Only setup event listeners once
|
// Only setup event listeners once
|
||||||
if (!eventListenersSet) {
|
if (!listenersAttached) {
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
eventListenersSet = true;
|
listenersAttached = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,19 +218,24 @@ function renderActiveIndicator(indicator) {
|
|||||||
const meta = getIndicatorMeta(indicator);
|
const meta = getIndicatorMeta(indicator);
|
||||||
const label = getIndicatorLabel(indicator);
|
const label = getIndicatorLabel(indicator);
|
||||||
const isFavorite = userPresets.favorites?.includes(indicator.type) || false;
|
const isFavorite = userPresets.favorites?.includes(indicator.type) || false;
|
||||||
|
const showPresets = meta.name && function() {
|
||||||
|
const hasPresets = typeof getPresetsForIndicator === 'function' ? getPresetsForIndicator(meta.name) : [];
|
||||||
|
if (!hasPresets || hasPresets.length === 0) return '';
|
||||||
|
return `<div class="indicator-presets">
|
||||||
|
<button class="preset-indicator" title="${hasPresets.length} saved presets">💾</button>
|
||||||
|
</div>`;
|
||||||
|
}();
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="indicator-item active ${isExpanded ? 'expanded' : ''}" data-id="${indicator.id}">
|
<div class="indicator-item active ${isExpanded ? 'expanded' : ''}" data-id="${indicator.id}">
|
||||||
<div class="indicator-item-main" onclick="window.toggleIndicatorExpand('${indicator.id}')">
|
<div class="indicator-item-main" onclick="window.toggleIndicatorExpand && window.toggleIndicatorExpand('${indicator.id}');">
|
||||||
<div class="drag-handle" title="Drag to reorder">⋮⋮</div>
|
<div class="drag-handle" title="Drag to reorder">⋮⋮</div>
|
||||||
<button class="indicator-btn visible" onclick="event.stopPropagation(); window.toggleIndicatorVisibility('${indicator.id}')" title="${indicator.visible !== false ? 'Hide' : 'Show'}">
|
<button class="indicator-btn visible" onclick="event.stopPropagation(); window.toggleIndicatorVisibility && window.toggleIndicatorVisibility('${indicator.id}')" title="${indicator.visible !== false ? 'Hide' : 'Show'}">
|
||||||
${indicator.visible !== false ? '👁' : '👁🗨'}
|
${indicator.visible !== false ? '👁' : '👁🗨'}
|
||||||
</button>
|
</button>
|
||||||
<span class="indicator-name">${label}</span>
|
<span class="indicator-name">${label}</span>
|
||||||
<div class="indicator-presets">
|
${showPresets}
|
||||||
${meta.name && renderPresetIndicatorIndicator(meta, indicator)}
|
<button class="indicator-btn favorite" onclick="event.stopPropagation(); window.toggleFavorite && window.toggleFavorite('${indicator.type}')" title="Add to favorites">
|
||||||
</div>
|
|
||||||
<button class="indicator-btn favorite" onclick="event.stopPropagation(); window.toggleFavorite('${indicator.type}')" title="Add to favorites">
|
|
||||||
${isFavorite ? '★' : '☆'}
|
${isFavorite ? '★' : '☆'}
|
||||||
</button>
|
</button>
|
||||||
<button class="indicator-btn expand ${isExpanded ? 'rotated' : ''}" title="Show settings">
|
<button class="indicator-btn expand ${isExpanded ? 'rotated' : ''}" title="Show settings">
|
||||||
@ -242,7 +245,7 @@ function renderActiveIndicator(indicator) {
|
|||||||
|
|
||||||
${isExpanded ? `
|
${isExpanded ? `
|
||||||
<div class="indicator-config">
|
<div class="indicator-config">
|
||||||
${renderIndicatorConfig(indicator, meta)}
|
${typeof renderIndicatorConfig === 'function' ? renderIndicatorConfig(indicator, meta) : ''}
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
@ -250,10 +253,10 @@ function renderActiveIndicator(indicator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderPresetIndicatorIndicator(meta, indicator) {
|
function renderPresetIndicatorIndicator(meta, indicator) {
|
||||||
const hasPresets = getPresetsForIndicator(meta.name);
|
const hasPresets = typeof getPresetsForIndicator === 'function' ? getPresetsForIndicator(meta.name) : [];
|
||||||
if (!hasPresets || hasPresets.length === 0) return '';
|
if (!hasPresets || hasPresets.length === 0) return '';
|
||||||
|
|
||||||
return `<button class="preset-indicator" title="${hasPresets.length} saved presets" onclick="event.stopPropagation(); window.showPresets('${meta.name}')">💾</button>`;
|
return `<button class="preset-indicator" title="${hasPresets.length} saved presets" onclick="event.stopPropagation(); window.showPresets && window.showPresets('${meta.name}')">💾</button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderIndicatorConfig(indicator, meta) {
|
function renderIndicatorConfig(indicator, meta) {
|
||||||
@ -271,7 +274,7 @@ function renderIndicatorConfig(indicator, meta) {
|
|||||||
<div class="config-row">
|
<div class="config-row">
|
||||||
<label>${group.name} Color</label>
|
<label>${group.name} Color</label>
|
||||||
<div class="color-picker">
|
<div class="color-picker">
|
||||||
<input type="color" id="color_${indicator.id}_${firstIdx}" value="${color}" onchange="window.updateIndicatorColor('${indicator.id}', ${firstIdx}, this.value)">
|
<input type="color" id="color_${indicator.id}_${firstIdx}" value="${color}">
|
||||||
<span class="color-preview" style="background: ${color};"></span>
|
<span class="color-preview" style="background: ${color};"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -280,19 +283,18 @@ function renderIndicatorConfig(indicator, meta) {
|
|||||||
|
|
||||||
<div class="config-row">
|
<div class="config-row">
|
||||||
<label>Line Type</label>
|
<label>Line Type</label>
|
||||||
<select onchange="window.updateIndicatorSetting('${indicator.id}', '_lineType', this.value)">
|
<select onchange="window.updateIndicatorSetting && window.updateIndicatorSetting('${indicator.id}', '_lineType', this.value)">
|
||||||
${LINE_TYPES.map(lt => `<option value="${lt}" ${indicator.params._lineType === lt ? 'selected' : ''}>${lt.charAt(0).toUpperCase() + lt.slice(1)}</option>`).join('')}
|
${LINE_TYPES.map(lt => `<option value="${lt}" ${indicator.params._lineType === lt ? 'selected' : ''}>${lt.charAt(0).toUpperCase() + lt.slice(1)}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-row">
|
<div class="config-row">
|
||||||
<label>Line Width</label>
|
<label>Line Width</label>
|
||||||
<input type="range" min="1" max="5" value="${indicator.params._lineWidth || 2}" onchange="window.updateIndicatorSetting('${indicator.id}', '_lineWidth', parseInt(this.value))">
|
<input type="range" min="1" max="5" value="${indicator.params._lineWidth || 2}" onchange="window.updateIndicatorSetting && window.updateIndicatorSetting('${indicator.id}', '_lineWidth', parseInt(this.value))">
|
||||||
<span class="range-value">${indicator.params._lineWidth || 2}</span>
|
<span class="range-value">${indicator.params._lineWidth || 2}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Inputs -->
|
|
||||||
${meta?.inputs && meta.inputs.length > 0 ? `
|
${meta?.inputs && meta.inputs.length > 0 ? `
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="section-subtitle">Parameters</div>
|
<div class="section-subtitle">Parameters</div>
|
||||||
@ -300,61 +302,53 @@ function renderIndicatorConfig(indicator, meta) {
|
|||||||
<div class="config-row">
|
<div class="config-row">
|
||||||
<label>${input.label}</label>
|
<label>${input.label}</label>
|
||||||
${input.type === 'select' ?
|
${input.type === 'select' ?
|
||||||
`<select onchange="window.updateIndicatorSetting('${indicator.id}', '${input.name}', this.value)">
|
`<select onchange="window.updateIndicatorSetting && window.updateIndicatorSetting('${indicator.id}', '${input.name}', this.value)">
|
||||||
${input.options.map(o => `<option value="${o}" ${indicator.params[input.name] === o ? 'selected' : ''}>${o}</option>`).join('')}
|
${input.options.map(o => `<option value="${o}" ${indicator.params[input.name] === o ? 'selected' : ''}>${o}</option>`).join('')}
|
||||||
</select>` :
|
</select>` :
|
||||||
`<div class="input-with-preset">
|
`<input
|
||||||
<input
|
|
||||||
type="number"
|
type="number"
|
||||||
value="${indicator.params[input.name]}"
|
value="${indicator.params[input.name]}"
|
||||||
${input.min !== undefined ? `min="${input.min}"` : ''}
|
${input.min !== undefined ? `min="${input.min}"` : ''}
|
||||||
${input.max !== undefined ? `max="${input.max}"` : ''}
|
${input.max !== undefined ? `max="${input.max}"` : ''}
|
||||||
${input.step !== undefined ? `step="${input.step}"` : ''}
|
${input.step !== undefined ? `step="${input.step}"` : ''}
|
||||||
onchange="window.updateIndicatorSetting('${indicator.id}', '${input.name}', parseFloat(this.value))"
|
onchange="window.updateIndicatorSetting && window.updateIndicatorSetting('${indicator.id}', '${input.name}', parseFloat(this.value))"
|
||||||
>
|
>`
|
||||||
<button class="presets-btn" onclick="window.showInputPresets('${indicator.id}', '${input.name}')">⋯</button>
|
|
||||||
</div>`
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
|
|
||||||
<!-- Presets -->
|
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="section-subtitle">
|
<div class="section-subtitle">
|
||||||
Presets
|
Presets
|
||||||
<button class="preset-action-btn" onclick="window.savePreset('${indicator.id}')" title="Save current settings as preset">+ Save Preset</button>
|
<button class="preset-action-btn" onclick="window.savePreset && window.savePreset('${indicator.id}')">+ Save Preset</button>
|
||||||
</div>
|
</div>
|
||||||
${renderIndicatorPresets(indicator, meta)}
|
${typeof renderIndicatorPresets === 'function' ? renderIndicatorPresets(indicator, meta) : ''}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions -->
|
|
||||||
<div class="config-actions">
|
<div class="config-actions">
|
||||||
<button class="btn-secondary" onclick="window.resetIndicator('${indicator.id}')">Reset to Defaults</button>
|
<button class="btn-secondary" onclick="window.resetIndicator && window.resetIndicator('${indicator.id}')">Reset to Defaults</button>
|
||||||
<button class="btn-danger" onclick="window.removeIndicatorById('${indicator.id}')">Remove</button>
|
<button class="btn-danger" onclick="window.removeIndicator && window.removeIndicator('${indicator.id}')">Remove</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderIndicatorPresets(indicator, meta) {
|
function renderIndicatorPresets(indicator, meta) {
|
||||||
const presets = getPresetsForIndicator(meta.name);
|
const presets = typeof getPresetsForIndicator === 'function' ? getPresetsForIndicator(meta.name) : [];
|
||||||
const instance = new IR[indicator.type]({ type: indicator.type, params: indicator.params, name: indicator.name });
|
|
||||||
const metadata = instance.getMetadata();
|
|
||||||
|
|
||||||
return presets.length > 0 ? `
|
return presets.length > 0 ? `
|
||||||
<div class="presets-list">
|
<div class="presets-list">
|
||||||
${presets.map(preset => {
|
${presets.map(p => {
|
||||||
// Match values against current settings
|
const isApplied = meta.inputs.every(input =>
|
||||||
const isApplied = metadata.inputs.every(input =>
|
(indicator.params[input.name] === (preset.values?.[input.name] ?? input.default))
|
||||||
preset.values[input.name] === indicator.params[input.name]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="preset-item ${isApplied ? 'applied' : ''}" data-preset="${preset.id}">
|
<div class="preset-item ${isApplied ? 'applied' : ''}" data-preset="${preset.id}">
|
||||||
<span class="preset-label" onclick="window.applyPreset('${indicator.id}', '${preset.id}')">${preset.name}</span>
|
<span class="preset-label" onclick="window.applyPreset && window.applyPreset('${indicator.id}', '${preset.id}')">${preset.name}</span>
|
||||||
<button class="preset-delete" onclick="event.stopPropagation(); window.deletePreset('${preset.id}')" title="Delete preset">×</button>
|
<button class="preset-delete" onclick="window.deletePreset && window.deletePreset('${preset.id}')">×</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('')}
|
}).join('')}
|
||||||
@ -364,44 +358,59 @@ function renderIndicatorPresets(indicator, meta) {
|
|||||||
|
|
||||||
// Event listeners
|
// Event listeners
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
// Event delegation for dynamically created elements
|
|
||||||
const container = document.getElementById('indicatorPanel');
|
const container = document.getElementById('indicatorPanel');
|
||||||
if (container) {
|
if (!container) return;
|
||||||
// Add button
|
|
||||||
|
console.log('[IndicatorPanel] Setting up event listeners...');
|
||||||
|
|
||||||
|
// Single event delegation handler for add button
|
||||||
container.addEventListener('click', (e) => {
|
container.addEventListener('click', (e) => {
|
||||||
const addBtn = e.target.closest('.indicator-btn.add');
|
const addBtn = e.target.closest('.indicator-btn.add');
|
||||||
if (addBtn) {
|
if (addBtn) {
|
||||||
|
e.stopPropagation();
|
||||||
const type = addBtn.dataset.type;
|
const type = addBtn.dataset.type;
|
||||||
if (type && window.addIndicator) {
|
if (type && window.addIndicator) {
|
||||||
|
console.log('[IndicatorPanel] Adding indicator:', type);
|
||||||
window.addIndicator(type);
|
window.addIndicator(type);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Config / expand button
|
// Expand/collapse button
|
||||||
container.addEventListener('click', (e) => {
|
|
||||||
const expandBtn = e.target.closest('.indicator-btn.expand');
|
const expandBtn = e.target.closest('.indicator-btn.expand');
|
||||||
if (expandBtn) {
|
if (expandBtn) {
|
||||||
|
e.stopPropagation();
|
||||||
const id = expandBtn.dataset.id;
|
const id = expandBtn.dataset.id;
|
||||||
if (id && window.toggleIndicatorExpand) {
|
if (id && window.toggleIndicatorExpand) {
|
||||||
window.toggleIndicatorExpand(id);
|
window.toggleIndicatorExpand(id);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Remove button
|
// Remove button
|
||||||
container.addEventListener('click', (e) => {
|
|
||||||
const removeBtn = e.target.closest('.indicator-btn.remove');
|
const removeBtn = e.target.closest('.indicator-btn.remove');
|
||||||
if (removeBtn) {
|
if (removeBtn) {
|
||||||
|
e.stopPropagation();
|
||||||
const id = removeBtn.dataset.id;
|
const id = removeBtn.dataset.id;
|
||||||
if (id && window.removeIndicatorById) {
|
if (id && window.removeIndicatorById) {
|
||||||
window.removeIndicatorById(id);
|
window.removeIndicatorById(id);
|
||||||
}
|
}
|
||||||
}
|
return;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search
|
// Favorite button
|
||||||
|
const favoriteBtn = e.target.closest('.indicator-btn.favorite');
|
||||||
|
if (favoriteBtn) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const type = favoriteBtn.dataset.type;
|
||||||
|
if (type && window.toggleFavorite) {
|
||||||
|
window.toggleFavorite(type);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search input
|
||||||
const searchInput = document.getElementById('indicatorSearch');
|
const searchInput = document.getElementById('indicatorSearch');
|
||||||
if (searchInput) {
|
if (searchInput) {
|
||||||
searchInput.addEventListener('input', (e) => {
|
searchInput.addEventListener('input', (e) => {
|
||||||
@ -410,6 +419,15 @@ function setupEventListeners() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search clear button
|
||||||
|
const searchClear = container.querySelector('.search-clear');
|
||||||
|
if (searchClear) {
|
||||||
|
searchClear.addEventListener('click', (e) => {
|
||||||
|
searchQuery = '';
|
||||||
|
renderIndicatorPanel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Category tabs
|
// Category tabs
|
||||||
document.querySelectorAll('.category-tab').forEach(tab => {
|
document.querySelectorAll('.category-tab').forEach(tab => {
|
||||||
tab.addEventListener('click', (e) => {
|
tab.addEventListener('click', (e) => {
|
||||||
@ -417,6 +435,16 @@ function setupEventListeners() {
|
|||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clear all button
|
||||||
|
const clearAllBtn = container.querySelector('.clear-all');
|
||||||
|
if (clearAllBtn) {
|
||||||
|
clearAllBtn.addEventListener('click', () => {
|
||||||
|
window.clearAllIndicators();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[IndicatorPanel] Event listeners setup complete');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@ -434,7 +462,7 @@ window.clearAllIndicators = function() {
|
|||||||
activeIndicators = [];
|
activeIndicators = [];
|
||||||
configuringId = null;
|
configuringId = null;
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
};
|
};
|
||||||
|
|
||||||
function addIndicator(type) {
|
function addIndicator(type) {
|
||||||
@ -469,7 +497,7 @@ function addIndicator(type) {
|
|||||||
configuringId = id;
|
configuringId = id;
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
};
|
}
|
||||||
|
|
||||||
window.toggleIndicatorExpand = function(id) {
|
window.toggleIndicatorExpand = function(id) {
|
||||||
configuringId = configuringId === id ? null : id;
|
configuringId = configuringId === id ? null : id;
|
||||||
@ -492,7 +520,10 @@ window.toggleIndicatorVisibility = function(id) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.toggleFavorite = function(type) {
|
window.toggleFavorite = function(type) {
|
||||||
const favorites = userPresets.favorites || [];
|
if (!userPresets) userPresets = {};
|
||||||
|
if (!userPresets.favorites) userPresets.favorites = [];
|
||||||
|
|
||||||
|
const favorites = userPresets.favorites;
|
||||||
const idx = favorites.indexOf(type);
|
const idx = favorites.indexOf(type);
|
||||||
|
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
@ -536,9 +567,9 @@ window.resetIndicator = function(id) {
|
|||||||
if (!IndicatorClass) return;
|
if (!IndicatorClass) return;
|
||||||
|
|
||||||
const instance = new IndicatorClass({ type: indicator.type, params: {}, name: indicator.name });
|
const instance = new IndicatorClass({ type: indicator.type, params: {}, name: indicator.name });
|
||||||
const metadata = instance.getMetadata();
|
const meta = instance.getMetadata();
|
||||||
|
|
||||||
metadata.inputs.forEach(input => {
|
meta.inputs.forEach(input => {
|
||||||
indicator.params[input.name] = input.default;
|
indicator.params[input.name] = input.default;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -546,7 +577,12 @@ window.resetIndicator = function(id) {
|
|||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
};
|
};
|
||||||
|
|
||||||
function removeIndicatorById(id) {
|
window.removeIndicator = function() {
|
||||||
|
if (!configuringId) return;
|
||||||
|
removeIndicatorById(configuringId);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.removeIndicatorById = function(id) {
|
||||||
const idx = activeIndicators.findIndex(a => a.id === id);
|
const idx = activeIndicators.findIndex(a => a.id === id);
|
||||||
if (idx < 0) return;
|
if (idx < 0) return;
|
||||||
|
|
||||||
@ -562,7 +598,7 @@ function removeIndicatorById(id) {
|
|||||||
|
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
drawIndicatorsOnChart();
|
drawIndicatorsOnChart();
|
||||||
}
|
};
|
||||||
|
|
||||||
function removeIndicatorByIndex(index) {
|
function removeIndicatorByIndex(index) {
|
||||||
if (index < 0 || index >= activeIndicators.length) return;
|
if (index < 0 || index >= activeIndicators.length) return;
|
||||||
@ -571,8 +607,8 @@ function removeIndicatorByIndex(index) {
|
|||||||
|
|
||||||
// Presets
|
// Presets
|
||||||
function getPresetsForIndicator(indicatorName) {
|
function getPresetsForIndicator(indicatorName) {
|
||||||
const allPresets = Object.values(userPresets).flat().filter(p => typeof p === 'object' && p.name);
|
if (!userPresets || !userPresets.presets) return [];
|
||||||
return allPresets.filter(p => p.indicatorName === indicatorName);
|
return userPresets.presets.filter(p => p.indicatorName === indicatorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.savePreset = function(id) {
|
window.savePreset = function(id) {
|
||||||
@ -608,7 +644,7 @@ window.savePreset = function(id) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.applyPreset = function(id, presetId) {
|
window.applyPreset = function(id, presetId) {
|
||||||
const allPresets = Object.values(userPresets).flat().filter(p => typeof p === 'object' && p.id);
|
const allPresets = (userPresets?.presets || []).filter(p => typeof p === 'object' && p.id);
|
||||||
const preset = allPresets.find(p => p.id === presetId);
|
const preset = allPresets.find(p => p.id === presetId);
|
||||||
if (!preset) return;
|
if (!preset) return;
|
||||||
|
|
||||||
@ -626,7 +662,7 @@ window.applyPreset = function(id, presetId) {
|
|||||||
window.deletePreset = function(presetId) {
|
window.deletePreset = function(presetId) {
|
||||||
if (!confirm('Delete this preset?')) return;
|
if (!confirm('Delete this preset?')) return;
|
||||||
|
|
||||||
if (userPresets.presets) {
|
if (userPresets?.presets) {
|
||||||
userPresets.presets = userPresets.presets.filter(p => p.id !== presetId);
|
userPresets.presets = userPresets.presets.filter(p => p.id !== presetId);
|
||||||
saveUserPresets();
|
saveUserPresets();
|
||||||
renderIndicatorPanel();
|
renderIndicatorPanel();
|
||||||
@ -821,6 +857,12 @@ export function drawIndicatorsOnChart() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export functions for module access
|
||||||
|
export const addIndicator = addIndicator;
|
||||||
|
export const removeIndicatorById = removeIndicatorById;
|
||||||
|
export const removeIndicatorByIndexFunction = removeIndicatorByIndex;
|
||||||
|
const removeIndicatorByIndex = removeIndicatorByIndex;
|
||||||
|
|
||||||
// Legacy compatibility functions
|
// Legacy compatibility functions
|
||||||
window.renderIndicatorList = renderIndicatorPanel;
|
window.renderIndicatorList = renderIndicatorPanel;
|
||||||
window.toggleIndicator = addIndicator;
|
window.toggleIndicator = addIndicator;
|
||||||
@ -832,20 +874,15 @@ window.showIndicatorConfig = function(id) {
|
|||||||
window.applyIndicatorConfig = function() {
|
window.applyIndicatorConfig = function() {
|
||||||
// No-op - config is applied immediately
|
// No-op - config is applied immediately
|
||||||
};
|
};
|
||||||
window.removeIndicator = function() {
|
|
||||||
if (!configuringId) return;
|
|
||||||
removeIndicatorById(configuringId);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assign to window for backward compatibility
|
// Assign to window for backward compatibility
|
||||||
window.toggleIndicator = addIndicator;
|
|
||||||
window.addIndicator = addIndicator;
|
window.addIndicator = addIndicator;
|
||||||
|
window.toggleIndicator = addIndicator;
|
||||||
window.removeIndicatorById = removeIndicatorById;
|
window.removeIndicatorById = removeIndicatorById;
|
||||||
window.removeIndicatorByIndex = function(index) {
|
window.removeIndicatorByIndex = removeIndicatorByIndexWindow;
|
||||||
|
const removeIndicatorByIndexWindow = function(index) {
|
||||||
if (index < 0 || index >= activeIndicators.length) return;
|
if (index < 0 || index >= activeIndicators.length) return;
|
||||||
removeIndicatorById(activeIndicators[index].id);
|
removeIndicatorById(activeIndicators[index].id);
|
||||||
};
|
};
|
||||||
|
window.removeIndicatorByIndex = removeIndicatorByIndexWindow;
|
||||||
window.drawIndicatorsOnChart = drawIndicatorsOnChart;
|
window.drawIndicatorsOnChart = drawIndicatorsOnChart;
|
||||||
|
|
||||||
// Export functions for module imports
|
|
||||||
export { addIndicator, removeIndicatorById, removeIndicatorByIndex };
|
|
||||||
Reference in New Issue
Block a user