lint: auto eslint

This commit is contained in:
nahuhh 2025-01-16 21:26:20 +00:00
parent e548cf2b3b
commit f473d66de5
4 changed files with 119 additions and 119 deletions

View file

@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', () => {
const image = selectedOption.getAttribute('data-image') || ''; const image = selectedOption.getAttribute('data-image') || '';
const name = selectedOption.textContent.trim(); const name = selectedOption.textContent.trim();
select.style.backgroundImage = image ? `url(${image}?${new Date().getTime()})` : ''; select.style.backgroundImage = image ? `url(${image}?${new Date().getTime()})` : '';
const selectImage = select.nextElementSibling.querySelector('.select-image'); const selectImage = select.nextElementSibling.querySelector('.select-image');
if (selectImage) { if (selectImage) {
selectImage.src = image; selectImage.src = image;

View file

@ -1,5 +1,5 @@
window.addEventListener('DOMContentLoaded', (event) => { window.addEventListener('DOMContentLoaded', (event) => {
let err_msgs = document.querySelectorAll('p.error_msg'); const err_msgs = document.querySelectorAll('p.error_msg');
for (let i = 0; i < err_msgs.length; i++) { for (let i = 0; i < err_msgs.length; i++) {
err_msg = err_msgs[i].innerText; err_msg = err_msgs[i].innerText;
if (err_msg.indexOf('coin_to') >= 0 || err_msg.indexOf('Coin To') >= 0) { if (err_msg.indexOf('coin_to') >= 0 || err_msg.indexOf('Coin To') >= 0) {
@ -29,9 +29,9 @@ window.addEventListener('DOMContentLoaded', (event) => {
} }
// remove error class on input or select focus // remove error class on input or select focus
let inputs = document.querySelectorAll('input.error'); const inputs = document.querySelectorAll('input.error');
let selects = document.querySelectorAll('select.error'); const selects = document.querySelectorAll('select.error');
let elements = [...inputs, ...selects]; const elements = [...inputs, ...selects];
elements.forEach((element) => { elements.forEach((element) => {
element.addEventListener('focus', (event) => { element.addEventListener('focus', (event) => {
event.target.classList.remove('error'); event.target.classList.remove('error');

View file

@ -5,16 +5,16 @@ const EventManager = {
if (!this.listeners.has(element)) { if (!this.listeners.has(element)) {
this.listeners.set(element, new Map()); this.listeners.set(element, new Map());
} }
const elementListeners = this.listeners.get(element); const elementListeners = this.listeners.get(element);
if (!elementListeners.has(type)) { if (!elementListeners.has(type)) {
elementListeners.set(type, new Set()); elementListeners.set(type, new Set());
} }
const handlerInfo = { handler, options }; const handlerInfo = { handler, options };
elementListeners.get(type).add(handlerInfo); elementListeners.get(type).add(handlerInfo);
element.addEventListener(type, handler, options); element.addEventListener(type, handler, options);
return handlerInfo; return handlerInfo;
}, },
@ -188,7 +188,7 @@ const ScrollOptimizer = {
const tooltipId = tooltipTrigger.getAttribute('data-tooltip-target'); const tooltipId = tooltipTrigger.getAttribute('data-tooltip-target');
let tooltip = this.tooltipCache.get(tooltipTrigger); let tooltip = this.tooltipCache.get(tooltipTrigger);
if (!tooltip) { if (!tooltip) {
tooltip = document.getElementById(tooltipId); tooltip = document.getElementById(tooltipId);
if (tooltip) { if (tooltip) {
@ -259,7 +259,7 @@ const WebSocketManager = {
this.handlePageVisible(); this.handlePageVisible();
} }
}; };
document.addEventListener('visibilitychange', this.handlers.visibilityChange); document.addEventListener('visibilitychange', this.handlers.visibilityChange);
}, },
@ -721,11 +721,11 @@ const IdentityManager = {
const response = await fetch(`/json/identities/${address}`, { const response = await fetch(`/json/identities/${address}`, {
signal: AbortSignal.timeout(5000) signal: AbortSignal.timeout(5000)
}); });
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
return await response.json(); return await response.json();
} catch (error) { } catch (error) {
if (attempt >= this.maxRetries) { if (attempt >= this.maxRetries) {
@ -1182,7 +1182,7 @@ async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwn
const fromPriceUSD = latestPrices[fromSymbol]?.usd; const fromPriceUSD = latestPrices[fromSymbol]?.usd;
const toPriceUSD = latestPrices[toSymbol]?.usd; const toPriceUSD = latestPrices[toSymbol]?.usd;
if (fromPriceUSD === null || toPriceUSD === null || if (fromPriceUSD === null || toPriceUSD === null ||
fromPriceUSD === undefined || toPriceUSD === undefined) { fromPriceUSD === undefined || toPriceUSD === undefined) {
resolve(null); resolve(null);
return; return;
@ -1259,7 +1259,7 @@ async function fetchLatestPrices() {
const PRICES_CACHE_KEY = 'prices_coingecko'; const PRICES_CACHE_KEY = 'prices_coingecko';
const RETRY_DELAY = 5000; const RETRY_DELAY = 5000;
const MAX_RETRIES = 3; const MAX_RETRIES = 3;
const cachedData = CacheManager.get(PRICES_CACHE_KEY); const cachedData = CacheManager.get(PRICES_CACHE_KEY);
if (cachedData && cachedData.remainingTime > 30000) { if (cachedData && cachedData.remainingTime > 30000) {
console.log('Using cached price data'); console.log('Using cached price data');
@ -1268,7 +1268,7 @@ async function fetchLatestPrices() {
} }
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`; 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`;
let retryCount = 0; let retryCount = 0;
let data = null; let data = null;
@ -1282,7 +1282,7 @@ async function fetchLatestPrices() {
try { try {
console.log('Attempting price fetch with API key...'); console.log('Attempting price fetch with API key...');
const urlWithKey = `${baseUrl}&api_key=${offersConfig.apiKeys.coinGecko}`; const urlWithKey = `${baseUrl}&api_key=${offersConfig.apiKeys.coinGecko}`;
const response = await fetch('/json/readurl', { const response = await fetch('/json/readurl', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -1306,9 +1306,9 @@ async function fetchLatestPrices() {
continue; continue;
} }
const hasValidPrices = Object.values(responseData).some(coin => const hasValidPrices = Object.values(responseData).some(coin =>
coin && typeof coin === 'object' && coin && typeof coin === 'object' &&
typeof coin.usd === 'number' && typeof coin.usd === 'number' &&
!isNaN(coin.usd) !isNaN(coin.usd)
); );
@ -1343,7 +1343,7 @@ async function fetchLatestPrices() {
tableRateModule.setFallbackValue(coin, prices.usd); tableRateModule.setFallbackValue(coin, prices.usd);
} }
}); });
return data; return data;
} }
@ -1605,7 +1605,7 @@ function updateClearFiltersButton() {
function cleanupRow(row) { function cleanupRow(row) {
EventManager.removeAll(row); EventManager.removeAll(row);
const tooltips = row.querySelectorAll('[data-tooltip-target]'); const tooltips = row.querySelectorAll('[data-tooltip-target]');
tooltips.forEach(tooltip => { tooltips.forEach(tooltip => {
const tooltipId = tooltip.getAttribute('data-tooltip-target'); const tooltipId = tooltip.getAttribute('data-tooltip-target');
@ -1618,7 +1618,7 @@ function cleanupRow(row) {
function cleanupTable() { function cleanupTable() {
EventManager.clearAll(); EventManager.clearAll();
if (offersBody) { if (offersBody) {
const existingRows = offersBody.querySelectorAll('tr'); const existingRows = offersBody.querySelectorAll('tr');
existingRows.forEach(row => { existingRows.forEach(row => {
@ -1686,13 +1686,13 @@ async function updateOffersTable() {
const BATCH_SIZE = 5; const BATCH_SIZE = 5;
const identities = []; const identities = [];
for (let i = 0; i < itemsToDisplay.length; i += BATCH_SIZE) { for (let i = 0; i < itemsToDisplay.length; i += BATCH_SIZE) {
const batch = itemsToDisplay.slice(i, i + BATCH_SIZE); const batch = itemsToDisplay.slice(i, i + BATCH_SIZE);
const batchPromises = batch.map(offer => const batchPromises = batch.map(offer =>
offer.addr_from ? IdentityManager.getIdentityData(offer.addr_from) : Promise.resolve(null) offer.addr_from ? IdentityManager.getIdentityData(offer.addr_from) : Promise.resolve(null)
); );
const batchResults = await Promise.all(batchPromises); const batchResults = await Promise.all(batchPromises);
identities.push(...batchResults); identities.push(...batchResults);
@ -2329,8 +2329,8 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
const getPriceKey = (coin) => { const getPriceKey = (coin) => {
const lowerCoin = coin.toLowerCase(); const lowerCoin = coin.toLowerCase();
return lowerCoin === 'firo' || lowerCoin === 'zcoin' ? 'zcoin' : return lowerCoin === 'firo' || lowerCoin === 'zcoin' ? 'zcoin' :
lowerCoin === 'bitcoin cash' ? 'bitcoin-cash' : lowerCoin === 'bitcoin cash' ? 'bitcoin-cash' :
lowerCoin === 'particl anon' || lowerCoin === 'particl blind' ? 'particl' : lowerCoin === 'particl anon' || lowerCoin === 'particl blind' ? 'particl' :
coinNameToSymbol[coin] || lowerCoin; coinNameToSymbol[coin] || lowerCoin;
}; };
@ -2340,7 +2340,7 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
const fromPriceUSD = latestPrices[fromSymbol]?.usd; const fromPriceUSD = latestPrices[fromSymbol]?.usd;
const toPriceUSD = latestPrices[toSymbol]?.usd; const toPriceUSD = latestPrices[toSymbol]?.usd;
if (fromPriceUSD === null || toPriceUSD === null || if (fromPriceUSD === null || toPriceUSD === null ||
fromPriceUSD === undefined || toPriceUSD === undefined) { fromPriceUSD === undefined || toPriceUSD === undefined) {
return `<p class="font-bold mb-1">Price Information Unavailable</p> return `<p class="font-bold mb-1">Price Information Unavailable</p>
<p>Current market prices are temporarily unavailable.</p> <p>Current market prices are temporarily unavailable.</p>
@ -2421,7 +2421,7 @@ function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) {
const fromPriceUSD = latestPrices[fromSymbol]?.usd; const fromPriceUSD = latestPrices[fromSymbol]?.usd;
const toPriceUSD = latestPrices[toSymbol]?.usd; const toPriceUSD = latestPrices[toSymbol]?.usd;
if (fromPriceUSD === null || toPriceUSD === null || if (fromPriceUSD === null || toPriceUSD === null ||
fromPriceUSD === undefined || toPriceUSD === undefined) { fromPriceUSD === undefined || toPriceUSD === undefined) {
return ` return `
<p class="font-bold mb-1">Exchange Rate Information</p> <p class="font-bold mb-1">Exchange Rate Information</p>
@ -2880,7 +2880,7 @@ function setupRowEventListeners(row, offer) {
tooltip.classList.remove('invisible', 'opacity-0'); tooltip.classList.remove('invisible', 'opacity-0');
} }
}); });
EventManager.add(trigger, 'mouseleave', () => { EventManager.add(trigger, 'mouseleave', () => {
const tooltipId = trigger.getAttribute('data-tooltip-target'); const tooltipId = trigger.getAttribute('data-tooltip-target');
const tooltip = document.getElementById(tooltipId); const tooltip = document.getElementById(tooltipId);
@ -3015,7 +3015,7 @@ async function cleanup() {
console.log(`Total cleanup time: ${totalTime}ms`); console.log(`Total cleanup time: ${totalTime}ms`);
console.log('Steps completed:', this.steps.length); console.log('Steps completed:', this.steps.length);
console.log('Errors encountered:', this.errors.length); console.log('Errors encountered:', this.errors.length);
if (this.steps.length > 0) { if (this.steps.length > 0) {
console.group('Steps Timeline'); console.group('Steps Timeline');
this.steps.forEach(({step, time}) => { this.steps.forEach(({step, time}) => {
@ -3023,7 +3023,7 @@ async function cleanup() {
}); });
console.groupEnd(); console.groupEnd();
} }
if (this.errors.length > 0) { if (this.errors.length > 0) {
console.group('Errors'); console.group('Errors');
this.errors.forEach(({step, error, time}) => { this.errors.forEach(({step, error, time}) => {

View file

@ -40,9 +40,9 @@ const config = {
// UTILS // UTILS
const utils = { const utils = {
formatNumber: (number, decimals = 2) => formatNumber: (number, decimals = 2) =>
number.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ','), number.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ','),
formatDate: (timestamp, resolution) => { formatDate: (timestamp, resolution) => {
const date = new Date(timestamp); const date = new Date(timestamp);
const options = { const options = {
@ -114,7 +114,7 @@ const api = {
})); }));
}); });
}, },
fetchCryptoCompareDataXHR: (coin) => { fetchCryptoCompareDataXHR: (coin) => {
const url = `${config.apiEndpoints.cryptoCompare}?fsyms=${coin}&tsyms=USD,BTC&api_key=${config.apiKeys.cryptoCompare}`; const url = `${config.apiEndpoints.cryptoCompare}?fsyms=${coin}&tsyms=USD,BTC&api_key=${config.apiKeys.cryptoCompare}`;
const headers = { const headers = {
@ -126,10 +126,10 @@ const api = {
error: error.message error: error.message
})); }));
}, },
fetchCoinGeckoDataXHR: async () => { fetchCoinGeckoDataXHR: async () => {
const cacheKey = 'coinGeckoOneLiner'; const cacheKey = 'coinGeckoOneLiner';
let cachedData = cache.get(cacheKey); const cachedData = cache.get(cacheKey);
if (cachedData) { if (cachedData) {
console.log('Using cached CoinGecko data'); console.log('Using cached CoinGecko data');
@ -143,15 +143,15 @@ const api = {
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}`; 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}`); //console.log(`Fetching data for multiple coins from CoinGecko: ${url}`);
try { try {
const data = await api.makePostRequest(url); const data = await api.makePostRequest(url);
//console.log(`Raw CoinGecko data:`, data); //console.log(`Raw CoinGecko data:`, data);
if (typeof data !== 'object' || data === null) { if (typeof data !== 'object' || data === null) {
throw new AppError(`Invalid data structure received from CoinGecko`); throw new AppError(`Invalid data structure received from CoinGecko`);
} }
const transformedData = {}; const transformedData = {};
Object.entries(data).forEach(([id, values]) => { Object.entries(data).forEach(([id, values]) => {
const coinConfig = config.coins.find(coin => coin.name === id); const coinConfig = config.coins.find(coin => coin.name === id);
@ -164,7 +164,7 @@ const api = {
displayName: coinConfig?.displayName || coinConfig?.symbol || id displayName: coinConfig?.displayName || coinConfig?.symbol || id
}; };
}); });
//console.log(`Transformed CoinGecko data:`, transformedData); //console.log(`Transformed CoinGecko data:`, transformedData);
cache.set(cacheKey, transformedData); cache.set(cacheKey, transformedData);
return transformedData; return transformedData;
@ -296,16 +296,16 @@ displayCoinData: (coin, data) => {
const volumeElement = document.querySelector(`#${coin.toLowerCase()}-volume-24h`); const volumeElement = document.querySelector(`#${coin.toLowerCase()}-volume-24h`);
const btcPriceDiv = document.querySelector(`#${coin.toLowerCase()}-btc-price-div`); const btcPriceDiv = document.querySelector(`#${coin.toLowerCase()}-btc-price-div`);
const priceBtcElement = document.querySelector(`#${coin.toLowerCase()}-price-btc`); const priceBtcElement = document.querySelector(`#${coin.toLowerCase()}-price-btc`);
if (priceUsdElement) { if (priceUsdElement) {
priceUsdElement.textContent = isError ? 'N/A' : `$ ${ui.formatPrice(coin, priceUSD)}`; priceUsdElement.textContent = isError ? 'N/A' : `$ ${ui.formatPrice(coin, priceUSD)}`;
} }
if (volumeDiv && volumeElement) { if (volumeDiv && volumeElement) {
volumeElement.textContent = isError ? 'N/A' : `${utils.formatNumber(volume24h, 0)} USD`; volumeElement.textContent = isError ? 'N/A' : `${utils.formatNumber(volume24h, 0)} USD`;
volumeDiv.style.display = volumeToggle.isVisible ? 'flex' : 'none'; volumeDiv.style.display = volumeToggle.isVisible ? 'flex' : 'none';
} }
if (btcPriceDiv && priceBtcElement) { if (btcPriceDiv && priceBtcElement) {
if (coin === 'BTC') { if (coin === 'BTC') {
btcPriceDiv.style.display = 'none'; btcPriceDiv.style.display = 'none';
@ -314,10 +314,10 @@ displayCoinData: (coin, data) => {
btcPriceDiv.style.display = 'flex'; btcPriceDiv.style.display = 'flex';
} }
} }
ui.updatePriceChangeContainer(coin, isError ? null : priceChange1d); ui.updatePriceChangeContainer(coin, isError ? null : priceChange1d);
}; };
try { try {
if (data.error) { if (data.error) {
throw new Error(data.error); throw new Error(data.error);
@ -325,51 +325,51 @@ displayCoinData: (coin, data) => {
if (!data || !data.current_price) { if (!data || !data.current_price) {
throw new Error(`Invalid CoinGecko data structure for ${coin}`); throw new Error(`Invalid CoinGecko data structure for ${coin}`);
} }
priceUSD = data.current_price; priceUSD = data.current_price;
priceBTC = coin === 'BTC' ? 1 : data.price_btc || (data.current_price / app.btcPriceUSD); priceBTC = coin === 'BTC' ? 1 : data.price_btc || (data.current_price / app.btcPriceUSD);
priceChange1d = data.price_change_percentage_24h; priceChange1d = data.price_change_percentage_24h;
volume24h = data.total_volume; volume24h = data.total_volume;
if (isNaN(priceUSD) || isNaN(priceBTC) || isNaN(volume24h)) { if (isNaN(priceUSD) || isNaN(priceBTC) || isNaN(volume24h)) {
throw new Error(`Invalid numeric values in data for ${coin}`); throw new Error(`Invalid numeric values in data for ${coin}`);
} }
updateUI(false); updateUI(false);
} catch (error) { } catch (error) {
//console.error(`Error displaying data for ${coin}:`, error.message); //console.error(`Error displaying data for ${coin}:`, error.message);
updateUI(true); updateUI(true);
} }
}, },
showLoader: () => { showLoader: () => {
const loader = document.getElementById('loader'); const loader = document.getElementById('loader');
if (loader) { if (loader) {
loader.classList.remove('hidden'); loader.classList.remove('hidden');
} }
}, },
hideLoader: () => { hideLoader: () => {
const loader = document.getElementById('loader'); const loader = document.getElementById('loader');
if (loader) { if (loader) {
loader.classList.add('hidden'); loader.classList.add('hidden');
} }
}, },
showCoinLoader: (coinSymbol) => { showCoinLoader: (coinSymbol) => {
const loader = document.getElementById(`${coinSymbol.toLowerCase()}-loader`); const loader = document.getElementById(`${coinSymbol.toLowerCase()}-loader`);
if (loader) { if (loader) {
loader.classList.remove('hidden'); loader.classList.remove('hidden');
} }
}, },
hideCoinLoader: (coinSymbol) => { hideCoinLoader: (coinSymbol) => {
const loader = document.getElementById(`${coinSymbol.toLowerCase()}-loader`); const loader = document.getElementById(`${coinSymbol.toLowerCase()}-loader`);
if (loader) { if (loader) {
loader.classList.add('hidden'); loader.classList.add('hidden');
} }
}, },
updateCacheStatus: (isCached) => { updateCacheStatus: (isCached) => {
const cacheStatusElement = document.getElementById('cache-status'); const cacheStatusElement = document.getElementById('cache-status');
if (cacheStatusElement) { if (cacheStatusElement) {
@ -378,15 +378,15 @@ displayCoinData: (coin, data) => {
cacheStatusElement.classList.toggle('text-blue-500', !isCached); cacheStatusElement.classList.toggle('text-blue-500', !isCached);
} }
}, },
updateLoadTimeAndCache: (loadTime, cachedData) => { updateLoadTimeAndCache: (loadTime, cachedData) => {
const loadTimeElement = document.getElementById('load-time'); const loadTimeElement = document.getElementById('load-time');
const cacheStatusElement = document.getElementById('cache-status'); const cacheStatusElement = document.getElementById('cache-status');
if (loadTimeElement) { if (loadTimeElement) {
loadTimeElement.textContent = `Load time: ${loadTime}ms`; loadTimeElement.textContent = `Load time: ${loadTime}ms`;
} }
if (cacheStatusElement) { if (cacheStatusElement) {
if (cachedData && cachedData.remainingTime) { if (cachedData && cachedData.remainingTime) {
const remainingMinutes = Math.ceil(cachedData.remainingTime / 60000); const remainingMinutes = Math.ceil(cachedData.remainingTime / 60000);
@ -402,7 +402,7 @@ displayCoinData: (coin, data) => {
ui.updateLastRefreshedTime(); ui.updateLastRefreshedTime();
}, },
updatePriceChangeContainer: (coin, priceChange) => { updatePriceChangeContainer: (coin, priceChange) => {
const container = document.querySelector(`#${coin.toLowerCase()}-price-change-container`); const container = document.querySelector(`#${coin.toLowerCase()}-price-change-container`);
if (container) { if (container) {
@ -411,7 +411,7 @@ displayCoinData: (coin, data) => {
'N/A'; 'N/A';
} }
}, },
updateLastRefreshedTime: () => { updateLastRefreshedTime: () => {
const lastRefreshedElement = document.getElementById('last-refreshed-time'); const lastRefreshedElement = document.getElementById('last-refreshed-time');
if (lastRefreshedElement && app.lastRefreshedTime) { if (lastRefreshedElement && app.lastRefreshedTime) {
@ -419,7 +419,7 @@ displayCoinData: (coin, data) => {
lastRefreshedElement.textContent = `Last Refreshed: ${formattedTime}`; lastRefreshedElement.textContent = `Last Refreshed: ${formattedTime}`;
} }
}, },
positivePriceChangeHTML: (value) => ` positivePriceChangeHTML: (value) => `
<div class="flex flex-wrap items-center py-px px-1 border border-green-500 rounded-full"> <div class="flex flex-wrap items-center py-px px-1 border border-green-500 rounded-full">
<svg class="mr-0.5" width="15" height="10" viewBox="0 0 15 10" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="mr-0.5" width="15" height="10" viewBox="0 0 15 10" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -428,7 +428,7 @@ displayCoinData: (coin, data) => {
<span class="text-xs text-green-500 font-medium">${value.toFixed(2)}%</span> <span class="text-xs text-green-500 font-medium">${value.toFixed(2)}%</span>
</div> </div>
`, `,
negativePriceChangeHTML: (value) => ` negativePriceChangeHTML: (value) => `
<div class="flex flex-wrap items-center py-px px-1 border border-red-500 rounded-full"> <div class="flex flex-wrap items-center py-px px-1 border border-red-500 rounded-full">
<svg class="mr-0.5" width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="mr-0.5" width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -437,7 +437,7 @@ displayCoinData: (coin, data) => {
<span class="text-xs text-red-500 font-medium">${Math.abs(value).toFixed(2)}%</span> <span class="text-xs text-red-500 font-medium">${Math.abs(value).toFixed(2)}%</span>
</div> </div>
`, `,
formatPrice: (coin, price) => { formatPrice: (coin, price) => {
if (typeof price !== 'number' || isNaN(price)) { if (typeof price !== 'number' || isNaN(price)) {
logger.error(`Invalid price for ${coin}:`, price); logger.error(`Invalid price for ${coin}:`, price);
@ -449,7 +449,7 @@ displayCoinData: (coin, data) => {
if (price < 1000) return price.toFixed(2); if (price < 1000) return price.toFixed(2);
return price.toFixed(1); return price.toFixed(1);
}, },
setActiveContainer: (containerId) => { setActiveContainer: (containerId) => {
const containerIds = ['btc', 'xmr', 'part', 'pivx', 'firo', 'dash', 'ltc', 'doge', 'eth', 'dcr', 'zano', 'wow', 'bch'].map(id => `${id}-container`); const containerIds = ['btc', 'xmr', 'part', 'pivx', 'firo', 'dash', 'ltc', 'doge', 'eth', 'dcr', 'zano', 'wow', 'bch'].map(id => `${id}-container`);
containerIds.forEach(id => { containerIds.forEach(id => {
@ -460,7 +460,7 @@ displayCoinData: (coin, data) => {
} }
}); });
}, },
displayErrorMessage: (message) => { displayErrorMessage: (message) => {
const errorOverlay = document.getElementById('error-overlay'); const errorOverlay = document.getElementById('error-overlay');
const errorMessage = document.getElementById('error-message'); const errorMessage = document.getElementById('error-message');
@ -471,7 +471,7 @@ displayCoinData: (coin, data) => {
chartContainer.classList.add('blurred'); chartContainer.classList.add('blurred');
} }
}, },
hideErrorMessage: () => { hideErrorMessage: () => {
const errorOverlay = document.getElementById('error-overlay'); const errorOverlay = document.getElementById('error-overlay');
const containersToBlur = document.querySelectorAll('.container-to-blur'); const containersToBlur = document.querySelectorAll('.container-to-blur');
@ -487,7 +487,7 @@ const chartModule = {
chart: null, chart: null,
currentCoin: 'BTC', currentCoin: 'BTC',
loadStartTime: 0, loadStartTime: 0,
cleanup: () => { cleanup: () => {
if (chartModule.chart) { if (chartModule.chart) {
chartModule.chart.destroy(); chartModule.chart.destroy();
@ -675,7 +675,7 @@ const chartModule = {
plugins: [chartModule.verticalLinePlugin] plugins: [chartModule.verticalLinePlugin]
}); });
}, },
prepareChartData: (coinSymbol, data) => { prepareChartData: (coinSymbol, data) => {
if (!data) { if (!data) {
return []; return [];
@ -689,13 +689,13 @@ const chartModule = {
endTime.setUTCMinutes(0, 0, 0); endTime.setUTCMinutes(0, 0, 0);
const endUnix = endTime.getTime(); const endUnix = endTime.getTime();
const startUnix = endUnix - (24 * 3600000); const startUnix = endUnix - (24 * 3600000);
const hourlyPoints = []; const hourlyPoints = [];
for (let hourUnix = startUnix; hourUnix <= endUnix; hourUnix += 3600000) { for (let hourUnix = startUnix; hourUnix <= endUnix; hourUnix += 3600000) {
const targetHour = new Date(hourUnix); const targetHour = new Date(hourUnix);
targetHour.setUTCMinutes(0, 0, 0); targetHour.setUTCMinutes(0, 0, 0);
const closestPoint = data.reduce((prev, curr) => { const closestPoint = data.reduce((prev, curr) => {
const prevTime = new Date(prev[0]); const prevTime = new Date(prev[0]);
const currTime = new Date(curr[0]); const currTime = new Date(curr[0]);
@ -756,11 +756,11 @@ const chartModule = {
for (let i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
const targetTime = new Date(twentyFourHoursAgo.getTime() + i * 60 * 60 * 1000); const targetTime = new Date(twentyFourHoursAgo.getTime() + i * 60 * 60 * 1000);
const closestDataPoint = data.reduce((prev, curr) => const closestDataPoint = data.reduce((prev, curr) =>
Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) < Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) <
Math.abs(new Date(prev.x).getTime() - targetTime.getTime()) ? curr : prev Math.abs(new Date(prev.x).getTime() - targetTime.getTime()) ? curr : prev
); );
hourlyData.push({ hourlyData.push({
x: targetTime.getTime(), x: targetTime.getTime(),
y: closestDataPoint.y y: closestDataPoint.y
@ -774,11 +774,11 @@ const chartModule = {
try { try {
chartModule.showChartLoader(); chartModule.showChartLoader();
chartModule.loadStartTime = Date.now(); chartModule.loadStartTime = Date.now();
const cacheKey = `chartData_${coinSymbol}_${config.currentResolution}`; const cacheKey = `chartData_${coinSymbol}_${config.currentResolution}`;
let cachedData = !forceRefresh ? cache.get(cacheKey) : null; let cachedData = !forceRefresh ? cache.get(cacheKey) : null;
let data; let data;
if (cachedData && Object.keys(cachedData.value).length > 0) { if (cachedData && Object.keys(cachedData.value).length > 0) {
data = cachedData.value; data = cachedData.value;
} else { } else {
@ -807,7 +807,7 @@ const chartModule = {
} else { } else {
const resolution = config.resolutions[config.currentResolution]; const resolution = config.resolutions[config.currentResolution];
chartModule.chart.options.scales.x.time.unit = resolution.interval === 'hourly' ? 'hour' : 'day'; chartModule.chart.options.scales.x.time.unit = resolution.interval === 'hourly' ? 'hour' : 'day';
if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') { if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') {
chartModule.chart.options.scales.x.time.unit = 'month'; chartModule.chart.options.scales.x.time.unit = 'month';
} }
@ -840,25 +840,25 @@ const chartModule = {
showChartLoader: () => { showChartLoader: () => {
const loader = document.getElementById('chart-loader'); const loader = document.getElementById('chart-loader');
const chart = document.getElementById('coin-chart'); const chart = document.getElementById('coin-chart');
if (!loader || !chart) { if (!loader || !chart) {
return; return;
} }
loader.classList.remove('hidden'); loader.classList.remove('hidden');
chart.classList.add('hidden'); chart.classList.add('hidden');
}, },
hideChartLoader: () => { hideChartLoader: () => {
const loader = document.getElementById('chart-loader'); const loader = document.getElementById('chart-loader');
const chart = document.getElementById('coin-chart'); const chart = document.getElementById('coin-chart');
if (!loader || !chart) { if (!loader || !chart) {
return; return;
} }
loader.classList.add('hidden'); loader.classList.add('hidden');
chart.classList.remove('hidden'); chart.classList.remove('hidden');
} }
}; };
@ -866,7 +866,7 @@ Chart.register(chartModule.verticalLinePlugin);
const volumeToggle = { const volumeToggle = {
isVisible: localStorage.getItem('volumeToggleState') === 'true', isVisible: localStorage.getItem('volumeToggleState') === 'true',
cleanup: () => { cleanup: () => {
const toggleButton = document.getElementById('toggle-volume'); const toggleButton = document.getElementById('toggle-volume');
if (toggleButton) { if (toggleButton) {
@ -876,7 +876,7 @@ const volumeToggle = {
init: () => { init: () => {
volumeToggle.cleanup(); volumeToggle.cleanup();
const toggleButton = document.getElementById('toggle-volume'); const toggleButton = document.getElementById('toggle-volume');
if (toggleButton) { if (toggleButton) {
toggleButton.addEventListener('click', volumeToggle.toggle); toggleButton.addEventListener('click', volumeToggle.toggle);
@ -942,7 +942,7 @@ const app = {
app.visibilityCleanup(); app.visibilityCleanup();
app.visibilityCleanup = null; app.visibilityCleanup = null;
} }
volumeToggle.cleanup(); volumeToggle.cleanup();
app.removeEventListeners(); app.removeEventListeners();
@ -980,11 +980,11 @@ const app = {
button.removeEventListener('click', window[oldListener]); button.removeEventListener('click', window[oldListener]);
delete window[oldListener]; delete window[oldListener];
} }
const listener = () => { const listener = () => {
const resolution = button.id.split('-')[1]; const resolution = button.id.split('-')[1];
const currentCoin = chartModule.currentCoin; const currentCoin = chartModule.currentCoin;
if (currentCoin !== 'WOW' || resolution === 'day') { if (currentCoin !== 'WOW' || resolution === 'day') {
config.currentResolution = resolution; config.currentResolution = resolution;
chartModule.updateChart(currentCoin, true); chartModule.updateChart(currentCoin, true);
@ -1041,21 +1041,21 @@ const app = {
chartModule.initChart(); chartModule.initChart();
chartModule.showChartLoader(); chartModule.showChartLoader();
} }
console.log('Loading all coin data...'); console.log('Loading all coin data...');
await app.loadAllCoinData(); await app.loadAllCoinData();
if (chartModule.chart) { if (chartModule.chart) {
config.currentResolution = 'day'; config.currentResolution = 'day';
await chartModule.updateChart('BTC'); await chartModule.updateChart('BTC');
app.updateResolutionButtons('BTC'); app.updateResolutionButtons('BTC');
} }
ui.setActiveContainer('btc-container'); ui.setActiveContainer('btc-container');
app.setupEventListeners(); app.setupEventListeners();
app.initializeSelectImages(); app.initializeSelectImages();
app.initAutoRefresh(); app.initAutoRefresh();
} catch (error) { } catch (error) {
ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.'); ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
} finally { } finally {
@ -1135,19 +1135,19 @@ const app = {
}); });
} }
}); });
const refreshAllButton = document.getElementById('refresh-all'); const refreshAllButton = document.getElementById('refresh-all');
if (refreshAllButton) { if (refreshAllButton) {
app.addEventListenerWithCleanup(refreshAllButton, 'click', app.refreshAllData); app.addEventListenerWithCleanup(refreshAllButton, 'click', app.refreshAllData);
} }
const headers = document.querySelectorAll('th'); const headers = document.querySelectorAll('th');
headers.forEach((header, index) => { headers.forEach((header, index) => {
app.addEventListenerWithCleanup(header, 'click', () => app.addEventListenerWithCleanup(header, 'click', () =>
app.sortTable(index, header.classList.contains('disabled')) app.sortTable(index, header.classList.contains('disabled'))
); );
}); });
const closeErrorButton = document.getElementById('close-error'); const closeErrorButton = document.getElementById('close-error');
if (closeErrorButton) { if (closeErrorButton) {
app.addEventListenerWithCleanup(closeErrorButton, 'click', ui.hideErrorMessage); app.addEventListenerWithCleanup(closeErrorButton, 'click', ui.hideErrorMessage);
@ -1222,16 +1222,16 @@ const app = {
app.isRefreshing = true; app.isRefreshing = true;
ui.showLoader(); ui.showLoader();
chartModule.showChartLoader(); chartModule.showChartLoader();
try { try {
cache.clear(); cache.clear();
await app.updateBTCPrice(); await app.updateBTCPrice();
const allCoinData = await api.fetchCoinGeckoDataXHR(); const allCoinData = await api.fetchCoinGeckoDataXHR();
if (allCoinData.error) { if (allCoinData.error) {
throw new Error(allCoinData.error); throw new Error(allCoinData.error);
} }
for (const coin of config.coins) { for (const coin of config.coins) {
const symbol = coin.symbol.toLowerCase(); const symbol = coin.symbol.toLowerCase();
const coinData = allCoinData[symbol]; const coinData = allCoinData[symbol];
@ -1242,15 +1242,15 @@ const app = {
cache.set(cacheKey, coinData); cache.set(cacheKey, coinData);
} }
} }
if (chartModule.currentCoin) { if (chartModule.currentCoin) {
await chartModule.updateChart(chartModule.currentCoin, true); await chartModule.updateChart(chartModule.currentCoin, true);
} }
app.lastRefreshedTime = new Date(); app.lastRefreshedTime = new Date();
localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString()); localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
ui.updateLastRefreshedTime(); ui.updateLastRefreshedTime();
} catch (error) { } catch (error) {
ui.displayErrorMessage('Failed to refresh all data. Please try again.'); ui.displayErrorMessage('Failed to refresh all data. Please try again.');
} finally { } finally {
@ -1268,7 +1268,7 @@ const app = {
const nextRefreshSpan = document.getElementById('next-refresh-time'); const nextRefreshSpan = document.getElementById('next-refresh-time');
const labelElement = document.getElementById('next-refresh-label'); const labelElement = document.getElementById('next-refresh-label');
const valueElement = document.getElementById('next-refresh-value'); const valueElement = document.getElementById('next-refresh-value');
if (nextRefreshSpan && labelElement && valueElement) { if (nextRefreshSpan && labelElement && valueElement) {
if (app.nextRefreshTime) { if (app.nextRefreshTime) {
if (app.updateNextRefreshTimeRAF) { if (app.updateNextRefreshTimeRAF) {
@ -1277,7 +1277,7 @@ const app = {
const updateDisplay = () => { const updateDisplay = () => {
const timeUntilRefresh = Math.max(0, Math.ceil((app.nextRefreshTime - Date.now()) / 1000)); const timeUntilRefresh = Math.max(0, Math.ceil((app.nextRefreshTime - Date.now()) / 1000));
if (timeUntilRefresh === 0) { if (timeUntilRefresh === 0) {
labelElement.textContent = ''; labelElement.textContent = '';
valueElement.textContent = app.refreshTexts.justRefreshed; valueElement.textContent = app.refreshTexts.justRefreshed;
@ -1287,12 +1287,12 @@ const app = {
labelElement.textContent = `${app.refreshTexts.label}: `; labelElement.textContent = `${app.refreshTexts.label}: `;
valueElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`; valueElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
} }
if (timeUntilRefresh > 0) { if (timeUntilRefresh > 0) {
app.updateNextRefreshTimeRAF = requestAnimationFrame(updateDisplay); app.updateNextRefreshTimeRAF = requestAnimationFrame(updateDisplay);
} }
}; };
updateDisplay(); updateDisplay();
} else { } else {
labelElement.textContent = ''; labelElement.textContent = '';
@ -1372,18 +1372,18 @@ const app = {
if (!sortableColumns.includes(columnIndex)) { if (!sortableColumns.includes(columnIndex)) {
return; return;
} }
const table = document.querySelector('table'); const table = document.querySelector('table');
if (!table) { if (!table) {
return; return;
} }
const rows = Array.from(table.querySelectorAll('tbody tr')); const rows = Array.from(table.querySelectorAll('tbody tr'));
const sortIcon = document.getElementById(`sort-icon-${columnIndex}`); const sortIcon = document.getElementById(`sort-icon-${columnIndex}`);
if (!sortIcon) { if (!sortIcon) {
return; return;
} }
const sortOrder = sortIcon.textContent === '↓' ? 1 : -1; const sortOrder = sortIcon.textContent === '↓' ? 1 : -1;
sortIcon.textContent = sortOrder === 1 ? '↑' : '↓'; sortIcon.textContent = sortOrder === 1 ? '↑' : '↓';
@ -1408,7 +1408,7 @@ const app = {
} }
}; };
return (parseTime(bValue) - parseTime(aValue)) * sortOrder; return (parseTime(bValue) - parseTime(aValue)) * sortOrder;
case 5: // Rate case 5: // Rate
case 6: // Market +/- case 6: // Market +/-
aValue = getSafeTextContent(a.cells[columnIndex]); aValue = getSafeTextContent(a.cells[columnIndex]);
@ -1417,26 +1417,26 @@ const app = {
aValue = parseFloat(aValue.replace(/[^\d.-]/g, '') || '0'); aValue = parseFloat(aValue.replace(/[^\d.-]/g, '') || '0');
bValue = parseFloat(bValue.replace(/[^\d.-]/g, '') || '0'); bValue = parseFloat(bValue.replace(/[^\d.-]/g, '') || '0');
return (aValue - bValue) * sortOrder; return (aValue - bValue) * sortOrder;
case 7: // Trade case 7: // Trade
const aCell = a.cells[columnIndex]; const aCell = a.cells[columnIndex];
const bCell = b.cells[columnIndex]; const bCell = b.cells[columnIndex];
aValue = getSafeTextContent(aCell.querySelector('a')) || aValue = getSafeTextContent(aCell.querySelector('a')) ||
getSafeTextContent(aCell.querySelector('button')) || getSafeTextContent(aCell.querySelector('button')) ||
getSafeTextContent(aCell); getSafeTextContent(aCell);
bValue = getSafeTextContent(bCell.querySelector('a')) || bValue = getSafeTextContent(bCell.querySelector('a')) ||
getSafeTextContent(bCell.querySelector('button')) || getSafeTextContent(bCell.querySelector('button')) ||
getSafeTextContent(bCell); getSafeTextContent(bCell);
aValue = aValue.toLowerCase(); aValue = aValue.toLowerCase();
bValue = bValue.toLowerCase(); bValue = bValue.toLowerCase();
if (aValue === bValue) return 0; if (aValue === bValue) return 0;
if (aValue === "swap") return -1 * sortOrder; if (aValue === "swap") return -1 * sortOrder;
if (bValue === "swap") return 1 * sortOrder; if (bValue === "swap") return 1 * sortOrder;
return aValue.localeCompare(bValue) * sortOrder; return aValue.localeCompare(bValue) * sortOrder;
default: default:
aValue = getSafeTextContent(a.cells[columnIndex]); aValue = getSafeTextContent(a.cells[columnIndex]);
bValue = getSafeTextContent(b.cells[columnIndex]); bValue = getSafeTextContent(b.cells[columnIndex]);
@ -1528,7 +1528,7 @@ const app = {
console.log('Toggling auto-refresh'); console.log('Toggling auto-refresh');
app.isAutoRefreshEnabled = !app.isAutoRefreshEnabled; app.isAutoRefreshEnabled = !app.isAutoRefreshEnabled;
localStorage.setItem('autoRefreshEnabled', app.isAutoRefreshEnabled.toString()); localStorage.setItem('autoRefreshEnabled', app.isAutoRefreshEnabled.toString());
if (app.isAutoRefreshEnabled) { if (app.isAutoRefreshEnabled) {
console.log('Auto-refresh enabled, scheduling next refresh'); console.log('Auto-refresh enabled, scheduling next refresh');
app.scheduleNextRefresh(); app.scheduleNextRefresh();
@ -1541,7 +1541,7 @@ const app = {
app.nextRefreshTime = null; app.nextRefreshTime = null;
localStorage.removeItem('nextRefreshTime'); localStorage.removeItem('nextRefreshTime');
} }
app.updateAutoRefreshButton(); app.updateAutoRefreshButton();
app.updateNextRefreshTime(); app.updateNextRefreshTime();
} }