Fix: Better memory/tooltip/clean-up managers, various fixes.

This commit is contained in:
gerlofvanek
2025-05-08 21:01:02 +02:00
parent d57a148ff4
commit 868b2475c1
7 changed files with 1266 additions and 856 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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');

View File

@@ -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,20 +229,32 @@ 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();
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;
}
this.pendingTimeouts.forEach(id => {
clearTimeout(id);
});
this.pendingTimeouts.clear();
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
const elements = document.querySelectorAll('[data-tooltip-trigger-id]:not(:hover)');
const batchSize = 20;
const processElementsBatch = (startIdx) => {
@@ -238,11 +265,9 @@ const TooltipManager = (function() {
}
if (endIdx < elements.length) {
const rafId = requestAnimationFrame(() => {
this.pendingAnimationFrames.delete(rafId);
CleanupManager.requestAnimationFrame(() => {
processElementsBatch(endIdx);
});
this.pendingAnimationFrames.add(rafId);
} else {
this.cleanupOrphanedTooltips();
}
@@ -253,9 +278,95 @@ const TooltipManager = (function() {
} else {
this.cleanupOrphanedTooltips();
}
} catch (error) {
console.error('Error during cleanup:', error);
}
}
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() {
try {
const tippyElements = document.querySelectorAll('[data-tippy-root]');
let removed = 0;
@@ -278,14 +389,15 @@ const TooltipManager = (function() {
}
return removed;
} catch (error) {
console.error('Error cleaning up orphaned tooltips:', error);
return 0;
}
}
setupMutationObserver() {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
this.mutationObserver = new MutationObserver(mutations => {
try {
const mutationObserver = new MutationObserver(mutations => {
let needsCleanup = false;
mutations.forEach(mutation => {
@@ -316,25 +428,36 @@ const TooltipManager = (function() {
}
});
this.mutationObserver.observe(document.body, {
mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
return this.mutationObserver;
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);
}
this.disconnectedCheckInterval = setInterval(() => {
try {
this.resources.disconnectedCheckInterval = CleanupManager.setInterval(() => {
this.checkForDisconnectedElements();
}, 60000);
} catch (error) {
console.error('Error starting disconnected elements check:', error);
}
}
checkForDisconnectedElements() {
try {
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
let removedCount = 0;
@@ -349,44 +472,147 @@ const TooltipManager = (function() {
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);
}
this.cleanupInterval = setInterval(() => {
try {
this.resources.cleanupInterval = CleanupManager.setInterval(() => {
this.performPeriodicCleanup();
}, 120000);
} catch (error) {
console.error('Error starting periodic cleanup:', error);
}
}
performPeriodicCleanup(force = false) {
this.cleanupOrphanedTooltips();
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();
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');
}
}
} catch (error) {
console.error('Error performing periodic cleanup:', error);
}
}
setupStyles() {
if (document.getElementById('tooltip-styles')) return;
try {
const style = document.createElement('style');
style.id = 'tooltip-styles';
style.textContent = `
[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;
}
`;
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] {
@@ -474,9 +700,27 @@ const TooltipManager = (function() {
}
</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]') {
try {
document.querySelectorAll(selector).forEach(element => {
const targetId = element.getAttribute('data-tooltip-target');
if (!targetId) return;
@@ -489,45 +733,31 @@ const TooltipManager = (function() {
});
}
});
} catch (error) {
console.error('Error initializing tooltips:', error);
}
}
dispose() {
this.log('Disposing TooltipManager');
try {
this.cleanup();
this.pendingAnimationFrames.forEach(id => {
cancelAnimationFrame(id);
Object.values(this.resources).forEach(resourceId => {
if (resourceId) {
CleanupManager.unregisterResource(resourceId);
}
});
this.pendingAnimationFrames.clear();
this.pendingTimeouts.forEach(id => {
clearTimeout(id);
});
this.pendingTimeouts.clear();
if (this.disconnectedCheckInterval) {
clearInterval(this.disconnectedCheckInterval);
this.disconnectedCheckInterval = null;
}
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);
}
this.resources = {};
instance = null;
return true;
} catch (error) {
console.error('Error disposing TooltipManager:', error);
return false;
}
}
setDebugMode(enabled) {
@@ -536,6 +766,7 @@ const TooltipManager = (function() {
}
initialize(options = {}) {
try {
if (options.maxTooltips) {
this.maxTooltips = options.maxTooltips;
}
@@ -544,8 +775,17 @@ const TooltipManager = (function() {
this.setDebugMode(options.debug);
}
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;
if (typeof module !== 'undefined' && module.exports) {
module.exports = TooltipManager;
}
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
});
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();
initializeTooltipManager();
} else {
document.addEventListener('DOMContentLoaded', installTooltipManager);
document.addEventListener('DOMContentLoaded', initializeTooltipManager, { once: true });
}
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = TooltipManager;
if (typeof window !== 'undefined' && typeof console !== 'undefined') {
console.log('TooltipManager initialized');
}
window.TooltipManager = TooltipManager;
console.log('TooltipManager initialized');

View File

@@ -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();
}
}
console.log('Offers.js cleanup completed');
//console.log('Offers.js cleanup completed');
} catch (error) {
console.error('Error during cleanup:', error);
}

View File

@@ -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();