mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 10:28:10 +01:00
Fix: Better memory/tooltip/clean-up managers, various fixes.
This commit is contained in:
@@ -311,7 +311,6 @@ CleanupManager.addListener(document, 'visibilitychange', () => {
|
||||
window.TooltipManager.cleanup();
|
||||
}
|
||||
|
||||
// Run memory optimization
|
||||
if (window.MemoryManager) {
|
||||
MemoryManager.forceCleanup();
|
||||
}
|
||||
@@ -1883,7 +1882,6 @@ function setupMemoryMonitoring() {
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
// Init
|
||||
function initialize() {
|
||||
const filterElements = {
|
||||
stateSelect: document.getElementById('state'),
|
||||
@@ -1901,8 +1899,6 @@ function initialize() {
|
||||
if (filterElements.coinFrom) filterElements.coinFrom.value = 'any';
|
||||
if (filterElements.coinTo) filterElements.coinTo.value = 'any';
|
||||
|
||||
setupMemoryMonitoring();
|
||||
|
||||
setTimeout(() => {
|
||||
WebSocketManager.initialize();
|
||||
setupEventListeners();
|
||||
@@ -1921,12 +1917,6 @@ function initialize() {
|
||||
updateBidsTable();
|
||||
}, 100);
|
||||
|
||||
setInterval(() => {
|
||||
if ((state.data.sent.length + state.data.received.length) > 1000) {
|
||||
optimizeMemoryUsage();
|
||||
}
|
||||
}, 5 * 60 * 1000); // Check every 5 minutes
|
||||
|
||||
window.cleanupBidsTable = cleanup;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const CleanupManager = (function() {
|
||||
|
||||
const state = {
|
||||
eventListeners: [],
|
||||
timeouts: [],
|
||||
intervals: [],
|
||||
animationFrames: [],
|
||||
resources: new Map(),
|
||||
debug: false
|
||||
debug: false,
|
||||
memoryOptimizationInterval: null
|
||||
};
|
||||
|
||||
function log(message, ...args) {
|
||||
@@ -232,6 +232,229 @@ const CleanupManager = (function() {
|
||||
};
|
||||
},
|
||||
|
||||
setupMemoryOptimization: function(options = {}) {
|
||||
const memoryCheckInterval = options.interval || 2 * 60 * 1000; // Default: 2 minutes
|
||||
const maxCacheSize = options.maxCacheSize || 100;
|
||||
const maxDataSize = options.maxDataSize || 1000;
|
||||
|
||||
if (state.memoryOptimizationInterval) {
|
||||
this.clearInterval(state.memoryOptimizationInterval);
|
||||
}
|
||||
|
||||
this.addListener(document, 'visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
log('Tab hidden - running memory optimization');
|
||||
this.optimizeMemory({
|
||||
maxCacheSize: maxCacheSize,
|
||||
maxDataSize: maxDataSize
|
||||
});
|
||||
} else if (window.TooltipManager) {
|
||||
window.TooltipManager.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
state.memoryOptimizationInterval = this.setInterval(() => {
|
||||
if (document.hidden) {
|
||||
log('Periodic memory optimization');
|
||||
this.optimizeMemory({
|
||||
maxCacheSize: maxCacheSize,
|
||||
maxDataSize: maxDataSize
|
||||
});
|
||||
}
|
||||
}, memoryCheckInterval);
|
||||
|
||||
log('Memory optimization setup complete');
|
||||
return state.memoryOptimizationInterval;
|
||||
},
|
||||
|
||||
optimizeMemory: function(options = {}) {
|
||||
log('Running memory optimization');
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.cleanup === 'function') {
|
||||
window.TooltipManager.cleanup();
|
||||
}
|
||||
|
||||
if (window.IdentityManager && typeof window.IdentityManager.limitCacheSize === 'function') {
|
||||
window.IdentityManager.limitCacheSize(options.maxCacheSize || 100);
|
||||
}
|
||||
|
||||
this.cleanupOrphanedResources();
|
||||
|
||||
if (window.gc) {
|
||||
try {
|
||||
window.gc();
|
||||
log('Forced garbage collection');
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent('memoryOptimized', {
|
||||
detail: {
|
||||
timestamp: Date.now(),
|
||||
maxDataSize: options.maxDataSize || 1000
|
||||
}
|
||||
}));
|
||||
|
||||
log('Memory optimization complete');
|
||||
},
|
||||
|
||||
cleanupOrphanedResources: function() {
|
||||
let removedListeners = 0;
|
||||
const validListeners = [];
|
||||
|
||||
for (let i = 0; i < state.eventListeners.length; i++) {
|
||||
const listener = state.eventListeners[i];
|
||||
if (!listener.element) {
|
||||
removedListeners++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const isDetached = !(listener.element instanceof Node) ||
|
||||
!document.body.contains(listener.element) ||
|
||||
(listener.element.classList && listener.element.classList.contains('hidden')) ||
|
||||
(listener.element.style && listener.element.style.display === 'none');
|
||||
|
||||
if (isDetached) {
|
||||
try {
|
||||
if (listener.element instanceof Node) {
|
||||
listener.element.removeEventListener(listener.type, listener.handler, listener.options);
|
||||
}
|
||||
removedListeners++;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
} else {
|
||||
validListeners.push(listener);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
log(`Error checking listener (removing): ${e.message}`);
|
||||
removedListeners++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedListeners > 0) {
|
||||
state.eventListeners = validListeners;
|
||||
log(`Removed ${removedListeners} event listeners for detached/hidden elements`);
|
||||
}
|
||||
|
||||
let removedResources = 0;
|
||||
const resourcesForRemoval = [];
|
||||
|
||||
state.resources.forEach((info, id) => {
|
||||
const resource = info.resource;
|
||||
|
||||
try {
|
||||
|
||||
if (resource instanceof Element && !document.body.contains(resource)) {
|
||||
resourcesForRemoval.push(id);
|
||||
}
|
||||
|
||||
if (resource && resource.element) {
|
||||
|
||||
if (resource.element instanceof Node && !document.body.contains(resource.element)) {
|
||||
resourcesForRemoval.push(id);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(`Error checking resource ${id}: ${e.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
resourcesForRemoval.forEach(id => {
|
||||
this.unregisterResource(id);
|
||||
removedResources++;
|
||||
});
|
||||
|
||||
if (removedResources > 0) {
|
||||
log(`Removed ${removedResources} orphaned resources`);
|
||||
}
|
||||
|
||||
if (window.TooltipManager) {
|
||||
if (typeof window.TooltipManager.cleanupOrphanedTooltips === 'function') {
|
||||
try {
|
||||
window.TooltipManager.cleanupOrphanedTooltips();
|
||||
} catch (e) {
|
||||
|
||||
if (typeof window.TooltipManager.cleanup === 'function') {
|
||||
try {
|
||||
window.TooltipManager.cleanup();
|
||||
} catch (err) {
|
||||
log(`Error cleaning up tooltips: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof window.TooltipManager.cleanup === 'function') {
|
||||
try {
|
||||
window.TooltipManager.cleanup();
|
||||
} catch (e) {
|
||||
log(`Error cleaning up tooltips: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.cleanupTooltipDOM();
|
||||
} catch (e) {
|
||||
log(`Error in cleanupTooltipDOM: ${e.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
cleanupTooltipDOM: function() {
|
||||
let removedElements = 0;
|
||||
|
||||
try {
|
||||
|
||||
const tooltipSelectors = [
|
||||
'[role="tooltip"]',
|
||||
'[id^="tooltip-"]',
|
||||
'.tippy-box',
|
||||
'[data-tippy-root]'
|
||||
];
|
||||
|
||||
tooltipSelectors.forEach(selector => {
|
||||
try {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
|
||||
elements.forEach(element => {
|
||||
try {
|
||||
|
||||
if (!(element instanceof Element)) return;
|
||||
|
||||
const isDetached = !element.parentElement ||
|
||||
!document.body.contains(element.parentElement) ||
|
||||
element.classList.contains('hidden') ||
|
||||
element.style.display === 'none' ||
|
||||
element.style.visibility === 'hidden';
|
||||
|
||||
if (isDetached) {
|
||||
try {
|
||||
element.remove();
|
||||
removedElements++;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
log(`Error querying for ${selector}: ${err.message}`);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
log(`Error in tooltip DOM cleanup: ${e.message}`);
|
||||
}
|
||||
|
||||
if (removedElements > 0) {
|
||||
log(`Removed ${removedElements} detached tooltip elements`);
|
||||
}
|
||||
},
|
||||
|
||||
setDebugMode: function(enabled) {
|
||||
state.debug = Boolean(enabled);
|
||||
log(`Debug mode ${state.debug ? 'enabled' : 'disabled'}`);
|
||||
@@ -247,6 +470,17 @@ const CleanupManager = (function() {
|
||||
if (options.debug !== undefined) {
|
||||
this.setDebugMode(options.debug);
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && !options.noAutoCleanup) {
|
||||
this.addListener(window, 'beforeunload', () => {
|
||||
this.clearAll();
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && !options.noMemoryOptimization) {
|
||||
this.setupMemoryOptimization(options.memoryOptions || {});
|
||||
}
|
||||
|
||||
log('CleanupManager initialized');
|
||||
return this;
|
||||
}
|
||||
@@ -255,16 +489,20 @@ const CleanupManager = (function() {
|
||||
return publicAPI;
|
||||
})();
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = CleanupManager;
|
||||
}
|
||||
|
||||
window.CleanupManager = CleanupManager;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.CleanupManager = CleanupManager;
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!window.cleanupManagerInitialized) {
|
||||
CleanupManager.initialize();
|
||||
window.cleanupManagerInitialized = true;
|
||||
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
CleanupManager.initialize({ debug: false });
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
CleanupManager.initialize({ debug: false });
|
||||
}, { once: true });
|
||||
}
|
||||
});
|
||||
|
||||
//console.log('CleanupManager initialized with methods:', Object.keys(CleanupManager));
|
||||
console.log('CleanupManager initialized');
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,7 @@ const PriceManager = (function() {
|
||||
return fetchPromise;
|
||||
}
|
||||
|
||||
console.log('PriceManager: Fetching latest prices.');
|
||||
//console.log('PriceManager: Fetching latest prices.');
|
||||
lastFetchTime = Date.now();
|
||||
fetchPromise = this.fetchPrices()
|
||||
.then(prices => {
|
||||
@@ -89,7 +89,7 @@ const PriceManager = (function() {
|
||||
? window.config.coins.map(c => c.symbol).filter(symbol => symbol && symbol.trim() !== '')
|
||||
: ['BTC', 'XMR', 'PART', 'BCH', 'PIVX', 'FIRO', 'DASH', 'LTC', 'DOGE', 'DCR', 'NMC', 'WOW']);
|
||||
|
||||
console.log('PriceManager: lookupFiatRates ' + coinSymbols.join(', '));
|
||||
//console.log('PriceManager: lookupFiatRates ' + coinSymbols.join(', '));
|
||||
|
||||
if (!coinSymbols.length) {
|
||||
throw new Error('No valid coins configured');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const TooltipManager = (function() {
|
||||
let instance = null;
|
||||
|
||||
const tooltipInstanceMap = new WeakMap();
|
||||
|
||||
class TooltipManagerImpl {
|
||||
@@ -8,20 +7,22 @@ const TooltipManager = (function() {
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
this.pendingAnimationFrames = new Set();
|
||||
this.pendingTimeouts = new Set();
|
||||
|
||||
this.tooltipIdCounter = 0;
|
||||
this.maxTooltips = 200;
|
||||
this.cleanupThreshold = 1.2;
|
||||
this.disconnectedCheckInterval = null;
|
||||
this.cleanupInterval = null;
|
||||
this.mutationObserver = null;
|
||||
this.debug = false;
|
||||
this.tooltipData = new WeakMap();
|
||||
this.setupStyles();
|
||||
this.setupMutationObserver();
|
||||
this.startPeriodicCleanup();
|
||||
this.startDisconnectedElementsCheck();
|
||||
this.resources = {};
|
||||
|
||||
if (window.CleanupManager) {
|
||||
CleanupManager.registerResource(
|
||||
'tooltipManager',
|
||||
this,
|
||||
(manager) => manager.dispose()
|
||||
);
|
||||
}
|
||||
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@@ -47,9 +48,7 @@ const TooltipManager = (function() {
|
||||
this.performPeriodicCleanup(true);
|
||||
}
|
||||
|
||||
const rafId = requestAnimationFrame(() => {
|
||||
this.pendingAnimationFrames.delete(rafId);
|
||||
|
||||
const createTooltip = () => {
|
||||
if (!document.body.contains(element)) return;
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
@@ -67,30 +66,29 @@ const TooltipManager = (function() {
|
||||
}
|
||||
} else {
|
||||
retryCount++;
|
||||
const timeoutId = setTimeout(() => {
|
||||
this.pendingTimeouts.delete(timeoutId);
|
||||
const newRafId = requestAnimationFrame(retryCreate);
|
||||
this.pendingAnimationFrames.add(newRafId);
|
||||
CleanupManager.setTimeout(() => {
|
||||
CleanupManager.requestAnimationFrame(retryCreate);
|
||||
}, 100);
|
||||
this.pendingTimeouts.add(timeoutId);
|
||||
}
|
||||
};
|
||||
|
||||
const initialTimeoutId = setTimeout(() => {
|
||||
this.pendingTimeouts.delete(initialTimeoutId);
|
||||
const retryRafId = requestAnimationFrame(retryCreate);
|
||||
this.pendingAnimationFrames.add(retryRafId);
|
||||
CleanupManager.setTimeout(() => {
|
||||
CleanupManager.requestAnimationFrame(retryCreate);
|
||||
}, 100);
|
||||
this.pendingTimeouts.add(initialTimeoutId);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.pendingAnimationFrames.add(rafId);
|
||||
CleanupManager.requestAnimationFrame(createTooltip);
|
||||
return null;
|
||||
}
|
||||
|
||||
createTooltipInstance(element, content, options = {}) {
|
||||
if (!element || !document.body.contains(element) || !window.tippy) {
|
||||
if (!element || !document.body.contains(element)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof window.tippy !== 'function') {
|
||||
console.error('Tippy.js is not available.');
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -130,7 +128,7 @@ const TooltipManager = (function() {
|
||||
},
|
||||
onHidden(instance) {
|
||||
if (!document.body.contains(element)) {
|
||||
setTimeout(() => {
|
||||
CleanupManager.setTimeout(() => {
|
||||
if (instance && instance.destroy) {
|
||||
instance.destroy();
|
||||
}
|
||||
@@ -168,9 +166,26 @@ const TooltipManager = (function() {
|
||||
});
|
||||
|
||||
element.setAttribute('data-tooltip-trigger-id', tooltipId);
|
||||
|
||||
tooltipInstanceMap.set(element, tippyInstance[0]);
|
||||
|
||||
const resourceId = CleanupManager.registerResource(
|
||||
'tooltip',
|
||||
{ element, instance: tippyInstance[0] },
|
||||
(resource) => {
|
||||
try {
|
||||
if (resource.instance && resource.instance.destroy) {
|
||||
resource.instance.destroy();
|
||||
}
|
||||
if (resource.element) {
|
||||
resource.element.removeAttribute('data-tooltip-trigger-id');
|
||||
resource.element.removeAttribute('aria-describedby');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error destroying tooltip during cleanup:', e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return tippyInstance[0];
|
||||
}
|
||||
|
||||
@@ -214,181 +229,292 @@ const TooltipManager = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
getActiveTooltipInstances() {
|
||||
const result = [];
|
||||
try {
|
||||
document.querySelectorAll('[data-tooltip-trigger-id]').forEach(element => {
|
||||
const instance = element._tippy ? [element._tippy] : null;
|
||||
if (instance) {
|
||||
result.push([element, instance]);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error getting active tooltip instances:', error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.log('Running tooltip cleanup');
|
||||
|
||||
this.pendingAnimationFrames.forEach(id => {
|
||||
cancelAnimationFrame(id);
|
||||
});
|
||||
this.pendingAnimationFrames.clear();
|
||||
|
||||
this.pendingTimeouts.forEach(id => {
|
||||
clearTimeout(id);
|
||||
});
|
||||
this.pendingTimeouts.clear();
|
||||
|
||||
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
|
||||
const batchSize = 20;
|
||||
|
||||
const processElementsBatch = (startIdx) => {
|
||||
const endIdx = Math.min(startIdx + batchSize, elements.length);
|
||||
|
||||
for (let i = startIdx; i < endIdx; i++) {
|
||||
this.destroy(elements[i]);
|
||||
try {
|
||||
if ((window.location.pathname.includes('/offers') || window.location.pathname.includes('/bids')) &&
|
||||
(document.querySelector('[data-tippy-root]:hover') || document.querySelector('[data-tooltip-trigger-id]:hover'))) {
|
||||
console.log('Skipping tooltip cleanup - tooltip is being hovered');
|
||||
return;
|
||||
}
|
||||
|
||||
if (endIdx < elements.length) {
|
||||
const rafId = requestAnimationFrame(() => {
|
||||
this.pendingAnimationFrames.delete(rafId);
|
||||
processElementsBatch(endIdx);
|
||||
});
|
||||
this.pendingAnimationFrames.add(rafId);
|
||||
const elements = document.querySelectorAll('[data-tooltip-trigger-id]:not(:hover)');
|
||||
const batchSize = 20;
|
||||
|
||||
const processElementsBatch = (startIdx) => {
|
||||
const endIdx = Math.min(startIdx + batchSize, elements.length);
|
||||
|
||||
for (let i = startIdx; i < endIdx; i++) {
|
||||
this.destroy(elements[i]);
|
||||
}
|
||||
|
||||
if (endIdx < elements.length) {
|
||||
CleanupManager.requestAnimationFrame(() => {
|
||||
processElementsBatch(endIdx);
|
||||
});
|
||||
} else {
|
||||
this.cleanupOrphanedTooltips();
|
||||
}
|
||||
};
|
||||
|
||||
if (elements.length > 0) {
|
||||
processElementsBatch(0);
|
||||
} else {
|
||||
this.cleanupOrphanedTooltips();
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error during cleanup:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.length > 0) {
|
||||
processElementsBatch(0);
|
||||
} else {
|
||||
this.cleanupOrphanedTooltips();
|
||||
thoroughCleanup() {
|
||||
this.log('Running thorough tooltip cleanup');
|
||||
|
||||
try {
|
||||
this.cleanup();
|
||||
this.cleanupAllTooltips();
|
||||
this.log('Thorough tooltip cleanup completed');
|
||||
} catch (error) {
|
||||
console.error('Error in thorough tooltip cleanup:', error);
|
||||
}
|
||||
}
|
||||
|
||||
cleanupAllTooltips() {
|
||||
this.log('Cleaning up all tooltips');
|
||||
|
||||
try {
|
||||
if ((window.location.pathname.includes('/offers') || window.location.pathname.includes('/bids')) &&
|
||||
document.querySelector('#offers-body tr:hover')) {
|
||||
this.log('Skipping all tooltips cleanup on offers/bids page with row hover');
|
||||
return;
|
||||
}
|
||||
|
||||
const tooltipRoots = document.querySelectorAll('[data-tippy-root]');
|
||||
const tooltipTriggers = document.querySelectorAll('[data-tooltip-trigger-id]');
|
||||
const tooltipElements = document.querySelectorAll('.tooltip');
|
||||
|
||||
const isHovered = element => {
|
||||
try {
|
||||
return element.matches && element.matches(':hover');
|
||||
} catch (e) {
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
tooltipRoots.forEach(root => {
|
||||
if (!isHovered(root) && root.parentNode) {
|
||||
root.parentNode.removeChild(root);
|
||||
}
|
||||
});
|
||||
|
||||
tooltipTriggers.forEach(trigger => {
|
||||
if (!isHovered(trigger)) {
|
||||
trigger.removeAttribute('data-tooltip-trigger-id');
|
||||
trigger.removeAttribute('aria-describedby');
|
||||
|
||||
if (trigger._tippy) {
|
||||
try {
|
||||
trigger._tippy.destroy();
|
||||
trigger._tippy = null;
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tooltipElements.forEach(tooltip => {
|
||||
if (!isHovered(tooltip) && tooltip.parentNode) {
|
||||
let closestHoveredRow = false;
|
||||
|
||||
try {
|
||||
if (tooltip.closest && tooltip.closest('tr') && isHovered(tooltip.closest('tr'))) {
|
||||
closestHoveredRow = true;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (!closestHoveredRow) {
|
||||
const style = window.getComputedStyle(tooltip);
|
||||
const isVisible = style.display !== 'none' &&
|
||||
style.visibility !== 'hidden' &&
|
||||
style.opacity !== '0';
|
||||
|
||||
if (!isVisible) {
|
||||
tooltip.parentNode.removeChild(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error cleaning up all tooltips:', error);
|
||||
}
|
||||
}
|
||||
|
||||
cleanupOrphanedTooltips() {
|
||||
const tippyElements = document.querySelectorAll('[data-tippy-root]');
|
||||
let removed = 0;
|
||||
try {
|
||||
const tippyElements = document.querySelectorAll('[data-tippy-root]');
|
||||
let removed = 0;
|
||||
|
||||
tippyElements.forEach(element => {
|
||||
const tooltipId = element.getAttribute('data-for-tooltip-id');
|
||||
const trigger = tooltipId ?
|
||||
document.querySelector(`[data-tooltip-trigger-id="${tooltipId}"]`) :
|
||||
null;
|
||||
tippyElements.forEach(element => {
|
||||
const tooltipId = element.getAttribute('data-for-tooltip-id');
|
||||
const trigger = tooltipId ?
|
||||
document.querySelector(`[data-tooltip-trigger-id="${tooltipId}"]`) :
|
||||
null;
|
||||
|
||||
if (!trigger || !document.body.contains(trigger)) {
|
||||
if (element.parentNode) {
|
||||
element.parentNode.removeChild(element);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (removed > 0) {
|
||||
this.log(`Removed ${removed} orphaned tooltip elements`);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
setupMutationObserver() {
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
|
||||
this.mutationObserver = new MutationObserver(mutations => {
|
||||
let needsCleanup = false;
|
||||
|
||||
mutations.forEach(mutation => {
|
||||
if (mutation.removedNodes.length) {
|
||||
Array.from(mutation.removedNodes).forEach(node => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (node.hasAttribute && node.hasAttribute('data-tooltip-trigger-id')) {
|
||||
this.destroy(node);
|
||||
needsCleanup = true;
|
||||
}
|
||||
|
||||
if (node.querySelectorAll) {
|
||||
const tooltipTriggers = node.querySelectorAll('[data-tooltip-trigger-id]');
|
||||
if (tooltipTriggers.length > 0) {
|
||||
tooltipTriggers.forEach(trigger => {
|
||||
this.destroy(trigger);
|
||||
});
|
||||
needsCleanup = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!trigger || !document.body.contains(trigger)) {
|
||||
if (element.parentNode) {
|
||||
element.parentNode.removeChild(element);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (needsCleanup) {
|
||||
this.cleanupOrphanedTooltips();
|
||||
if (removed > 0) {
|
||||
this.log(`Removed ${removed} orphaned tooltip elements`);
|
||||
}
|
||||
});
|
||||
|
||||
this.mutationObserver.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
return removed;
|
||||
} catch (error) {
|
||||
console.error('Error cleaning up orphaned tooltips:', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return this.mutationObserver;
|
||||
setupMutationObserver() {
|
||||
try {
|
||||
const mutationObserver = new MutationObserver(mutations => {
|
||||
let needsCleanup = false;
|
||||
|
||||
mutations.forEach(mutation => {
|
||||
if (mutation.removedNodes.length) {
|
||||
Array.from(mutation.removedNodes).forEach(node => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (node.hasAttribute && node.hasAttribute('data-tooltip-trigger-id')) {
|
||||
this.destroy(node);
|
||||
needsCleanup = true;
|
||||
}
|
||||
|
||||
if (node.querySelectorAll) {
|
||||
const tooltipTriggers = node.querySelectorAll('[data-tooltip-trigger-id]');
|
||||
if (tooltipTriggers.length > 0) {
|
||||
tooltipTriggers.forEach(trigger => {
|
||||
this.destroy(trigger);
|
||||
});
|
||||
needsCleanup = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (needsCleanup) {
|
||||
this.cleanupOrphanedTooltips();
|
||||
}
|
||||
});
|
||||
|
||||
mutationObserver.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
this.resources.mutationObserver = CleanupManager.registerResource(
|
||||
'mutationObserver',
|
||||
mutationObserver,
|
||||
(observer) => observer.disconnect()
|
||||
);
|
||||
|
||||
return mutationObserver;
|
||||
} catch (error) {
|
||||
console.error('Error setting up mutation observer:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
startDisconnectedElementsCheck() {
|
||||
if (this.disconnectedCheckInterval) {
|
||||
clearInterval(this.disconnectedCheckInterval);
|
||||
try {
|
||||
this.resources.disconnectedCheckInterval = CleanupManager.setInterval(() => {
|
||||
this.checkForDisconnectedElements();
|
||||
}, 60000);
|
||||
} catch (error) {
|
||||
console.error('Error starting disconnected elements check:', error);
|
||||
}
|
||||
|
||||
this.disconnectedCheckInterval = setInterval(() => {
|
||||
this.checkForDisconnectedElements();
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
checkForDisconnectedElements() {
|
||||
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
|
||||
let removedCount = 0;
|
||||
try {
|
||||
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
|
||||
let removedCount = 0;
|
||||
|
||||
elements.forEach(element => {
|
||||
if (!document.body.contains(element)) {
|
||||
this.destroy(element);
|
||||
removedCount++;
|
||||
elements.forEach(element => {
|
||||
if (!document.body.contains(element)) {
|
||||
this.destroy(element);
|
||||
removedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.log(`Removed ${removedCount} tooltips for disconnected elements`);
|
||||
this.cleanupOrphanedTooltips();
|
||||
}
|
||||
});
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.log(`Removed ${removedCount} tooltips for disconnected elements`);
|
||||
this.cleanupOrphanedTooltips();
|
||||
} catch (error) {
|
||||
console.error('Error checking for disconnected elements:', error);
|
||||
}
|
||||
}
|
||||
|
||||
startPeriodicCleanup() {
|
||||
if (this.cleanupInterval) {
|
||||
clearInterval(this.cleanupInterval);
|
||||
try {
|
||||
this.resources.cleanupInterval = CleanupManager.setInterval(() => {
|
||||
this.performPeriodicCleanup();
|
||||
}, 120000);
|
||||
} catch (error) {
|
||||
console.error('Error starting periodic cleanup:', error);
|
||||
}
|
||||
|
||||
this.cleanupInterval = setInterval(() => {
|
||||
this.performPeriodicCleanup();
|
||||
}, 120000);
|
||||
}
|
||||
|
||||
performPeriodicCleanup(force = false) {
|
||||
this.cleanupOrphanedTooltips();
|
||||
|
||||
this.checkForDisconnectedElements();
|
||||
|
||||
const tooltipCount = document.querySelectorAll('[data-tippy-root]').length;
|
||||
|
||||
if (force || tooltipCount > this.maxTooltips) {
|
||||
this.log(`Performing aggressive cleanup (${tooltipCount} tooltips)`);
|
||||
|
||||
this.cleanup();
|
||||
|
||||
if (window.gc) {
|
||||
window.gc();
|
||||
} else {
|
||||
const arr = new Array(1000);
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
arr[i] = new Array(10000).join('x');
|
||||
}
|
||||
try {
|
||||
if ((window.location.pathname.includes('/offers') || window.location.pathname.includes('/bids')) &&
|
||||
!force) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cleanupOrphanedTooltips();
|
||||
this.checkForDisconnectedElements();
|
||||
|
||||
const tooltipCount = document.querySelectorAll('[data-tippy-root]').length;
|
||||
|
||||
if (force || tooltipCount > this.maxTooltips) {
|
||||
this.log(`Performing aggressive cleanup (${tooltipCount} tooltips)`);
|
||||
this.cleanup();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error performing periodic cleanup:', error);
|
||||
}
|
||||
}
|
||||
|
||||
setupStyles() {
|
||||
if (document.getElementById('tooltip-styles')) return;
|
||||
|
||||
document.head.insertAdjacentHTML('beforeend', `
|
||||
<style id="tooltip-styles">
|
||||
try {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'tooltip-styles';
|
||||
style.textContent = `
|
||||
[data-tippy-root] {
|
||||
position: fixed !important;
|
||||
z-index: 9999 !important;
|
||||
@@ -472,62 +598,166 @@ const TooltipManager = (function() {
|
||||
.tippy-box[data-placement^='right'] > .tippy-arrow {
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
this.resources.tooltipStyles = CleanupManager.registerResource(
|
||||
'tooltipStyles',
|
||||
style,
|
||||
(styleElement) => {
|
||||
if (styleElement && styleElement.parentNode) {
|
||||
styleElement.parentNode.removeChild(styleElement);
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error setting up styles:', error);
|
||||
try {
|
||||
document.head.insertAdjacentHTML('beforeend', `
|
||||
<style id="tooltip-styles">
|
||||
[data-tippy-root] {
|
||||
position: fixed !important;
|
||||
z-index: 9999 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.tippy-box {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 500;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
position: relative !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
.tippy-content {
|
||||
padding: 0.5rem 0.75rem !important;
|
||||
}
|
||||
|
||||
.tippy-box .bg-gray-400 {
|
||||
background-color: rgb(156 163 175);
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
.tippy-box:has(.bg-gray-400) .tippy-arrow {
|
||||
color: rgb(156 163 175);
|
||||
}
|
||||
|
||||
.tippy-box .bg-red-500 {
|
||||
background-color: rgb(239 68 68);
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
.tippy-box:has(.bg-red-500) .tippy-arrow {
|
||||
color: rgb(239 68 68);
|
||||
}
|
||||
|
||||
.tippy-box .bg-gray-300 {
|
||||
background-color: rgb(209 213 219);
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
.tippy-box:has(.bg-gray-300) .tippy-arrow {
|
||||
color: rgb(209 213 219);
|
||||
}
|
||||
|
||||
.tippy-box .bg-green-700 {
|
||||
background-color: rgb(21 128 61);
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
.tippy-box:has(.bg-green-700) .tippy-arrow {
|
||||
color: rgb(21 128 61);
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='top'] > .tippy-arrow::before {
|
||||
border-top-color: currentColor;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='bottom'] > .tippy-arrow::before {
|
||||
border-bottom-color: currentColor;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='left'] > .tippy-arrow::before {
|
||||
border-left-color: currentColor;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='right'] > .tippy-arrow::before {
|
||||
border-right-color: currentColor;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='top'] > .tippy-arrow {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='bottom'] > .tippy-arrow {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='left'] > .tippy-arrow {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='right'] > .tippy-arrow {
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
|
||||
const styleElement = document.getElementById('tooltip-styles');
|
||||
if (styleElement) {
|
||||
this.resources.tooltipStyles = CleanupManager.registerResource(
|
||||
'tooltipStyles',
|
||||
styleElement,
|
||||
(elem) => {
|
||||
if (elem && elem.parentNode) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to add tooltip styles:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializeTooltips(selector = '[data-tooltip-target]') {
|
||||
document.querySelectorAll(selector).forEach(element => {
|
||||
const targetId = element.getAttribute('data-tooltip-target');
|
||||
if (!targetId) return;
|
||||
try {
|
||||
document.querySelectorAll(selector).forEach(element => {
|
||||
const targetId = element.getAttribute('data-tooltip-target');
|
||||
if (!targetId) return;
|
||||
|
||||
const tooltipContent = document.getElementById(targetId);
|
||||
const tooltipContent = document.getElementById(targetId);
|
||||
|
||||
if (tooltipContent) {
|
||||
this.create(element, tooltipContent.innerHTML, {
|
||||
placement: element.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
}
|
||||
});
|
||||
if (tooltipContent) {
|
||||
this.create(element, tooltipContent.innerHTML, {
|
||||
placement: element.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error initializing tooltips:', error);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.log('Disposing TooltipManager');
|
||||
|
||||
this.cleanup();
|
||||
try {
|
||||
this.cleanup();
|
||||
|
||||
this.pendingAnimationFrames.forEach(id => {
|
||||
cancelAnimationFrame(id);
|
||||
});
|
||||
this.pendingAnimationFrames.clear();
|
||||
Object.values(this.resources).forEach(resourceId => {
|
||||
if (resourceId) {
|
||||
CleanupManager.unregisterResource(resourceId);
|
||||
}
|
||||
});
|
||||
|
||||
this.pendingTimeouts.forEach(id => {
|
||||
clearTimeout(id);
|
||||
});
|
||||
this.pendingTimeouts.clear();
|
||||
this.resources = {};
|
||||
|
||||
if (this.disconnectedCheckInterval) {
|
||||
clearInterval(this.disconnectedCheckInterval);
|
||||
this.disconnectedCheckInterval = null;
|
||||
instance = null;
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error disposing TooltipManager:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cleanupInterval) {
|
||||
clearInterval(this.cleanupInterval);
|
||||
this.cleanupInterval = null;
|
||||
}
|
||||
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.disconnect();
|
||||
this.mutationObserver = null;
|
||||
}
|
||||
|
||||
const styleElement = document.getElementById('tooltip-styles');
|
||||
if (styleElement && styleElement.parentNode) {
|
||||
styleElement.parentNode.removeChild(styleElement);
|
||||
}
|
||||
|
||||
instance = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
setDebugMode(enabled) {
|
||||
@@ -536,16 +766,26 @@ const TooltipManager = (function() {
|
||||
}
|
||||
|
||||
initialize(options = {}) {
|
||||
if (options.maxTooltips) {
|
||||
this.maxTooltips = options.maxTooltips;
|
||||
}
|
||||
try {
|
||||
if (options.maxTooltips) {
|
||||
this.maxTooltips = options.maxTooltips;
|
||||
}
|
||||
|
||||
if (options.debug !== undefined) {
|
||||
this.setDebugMode(options.debug);
|
||||
}
|
||||
if (options.debug !== undefined) {
|
||||
this.setDebugMode(options.debug);
|
||||
}
|
||||
|
||||
this.log('TooltipManager initialized');
|
||||
return this;
|
||||
this.setupStyles();
|
||||
this.setupMutationObserver();
|
||||
this.startPeriodicCleanup();
|
||||
this.startDisconnectedElementsCheck();
|
||||
|
||||
this.log('TooltipManager initialized');
|
||||
return this;
|
||||
} catch (error) {
|
||||
console.error('Error initializing TooltipManager:', error);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,6 +820,11 @@ const TooltipManager = (function() {
|
||||
return manager.cleanup(...args);
|
||||
},
|
||||
|
||||
thoroughCleanup: function() {
|
||||
const manager = this.getInstance();
|
||||
return manager.thoroughCleanup();
|
||||
},
|
||||
|
||||
initializeTooltips: function(...args) {
|
||||
const manager = this.getInstance();
|
||||
return manager.initializeTooltips(...args);
|
||||
@@ -590,6 +835,11 @@ const TooltipManager = (function() {
|
||||
return manager.setDebugMode(enabled);
|
||||
},
|
||||
|
||||
getActiveTooltipInstances: function() {
|
||||
const manager = this.getInstance();
|
||||
return manager.getActiveTooltipInstances();
|
||||
},
|
||||
|
||||
dispose: function(...args) {
|
||||
const manager = this.getInstance();
|
||||
return manager.dispose(...args);
|
||||
@@ -597,37 +847,53 @@ const TooltipManager = (function() {
|
||||
};
|
||||
})();
|
||||
|
||||
function installTooltipManager() {
|
||||
const originalTooltipManager = window.TooltipManager;
|
||||
|
||||
window.TooltipManager = TooltipManager;
|
||||
|
||||
window.TooltipManager.initialize({
|
||||
maxTooltips: 200,
|
||||
debug: false
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!window.tooltipManagerInitialized) {
|
||||
window.TooltipManager.initializeTooltips();
|
||||
window.tooltipManagerInitialized = true;
|
||||
}
|
||||
});
|
||||
|
||||
return originalTooltipManager;
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
installTooltipManager();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', installTooltipManager);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = TooltipManager;
|
||||
}
|
||||
|
||||
window.TooltipManager = TooltipManager;
|
||||
console.log('TooltipManager initialized');
|
||||
if (typeof window !== 'undefined') {
|
||||
window.TooltipManager = TooltipManager;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
||||
function initializeTooltipManager() {
|
||||
if (!window.tooltipManagerInitialized) {
|
||||
|
||||
if (!window.CleanupManager) {
|
||||
console.warn('CleanupManager not found. TooltipManager will run with limited functionality.');
|
||||
|
||||
window.CleanupManager = window.CleanupManager || {
|
||||
registerResource: (type, resource, cleanup) => {
|
||||
return Math.random().toString(36).substring(2, 9);
|
||||
},
|
||||
unregisterResource: () => {},
|
||||
setTimeout: (callback, delay) => setTimeout(callback, delay),
|
||||
setInterval: (callback, delay) => setInterval(callback, delay),
|
||||
requestAnimationFrame: (callback) => requestAnimationFrame(callback),
|
||||
addListener: (element, type, handler, options) => {
|
||||
element.addEventListener(type, handler, options);
|
||||
return handler;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
window.TooltipManager.initialize({
|
||||
maxTooltips: 200,
|
||||
debug: false
|
||||
});
|
||||
|
||||
window.TooltipManager.initializeTooltips();
|
||||
window.tooltipManagerInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
initializeTooltipManager();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', initializeTooltipManager, { once: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && typeof console !== 'undefined') {
|
||||
console.log('TooltipManager initialized');
|
||||
}
|
||||
|
||||
@@ -1370,7 +1370,6 @@ function createRecipientTooltip(uniqueId, identityInfo, identity, successRate, t
|
||||
return 'text-red-600';
|
||||
};
|
||||
|
||||
|
||||
const truncateText = (text, maxLength) => {
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.substring(0, maxLength) + '...';
|
||||
@@ -2167,6 +2166,18 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
tableRateModule.init();
|
||||
}
|
||||
|
||||
document.addEventListener('memoryOptimized', (e) => {
|
||||
if (jsonData && jsonData.length > e.detail.maxDataSize) {
|
||||
console.log(`Trimming offers data from ${jsonData.length} to ${e.detail.maxDataSize} items`);
|
||||
jsonData = jsonData.slice(0, e.detail.maxDataSize);
|
||||
}
|
||||
|
||||
if (originalJsonData && originalJsonData.length > e.detail.maxDataSize) {
|
||||
console.log(`Trimming original offers data from ${originalJsonData.length} to ${e.detail.maxDataSize} items`);
|
||||
originalJsonData = originalJsonData.slice(0, e.detail.maxDataSize);
|
||||
}
|
||||
});
|
||||
|
||||
await initializeTableAndData();
|
||||
|
||||
if (window.PriceManager) {
|
||||
@@ -2179,7 +2190,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
if (window.WebSocketManager) {
|
||||
WebSocketManager.addMessageHandler('message', async (data) => {
|
||||
if (data.event === 'new_offer' || data.event === 'offer_revoked') {
|
||||
console.log('WebSocket event received:', data.event);
|
||||
//console.log('WebSocket event received:', data.event);
|
||||
try {
|
||||
|
||||
const previousPrices = latestPrices;
|
||||
@@ -2230,7 +2241,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
|
||||
updatePaginationInfo();
|
||||
|
||||
console.log('WebSocket-triggered refresh completed successfully');
|
||||
//console.log('WebSocket-triggered refresh completed successfully');
|
||||
} catch (error) {
|
||||
console.error('Error during WebSocket-triggered refresh:', error);
|
||||
NetworkManager.handleNetworkError(error);
|
||||
@@ -2357,28 +2368,10 @@ function cleanup() {
|
||||
if (window.CleanupManager) {
|
||||
window.CleanupManager.removeListenersByElement(row);
|
||||
}
|
||||
|
||||
while (row.attributes && row.attributes.length > 0) {
|
||||
row.removeAttribute(row.attributes[0].name);
|
||||
}
|
||||
|
||||
while (row.firstChild) {
|
||||
row.removeChild(row.firstChild);
|
||||
}
|
||||
});
|
||||
|
||||
offersBody.innerHTML = '';
|
||||
}
|
||||
|
||||
const filterForm = document.getElementById('filterForm');
|
||||
if (filterForm) {
|
||||
CleanupManager.removeListenersByElement(filterForm);
|
||||
|
||||
filterForm.querySelectorAll('select').forEach(select => {
|
||||
CleanupManager.removeListenersByElement(select);
|
||||
});
|
||||
}
|
||||
|
||||
jsonData = null;
|
||||
originalJsonData = null;
|
||||
latestPrices = null;
|
||||
@@ -2388,13 +2381,12 @@ function cleanup() {
|
||||
}
|
||||
|
||||
if (window.MemoryManager) {
|
||||
if (window.MemoryManager.cleanupTooltips) {
|
||||
window.MemoryManager.cleanupTooltips(true);
|
||||
if (window.MemoryManager.forceCleanup) {
|
||||
window.MemoryManager.forceCleanup();
|
||||
}
|
||||
window.MemoryManager.forceCleanup();
|
||||
}
|
||||
|
||||
console.log('Offers.js cleanup completed');
|
||||
//console.log('Offers.js cleanup completed');
|
||||
} catch (error) {
|
||||
console.error('Error during cleanup:', error);
|
||||
}
|
||||
|
||||
@@ -578,7 +578,7 @@ const chartModule = {
|
||||
this.chartRefs.set(element, chart);
|
||||
},
|
||||
|
||||
destroyChart: function() {
|
||||
destroyChart: function() {
|
||||
if (chartModule.chart) {
|
||||
try {
|
||||
const chartInstance = chartModule.chart;
|
||||
@@ -592,12 +592,17 @@ const chartModule = {
|
||||
|
||||
if (canvas) {
|
||||
chartModule.chartRefs.delete(canvas);
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error destroying chart:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
initChart: function() {
|
||||
this.destroyChart();
|
||||
|
||||
Reference in New Issue
Block a user