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();
|
window.TooltipManager.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run memory optimization
|
|
||||||
if (window.MemoryManager) {
|
if (window.MemoryManager) {
|
||||||
MemoryManager.forceCleanup();
|
MemoryManager.forceCleanup();
|
||||||
}
|
}
|
||||||
@@ -1883,7 +1882,6 @@ function setupMemoryMonitoring() {
|
|||||||
}, { once: true });
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
const filterElements = {
|
const filterElements = {
|
||||||
stateSelect: document.getElementById('state'),
|
stateSelect: document.getElementById('state'),
|
||||||
@@ -1901,8 +1899,6 @@ function initialize() {
|
|||||||
if (filterElements.coinFrom) filterElements.coinFrom.value = 'any';
|
if (filterElements.coinFrom) filterElements.coinFrom.value = 'any';
|
||||||
if (filterElements.coinTo) filterElements.coinTo.value = 'any';
|
if (filterElements.coinTo) filterElements.coinTo.value = 'any';
|
||||||
|
|
||||||
setupMemoryMonitoring();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
WebSocketManager.initialize();
|
WebSocketManager.initialize();
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
@@ -1921,12 +1917,6 @@ function initialize() {
|
|||||||
updateBidsTable();
|
updateBidsTable();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
if ((state.data.sent.length + state.data.received.length) > 1000) {
|
|
||||||
optimizeMemoryUsage();
|
|
||||||
}
|
|
||||||
}, 5 * 60 * 1000); // Check every 5 minutes
|
|
||||||
|
|
||||||
window.cleanupBidsTable = cleanup;
|
window.cleanupBidsTable = cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
const CleanupManager = (function() {
|
const CleanupManager = (function() {
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
eventListeners: [],
|
eventListeners: [],
|
||||||
timeouts: [],
|
timeouts: [],
|
||||||
intervals: [],
|
intervals: [],
|
||||||
animationFrames: [],
|
animationFrames: [],
|
||||||
resources: new Map(),
|
resources: new Map(),
|
||||||
debug: false
|
debug: false,
|
||||||
|
memoryOptimizationInterval: null
|
||||||
};
|
};
|
||||||
|
|
||||||
function log(message, ...args) {
|
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) {
|
setDebugMode: function(enabled) {
|
||||||
state.debug = Boolean(enabled);
|
state.debug = Boolean(enabled);
|
||||||
log(`Debug mode ${state.debug ? 'enabled' : 'disabled'}`);
|
log(`Debug mode ${state.debug ? 'enabled' : 'disabled'}`);
|
||||||
@@ -247,6 +470,17 @@ const CleanupManager = (function() {
|
|||||||
if (options.debug !== undefined) {
|
if (options.debug !== undefined) {
|
||||||
this.setDebugMode(options.debug);
|
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');
|
log('CleanupManager initialized');
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -255,16 +489,20 @@ const CleanupManager = (function() {
|
|||||||
return publicAPI;
|
return publicAPI;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = CleanupManager;
|
||||||
|
}
|
||||||
|
|
||||||
window.CleanupManager = CleanupManager;
|
if (typeof window !== 'undefined') {
|
||||||
|
window.CleanupManager = CleanupManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||||
if (!window.cleanupManagerInitialized) {
|
CleanupManager.initialize({ debug: false });
|
||||||
CleanupManager.initialize();
|
} else {
|
||||||
window.cleanupManagerInitialized = true;
|
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;
|
return fetchPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('PriceManager: Fetching latest prices.');
|
//console.log('PriceManager: Fetching latest prices.');
|
||||||
lastFetchTime = Date.now();
|
lastFetchTime = Date.now();
|
||||||
fetchPromise = this.fetchPrices()
|
fetchPromise = this.fetchPrices()
|
||||||
.then(prices => {
|
.then(prices => {
|
||||||
@@ -89,7 +89,7 @@ const PriceManager = (function() {
|
|||||||
? window.config.coins.map(c => c.symbol).filter(symbol => symbol && symbol.trim() !== '')
|
? window.config.coins.map(c => c.symbol).filter(symbol => symbol && symbol.trim() !== '')
|
||||||
: ['BTC', 'XMR', 'PART', 'BCH', 'PIVX', 'FIRO', 'DASH', 'LTC', 'DOGE', 'DCR', 'NMC', 'WOW']);
|
: ['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) {
|
if (!coinSymbols.length) {
|
||||||
throw new Error('No valid coins configured');
|
throw new Error('No valid coins configured');
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
const TooltipManager = (function() {
|
const TooltipManager = (function() {
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
|
||||||
const tooltipInstanceMap = new WeakMap();
|
const tooltipInstanceMap = new WeakMap();
|
||||||
|
|
||||||
class TooltipManagerImpl {
|
class TooltipManagerImpl {
|
||||||
@@ -8,20 +7,22 @@ const TooltipManager = (function() {
|
|||||||
if (instance) {
|
if (instance) {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
this.pendingAnimationFrames = new Set();
|
|
||||||
this.pendingTimeouts = new Set();
|
|
||||||
this.tooltipIdCounter = 0;
|
this.tooltipIdCounter = 0;
|
||||||
this.maxTooltips = 200;
|
this.maxTooltips = 200;
|
||||||
this.cleanupThreshold = 1.2;
|
this.cleanupThreshold = 1.2;
|
||||||
this.disconnectedCheckInterval = null;
|
|
||||||
this.cleanupInterval = null;
|
|
||||||
this.mutationObserver = null;
|
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
this.tooltipData = new WeakMap();
|
this.tooltipData = new WeakMap();
|
||||||
this.setupStyles();
|
this.resources = {};
|
||||||
this.setupMutationObserver();
|
|
||||||
this.startPeriodicCleanup();
|
if (window.CleanupManager) {
|
||||||
this.startDisconnectedElementsCheck();
|
CleanupManager.registerResource(
|
||||||
|
'tooltipManager',
|
||||||
|
this,
|
||||||
|
(manager) => manager.dispose()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
instance = this;
|
instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ const TooltipManager = (function() {
|
|||||||
|
|
||||||
create(element, content, options = {}) {
|
create(element, content, options = {}) {
|
||||||
if (!element || !document.body.contains(element)) return null;
|
if (!element || !document.body.contains(element)) return null;
|
||||||
|
|
||||||
if (!document.contains(element)) {
|
if (!document.contains(element)) {
|
||||||
this.log('Tried to create tooltip for detached element');
|
this.log('Tried to create tooltip for detached element');
|
||||||
return null;
|
return null;
|
||||||
@@ -47,9 +48,7 @@ const TooltipManager = (function() {
|
|||||||
this.performPeriodicCleanup(true);
|
this.performPeriodicCleanup(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rafId = requestAnimationFrame(() => {
|
const createTooltip = () => {
|
||||||
this.pendingAnimationFrames.delete(rafId);
|
|
||||||
|
|
||||||
if (!document.body.contains(element)) return;
|
if (!document.body.contains(element)) return;
|
||||||
|
|
||||||
const rect = element.getBoundingClientRect();
|
const rect = element.getBoundingClientRect();
|
||||||
@@ -58,7 +57,7 @@ const TooltipManager = (function() {
|
|||||||
} else {
|
} else {
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = 3;
|
const maxRetries = 3;
|
||||||
|
|
||||||
const retryCreate = () => {
|
const retryCreate = () => {
|
||||||
const newRect = element.getBoundingClientRect();
|
const newRect = element.getBoundingClientRect();
|
||||||
if ((newRect.width > 0 && newRect.height > 0) || retryCount >= maxRetries) {
|
if ((newRect.width > 0 && newRect.height > 0) || retryCount >= maxRetries) {
|
||||||
@@ -67,30 +66,29 @@ const TooltipManager = (function() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
retryCount++;
|
retryCount++;
|
||||||
const timeoutId = setTimeout(() => {
|
CleanupManager.setTimeout(() => {
|
||||||
this.pendingTimeouts.delete(timeoutId);
|
CleanupManager.requestAnimationFrame(retryCreate);
|
||||||
const newRafId = requestAnimationFrame(retryCreate);
|
|
||||||
this.pendingAnimationFrames.add(newRafId);
|
|
||||||
}, 100);
|
}, 100);
|
||||||
this.pendingTimeouts.add(timeoutId);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialTimeoutId = setTimeout(() => {
|
CleanupManager.setTimeout(() => {
|
||||||
this.pendingTimeouts.delete(initialTimeoutId);
|
CleanupManager.requestAnimationFrame(retryCreate);
|
||||||
const retryRafId = requestAnimationFrame(retryCreate);
|
|
||||||
this.pendingAnimationFrames.add(retryRafId);
|
|
||||||
}, 100);
|
}, 100);
|
||||||
this.pendingTimeouts.add(initialTimeoutId);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
this.pendingAnimationFrames.add(rafId);
|
CleanupManager.requestAnimationFrame(createTooltip);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
createTooltipInstance(element, content, options = {}) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +128,7 @@ const TooltipManager = (function() {
|
|||||||
},
|
},
|
||||||
onHidden(instance) {
|
onHidden(instance) {
|
||||||
if (!document.body.contains(element)) {
|
if (!document.body.contains(element)) {
|
||||||
setTimeout(() => {
|
CleanupManager.setTimeout(() => {
|
||||||
if (instance && instance.destroy) {
|
if (instance && instance.destroy) {
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
}
|
}
|
||||||
@@ -168,9 +166,26 @@ const TooltipManager = (function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
element.setAttribute('data-tooltip-trigger-id', tooltipId);
|
element.setAttribute('data-tooltip-trigger-id', tooltipId);
|
||||||
|
|
||||||
tooltipInstanceMap.set(element, tippyInstance[0]);
|
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];
|
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() {
|
cleanup() {
|
||||||
this.log('Running tooltip cleanup');
|
this.log('Running tooltip cleanup');
|
||||||
|
|
||||||
this.pendingAnimationFrames.forEach(id => {
|
try {
|
||||||
cancelAnimationFrame(id);
|
if ((window.location.pathname.includes('/offers') || window.location.pathname.includes('/bids')) &&
|
||||||
});
|
(document.querySelector('[data-tippy-root]:hover') || document.querySelector('[data-tooltip-trigger-id]:hover'))) {
|
||||||
this.pendingAnimationFrames.clear();
|
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 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) {
|
const elements = document.querySelectorAll('[data-tooltip-trigger-id]:not(:hover)');
|
||||||
const rafId = requestAnimationFrame(() => {
|
const batchSize = 20;
|
||||||
this.pendingAnimationFrames.delete(rafId);
|
|
||||||
processElementsBatch(endIdx);
|
const processElementsBatch = (startIdx) => {
|
||||||
});
|
const endIdx = Math.min(startIdx + batchSize, elements.length);
|
||||||
this.pendingAnimationFrames.add(rafId);
|
|
||||||
|
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 {
|
} else {
|
||||||
this.cleanupOrphanedTooltips();
|
this.cleanupOrphanedTooltips();
|
||||||
}
|
}
|
||||||
};
|
} catch (error) {
|
||||||
|
console.error('Error during cleanup:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (elements.length > 0) {
|
thoroughCleanup() {
|
||||||
processElementsBatch(0);
|
this.log('Running thorough tooltip cleanup');
|
||||||
} else {
|
|
||||||
this.cleanupOrphanedTooltips();
|
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() {
|
cleanupOrphanedTooltips() {
|
||||||
const tippyElements = document.querySelectorAll('[data-tippy-root]');
|
try {
|
||||||
let removed = 0;
|
const tippyElements = document.querySelectorAll('[data-tippy-root]');
|
||||||
|
let removed = 0;
|
||||||
|
|
||||||
tippyElements.forEach(element => {
|
tippyElements.forEach(element => {
|
||||||
const tooltipId = element.getAttribute('data-for-tooltip-id');
|
const tooltipId = element.getAttribute('data-for-tooltip-id');
|
||||||
const trigger = tooltipId ?
|
const trigger = tooltipId ?
|
||||||
document.querySelector(`[data-tooltip-trigger-id="${tooltipId}"]`) :
|
document.querySelector(`[data-tooltip-trigger-id="${tooltipId}"]`) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (!trigger || !document.body.contains(trigger)) {
|
if (!trigger || !document.body.contains(trigger)) {
|
||||||
if (element.parentNode) {
|
if (element.parentNode) {
|
||||||
element.parentNode.removeChild(element);
|
element.parentNode.removeChild(element);
|
||||||
removed++;
|
removed++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (removed > 0) {
|
||||||
|
this.log(`Removed ${removed} orphaned tooltip elements`);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (removed > 0) {
|
return removed;
|
||||||
this.log(`Removed ${removed} orphaned tooltip elements`);
|
} catch (error) {
|
||||||
|
console.error('Error cleaning up orphaned tooltips:', error);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return removed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupMutationObserver() {
|
setupMutationObserver() {
|
||||||
if (this.mutationObserver) {
|
try {
|
||||||
this.mutationObserver.disconnect();
|
const mutationObserver = new MutationObserver(mutations => {
|
||||||
}
|
let needsCleanup = false;
|
||||||
|
|
||||||
this.mutationObserver = new MutationObserver(mutations => {
|
mutations.forEach(mutation => {
|
||||||
let needsCleanup = false;
|
if (mutation.removedNodes.length) {
|
||||||
|
Array.from(mutation.removedNodes).forEach(node => {
|
||||||
mutations.forEach(mutation => {
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
if (mutation.removedNodes.length) {
|
if (node.hasAttribute && node.hasAttribute('data-tooltip-trigger-id')) {
|
||||||
Array.from(mutation.removedNodes).forEach(node => {
|
this.destroy(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;
|
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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (needsCleanup) {
|
|
||||||
this.cleanupOrphanedTooltips();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.mutationObserver.observe(document.body, {
|
mutationObserver.observe(document.body, {
|
||||||
childList: true,
|
childList: true,
|
||||||
subtree: 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() {
|
startDisconnectedElementsCheck() {
|
||||||
if (this.disconnectedCheckInterval) {
|
try {
|
||||||
clearInterval(this.disconnectedCheckInterval);
|
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() {
|
checkForDisconnectedElements() {
|
||||||
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
|
try {
|
||||||
let removedCount = 0;
|
const elements = document.querySelectorAll('[data-tooltip-trigger-id]');
|
||||||
|
let removedCount = 0;
|
||||||
|
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
if (!document.body.contains(element)) {
|
if (!document.body.contains(element)) {
|
||||||
this.destroy(element);
|
this.destroy(element);
|
||||||
removedCount++;
|
removedCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (removedCount > 0) {
|
||||||
|
this.log(`Removed ${removedCount} tooltips for disconnected elements`);
|
||||||
|
this.cleanupOrphanedTooltips();
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
console.error('Error checking for disconnected elements:', error);
|
||||||
if (removedCount > 0) {
|
|
||||||
this.log(`Removed ${removedCount} tooltips for disconnected elements`);
|
|
||||||
this.cleanupOrphanedTooltips();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startPeriodicCleanup() {
|
startPeriodicCleanup() {
|
||||||
if (this.cleanupInterval) {
|
try {
|
||||||
clearInterval(this.cleanupInterval);
|
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) {
|
performPeriodicCleanup(force = false) {
|
||||||
this.cleanupOrphanedTooltips();
|
try {
|
||||||
|
if ((window.location.pathname.includes('/offers') || window.location.pathname.includes('/bids')) &&
|
||||||
this.checkForDisconnectedElements();
|
!force) {
|
||||||
|
return;
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
setupStyles() {
|
||||||
if (document.getElementById('tooltip-styles')) return;
|
if (document.getElementById('tooltip-styles')) return;
|
||||||
|
|
||||||
document.head.insertAdjacentHTML('beforeend', `
|
try {
|
||||||
<style id="tooltip-styles">
|
const style = document.createElement('style');
|
||||||
|
style.id = 'tooltip-styles';
|
||||||
|
style.textContent = `
|
||||||
[data-tippy-root] {
|
[data-tippy-root] {
|
||||||
position: fixed !important;
|
position: fixed !important;
|
||||||
z-index: 9999 !important;
|
z-index: 9999 !important;
|
||||||
@@ -472,62 +598,166 @@ const TooltipManager = (function() {
|
|||||||
.tippy-box[data-placement^='right'] > .tippy-arrow {
|
.tippy-box[data-placement^='right'] > .tippy-arrow {
|
||||||
left: 0;
|
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]') {
|
initializeTooltips(selector = '[data-tooltip-target]') {
|
||||||
document.querySelectorAll(selector).forEach(element => {
|
try {
|
||||||
const targetId = element.getAttribute('data-tooltip-target');
|
document.querySelectorAll(selector).forEach(element => {
|
||||||
if (!targetId) return;
|
const targetId = element.getAttribute('data-tooltip-target');
|
||||||
|
if (!targetId) return;
|
||||||
|
|
||||||
const tooltipContent = document.getElementById(targetId);
|
const tooltipContent = document.getElementById(targetId);
|
||||||
|
|
||||||
if (tooltipContent) {
|
if (tooltipContent) {
|
||||||
this.create(element, tooltipContent.innerHTML, {
|
this.create(element, tooltipContent.innerHTML, {
|
||||||
placement: element.getAttribute('data-tooltip-placement') || 'top'
|
placement: element.getAttribute('data-tooltip-placement') || 'top'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing tooltips:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.log('Disposing TooltipManager');
|
this.log('Disposing TooltipManager');
|
||||||
|
|
||||||
this.cleanup();
|
|
||||||
|
|
||||||
this.pendingAnimationFrames.forEach(id => {
|
try {
|
||||||
cancelAnimationFrame(id);
|
this.cleanup();
|
||||||
});
|
|
||||||
this.pendingAnimationFrames.clear();
|
|
||||||
|
|
||||||
this.pendingTimeouts.forEach(id => {
|
Object.values(this.resources).forEach(resourceId => {
|
||||||
clearTimeout(id);
|
if (resourceId) {
|
||||||
});
|
CleanupManager.unregisterResource(resourceId);
|
||||||
this.pendingTimeouts.clear();
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (this.disconnectedCheckInterval) {
|
this.resources = {};
|
||||||
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) {
|
setDebugMode(enabled) {
|
||||||
@@ -536,16 +766,26 @@ const TooltipManager = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize(options = {}) {
|
initialize(options = {}) {
|
||||||
if (options.maxTooltips) {
|
try {
|
||||||
this.maxTooltips = options.maxTooltips;
|
if (options.maxTooltips) {
|
||||||
}
|
this.maxTooltips = options.maxTooltips;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.debug !== undefined) {
|
if (options.debug !== undefined) {
|
||||||
this.setDebugMode(options.debug);
|
this.setDebugMode(options.debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log('TooltipManager initialized');
|
this.setupStyles();
|
||||||
return this;
|
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);
|
return manager.cleanup(...args);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
thoroughCleanup: function() {
|
||||||
|
const manager = this.getInstance();
|
||||||
|
return manager.thoroughCleanup();
|
||||||
|
},
|
||||||
|
|
||||||
initializeTooltips: function(...args) {
|
initializeTooltips: function(...args) {
|
||||||
const manager = this.getInstance();
|
const manager = this.getInstance();
|
||||||
return manager.initializeTooltips(...args);
|
return manager.initializeTooltips(...args);
|
||||||
@@ -590,6 +835,11 @@ const TooltipManager = (function() {
|
|||||||
return manager.setDebugMode(enabled);
|
return manager.setDebugMode(enabled);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getActiveTooltipInstances: function() {
|
||||||
|
const manager = this.getInstance();
|
||||||
|
return manager.getActiveTooltipInstances();
|
||||||
|
},
|
||||||
|
|
||||||
dispose: function(...args) {
|
dispose: function(...args) {
|
||||||
const manager = this.getInstance();
|
const manager = this.getInstance();
|
||||||
return manager.dispose(...args);
|
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) {
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
module.exports = TooltipManager;
|
module.exports = TooltipManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.TooltipManager = TooltipManager;
|
if (typeof window !== 'undefined') {
|
||||||
console.log('TooltipManager initialized');
|
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');
|
||||||
|
}
|
||||||
|
|||||||
@@ -990,7 +990,7 @@ function createTableRow(offer, identity = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let coinFromDisplay, coinToDisplay;
|
let coinFromDisplay, coinToDisplay;
|
||||||
|
|
||||||
if (window.CoinManager) {
|
if (window.CoinManager) {
|
||||||
coinFromDisplay = window.CoinManager.getDisplayName(coinFrom) || coinFrom;
|
coinFromDisplay = window.CoinManager.getDisplayName(coinFrom) || coinFrom;
|
||||||
coinToDisplay = window.CoinManager.getDisplayName(coinTo) || coinTo;
|
coinToDisplay = window.CoinManager.getDisplayName(coinTo) || coinTo;
|
||||||
@@ -1000,7 +1000,7 @@ function createTableRow(offer, identity = null) {
|
|||||||
if (coinFromDisplay.toLowerCase() === 'zcoin') coinFromDisplay = 'Firo';
|
if (coinFromDisplay.toLowerCase() === 'zcoin') coinFromDisplay = 'Firo';
|
||||||
if (coinToDisplay.toLowerCase() === 'zcoin') coinToDisplay = 'Firo';
|
if (coinToDisplay.toLowerCase() === 'zcoin') coinToDisplay = 'Firo';
|
||||||
}
|
}
|
||||||
|
|
||||||
const postedTime = formatTime(createdAt, true);
|
const postedTime = formatTime(createdAt, true);
|
||||||
const expiresIn = formatTime(expireAt);
|
const expiresIn = formatTime(expireAt);
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
@@ -1370,7 +1370,6 @@ function createRecipientTooltip(uniqueId, identityInfo, identity, successRate, t
|
|||||||
return 'text-red-600';
|
return 'text-red-600';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const truncateText = (text, maxLength) => {
|
const truncateText = (text, maxLength) => {
|
||||||
if (text.length <= maxLength) return text;
|
if (text.length <= maxLength) return text;
|
||||||
return text.substring(0, maxLength) + '...';
|
return text.substring(0, maxLength) + '...';
|
||||||
@@ -1454,7 +1453,7 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
|
|||||||
|
|
||||||
const getPriceKey = (coin) => {
|
const getPriceKey = (coin) => {
|
||||||
if (!coin) return null;
|
if (!coin) return null;
|
||||||
|
|
||||||
const lowerCoin = coin.toLowerCase();
|
const lowerCoin = coin.toLowerCase();
|
||||||
|
|
||||||
if (lowerCoin === 'zcoin') return 'firo';
|
if (lowerCoin === 'zcoin') return 'firo';
|
||||||
@@ -2167,6 +2166,18 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||||||
tableRateModule.init();
|
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();
|
await initializeTableAndData();
|
||||||
|
|
||||||
if (window.PriceManager) {
|
if (window.PriceManager) {
|
||||||
@@ -2179,7 +2190,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||||||
if (window.WebSocketManager) {
|
if (window.WebSocketManager) {
|
||||||
WebSocketManager.addMessageHandler('message', async (data) => {
|
WebSocketManager.addMessageHandler('message', async (data) => {
|
||||||
if (data.event === 'new_offer' || data.event === 'offer_revoked') {
|
if (data.event === 'new_offer' || data.event === 'offer_revoked') {
|
||||||
console.log('WebSocket event received:', data.event);
|
//console.log('WebSocket event received:', data.event);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const previousPrices = latestPrices;
|
const previousPrices = latestPrices;
|
||||||
@@ -2188,7 +2199,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||||||
if (!offersResponse.ok) {
|
if (!offersResponse.ok) {
|
||||||
throw new Error(`HTTP error! status: ${offersResponse.status}`);
|
throw new Error(`HTTP error! status: ${offersResponse.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newData = await offersResponse.json();
|
const newData = await offersResponse.json();
|
||||||
const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
|
const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
|
||||||
jsonData = formatInitialData(processedNewData);
|
jsonData = formatInitialData(processedNewData);
|
||||||
@@ -2200,7 +2211,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||||||
} else {
|
} else {
|
||||||
priceData = await fetchLatestPrices();
|
priceData = await fetchLatestPrices();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priceData) {
|
if (priceData) {
|
||||||
latestPrices = priceData;
|
latestPrices = priceData;
|
||||||
CacheManager.set('prices_coingecko', priceData, 'prices');
|
CacheManager.set('prices_coingecko', priceData, 'prices');
|
||||||
@@ -2230,7 +2241,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||||||
|
|
||||||
updatePaginationInfo();
|
updatePaginationInfo();
|
||||||
|
|
||||||
console.log('WebSocket-triggered refresh completed successfully');
|
//console.log('WebSocket-triggered refresh completed successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during WebSocket-triggered refresh:', error);
|
console.error('Error during WebSocket-triggered refresh:', error);
|
||||||
NetworkManager.handleNetworkError(error);
|
NetworkManager.handleNetworkError(error);
|
||||||
@@ -2357,28 +2368,10 @@ function cleanup() {
|
|||||||
if (window.CleanupManager) {
|
if (window.CleanupManager) {
|
||||||
window.CleanupManager.removeListenersByElement(row);
|
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 = '';
|
offersBody.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterForm = document.getElementById('filterForm');
|
|
||||||
if (filterForm) {
|
|
||||||
CleanupManager.removeListenersByElement(filterForm);
|
|
||||||
|
|
||||||
filterForm.querySelectorAll('select').forEach(select => {
|
|
||||||
CleanupManager.removeListenersByElement(select);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonData = null;
|
jsonData = null;
|
||||||
originalJsonData = null;
|
originalJsonData = null;
|
||||||
latestPrices = null;
|
latestPrices = null;
|
||||||
@@ -2388,13 +2381,12 @@ function cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (window.MemoryManager) {
|
if (window.MemoryManager) {
|
||||||
if (window.MemoryManager.cleanupTooltips) {
|
if (window.MemoryManager.forceCleanup) {
|
||||||
window.MemoryManager.cleanupTooltips(true);
|
window.MemoryManager.forceCleanup();
|
||||||
}
|
}
|
||||||
window.MemoryManager.forceCleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Offers.js cleanup completed');
|
//console.log('Offers.js cleanup completed');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during cleanup:', error);
|
console.error('Error during cleanup:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -578,7 +578,7 @@ const chartModule = {
|
|||||||
this.chartRefs.set(element, chart);
|
this.chartRefs.set(element, chart);
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyChart: function() {
|
destroyChart: function() {
|
||||||
if (chartModule.chart) {
|
if (chartModule.chart) {
|
||||||
try {
|
try {
|
||||||
const chartInstance = chartModule.chart;
|
const chartInstance = chartModule.chart;
|
||||||
@@ -592,12 +592,17 @@ const chartModule = {
|
|||||||
|
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
chartModule.chartRefs.delete(canvas);
|
chartModule.chartRefs.delete(canvas);
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (ctx) {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error destroying chart:', e);
|
console.error('Error destroying chart:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initChart: function() {
|
initChart: function() {
|
||||||
this.destroyChart();
|
this.destroyChart();
|
||||||
|
|||||||
Reference in New Issue
Block a user