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 selectedCategory = 'all';
|
||||
let nextInstanceId = 1;
|
||||
let eventListenersSet = false;
|
||||
let listenersAttached = false; // Single flag to track if any listeners are attached
|
||||
|
||||
// Chart pane management
|
||||
let indicatorPanes = new Map();
|
||||
@ -35,14 +35,6 @@ const CATEGORY_MAP = {
|
||||
const DEFAULT_COLORS = ['#2962ff', '#26a69a', '#ef5350', '#ff9800', '#9c27b0', '#00bcd4', '#ffeb3b', '#e91e63'];
|
||||
const LINE_TYPES = ['solid', 'dotted', 'dashed'];
|
||||
|
||||
// Initialize
|
||||
export function initIndicatorPanel() {
|
||||
console.log('[IndicatorPanel] Initializing...');
|
||||
renderIndicatorPanel();
|
||||
setupEventListeners();
|
||||
console.log('[IndicatorPanel] Initialized');
|
||||
}
|
||||
|
||||
function getDefaultColor(index) {
|
||||
return DEFAULT_COLORS[index % DEFAULT_COLORS.length];
|
||||
}
|
||||
@ -92,6 +84,12 @@ function groupPlotsByColor(plots) {
|
||||
return Object.values(groups);
|
||||
}
|
||||
|
||||
export function initIndicatorPanel() {
|
||||
console.log('[IndicatorPanel] Initializing...');
|
||||
renderIndicatorPanel();
|
||||
console.log('[IndicatorPanel] Initialized');
|
||||
}
|
||||
|
||||
export function getActiveIndicators() {
|
||||
return activeIndicators;
|
||||
}
|
||||
@ -136,7 +134,7 @@ export function renderIndicatorPanel() {
|
||||
value="${searchQuery}"
|
||||
autocomplete="off"
|
||||
>
|
||||
${searchQuery ? `<button class="search-clear" onclick="clearSearch()">×</button>` : ''}
|
||||
${searchQuery ? `<button class="search-clear">×</button>` : ''}
|
||||
</div>
|
||||
|
||||
<!-- Categories -->
|
||||
@ -167,7 +165,7 @@ export function renderIndicatorPanel() {
|
||||
<div class="indicator-section active">
|
||||
<div class="section-title">
|
||||
${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>
|
||||
${activeIndicators.slice().reverse().map(ind => renderActiveIndicator(ind)).join('')}
|
||||
</div>
|
||||
@ -188,9 +186,9 @@ export function renderIndicatorPanel() {
|
||||
`;
|
||||
|
||||
// Only setup event listeners once
|
||||
if (!eventListenersSet) {
|
||||
if (!listenersAttached) {
|
||||
setupEventListeners();
|
||||
eventListenersSet = true;
|
||||
listenersAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,19 +218,24 @@ function renderActiveIndicator(indicator) {
|
||||
const meta = getIndicatorMeta(indicator);
|
||||
const label = getIndicatorLabel(indicator);
|
||||
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 `
|
||||
<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>
|
||||
<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 ? '👁' : '👁🗨'}
|
||||
</button>
|
||||
<span class="indicator-name">${label}</span>
|
||||
<div class="indicator-presets">
|
||||
${meta.name && renderPresetIndicatorIndicator(meta, indicator)}
|
||||
</div>
|
||||
<button class="indicator-btn favorite" onclick="event.stopPropagation(); window.toggleFavorite('${indicator.type}')" title="Add to favorites">
|
||||
${showPresets}
|
||||
<button class="indicator-btn favorite" onclick="event.stopPropagation(); window.toggleFavorite && window.toggleFavorite('${indicator.type}')" title="Add to favorites">
|
||||
${isFavorite ? '★' : '☆'}
|
||||
</button>
|
||||
<button class="indicator-btn expand ${isExpanded ? 'rotated' : ''}" title="Show settings">
|
||||
@ -242,7 +245,7 @@ function renderActiveIndicator(indicator) {
|
||||
|
||||
${isExpanded ? `
|
||||
<div class="indicator-config">
|
||||
${renderIndicatorConfig(indicator, meta)}
|
||||
${typeof renderIndicatorConfig === 'function' ? renderIndicatorConfig(indicator, meta) : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
@ -250,10 +253,10 @@ function renderActiveIndicator(indicator) {
|
||||
}
|
||||
|
||||
function renderPresetIndicatorIndicator(meta, indicator) {
|
||||
const hasPresets = getPresetsForIndicator(meta.name);
|
||||
const hasPresets = typeof getPresetsForIndicator === 'function' ? getPresetsForIndicator(meta.name) : [];
|
||||
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) {
|
||||
@ -271,7 +274,7 @@ function renderIndicatorConfig(indicator, meta) {
|
||||
<div class="config-row">
|
||||
<label>${group.name} Color</label>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -280,19 +283,18 @@ function renderIndicatorConfig(indicator, meta) {
|
||||
|
||||
<div class="config-row">
|
||||
<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('')}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="config-row">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inputs -->
|
||||
${meta?.inputs && meta.inputs.length > 0 ? `
|
||||
<div class="config-section">
|
||||
<div class="section-subtitle">Parameters</div>
|
||||
@ -300,61 +302,53 @@ function renderIndicatorConfig(indicator, meta) {
|
||||
<div class="config-row">
|
||||
<label>${input.label}</label>
|
||||
${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('')}
|
||||
</select>` :
|
||||
`<div class="input-with-preset">
|
||||
<input
|
||||
type="number"
|
||||
value="${indicator.params[input.name]}"
|
||||
${input.min !== undefined ? `min="${input.min}"` : ''}
|
||||
${input.max !== undefined ? `max="${input.max}"` : ''}
|
||||
${input.step !== undefined ? `step="${input.step}"` : ''}
|
||||
onchange="window.updateIndicatorSetting('${indicator.id}', '${input.name}', parseFloat(this.value))"
|
||||
>
|
||||
<button class="presets-btn" onclick="window.showInputPresets('${indicator.id}', '${input.name}')">⋯</button>
|
||||
</div>`
|
||||
`<input
|
||||
type="number"
|
||||
value="${indicator.params[input.name]}"
|
||||
${input.min !== undefined ? `min="${input.min}"` : ''}
|
||||
${input.max !== undefined ? `max="${input.max}"` : ''}
|
||||
${input.step !== undefined ? `step="${input.step}"` : ''}
|
||||
onchange="window.updateIndicatorSetting && window.updateIndicatorSetting('${indicator.id}', '${input.name}', parseFloat(this.value))"
|
||||
>`
|
||||
}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Presets -->
|
||||
<div class="config-section">
|
||||
<div class="section-subtitle">
|
||||
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>
|
||||
${renderIndicatorPresets(indicator, meta)}
|
||||
${typeof renderIndicatorPresets === 'function' ? renderIndicatorPresets(indicator, meta) : ''}
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="config-actions">
|
||||
<button class="btn-secondary" onclick="window.resetIndicator('${indicator.id}')">Reset to Defaults</button>
|
||||
<button class="btn-danger" onclick="window.removeIndicatorById('${indicator.id}')">Remove</button>
|
||||
<button class="btn-secondary" onclick="window.resetIndicator && window.resetIndicator('${indicator.id}')">Reset to Defaults</button>
|
||||
<button class="btn-danger" onclick="window.removeIndicator && window.removeIndicator('${indicator.id}')">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderIndicatorPresets(indicator, meta) {
|
||||
const presets = getPresetsForIndicator(meta.name);
|
||||
const instance = new IR[indicator.type]({ type: indicator.type, params: indicator.params, name: indicator.name });
|
||||
const metadata = instance.getMetadata();
|
||||
const presets = typeof getPresetsForIndicator === 'function' ? getPresetsForIndicator(meta.name) : [];
|
||||
|
||||
return presets.length > 0 ? `
|
||||
<div class="presets-list">
|
||||
${presets.map(preset => {
|
||||
// Match values against current settings
|
||||
const isApplied = metadata.inputs.every(input =>
|
||||
preset.values[input.name] === indicator.params[input.name]
|
||||
${presets.map(p => {
|
||||
const isApplied = meta.inputs.every(input =>
|
||||
(indicator.params[input.name] === (preset.values?.[input.name] ?? input.default))
|
||||
);
|
||||
|
||||
return `
|
||||
<div class="preset-item ${isApplied ? 'applied' : ''}" data-preset="${preset.id}">
|
||||
<span class="preset-label" onclick="window.applyPreset('${indicator.id}', '${preset.id}')">${preset.name}</span>
|
||||
<button class="preset-delete" onclick="event.stopPropagation(); window.deletePreset('${preset.id}')" title="Delete preset">×</button>
|
||||
<span class="preset-label" onclick="window.applyPreset && window.applyPreset('${indicator.id}', '${preset.id}')">${preset.name}</span>
|
||||
<button class="preset-delete" onclick="window.deletePreset && window.deletePreset('${preset.id}')">×</button>
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
@ -364,44 +358,59 @@ function renderIndicatorPresets(indicator, meta) {
|
||||
|
||||
// Event listeners
|
||||
function setupEventListeners() {
|
||||
// Event delegation for dynamically created elements
|
||||
const container = document.getElementById('indicatorPanel');
|
||||
if (container) {
|
||||
// Add button
|
||||
container.addEventListener('click', (e) => {
|
||||
const addBtn = e.target.closest('.indicator-btn.add');
|
||||
if (addBtn) {
|
||||
const type = addBtn.dataset.type;
|
||||
if (type && window.addIndicator) {
|
||||
window.addIndicator(type);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!container) return;
|
||||
|
||||
// Config / expand button
|
||||
container.addEventListener('click', (e) => {
|
||||
const expandBtn = e.target.closest('.indicator-btn.expand');
|
||||
if (expandBtn) {
|
||||
const id = expandBtn.dataset.id;
|
||||
if (id && window.toggleIndicatorExpand) {
|
||||
window.toggleIndicatorExpand(id);
|
||||
}
|
||||
console.log('[IndicatorPanel] Setting up event listeners...');
|
||||
|
||||
// Single event delegation handler for add button
|
||||
container.addEventListener('click', (e) => {
|
||||
const addBtn = e.target.closest('.indicator-btn.add');
|
||||
if (addBtn) {
|
||||
e.stopPropagation();
|
||||
const type = addBtn.dataset.type;
|
||||
if (type && window.addIndicator) {
|
||||
console.log('[IndicatorPanel] Adding indicator:', type);
|
||||
window.addIndicator(type);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Expand/collapse button
|
||||
const expandBtn = e.target.closest('.indicator-btn.expand');
|
||||
if (expandBtn) {
|
||||
e.stopPropagation();
|
||||
const id = expandBtn.dataset.id;
|
||||
if (id && window.toggleIndicatorExpand) {
|
||||
window.toggleIndicatorExpand(id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove button
|
||||
container.addEventListener('click', (e) => {
|
||||
const removeBtn = e.target.closest('.indicator-btn.remove');
|
||||
if (removeBtn) {
|
||||
const id = removeBtn.dataset.id;
|
||||
if (id && window.removeIndicatorById) {
|
||||
window.removeIndicatorById(id);
|
||||
}
|
||||
const removeBtn = e.target.closest('.indicator-btn.remove');
|
||||
if (removeBtn) {
|
||||
e.stopPropagation();
|
||||
const id = removeBtn.dataset.id;
|
||||
if (id && window.removeIndicatorById) {
|
||||
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');
|
||||
if (searchInput) {
|
||||
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
|
||||
document.querySelectorAll('.category-tab').forEach(tab => {
|
||||
tab.addEventListener('click', (e) => {
|
||||
@ -417,6 +435,16 @@ function setupEventListeners() {
|
||||
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
|
||||
@ -434,7 +462,7 @@ window.clearAllIndicators = function() {
|
||||
activeIndicators = [];
|
||||
configuringId = null;
|
||||
renderIndicatorPanel();
|
||||
drawIndicatorsOnChart();
|
||||
drawIndicatorsOnChart();
|
||||
};
|
||||
|
||||
function addIndicator(type) {
|
||||
@ -469,7 +497,7 @@ function addIndicator(type) {
|
||||
configuringId = id;
|
||||
renderIndicatorPanel();
|
||||
drawIndicatorsOnChart();
|
||||
};
|
||||
}
|
||||
|
||||
window.toggleIndicatorExpand = function(id) {
|
||||
configuringId = configuringId === id ? null : id;
|
||||
@ -492,7 +520,10 @@ window.toggleIndicatorVisibility = function(id) {
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (idx >= 0) {
|
||||
@ -536,9 +567,9 @@ window.resetIndicator = function(id) {
|
||||
if (!IndicatorClass) return;
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
@ -546,7 +577,12 @@ window.resetIndicator = function(id) {
|
||||
drawIndicatorsOnChart();
|
||||
};
|
||||
|
||||
function removeIndicatorById(id) {
|
||||
window.removeIndicator = function() {
|
||||
if (!configuringId) return;
|
||||
removeIndicatorById(configuringId);
|
||||
};
|
||||
|
||||
window.removeIndicatorById = function(id) {
|
||||
const idx = activeIndicators.findIndex(a => a.id === id);
|
||||
if (idx < 0) return;
|
||||
|
||||
@ -562,7 +598,7 @@ function removeIndicatorById(id) {
|
||||
|
||||
renderIndicatorPanel();
|
||||
drawIndicatorsOnChart();
|
||||
}
|
||||
};
|
||||
|
||||
function removeIndicatorByIndex(index) {
|
||||
if (index < 0 || index >= activeIndicators.length) return;
|
||||
@ -571,8 +607,8 @@ function removeIndicatorByIndex(index) {
|
||||
|
||||
// Presets
|
||||
function getPresetsForIndicator(indicatorName) {
|
||||
const allPresets = Object.values(userPresets).flat().filter(p => typeof p === 'object' && p.name);
|
||||
return allPresets.filter(p => p.indicatorName === indicatorName);
|
||||
if (!userPresets || !userPresets.presets) return [];
|
||||
return userPresets.presets.filter(p => p.indicatorName === indicatorName);
|
||||
}
|
||||
|
||||
window.savePreset = function(id) {
|
||||
@ -608,7 +644,7 @@ window.savePreset = function(id) {
|
||||
};
|
||||
|
||||
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);
|
||||
if (!preset) return;
|
||||
|
||||
@ -626,7 +662,7 @@ window.applyPreset = function(id, presetId) {
|
||||
window.deletePreset = function(presetId) {
|
||||
if (!confirm('Delete this preset?')) return;
|
||||
|
||||
if (userPresets.presets) {
|
||||
if (userPresets?.presets) {
|
||||
userPresets.presets = userPresets.presets.filter(p => p.id !== presetId);
|
||||
saveUserPresets();
|
||||
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
|
||||
window.renderIndicatorList = renderIndicatorPanel;
|
||||
window.toggleIndicator = addIndicator;
|
||||
@ -832,20 +874,15 @@ window.showIndicatorConfig = function(id) {
|
||||
window.applyIndicatorConfig = function() {
|
||||
// No-op - config is applied immediately
|
||||
};
|
||||
window.removeIndicator = function() {
|
||||
if (!configuringId) return;
|
||||
removeIndicatorById(configuringId);
|
||||
};
|
||||
|
||||
// Assign to window for backward compatibility
|
||||
window.toggleIndicator = addIndicator;
|
||||
window.addIndicator = addIndicator;
|
||||
window.toggleIndicator = addIndicator;
|
||||
window.removeIndicatorById = removeIndicatorById;
|
||||
window.removeIndicatorByIndex = function(index) {
|
||||
window.removeIndicatorByIndex = removeIndicatorByIndexWindow;
|
||||
const removeIndicatorByIndexWindow = function(index) {
|
||||
if (index < 0 || index >= activeIndicators.length) return;
|
||||
removeIndicatorById(activeIndicators[index].id);
|
||||
};
|
||||
window.removeIndicatorByIndex = removeIndicatorByIndexWindow;
|
||||
window.drawIndicatorsOnChart = drawIndicatorsOnChart;
|
||||
|
||||
// Export functions for module imports
|
||||
export { addIndicator, removeIndicatorById, removeIndicatorByIndex };
|
||||
Reference in New Issue
Block a user