From c992ef571a50fe1ac340023a53f1806490547f50 Mon Sep 17 00:00:00 2001
From: gerlofvanek <gerlof@particl.io>
Date: Fri, 17 Jan 2025 11:34:28 +0100
Subject: [PATCH] JS: Cleanup + Fixes

---
 basicswap/static/js/offerstable.js | 442 ++++++++++-------
 basicswap/static/js/pricechart.js  | 767 ++++++++++++-----------------
 2 files changed, 597 insertions(+), 612 deletions(-)

diff --git a/basicswap/static/js/offerstable.js b/basicswap/static/js/offerstable.js
index 2652780..2a595d6 100644
--- a/basicswap/static/js/offerstable.js
+++ b/basicswap/static/js/offerstable.js
@@ -449,33 +449,49 @@ const CacheManager = {
         try {
             this.cleanup();
 
+            if (!value) {
+                console.warn('Attempted to cache null/undefined value for key:', key);
+                return false;
+            }
+
             const item = {
                 value: value,
                 timestamp: Date.now(),
                 expiresAt: Date.now() + (customTtl || CACHE_DURATION)
             };
 
-            const itemSize = new Blob([JSON.stringify(item)]).size;
-            if (itemSize > this.maxSize) {
-                //console.error(`Cache item exceeds maximum size (${(itemSize/1024/1024).toFixed(2)}MB)`);
+            try {
+                JSON.stringify(item);
+            } catch (e) {
+                console.error('Failed to serialize cache item:', e);
                 return false;
             }
 
-            localStorage.setItem(key, JSON.stringify(item));
-            return true;
+            const itemSize = new Blob([JSON.stringify(item)]).size;
+            if (itemSize > this.maxSize) {
+                console.warn(`Cache item exceeds maximum size (${(itemSize/1024/1024).toFixed(2)}MB)`);
+                return false;
+            }
+
+            try {
+                localStorage.setItem(key, JSON.stringify(item));
+                return true;
+            } catch (storageError) {
+                if (storageError.name === 'QuotaExceededError') {
+                    this.cleanup(true);
+                    try {
+                        localStorage.setItem(key, JSON.stringify(item));
+                        return true;
+                    } catch (retryError) {
+                        console.error('Storage quota exceeded even after cleanup:', retryError);
+                        return false;
+                    }
+                }
+                throw storageError;
+            }
 
         } catch (error) {
-            if (error.name === 'QuotaExceededError') {
-                this.cleanup(true); // Aggressive cleanup
-                try {
-                    localStorage.setItem(key, JSON.stringify(item));
-                    return true;
-                } catch (error) {
-                    console.error('Storage quota exceeded even after cleanup:', error.message);
-                    return false;
-                }
-            }
-            //console.error('Cache set error:', error);
+            console.error('Cache set error:', error);
             return false;
         }
     },
@@ -483,11 +499,26 @@ const CacheManager = {
     get: function(key) {
         try {
             const itemStr = localStorage.getItem(key);
-            if (!itemStr) return null;
+            if (!itemStr) {
+                return null;
+            }
+
+            let item;
+            try {
+                item = JSON.parse(itemStr);
+            } catch (parseError) {
+                console.error('Failed to parse cached item:', parseError);
+                localStorage.removeItem(key);
+                return null;
+            }
+
+            if (!item || typeof item.expiresAt !== 'number' || !item.hasOwnProperty('value')) {
+                console.warn('Invalid cache item structure for key:', key);
+                localStorage.removeItem(key);
+                return null;
+            }
 
-            const item = JSON.parse(itemStr);
             const now = Date.now();
-
             if (now < item.expiresAt) {
                 return {
                     value: item.value,
@@ -496,11 +527,17 @@ const CacheManager = {
             }
 
             localStorage.removeItem(key);
+            return null;
+
         } catch (error) {
-            console.error("An error occured:", error.message);
-            localStorage.removeItem(key);
+            console.error("Cache retrieval error:", error);
+            try {
+                localStorage.removeItem(key);
+            } catch (removeError) {
+                console.error("Failed to remove invalid cache entry:", removeError);
+            }
+            return null;
         }
-        return null;
     },
 
     cleanup: function(aggressive = false) {
@@ -533,7 +570,7 @@ const CacheManager = {
                 totalSize += size;
                 itemCount++;
             } catch (error) {
-                console.error("An error occured:", error.message);
+                console.error("Error processing cache item:", error);
                 localStorage.removeItem(key);
             }
         }
@@ -543,11 +580,21 @@ const CacheManager = {
 
             while ((totalSize > this.maxSize || itemCount > this.maxItems) && items.length > 0) {
                 const item = items.pop();
-                localStorage.removeItem(item.key);
-                totalSize -= item.size;
-                itemCount--;
+                try {
+                    localStorage.removeItem(item.key);
+                    totalSize -= item.size;
+                    itemCount--;
+                } catch (error) {
+                    console.error("Error removing cache item:", error);
+                }
             }
         }
+
+        return {
+            totalSize,
+            itemCount,
+            cleaned: items.length
+        };
     },
 
     clear: function() {
@@ -559,7 +606,13 @@ const CacheManager = {
             }
         }
 
-        keys.forEach(key => localStorage.removeItem(key));
+        keys.forEach(key => {
+            try {
+                localStorage.removeItem(key);
+            } catch (error) {
+                console.error("Error clearing cache item:", error);
+            }
+        });
     },
 
     getStats: function() {
@@ -584,7 +637,7 @@ const CacheManager = {
                     expiredCount++;
                 }
             } catch (error) {
-              console.error("An error occured:", error.message);
+                console.error("Error getting cache stats:", error);
             }
         }
 
@@ -597,6 +650,8 @@ const CacheManager = {
     }
 };
 
+window.CacheManager = CacheManager;
+
 // Identity cache management
 const IdentityManager = {
     cache: new Map(),
@@ -939,15 +994,44 @@ function filterAndSortData() {
                     comparison = a.offer_id.localeCompare(b.offer_id);
                     break;
             }
-
             return currentSortDirection === 'desc' ? -comparison : comparison;
         });
     }
-
     //console.log(`[Debug] Filtered data length: ${filteredData.length}`);
     return filteredData;
 }
 
+function getPriceWithFallback(coin, latestPrices) {
+    const getPriceKey = (coin) => {
+        const lowerCoin = coin.toLowerCase();
+        if (lowerCoin === 'firo' || lowerCoin === 'zcoin') {
+            return 'zcoin';
+        }
+        if (lowerCoin === 'bitcoin cash') {
+            return 'bitcoin-cash';
+        }
+        if (lowerCoin === 'particl anon' || lowerCoin === 'particl blind') {
+            return 'particl';
+        }
+        return coinNameToSymbol[coin] || lowerCoin;
+    };
+
+    const priceKey = getPriceKey(coin);
+    const livePrice = latestPrices[priceKey]?.usd;
+    if (livePrice !== undefined && livePrice !== null) {
+        return livePrice;
+    }
+
+    if (window.tableRateModule) {
+        const fallback = window.tableRateModule.getFallbackValue(priceKey);
+        if (fallback !== null) {
+            return fallback;
+        }
+    }
+    
+    return null;
+}
+
 async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer) {
     return new Promise((resolve) => {
         if (!latestPrices) {
@@ -972,26 +1056,33 @@ async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwn
 
         const fromSymbol = getPriceKey(fromCoin);
         const toSymbol = getPriceKey(toCoin);
+        let fromPriceUSD = latestPrices[fromSymbol]?.usd;
+        let toPriceUSD = latestPrices[toSymbol]?.usd;
 
-        const fromPriceUSD = latestPrices[fromSymbol]?.usd;
-        const toPriceUSD = latestPrices[toSymbol]?.usd;
-
-        if (fromPriceUSD === null || toPriceUSD === null ||
-            fromPriceUSD === undefined || toPriceUSD === undefined) {
+        if (!fromPriceUSD || !toPriceUSD) {
+            fromPriceUSD = tableRateModule.getFallbackValue(fromSymbol);
+            toPriceUSD = tableRateModule.getFallbackValue(toSymbol);
+        }
+        if (!fromPriceUSD || !toPriceUSD || isNaN(fromPriceUSD) || isNaN(toPriceUSD)) {
             resolve(null);
             return;
         }
-
         const fromValueUSD = fromAmount * fromPriceUSD;
         const toValueUSD = toAmount * toPriceUSD;
-
+        if (isNaN(fromValueUSD) || isNaN(toValueUSD) || fromValueUSD === 0 || toValueUSD === 0) {
+            resolve(null);
+            return;
+        }
         let percentDiff;
         if (isOwnOffer) {
             percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
         } else {
             percentDiff = ((fromValueUSD / toValueUSD) - 1) * 100;
         }
-
+        if (isNaN(percentDiff)) {
+            resolve(null);
+            return;
+        }
         resolve(percentDiff);
     });
 }
@@ -1015,94 +1106,75 @@ function getEmptyPriceData() {
 
 async function fetchLatestPrices() {
     const PRICES_CACHE_KEY = 'prices_coingecko';
-    const RETRY_DELAY = 5000;
-    const MAX_RETRIES = 3;
 
-    const cachedData = CacheManager.get(PRICES_CACHE_KEY);
-    if (cachedData && cachedData.remainingTime > 30000) {
-        console.log('Using cached price data');
-        latestPrices = cachedData.value;
-        return cachedData.value;
+    if (!window.isManualRefresh) {
+        const cachedData = CacheManager.get(PRICES_CACHE_KEY);
+        if (cachedData && cachedData.remainingTime > 60000) {
+            console.log('Using cached price data');
+            latestPrices = cachedData.value;
+            Object.entries(cachedData.value).forEach(([coin, prices]) => {
+                if (prices.usd) {
+                    tableRateModule.setFallbackValue(coin, prices.usd);
+                }
+            });
+            return cachedData.value;
+        }
     }
+    const url = `${offersConfig.apiEndpoints.coinGecko}/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC&api_key=${offersConfig.apiKeys.coinGecko}`;
 
-    const baseUrl = `${offersConfig.apiEndpoints.coinGecko}/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zcoin,zano,wownero&vs_currencies=USD,BTC`;
+    try {
+        console.log('Initiating fresh price data fetch...');
+        const response = await fetch('/json/readurl', {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/json'
+            },
+            body: JSON.stringify({
+                url: url,
+                headers: {}
+            })
+        });
 
-    let retryCount = 0;
-    let data = null;
-
-    while (!data && retryCount < MAX_RETRIES) {
-        if (retryCount > 0) {
-            const delay = RETRY_DELAY * Math.pow(2, retryCount - 1);
-            console.log(`Waiting ${delay}ms before retry ${retryCount + 1}...`);
-            await new Promise(resolve => setTimeout(resolve, delay));
+        if (!response.ok) {
+            throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
         }
 
-        try {
-            console.log('Attempting price fetch with API key...');
-            const urlWithKey = `${baseUrl}&api_key=${offersConfig.apiKeys.coinGecko}`;
+        const data = await response.json();
 
-            const response = await fetch('/json/readurl', {
-                method: 'POST',
-                headers: {
-                    'Content-Type': 'application/json'
-                },
-                body: JSON.stringify({
-                    url: urlWithKey,
-                    headers: {}
-                })
+        if (data.Error) {
+            console.error('API Error:', data.Error);
+            throw new Error(data.Error);
+        }
+
+        if (data && Object.keys(data).length > 0) {
+            console.log('Processing fresh price data...');
+            latestPrices = data;
+            CacheManager.set(PRICES_CACHE_KEY, data, CACHE_DURATION);
+
+            Object.entries(data).forEach(([coin, prices]) => {
+                if (prices.usd) {
+                    tableRateModule.setFallbackValue(coin, prices.usd);
+                }
             });
 
-            const responseData = await response.json();
-
-            if (responseData.error) {
-                if (responseData.error.includes('429')) {
-                    console.log('Rate limited, retrying...');
-                } else {
-                    console.warn('Invalid price data received:', responseData);
-                }
-                retryCount++;
-                continue;
-            }
-
-            const hasValidPrices = Object.values(responseData).some(coin =>
-                coin && typeof coin === 'object' &&
-                typeof coin.usd === 'number' &&
-                !isNaN(coin.usd)
-            );
-
-            if (!hasValidPrices) {
-                console.warn('No valid price data found in response');
-                retryCount++;
-                continue;
-            }
-
-            data = responseData;
-            break;
-
-        } catch (error) {
-            console.warn('Error fetching prices:', error);
-            retryCount++;
+            return data;
+        } else {
+            console.warn('No price data received');
+            return null;
         }
+    } catch (error) {
+        console.error('Price Fetch Error:', error);
+        const fallbackPrices = {};
+        Object.keys(getEmptyPriceData()).forEach(coin => {
+            const fallbackValue = tableRateModule.getFallbackValue(coin);
+            if (fallbackValue !== null) {
+                fallbackPrices[coin] = { usd: fallbackValue, btc: null };
+            }
+        });
+        return Object.keys(fallbackPrices).length > 0 ? fallbackPrices : null;
+    } finally {
+        window.isManualRefresh = false;
     }
-
-    if (!data) {
-        console.warn('All price fetch attempts failed, using empty price data');
-        const naData = getEmptyPriceData();
-        latestPrices = naData;
-        return naData;
-    }
-
-    console.log('Successfully fetched fresh price data');
-    latestPrices = data;
-    CacheManager.set(PRICES_CACHE_KEY, data, CACHE_DURATION);
-
-    Object.entries(data).forEach(([coin, prices]) => {
-        if (prices && typeof prices.usd === 'number' && !isNaN(prices.usd)) {
-            tableRateModule.setFallbackValue(coin, prices.usd);
-        }
-    });
-
-    return data;
 }
 
 async function fetchOffers() {
@@ -1275,20 +1347,18 @@ function updatePaginationControls(totalPages) {
 function updateProfitLoss(row, fromCoin, toCoin, fromAmount, toAmount, isOwnOffer) {
     const profitLossElement = row.querySelector('.profit-loss');
     if (!profitLossElement) {
-        //console.warn('Profit loss element not found in row');
         return;
     }
 
     if (!fromCoin || !toCoin) {
-        //console.error(`Invalid coin names: fromCoin=${fromCoin}, toCoin=${toCoin}`);
-        profitLossElement.textContent = 'Error';
-        profitLossElement.className = 'profit-loss text-lg font-bold text-red-500';
+        profitLossElement.textContent = 'N/A';
+        profitLossElement.className = 'profit-loss text-lg font-bold text-gray-300';
         return;
     }
 
     calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer)
         .then(percentDiff => {
-            if (percentDiff === null) {
+            if (percentDiff === null || isNaN(percentDiff)) {
                 profitLossElement.textContent = 'N/A';
                 profitLossElement.className = 'profit-loss text-lg font-bold text-gray-300';
                 return;
@@ -1302,6 +1372,7 @@ function updateProfitLoss(row, fromCoin, toCoin, fromAmount, toAmount, isOwnOffe
             profitLossElement.textContent = `${percentDiffDisplay}%`;
             profitLossElement.className = `profit-loss text-lg font-bold ${colorClass}`;
 
+            // Update tooltip if it exists
             const tooltipId = `percentage-tooltip-${row.getAttribute('data-offer-id')}`;
             const tooltipElement = document.getElementById(tooltipId);
             if (tooltipElement) {
@@ -1316,8 +1387,8 @@ function updateProfitLoss(row, fromCoin, toCoin, fromAmount, toAmount, isOwnOffe
         })
         .catch(error => {
             console.error('Error in updateProfitLoss:', error);
-            profitLossElement.textContent = 'Error';
-            profitLossElement.className = 'profit-loss text-lg font-bold text-red-500';
+            profitLossElement.textContent = 'N/A';
+            profitLossElement.className = 'profit-loss text-lg font-bold text-gray-300';
         });
 }
 
@@ -1802,18 +1873,27 @@ function createRateColumn(offer, coinFrom, coinTo) {
         if (lowerCoin === 'bitcoin cash') {
             return 'bitcoin-cash';
         }
+        if (lowerCoin === 'particl anon' || lowerCoin === 'particl blind') {
+            return 'particl';
+        }
         return coinNameToSymbol[coin] || lowerCoin;
     };
 
-    const toPriceUSD = latestPrices[getPriceKey(coinTo)]?.usd || 0;
-    const rateInUSD = rate * toPriceUSD;
+    const toSymbolKey = getPriceKey(coinTo);
+    let toPriceUSD = latestPrices[toSymbolKey]?.usd;
+
+    if (!toPriceUSD || isNaN(toPriceUSD)) {
+        toPriceUSD = tableRateModule.getFallbackValue(toSymbolKey);
+    }
+
+    const rateInUSD = toPriceUSD && !isNaN(toPriceUSD) && !isNaN(rate) ? rate * toPriceUSD : null;
 
     return `
         <td class="py-3 semibold monospace text-xs text-right items-center rate-table-info">
             <div class="relative">
                 <div class="flex flex-col items-end pr-3" data-tooltip-target="tooltip-rate-${offer.offer_id}">
                     <span class="text-sm bold text-gray-700 dark:text-white">
-                        $${rateInUSD.toFixed(2)} USD
+                        ${rateInUSD !== null ? `$${rateInUSD.toFixed(2)} USD` : 'N/A'}
                     </span>
                     <span class="bold text-gray-700 dark:text-white">
                         ${rate.toFixed(8)} ${toSymbol}/${fromSymbol}
@@ -2443,49 +2523,76 @@ function initializeTableEvents() {
         });
     }
 
-    const refreshButton = document.getElementById('refreshOffers');
-    if (refreshButton) {
-        EventManager.add(refreshButton, 'click', async () => {
-            console.log('Manual refresh initiated');
-            const refreshIcon = document.getElementById('refreshIcon');
-            const refreshText = document.getElementById('refreshText');
+const refreshButton = document.getElementById('refreshOffers');
+if (refreshButton) {
+    EventManager.add(refreshButton, 'click', async () => {
+        console.log('Manual refresh initiated');
+        const refreshIcon = document.getElementById('refreshIcon');
+        const refreshText = document.getElementById('refreshText');
 
-            refreshButton.disabled = true;
-            refreshIcon.classList.add('animate-spin');
-            refreshText.textContent = 'Refreshing...';
-            refreshButton.classList.add('opacity-75', 'cursor-wait');
+        refreshButton.disabled = true;
+        refreshIcon.classList.add('animate-spin');
+        refreshText.textContent = 'Refreshing...';
+        refreshButton.classList.add('opacity-75', 'cursor-wait');
 
-            try {
-                const endpoint = isSentOffers ? '/json/sentoffers' : '/json/offers';
-                const response = await fetch(endpoint);
-                if (!response.ok) {
-                    throw new Error(`HTTP error! status: ${response.status}`);
-                }
-                const newData = await response.json();
-
-                const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
-                console.log('Fetched offers:', processedNewData.length);
-
-                jsonData = formatInitialData(processedNewData);
-                originalJsonData = [...jsonData];
-
-                await updateOffersTable();
-                updateJsonView();
-                updatePaginationInfo();
-
-                console.log('Manual refresh completed successfully');
-
-            } catch (error) {
-                console.error('Error during manual refresh:', error);
-                ui.displayErrorMessage('Failed to refresh offers. Please try again later.');
-            } finally {
-                refreshButton.disabled = false;
-                refreshIcon.classList.remove('animate-spin');
-                refreshText.textContent = 'Refresh';
-                refreshButton.classList.remove('opacity-75', 'cursor-wait');
+        try {
+            const PRICES_CACHE_KEY = 'prices_coingecko';
+            localStorage.removeItem(PRICES_CACHE_KEY);
+            CacheManager.clear();
+            window.isManualRefresh = true;
+            const endpoint = isSentOffers ? '/json/sentoffers' : '/json/offers';
+            const response = await fetch(endpoint);
+            if (!response.ok) {
+                throw new Error(`HTTP error! status: ${response.status}`);
             }
-        });
-    }
+            const newData = await response.json();
+            const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
+            console.log('Fetched offers:', processedNewData.length);
+
+            jsonData = formatInitialData(processedNewData);
+            originalJsonData = [...jsonData];
+
+            const url = `${offersConfig.apiEndpoints.coinGecko}/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC&api_key=${offersConfig.apiKeys.coinGecko}`;
+            console.log('Fetching fresh prices...');
+            const priceResponse = await fetch('/json/readurl', {
+                method: 'POST',
+                headers: { 'Content-Type': 'application/json' },
+                body: JSON.stringify({ url: url, headers: {} })
+            });
+
+            if (priceResponse.ok) {
+                const priceData = await priceResponse.json();
+                if (priceData && Object.keys(priceData).length > 0) {
+                    console.log('Updating with fresh price data');
+                    latestPrices = priceData;
+                    CacheManager.set(PRICES_CACHE_KEY, priceData, CACHE_DURATION);
+                    Object.entries(priceData).forEach(([coin, prices]) => {
+                        if (prices.usd) {
+                            tableRateModule.setFallbackValue(coin, prices.usd);
+                        }
+                    });
+                }
+            }
+
+            await updateOffersTable();
+            updateJsonView();
+            updatePaginationInfo();
+            lastRefreshTime = Date.now();
+            updateLastRefreshTime();
+
+            console.log('Manual refresh completed successfully');
+        } catch (error) {
+            console.error('Error during manual refresh:', error);
+            ui.displayErrorMessage('Failed to refresh offers. Please try again later.');
+        } finally {
+            window.isManualRefresh = false;
+            refreshButton.disabled = false;
+            refreshIcon.classList.remove('animate-spin');
+            refreshText.textContent = 'Refresh';
+            refreshButton.classList.remove('opacity-75', 'cursor-wait');
+        }
+    });
+}
 
     document.querySelectorAll('th[data-sortable="true"]').forEach(header => {
         EventManager.add(header, 'click', () => {
@@ -2718,11 +2825,6 @@ async function cleanup() {
         cleanupTable();
         debug.addStep('Table cleanup completed', `Cleaned up ${rowCount} rows`);
 
-        debug.addStep('Starting cache cleanup');
-        const cacheStats = CacheManager.getStats();
-        CacheManager.clear();
-        debug.addStep('Cache cleanup completed', `Cleared ${cacheStats.itemCount} cached items`);
-
         debug.addStep('Resetting global state');
         const globals = {
             currentPage: currentPage,
diff --git a/basicswap/static/js/pricechart.js b/basicswap/static/js/pricechart.js
index a4c63f2..0cc9163 100644
--- a/basicswap/static/js/pricechart.js
+++ b/basicswap/static/js/pricechart.js
@@ -42,7 +42,6 @@ const config = {
 const utils = {
   formatNumber: (number, decimals = 2) =>
     number.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ','),
-
   formatDate: (timestamp, resolution) => {
     const date = new Date(timestamp);
     const options = {
@@ -80,7 +79,6 @@ const logger = {
 // API
 const api = {
     makePostRequest: (url, headers = {}) => {
-      // unused // const apiKeys = getAPIKeys();
       return new Promise((resolve, reject) => {
       const xhr = new XMLHttpRequest();
       xhr.open('POST', '/json/readurl');
@@ -141,17 +139,13 @@ const api = {
             .map(coin => coin.name)
             .join(',');
         const url = `${config.apiEndpoints.coinGecko}/simple/price?ids=${coinIds}&vs_currencies=usd,btc&include_24hr_vol=true&include_24hr_change=true&api_key=${config.apiKeys.coinGecko}`;
-
         //console.log(`Fetching data for multiple coins from CoinGecko: ${url}`);
-
         try {
             const data = await api.makePostRequest(url);
             //console.log(`Raw CoinGecko data:`, data);
-
             if (typeof data !== 'object' || data === null) {
                 throw new AppError(`Invalid data structure received from CoinGecko`);
             }
-
             const transformedData = {};
             Object.entries(data).forEach(([id, values]) => {
                 const coinConfig = config.coins.find(coin => coin.name === id);
@@ -164,7 +158,6 @@ const api = {
                     displayName: coinConfig?.displayName || coinConfig?.symbol || id
                 };
             });
-
             //console.log(`Transformed CoinGecko data:`, transformedData);
             cache.set(cacheKey, transformedData);
             return transformedData;
@@ -212,9 +205,7 @@ const api = {
         } else {
           url = `${config.apiEndpoints.cryptoCompareHistorical}?fsym=${coin}&tsym=USD&limit=${resolution.days}&api_key=${config.apiKeys.cryptoCompare}`;
         }
-
         //console.log(`CryptoCompare URL for ${coin}: ${url}`);
-
         try {
           const response = await api.makePostRequest(url);
           if (response.Response === "Error") {
@@ -229,9 +220,7 @@ const api = {
         }
       }
     });
-
     await Promise.all(fetchPromises);
-
     //console.log('Final results object:', JSON.stringify(results, null, 2));
     return results;
   }
@@ -295,16 +284,13 @@ displayCoinData: (coin, data) => {
         const volumeElement = document.querySelector(`#${coin.toLowerCase()}-volume-24h`);
         const btcPriceDiv = document.querySelector(`#${coin.toLowerCase()}-btc-price-div`);
         const priceBtcElement = document.querySelector(`#${coin.toLowerCase()}-price-btc`);
-
         if (priceUsdElement) {
             priceUsdElement.textContent = isError ? 'N/A' : `$ ${ui.formatPrice(coin, priceUSD)}`;
         }
-
         if (volumeDiv && volumeElement) {
             volumeElement.textContent = isError ? 'N/A' : `${utils.formatNumber(volume24h, 0)} USD`;
             volumeDiv.style.display = volumeToggle.isVisible ? 'flex' : 'none';
         }
-
         if (btcPriceDiv && priceBtcElement) {
             if (coin === 'BTC') {
                 btcPriceDiv.style.display = 'none';
@@ -313,10 +299,8 @@ displayCoinData: (coin, data) => {
                 btcPriceDiv.style.display = 'flex';
             }
         }
-
         ui.updatePriceChangeContainer(coin, isError ? null : priceChange1d);
     };
-
     try {
         if (data.error) {
             throw new Error(data.error);
@@ -324,19 +308,15 @@ displayCoinData: (coin, data) => {
         if (!data || !data.current_price) {
             throw new Error(`Invalid CoinGecko data structure for ${coin}`);
         }
-
         priceUSD = data.current_price;
         priceBTC = coin === 'BTC' ? 1 : data.price_btc || (data.current_price / app.btcPriceUSD);
         priceChange1d = data.price_change_percentage_24h;
         volume24h = data.total_volume;
-
         if (isNaN(priceUSD) || isNaN(priceBTC) || isNaN(volume24h)) {
             throw new Error(`Invalid numeric values in data for ${coin}`);
         }
-
         updateUI(false);
     } catch (error) {
-        console.error(`Error displaying data for ${coin}:`, error.message);
         updateUI(true);
     }
 },
@@ -381,11 +361,9 @@ displayCoinData: (coin, data) => {
   updateLoadTimeAndCache: (loadTime, cachedData) => {
     const loadTimeElement = document.getElementById('load-time');
     const cacheStatusElement = document.getElementById('cache-status');
-
     if (loadTimeElement) {
       loadTimeElement.textContent = `Load time: ${loadTime}ms`;
     }
-
     if (cacheStatusElement) {
       if (cachedData && cachedData.remainingTime) {
         const remainingMinutes = Math.ceil(cachedData.remainingTime / 60000);
@@ -398,7 +376,6 @@ displayCoinData: (coin, data) => {
         cacheStatusElement.classList.remove('text-green-500');
       }
     }
-
     ui.updateLastRefreshedTime();
   },
 
@@ -486,13 +463,6 @@ const chartModule = {
   chart: null,
   currentCoin: 'BTC',
   loadStartTime: 0,
-
-  cleanup: () => {
-    if (chartModule.chart) {
-      chartModule.chart.destroy();
-      chartModule.chart = null;
-    }
-  },
   verticalLinePlugin: {
     id: 'verticalLine',
     beforeDraw: (chart, args, options) => {
@@ -520,11 +490,9 @@ const chartModule = {
       logger.error('Failed to get chart context. Make sure the canvas element exists.');
       return;
     }
-
     const gradient = ctx.createLinearGradient(0, 0, 0, 400);
     gradient.addColorStop(0, 'rgba(77, 132, 240, 0.2)');
     gradient.addColorStop(1, 'rgba(77, 132, 240, 0)');
-
     chartModule.chart = new Chart(ctx, {
       type: 'line',
       data: {
@@ -674,12 +642,10 @@ const chartModule = {
       plugins: [chartModule.verticalLinePlugin]
     });
   },
-
   prepareChartData: (coinSymbol, data) => {
     if (!data) {
       return [];
     }
-
     try {
       let preparedData;
 
@@ -688,13 +654,10 @@ const chartModule = {
         endTime.setUTCMinutes(0, 0, 0);
         const endUnix = endTime.getTime();
         const startUnix = endUnix - (24 * 3600000);
-
         const hourlyPoints = [];
-
         for (let hourUnix = startUnix; hourUnix <= endUnix; hourUnix += 3600000) {
           const targetHour = new Date(hourUnix);
           targetHour.setUTCMinutes(0, 0, 0);
-
           const closestPoint = data.reduce((prev, curr) => {
             const prevTime = new Date(prev[0]);
             const currTime = new Date(curr[0]);
@@ -737,13 +700,12 @@ const chartModule = {
       } else {
         return [];
       }
-
       return preparedData.map(point => ({
         x: new Date(point.x).getTime(),
         y: point.y
       }));
     } catch (error) {
-      console.error("An error occured:", error.message);
+      console.error(`Error preparing chart data for ${coinSymbol}:`, error);
       return [];
     }
   },
@@ -760,13 +722,11 @@ const chartModule = {
         Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) <
         Math.abs(new Date(prev.x).getTime() - targetTime.getTime()) ? curr : prev
       );
-
       hourlyData.push({
         x: targetTime.getTime(),
         y: closestDataPoint.y
       });
     }
-
     return hourlyData;
   },
 
@@ -774,24 +734,26 @@ const chartModule = {
     try {
       chartModule.showChartLoader();
       chartModule.loadStartTime = Date.now();
-
       const cacheKey = `chartData_${coinSymbol}_${config.currentResolution}`;
       let cachedData = !forceRefresh ? cache.get(cacheKey) : null;
       let data;
-
       if (cachedData && Object.keys(cachedData.value).length > 0) {
         data = cachedData.value;
+        //console.log(`Using cached data for ${coinSymbol} (${config.currentResolution})`);
       } else {
+        //console.log(`Fetching fresh data for ${coinSymbol} (${config.currentResolution})`);
         const allData = await api.fetchHistoricalDataXHR([coinSymbol]);
         data = allData[coinSymbol];
         if (!data || Object.keys(data).length === 0) {
           throw new Error(`No data returned for ${coinSymbol}`);
         }
+        //console.log(`Caching new data for ${cacheKey}`);
         cache.set(cacheKey, data, config.cacheTTL);
         cachedData = null;
       }
 
       const chartData = chartModule.prepareChartData(coinSymbol, data);
+      //console.log(`Prepared chart data for ${coinSymbol}:`, chartData.slice(0, 5));
 
       if (chartData.length === 0) {
         throw new Error(`No valid chart data for ${coinSymbol}`);
@@ -807,11 +769,9 @@ const chartModule = {
         } else {
           const resolution = config.resolutions[config.currentResolution];
           chartModule.chart.options.scales.x.time.unit = resolution.interval === 'hourly' ? 'hour' : 'day';
-
           if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') {
             chartModule.chart.options.scales.x.time.unit = 'month';
           }
-
           if (config.currentResolution === 'year') {
             chartModule.chart.options.scales.x.ticks.maxTicksLimit = 12;
           } else if (config.currentResolution === 'sixMonths') {
@@ -823,6 +783,7 @@ const chartModule = {
 
         chartModule.chart.update('active');
       } else {
+        //console.error('Chart object not initialized');
         throw new Error('Chart object not initialized');
       }
 
@@ -831,6 +792,7 @@ const chartModule = {
       ui.updateLoadTimeAndCache(loadTime, cachedData);
 
     } catch (error) {
+      //console.error(`Error updating chart for ${coinSymbol}:`, error);
       ui.displayErrorMessage(`Failed to update chart for ${coinSymbol}: ${error.message}`);
     } finally {
       chartModule.hideChartLoader();
@@ -840,11 +802,10 @@ const chartModule = {
   showChartLoader: () => {
     const loader = document.getElementById('chart-loader');
     const chart = document.getElementById('coin-chart');
-
     if (!loader || !chart) {
+      //console.warn('Chart loader or chart container elements not found');
       return;
     }
-
     loader.classList.remove('hidden');
     chart.classList.add('hidden');
   },
@@ -852,63 +813,49 @@ const chartModule = {
   hideChartLoader: () => {
     const loader = document.getElementById('chart-loader');
     const chart = document.getElementById('coin-chart');
-
     if (!loader || !chart) {
+      //console.warn('Chart loader or chart container elements not found');
       return;
     }
-
     loader.classList.add('hidden');
     chart.classList.remove('hidden');
-  }
+  },
 };
 
 Chart.register(chartModule.verticalLinePlugin);
 
-const volumeToggle = {
-  isVisible: localStorage.getItem('volumeToggleState') === 'true',
-
-  cleanup: () => {
-    const toggleButton = document.getElementById('toggle-volume');
-    if (toggleButton) {
-      toggleButton.removeEventListener('click', volumeToggle.toggle);
-    }
-  },
-
-  init: () => {
-    volumeToggle.cleanup();
-
-    const toggleButton = document.getElementById('toggle-volume');
-    if (toggleButton) {
-      toggleButton.addEventListener('click', volumeToggle.toggle);
+  const volumeToggle = {
+    isVisible: localStorage.getItem('volumeToggleState') === 'true',
+    init: () => {
+      const toggleButton = document.getElementById('toggle-volume');
+      if (toggleButton) {
+        toggleButton.addEventListener('click', volumeToggle.toggle);
+        volumeToggle.updateVolumeDisplay();
+      }
+    },
+    toggle: () => {
+      volumeToggle.isVisible = !volumeToggle.isVisible;
+      localStorage.setItem('volumeToggleState', volumeToggle.isVisible.toString());
       volumeToggle.updateVolumeDisplay();
+    },
+    updateVolumeDisplay: () => {
+      const volumeDivs = document.querySelectorAll('[id$="-volume-div"]');
+      volumeDivs.forEach(div => {
+        div.style.display = volumeToggle.isVisible ? 'flex' : 'none';
+      });
+      const toggleButton = document.getElementById('toggle-volume');
+      if (toggleButton) {
+        updateButtonStyles(toggleButton, volumeToggle.isVisible, 'green');
+      }
     }
-  },
+  };
 
-  toggle: () => {
-    volumeToggle.isVisible = !volumeToggle.isVisible;
-    localStorage.setItem('volumeToggleState', volumeToggle.isVisible.toString());
-    volumeToggle.updateVolumeDisplay();
-  },
-
-  updateVolumeDisplay: () => {
-    const volumeDivs = document.querySelectorAll('[id$="-volume-div"]');
-    volumeDivs.forEach(div => {
-      div.style.display = volumeToggle.isVisible ? 'flex' : 'none';
-    });
-
-    const toggleButton = document.getElementById('toggle-volume');
-    if (toggleButton) {
-      updateButtonStyles(toggleButton, volumeToggle.isVisible, 'green');
-    }
+  function updateButtonStyles(button, isActive, color) {
+    button.classList.toggle('text-' + color + '-500', isActive);
+    button.classList.toggle('text-gray-600', !isActive);
+    button.classList.toggle('dark:text-' + color + '-400', isActive);
+    button.classList.toggle('dark:text-gray-400', !isActive);
   }
-};
-
-function updateButtonStyles(button, isActive, color) {
-  button.classList.toggle('text-' + color + '-500', isActive);
-  button.classList.toggle('text-gray-600', !isActive);
-  button.classList.toggle('dark:text-' + color + '-400', isActive);
-  button.classList.toggle('dark:text-gray-400', !isActive);
-}
 
 const app = {
   btcPriceUSD: 0,
@@ -924,177 +871,90 @@ const app = {
   },
   cacheTTL: 5 * 60 * 1000, // 5 minutes
   minimumRefreshInterval: 60 * 1000, // 1 minute
-  eventListeners: new Map(),
-  visibilityCleanup: null,
-
-  cleanup: () => {
-    if (app.autoRefreshInterval) {
-      clearTimeout(app.autoRefreshInterval);
-      app.autoRefreshInterval = null;
-    }
-
-    if (app.updateNextRefreshTimeRAF) {
-      cancelAnimationFrame(app.updateNextRefreshTimeRAF);
-      app.updateNextRefreshTimeRAF = null;
-    }
-
-    if (typeof app.visibilityCleanup === 'function') {
-      app.visibilityCleanup();
-      app.visibilityCleanup = null;
-    }
-
-    volumeToggle.cleanup();
-
-    app.removeEventListeners();
-
-    if (chartModule.chart) {
-      chartModule.chart.destroy();
-      chartModule.chart = null;
-    }
-
-    cache.clear();
-  },
-
-  removeEventListeners: () => {
-    app.eventListeners.forEach((listener, element) => {
-      if (element && typeof element.removeEventListener === 'function') {
-        element.removeEventListener(listener.type, listener.fn);
-      }
-    });
-    app.eventListeners.clear();
-  },
-
-  addEventListenerWithCleanup: (element, type, fn) => {
-    if (element && typeof element.addEventListener === 'function') {
-      element.addEventListener(type, fn);
-      app.eventListeners.set(element, { type, fn });
-    }
-  },
-
-  initResolutionButtons: () => {
-    const resolutionButtons = document.querySelectorAll('.resolution-button');
-    resolutionButtons.forEach(button => {
-      // Remove existing listeners first
-      const oldListener = button.getAttribute('data-resolution-listener');
-      if (oldListener && window[oldListener]) {
-        button.removeEventListener('click', window[oldListener]);
-        delete window[oldListener];
-      }
-
-      const listener = () => {
-        const resolution = button.id.split('-')[1];
-        const currentCoin = chartModule.currentCoin;
-
-        if (currentCoin !== 'WOW' || resolution === 'day') {
-          config.currentResolution = resolution;
-          chartModule.updateChart(currentCoin, true);
-          app.updateResolutionButtons(currentCoin);
-        }
-      };
-
-      const listenerName = `resolutionListener_${button.id}`;
-      window[listenerName] = listener;
-      button.setAttribute('data-resolution-listener', listenerName);
-      button.addEventListener('click', listener);
-    });
-  },
-
-  setupVisibilityHandler: () => {
-  const cleanup = () => {
-    if (window.visibilityHandler) {
-      document.removeEventListener('visibilitychange', window.visibilityHandler);
-      delete window.visibilityHandler;
-    }
-  };
-
-  cleanup();
-
-  window.visibilityHandler = () => {
-    if (!document.hidden && chartModule.chart) {
-      chartModule.updateChart(chartModule.currentCoin, true);
-    }
-  };
-
-  document.addEventListener('visibilitychange', window.visibilityHandler);
-  return cleanup;
-},
 
   init: () => {
     console.log('Initializing app...');
-    app.cleanup();
     window.addEventListener('load', app.onLoad);
     app.loadLastRefreshedTime();
     app.updateAutoRefreshButton();
-    app.initResolutionButtons();
-    app.setupVisibilityHandler();
     console.log('App initialized');
   },
 
   onLoad: async () => {
-    console.log('App onLoad event triggered');
-    ui.showLoader();
-    try {
-      volumeToggle.init();
-      await app.updateBTCPrice();
-      const chartContainer = document.getElementById('coin-chart');
-      if (chartContainer) {
-        chartModule.initChart();
-        chartModule.showChartLoader();
-      }
-
-      console.log('Loading all coin data...');
-      await app.loadAllCoinData();
-
-      if (chartModule.chart) {
-        config.currentResolution = 'day';
-        await chartModule.updateChart('BTC');
-        app.updateResolutionButtons('BTC');
-      }
-      ui.setActiveContainer('btc-container');
-
-      app.setupEventListeners();
-      app.initializeSelectImages();
-      app.initAutoRefresh();
-
-    } catch (error) {
-      console.error("An error occured:", error.message);
-      ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
-    } finally {
-      ui.hideLoader();
-      if (chartModule.chart) {
-        chartModule.hideChartLoader();
-      }
-      console.log('App onLoad completed');
+  console.log('App onLoad event triggered');
+  ui.showLoader();
+  try {
+    volumeToggle.init();
+    await app.updateBTCPrice();
+    const chartContainer = document.getElementById('coin-chart');
+    if (chartContainer) {
+      chartModule.initChart();
+      chartModule.showChartLoader();
+    } else {
+      //console.warn('Chart container not found, skipping chart initialization');
     }
-  },
 
-  loadAllCoinData: async () => {
-    try {
-      const allCoinData = await api.fetchCoinGeckoDataXHR();
-      if (allCoinData.error) {
-        throw new Error(allCoinData.error);
-      }
+    console.log('Loading all coin data...');
+    await app.loadAllCoinData();
 
-      for (const coin of config.coins) {
-        const coinData = allCoinData[coin.symbol.toLowerCase()];
-        if (coinData) {
-          coinData.displayName = coin.displayName || coin.symbol;
-          ui.displayCoinData(coin.symbol, coinData);
-          const cacheKey = `coinData_${coin.symbol}`;
-          cache.set(cacheKey, coinData);
+    if (chartModule.chart) {
+      config.currentResolution = 'day';
+      await chartModule.updateChart('BTC');
+      app.updateResolutionButtons('BTC');
+    }
+    ui.setActiveContainer('btc-container');
+
+    //console.log('Setting up event listeners and initializations...');
+    app.setupEventListeners();
+    app.initializeSelectImages();
+    app.initAutoRefresh();
+
+  } catch (error) {
+    //console.error('Error during initialization:', error);
+    ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
+  } finally {
+    ui.hideLoader();
+    if (chartModule.chart) {
+      chartModule.hideChartLoader();
+    }
+    console.log('App onLoad completed');
+  }
+},
+
+    loadAllCoinData: async () => {
+        //console.log('Loading data for all coins...');
+        try {
+            const allCoinData = await api.fetchCoinGeckoDataXHR();
+            if (allCoinData.error) {
+                throw new Error(allCoinData.error);
+            }
+
+            for (const coin of config.coins) {
+                const coinData = allCoinData[coin.symbol.toLowerCase()];
+                if (coinData) {
+                    coinData.displayName = coin.displayName || coin.symbol;
+                    ui.displayCoinData(coin.symbol, coinData);
+                    const cacheKey = `coinData_${coin.symbol}`;
+                    cache.set(cacheKey, coinData);
+                } else {
+                    //console.warn(`No data found for ${coin.symbol}`);
+                }
+            }
+        } catch (error) {
+            //console.error('Error loading all coin data:', error);
+            ui.displayErrorMessage('Failed to load coin data. Please try refreshing the page.');
+        } finally {
+            //console.log('All coin data loaded');
         }
-      }
-    } catch (error) {
-      console.error("An error occured:", error.message);
-      ui.displayErrorMessage('Failed to load coin data. Please try refreshing the page.');
-    }
-  },
+    },
 
   loadCoinData: async (coin) => {
+    //console.log(`Loading data for ${coin.symbol}...`);
     const cacheKey = `coinData_${coin.symbol}`;
     let cachedData = cache.get(cacheKey);
     let data;
     if (cachedData) {
+      //console.log(`Using cached data for ${coin.symbol}`);
       data = cachedData.value;
     } else {
       try {
@@ -1107,9 +967,11 @@ const app = {
         if (data.error) {
           throw new Error(data.error);
         }
+        //console.log(`Caching new data for ${coin.symbol}`);
         cache.set(cacheKey, data);
         cachedData = null;
       } catch (error) {
+        //console.error(`Error fetching ${coin.symbol} data:`, error.message);
         data = {
           error: error.message
         };
@@ -1119,13 +981,16 @@ const app = {
     }
     ui.displayCoinData(coin.symbol, data);
     ui.updateLoadTimeAndCache(0, cachedData);
+    //console.log(`Data loaded for ${coin.symbol}`);
   },
 
   setupEventListeners: () => {
+    //console.log('Setting up event listeners...');
     config.coins.forEach(coin => {
       const container = document.getElementById(`${coin.symbol.toLowerCase()}-container`);
       if (container) {
-        app.addEventListenerWithCleanup(container, 'click', () => {
+        container.addEventListener('click', () => {
+          //console.log(`${coin.symbol} container clicked`);
           ui.setActiveContainer(`${coin.symbol.toLowerCase()}-container`);
           if (chartModule.chart) {
             if (coin.symbol === 'WOW') {
@@ -1140,27 +1005,26 @@ const app = {
 
     const refreshAllButton = document.getElementById('refresh-all');
     if (refreshAllButton) {
-      app.addEventListenerWithCleanup(refreshAllButton, 'click', app.refreshAllData);
+      refreshAllButton.addEventListener('click', app.refreshAllData);
     }
 
     const headers = document.querySelectorAll('th');
     headers.forEach((header, index) => {
-      app.addEventListenerWithCleanup(header, 'click', () =>
-        app.sortTable(index, header.classList.contains('disabled'))
-      );
+      header.addEventListener('click', () => app.sortTable(index, header.classList.contains('disabled')));
     });
 
     const closeErrorButton = document.getElementById('close-error');
     if (closeErrorButton) {
-      app.addEventListenerWithCleanup(closeErrorButton, 'click', ui.hideErrorMessage);
+      closeErrorButton.addEventListener('click', ui.hideErrorMessage);
     }
+    //console.log('Event listeners set up');
   },
 
   initAutoRefresh: () => {
     console.log('Initializing auto-refresh...');
     const toggleAutoRefreshButton = document.getElementById('toggle-auto-refresh');
     if (toggleAutoRefreshButton) {
-      app.addEventListenerWithCleanup(toggleAutoRefreshButton, 'click', app.toggleAutoRefresh);
+      toggleAutoRefreshButton.addEventListener('click', app.toggleAutoRefresh);
       app.updateAutoRefreshButton();
     }
 
@@ -1189,7 +1053,7 @@ const app = {
             earliestExpiration = Math.min(earliestExpiration, cachedItem.expiresAt);
           }
         } catch (error) {
-          console.error("An error occured:", error.message);
+          //console.error(`Error parsing cached item ${key}:`, error);
           localStorage.removeItem(key);
         }
       }
@@ -1214,65 +1078,72 @@ const app = {
     localStorage.setItem('nextRefreshTime', app.nextRefreshTime.toString());
     app.updateNextRefreshTime();
   },
-
-  refreshAllData: async () => {
-    if (app.isRefreshing) {
-      console.log('Refresh already in progress, skipping...');
-      return;
-    }
-
-    console.log('Refreshing all data...');
-    app.isRefreshing = true;
-    ui.showLoader();
-    chartModule.showChartLoader();
-
-    try {
-      cache.clear();
-      await app.updateBTCPrice();
-
-      const allCoinData = await api.fetchCoinGeckoDataXHR();
-      if (allCoinData.error) {
-        throw new Error(allCoinData.error);
-      }
-
-      for (const coin of config.coins) {
-        const symbol = coin.symbol.toLowerCase();
-        const coinData = allCoinData[symbol];
-        if (coinData) {
-          coinData.displayName = coin.displayName || coin.symbol;
-          ui.displayCoinData(coin.symbol, coinData);
-          const cacheKey = `coinData_${coin.symbol}`;
-          cache.set(cacheKey, coinData);
+  
+    refreshAllData: async () => {
+        if (app.isRefreshing) {
+            console.log('Refresh already in progress, skipping...');
+            return;
         }
-      }
 
-      if (chartModule.currentCoin) {
-        await chartModule.updateChart(chartModule.currentCoin, true);
-      }
+        console.log('Refreshing all data...');
+        app.isRefreshing = true;
+        ui.showLoader();
+        chartModule.showChartLoader();
+        
+        try {
 
-      app.lastRefreshedTime = new Date();
-      localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
-      ui.updateLastRefreshedTime();
+            cache.clear();
+            
+            await app.updateBTCPrice();
+            
+            const allCoinData = await api.fetchCoinGeckoDataXHR();
+            if (allCoinData.error) {
+                throw new Error(allCoinData.error);
+            }
+            
+            for (const coin of config.coins) {
+                const symbol = coin.symbol.toLowerCase();
+                const coinData = allCoinData[symbol];
+                if (coinData) {
+                    coinData.displayName = coin.displayName || coin.symbol;
 
-    } catch (error) {
-      console.error("An error occured:", error.message);
-      ui.displayErrorMessage('Failed to refresh all data. Please try again.');
-    } finally {
-      ui.hideLoader();
-      chartModule.hideChartLoader();
-      app.isRefreshing = false;
-      if (app.isAutoRefreshEnabled) {
-        app.scheduleNextRefresh();
-      }
-    }
-  },
+                    ui.displayCoinData(coin.symbol, coinData);
+
+                    const cacheKey = `coinData_${coin.symbol}`;
+                    cache.set(cacheKey, coinData);
+                } else {
+                    //console.error(`No data found for ${coin.symbol}`);
+                }
+            }
+            
+            if (chartModule.currentCoin) {
+                await chartModule.updateChart(chartModule.currentCoin, true);
+            }
+            
+            app.lastRefreshedTime = new Date();
+            localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
+            ui.updateLastRefreshedTime();
+            
+            console.log('All data refreshed successfully');
+            
+        } catch (error) {
+            //console.error('Error refreshing all data:', error);
+            ui.displayErrorMessage('Failed to refresh all data. Please try again.');
+        } finally {
+            ui.hideLoader();
+            chartModule.hideChartLoader();
+            app.isRefreshing = false;
+            if (app.isAutoRefreshEnabled) {
+                app.scheduleNextRefresh();
+            }
+        }
+    },
 
   updateNextRefreshTime: () => {
     console.log('Updating next refresh time display');
     const nextRefreshSpan = document.getElementById('next-refresh-time');
     const labelElement = document.getElementById('next-refresh-label');
     const valueElement = document.getElementById('next-refresh-value');
-
     if (nextRefreshSpan && labelElement && valueElement) {
       if (app.nextRefreshTime) {
         if (app.updateNextRefreshTimeRAF) {
@@ -1296,7 +1167,6 @@ const app = {
             app.updateNextRefreshTimeRAF = requestAnimationFrame(updateDisplay);
           }
         };
-
         updateDisplay();
       } else {
         labelElement.textContent = '';
@@ -1323,6 +1193,7 @@ const app = {
   },
 
   startSpinAnimation: () => {
+    //console.log('Starting spin animation on auto-refresh button');
     const svg = document.querySelector('#toggle-auto-refresh svg');
     if (svg) {
       svg.classList.add('animate-spin');
@@ -1333,6 +1204,7 @@ const app = {
   },
 
   stopSpinAnimation: () => {
+    //console.log('Stopping spin animation on auto-refresh button');
     const svg = document.querySelector('#toggle-auto-refresh svg');
     if (svg) {
       svg.classList.remove('animate-spin');
@@ -1340,6 +1212,7 @@ const app = {
   },
 
   updateLastRefreshedTime: () => {
+    //console.log('Updating last refreshed time');
     const lastRefreshedElement = document.getElementById('last-refreshed-time');
     if (lastRefreshedElement && app.lastRefreshedTime) {
       const formattedTime = app.lastRefreshedTime.toLocaleTimeString();
@@ -1356,127 +1229,135 @@ const app = {
     }
   },
 
-  updateBTCPrice: async () => {
-    try {
-      const priceData = await api.fetchCoinGeckoDataXHR();
-      if (priceData.error) {
-        app.btcPriceUSD = 0;
-      } else if (priceData.btc && priceData.btc.current_price) {
-        app.btcPriceUSD = priceData.btc.current_price;
-      } else {
-        app.btcPriceUSD = 0;
-      }
-    } catch (error) {
-      console.error("An error occured:", error.message);
-      app.btcPriceUSD = 0;
-    }
-  },
+    updateBTCPrice: async () => {
+        //console.log('Updating BTC price...');
+        try {
+            const priceData = await api.fetchCoinGeckoDataXHR();
+            if (priceData.error) {
+                //console.error('Error fetching BTC price:', priceData.error);
+                app.btcPriceUSD = 0;
+            } else if (priceData.btc && priceData.btc.current_price) {
 
-  sortTable: (columnIndex) => {
-    const sortableColumns = [0, 5, 6, 7]; // 0: Time, 5: Rate, 6: Market +/-, 7: Trade
-    if (!sortableColumns.includes(columnIndex)) {
-      return;
-    }
-
-    const table = document.querySelector('table');
-    if (!table) {
-      return;
-    }
-
-    const rows = Array.from(table.querySelectorAll('tbody tr'));
-    const sortIcon = document.getElementById(`sort-icon-${columnIndex}`);
-    if (!sortIcon) {
-      return;
-    }
-
-    const sortOrder = sortIcon.textContent === '↓' ? 1 : -1;
-    sortIcon.textContent = sortOrder === 1 ? '↑' : '↓';
-
-    const getSafeTextContent = (element) => element ? element.textContent.trim() : '';
-
-    rows.sort((a, b) => {
-      let aValue, bValue;
-      switch (columnIndex) {
-        case 1: // Time column
-          aValue = getSafeTextContent(a.querySelector('td:first-child .text-xs:first-child'));
-          bValue = getSafeTextContent(b.querySelector('td:first-child .text-xs:first-child'));
-
-          const parseTime = (timeStr) => {
-            const [value, unit] = timeStr.split(' ');
-            const numValue = parseFloat(value);
-            switch(unit) {
-              case 'seconds': return numValue;
-              case 'minutes': return numValue * 60;
-              case 'hours': return numValue * 3600;
-              case 'days': return numValue * 86400;
-              default: return 0;
+                app.btcPriceUSD = priceData.btc.current_price;
+            } else {
+                //console.error('Unexpected BTC data structure:', priceData);
+                app.btcPriceUSD = 0;
             }
-          };
-          return (parseTime(bValue) - parseTime(aValue)) * sortOrder;
+        } catch (error) {
+            //console.error('Error fetching BTC price:', error);
+            app.btcPriceUSD = 0;
+        }
+        //console.log('Current BTC price:', app.btcPriceUSD);
+    },
 
-        case 5: // Rate
-        case 6: // Market +/-
-          aValue = getSafeTextContent(a.cells[columnIndex]);
-          bValue = getSafeTextContent(b.cells[columnIndex]);
+sortTable: (columnIndex) => {
+  //console.log(`Sorting column: ${columnIndex}`);
+  const sortableColumns = [0, 5, 6, 7]; // 0: Time, 5: Rate, 6: Market +/-, 7: Trade
+  if (!sortableColumns.includes(columnIndex)) {
+    //console.log(`Column ${columnIndex} is not sortable`);
+    return;
+  }
+  const table = document.querySelector('table');
+  if (!table) {
+    //console.error("Table not found for sorting.");
+    return;
+  }
+  const rows = Array.from(table.querySelectorAll('tbody tr'));
+  console.log(`Found ${rows.length} rows to sort`);
+  const sortIcon = document.getElementById(`sort-icon-${columnIndex}`);
+  if (!sortIcon) {
+    //console.error("Sort icon not found.");
+    return;
+  }
+  const sortOrder = sortIcon.textContent === '↓' ? 1 : -1;
+  sortIcon.textContent = sortOrder === 1 ? '↑' : '↓';
 
-          aValue = parseFloat(aValue.replace(/[^\d.-]/g, '') || '0');
-          bValue = parseFloat(bValue.replace(/[^\d.-]/g, '') || '0');
-          return (aValue - bValue) * sortOrder;
+  const getSafeTextContent = (element) => element ? element.textContent.trim() : '';
 
-        case 7: // Trade
-          const aCell = a.cells[columnIndex];
-          const bCell = b.cells[columnIndex];
+  rows.sort((a, b) => {
+    let aValue, bValue;
+    switch (columnIndex) {
+      case 1: // Time column
+        aValue = getSafeTextContent(a.querySelector('td:first-child .text-xs:first-child'));
+        bValue = getSafeTextContent(b.querySelector('td:first-child .text-xs:first-child'));
+        //console.log(`Comparing times: "${aValue}" vs "${bValue}"`);
 
-          aValue = getSafeTextContent(aCell.querySelector('a')) ||
-                   getSafeTextContent(aCell.querySelector('button')) ||
-                   getSafeTextContent(aCell);
-          bValue = getSafeTextContent(bCell.querySelector('a')) ||
-                   getSafeTextContent(bCell.querySelector('button')) ||
-                   getSafeTextContent(bCell);
+        const parseTime = (timeStr) => {
+          const [value, unit] = timeStr.split(' ');
+          const numValue = parseFloat(value);
+          switch(unit) {
+            case 'seconds': return numValue;
+            case 'minutes': return numValue * 60;
+            case 'hours': return numValue * 3600;
+            case 'days': return numValue * 86400;
+            default: return 0;
+          }
+        };
+        return (parseTime(bValue) - parseTime(aValue)) * sortOrder;
+      
+      case 5: // Rate
+      case 6: // Market +/-
+        aValue = getSafeTextContent(a.cells[columnIndex]);
+        bValue = getSafeTextContent(b.cells[columnIndex]);
+        //console.log(`Comparing values: "${aValue}" vs "${bValue}"`);
 
-          aValue = aValue.toLowerCase();
-          bValue = bValue.toLowerCase();
-
-          if (aValue === bValue) return 0;
-          if (aValue === "swap") return -1 * sortOrder;
-          if (bValue === "swap") return 1 * sortOrder;
-          return aValue.localeCompare(bValue) * sortOrder;
-
-        default:
-          aValue = getSafeTextContent(a.cells[columnIndex]);
-          bValue = getSafeTextContent(b.cells[columnIndex]);
-          return aValue.localeCompare(bValue, undefined, {
-            numeric: true,
-            sensitivity: 'base'
-          }) * sortOrder;
-      }
-    });
-
-    const tbody = table.querySelector('tbody');
-    if (tbody) {
-      const fragment = document.createDocumentFragment();
-      rows.forEach(row => fragment.appendChild(row));
-      tbody.appendChild(fragment);
+        aValue = parseFloat(aValue.replace(/[^\d.-]/g, '') || '0');
+        bValue = parseFloat(bValue.replace(/[^\d.-]/g, '') || '0');
+        return (aValue - bValue) * sortOrder;
+      
+      case 7: // Trade
+        const aCell = a.cells[columnIndex];
+        const bCell = b.cells[columnIndex];
+        //console.log('aCell:', aCell ? aCell.outerHTML : 'null');
+        //console.log('bCell:', bCell ? bCell.outerHTML : 'null');
+        
+        aValue = getSafeTextContent(aCell.querySelector('a')) || 
+                 getSafeTextContent(aCell.querySelector('button')) || 
+                 getSafeTextContent(aCell);
+        bValue = getSafeTextContent(bCell.querySelector('a')) || 
+                 getSafeTextContent(bCell.querySelector('button')) || 
+                 getSafeTextContent(bCell);
+        
+        aValue = aValue.toLowerCase();
+        bValue = bValue.toLowerCase();
+        
+        //console.log(`Comparing trade actions: "${aValue}" vs "${bValue}"`);
+        
+        if (aValue === bValue) return 0;
+        if (aValue === "swap") return -1 * sortOrder;
+        if (bValue === "swap") return 1 * sortOrder;
+        return aValue.localeCompare(bValue) * sortOrder;
+      
+      default:
+        aValue = getSafeTextContent(a.cells[columnIndex]);
+        bValue = getSafeTextContent(b.cells[columnIndex]);
+        //console.log(`Comparing default values: "${aValue}" vs "${bValue}"`);
+        return aValue.localeCompare(bValue, undefined, {
+          numeric: true,
+          sensitivity: 'base'
+        }) * sortOrder;
     }
-  },
+  });
 
+  const tbody = table.querySelector('tbody');
+  if (tbody) {
+    rows.forEach(row => tbody.appendChild(row));
+  } else {
+    //console.error("Table body not found.");
+  }
+  //console.log('Sorting completed');
+},
+  
   initializeSelectImages: () => {
     const updateSelectedImage = (selectId) => {
       const select = document.getElementById(selectId);
       const button = document.getElementById(`${selectId}_button`);
       if (!select || !button) {
+        //console.error(`Elements not found for ${selectId}`);
         return;
       }
-
-      const oldListener = select.getAttribute('data-change-listener');
-      if (oldListener && window[oldListener]) {
-        select.removeEventListener('change', window[oldListener]);
-        delete window[oldListener];
-      }
-
       const selectedOption = select.options[select.selectedIndex];
       const imageURL = selectedOption?.getAttribute('data-image');
-
       requestAnimationFrame(() => {
         if (imageURL) {
           button.style.backgroundImage = `url('${imageURL}')`;
@@ -1490,50 +1371,46 @@ const app = {
         button.style.minHeight = '25px';
       });
     };
-
+    const handleSelectChange = (event) => {
+      updateSelectedImage(event.target.id);
+    };
     ['coin_to', 'coin_from'].forEach(selectId => {
       const select = document.getElementById(selectId);
       if (select) {
-
-        const listenerName = `selectChangeListener_${selectId}`;
-        window[listenerName] = () => updateSelectedImage(selectId);
-
-        select.setAttribute('data-change-listener', listenerName);
-
-        select.addEventListener('change', window[listenerName]);
-
+        select.addEventListener('change', handleSelectChange);
         updateSelectedImage(selectId);
-      }
-    });
-  },
-
-  updateResolutionButtons: (coinSymbol) => {
-    const resolutionButtons = document.querySelectorAll('.resolution-button');
-    resolutionButtons.forEach(button => {
-      const resolution = button.id.split('-')[1];
-      if (coinSymbol === 'WOW') {
-        if (resolution === 'day') {
-          button.classList.remove('text-gray-400', 'cursor-not-allowed', 'opacity-50', 'outline-none');
-          button.classList.add('active');
-          button.disabled = false;
-        } else {
-          button.classList.add('text-gray-400', 'cursor-not-allowed', 'opacity-50', 'outline-none');
-          button.classList.remove('active');
-          button.disabled = true;
-        }
       } else {
-        button.classList.remove('text-gray-400', 'cursor-not-allowed', 'opacity-50', 'outline-none');
-        button.classList.toggle('active', resolution === config.currentResolution);
-        button.disabled = false;
+        //console.error(`Select element not found for ${selectId}`);
       }
     });
   },
 
-  toggleAutoRefresh: () => {
+updateResolutionButtons: (coinSymbol) => {
+  const resolutionButtons = document.querySelectorAll('.resolution-button');
+  resolutionButtons.forEach(button => {
+    const resolution = button.id.split('-')[1];
+    if (coinSymbol === 'WOW') {
+      if (resolution === 'day') {
+        button.classList.remove('text-gray-400', 'cursor-not-allowed', 'opacity-50', 'outline-none');
+        button.classList.add('active');
+        button.disabled = false;
+      } else {
+        button.classList.add('text-gray-400', 'cursor-not-allowed', 'opacity-50', 'outline-none');
+        button.classList.remove('active');
+        button.disabled = true;
+      }
+    } else {
+      button.classList.remove('text-gray-400', 'cursor-not-allowed', 'opacity-50', 'outline-none');
+      button.classList.toggle('active', resolution === config.currentResolution);
+      button.disabled = false;
+    }
+  });
+},
+  
+ toggleAutoRefresh: () => {
     console.log('Toggling auto-refresh');
     app.isAutoRefreshEnabled = !app.isAutoRefreshEnabled;
     localStorage.setItem('autoRefreshEnabled', app.isAutoRefreshEnabled.toString());
-
     if (app.isAutoRefreshEnabled) {
       console.log('Auto-refresh enabled, scheduling next refresh');
       app.scheduleNextRefresh();
@@ -1546,18 +1423,24 @@ const app = {
       app.nextRefreshTime = null;
       localStorage.removeItem('nextRefreshTime');
     }
-
     app.updateAutoRefreshButton();
     app.updateNextRefreshTime();
   }
 };
 
+const resolutionButtons = document.querySelectorAll('.resolution-button');
+resolutionButtons.forEach(button => {
+  button.addEventListener('click', () => {
+    const resolution = button.id.split('-')[1];
+    const currentCoin = chartModule.currentCoin;
+    
+    if (currentCoin !== 'WOW' || resolution === 'day') {
+      config.currentResolution = resolution;
+      chartModule.updateChart(currentCoin, true);
+      app.updateResolutionButtons(currentCoin);
+    }
+  });
+});
 
 // LOAD
 app.init();
-app.visibilityCleanup = app.setupVisibilityHandler();
-
-window.addEventListener('beforeunload', () => {
-  console.log('Page unloading, cleaning up...');
-  app.cleanup();
-});