您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Auto-detect and convert Steam prices to any currency with live exchange rates
// ==UserScript== // @name Steam Multi-currency Live Converter // @name:ru Steam Авто-конвертер валют с живыми курсами // @name:de Steam Auto-Währungsumrechner mit Live-Kursen // @name:kk Steam Авто-валюта конвертері тікелей бағамдармен // @name:uk Steam Авто-конвертер валют з живими курсами // @namespace http://tampermonkey.net/ // @version 4.2 // @description Auto-detect and convert Steam prices to any currency with live exchange rates // @description:ru Автоматически определяет и конвертирует цены Steam в любую валюту с живыми курсами // @description:de Automatische Erkennung und Konvertierung von Steam-Preisen in jede Währung mit Live-Kursen // @description:kk Steam бағаларын тікелей бағамдармен кез келген валютаға автоматты анықтау және конвертация // @description:uk Автоматично визначає та конвертує ціни Steam у будь-яку валюту з живими курсами // @author Aze4ka // @license MIT // @match *://store.steampowered.com/* // @match *://steamcommunity.com/market/* // @grant GM_xmlhttpRequest // @connect cdn.jsdelivr.net // ==/UserScript== /* AAA 444444444 kkkkkkkk A:::A 4::::::::4 k::::::k A:::::A 4:::::::::4 k::::::k A:::::::A 4::::44::::4 k::::::k A:::::::::A zzzzzzzzzzzzzzzzz eeeeeeeeeeee 4::::4 4::::4 k:::::k kkkkkkkaaaaaaaaaaaaa A:::::A:::::A z:::::::::::::::z ee::::::::::::ee 4::::4 4::::4 k:::::k k:::::k a::::::::::::a A:::::A A:::::A z::::::::::::::z e::::::eeeee:::::ee 4::::4 4::::4 k:::::k k:::::k aaaaaaaaa:::::a A:::::A A:::::A zzzzzzzz::::::z e::::::e e:::::e4::::444444::::444 k:::::k k:::::k a::::a A:::::A A:::::A z::::::z e:::::::eeeee::::::e4::::::::::::::::4 k::::::k:::::k aaaaaaa:::::a A:::::AAAAAAAAA:::::A z::::::z e:::::::::::::::::e 4444444444:::::444 k:::::::::::k aa::::::::::::a A:::::::::::::::::::::A z::::::z e::::::eeeeeeeeeee 4::::4 k:::::::::::k a::::aaaa::::::a A:::::AAAAAAAAAAAAA:::::A z::::::z e:::::::e 4::::4 k::::::k:::::k a::::a a:::::a A:::::A A:::::A z::::::zzzzzzzze::::::::e 4::::4 k::::::k k:::::k a::::a a:::::a A:::::A A:::::A z::::::::::::::z e::::::::eeeeeeee 44::::::44k::::::k k:::::ka:::::aaaa::::::a A:::::A A:::::A z:::::::::::::::z ee:::::::::::::e 4::::::::4k::::::k k:::::ka::::::::::aa:::a AAAAAAA AAAAAAAzzzzzzzzzzzzzzzzz eeeeeeeeeeeeee 4444444444kkkkkkkk kkkkkkkaaaaaaaaaa aaaa */ (function() { 'use strict'; const currencySymbols = { USD: '$', EUR: '€', CHF: 'CHF ', PLN: 'zł', CZK: 'Kč', GBP: '£', CAD: 'CA$', AUD: 'A$', JPY: '¥', KZT: '₸', HUF: 'Ft', RON: 'lei', CNY: '元', TRY: '₺', INR: '₹', UAH: '₴', BYN: 'Br', RUB: '₽', ARS: '$AR' }; const ALL_CURRENCIES = [ 'USD','EUR','CHF','PLN','CZK','GBP', 'CAD','AUD','JPY','KZT','HUF','RON','CNY', 'TRY','INR','UAH','BYN','RUB','ARS' ]; const PANEL_ID = 'uah-currency-panel'; let originalCurrency = null; let priceElements = new Map(); let currentRate = 1; function getUserCurrency() { return localStorage.getItem('uah_currency') || 'CHF'; } function setUserCurrency(v) { localStorage.setItem('uah_currency', v); } function addCurrencyPanel() { let panel = document.getElementById(PANEL_ID); if (panel) return panel; panel = document.createElement('div'); panel.id = PANEL_ID; panel.style.position = 'fixed'; panel.style.top = '18px'; panel.style.right = '28px'; panel.style.zIndex = 10000; panel.style.background = 'linear-gradient(90deg,#18e95b 20%, #63f177 80%)'; panel.style.padding = '4px 10px 4px 10px'; panel.style.borderRadius = '18px'; panel.style.boxShadow = '0 0 10px 2px #11ff60dd,0 0 1px #222'; panel.style.display = 'flex'; panel.style.alignItems = 'center'; const sel = document.createElement('select'); sel.id = 'uah-currency-sel'; sel.style.fontSize = '15px'; sel.style.border = 'none'; sel.style.outline = 'none'; sel.style.background = 'transparent'; sel.style.fontWeight = 'bold'; sel.style.color = '#222'; sel.style.cursor = 'pointer'; sel.style.textShadow = '0 0 2px #fff, 0 0 1px #5fffaf'; ALL_CURRENCIES.forEach((c) => { const o = document.createElement('option'); o.value = c; o.textContent = c; if (getUserCurrency() === c) o.selected = true; sel.appendChild(o); }); // Request rate only when currency is switched sel.onchange = async function() { const newCurrency = sel.value; setUserCurrency(newCurrency); console.log(`Currency changed to: ${newCurrency}`); // Reset conversion flags for all elements priceElements.forEach((data, element) => { if (element && element.parentNode) { element.removeAttribute('data-price-converted'); } }); // Get rate only once when switching const fromCurrency = detectOriginalCurrency(); currentRate = await getExchangeRate(fromCurrency, newCurrency); console.log(`Switching from ${fromCurrency} to ${newCurrency}, rate: ${currentRate}`); convertAllPrices(); }; panel.appendChild(sel); document.body.appendChild(panel); return panel; } // Detecting page currency function detectOriginalCurrency() { if (originalCurrency) return originalCurrency; const symbolToCode = {}; Object.entries(currencySymbols).forEach(([code, sym]) => { if (sym && !/^[A-Za-z0-9 ]+$/.test(sym)) { symbolToCode[sym.trim()] = code; } }); // Additional mappings for different formats symbolToCode['AR$'] = 'ARS'; symbolToCode['руб.'] = 'RUB'; symbolToCode['руб'] = 'RUB'; symbolToCode['₽'] = 'RUB'; symbolToCode['$'] = 'USD'; symbolToCode['US$'] = 'USD'; symbolToCode['USD'] = 'USD'; symbolToCode['CA$'] = 'CAD'; symbolToCode['CDN$'] = 'CAD'; symbolToCode['A$'] = 'AUD'; symbolToCode['zł'] = 'PLN'; symbolToCode['Kč'] = 'CZK'; symbolToCode['Ft'] = 'HUF'; symbolToCode['lei'] = 'RON'; symbolToCode['元'] = 'CNY'; symbolToCode['₸'] = 'KZT'; symbolToCode['Br'] = 'BYN'; symbolToCode['CHF'] = 'CHF'; symbolToCode['CHF '] = 'CHF'; symbolToCode['£'] = 'GBP'; symbolToCode['₹'] = 'INR'; symbolToCode['¥'] = 'JPY'; const priceSelectors = [ '.discount_final_price', '.discount_original_price', '.game_purchase_price', '[class*="price"]', '.market_listing_price', '.price', '.discount_prices', '.game_purchase_action', '.discount_block' ]; for (let selector of priceSelectors) { const elements = document.querySelectorAll(selector); for (let el of elements) { const text = el.textContent || ''; // First check for exact matches of symbols for (let [symbol, code] of Object.entries(symbolToCode)) { if (text.includes(symbol)) { originalCurrency = code; console.log(`Detected original currency: ${code} from symbol: ${symbol} in text: "${text}"`); return originalCurrency; } } // Check for currency code patterns (e.g., "10 USD", "15 EUR") const currencyCodePattern = /\d+\s*([A-Z]{3})/g; let match; while ((match = currencyCodePattern.exec(text)) !== null) { const code = match[1]; if (ALL_CURRENCIES.includes(code)) { originalCurrency = code; console.log(`Detected original currency: ${code} from currency code pattern in text: "${text}"`); return originalCurrency; } } // Special check for USD - look for numbers ending with $ or starting with $ if (/\$\s*\d+|\d+\s*\$/i.test(text)) { originalCurrency = 'USD'; console.log(`Detected USD from price pattern: ${text}`); return originalCurrency; } // Special check for CHF - look for numbers ending with CHF if (/CHF\s*\d+|\d+\s*CHF/i.test(text)) { originalCurrency = 'CHF'; console.log(`Detected CHF from price pattern: ${text}`); return originalCurrency; } // Special check for RUB - look for numbers ending with руб. if (/руб\.?\s*\d+|\d+\s*руб\.?/i.test(text)) { originalCurrency = 'RUB'; console.log(`Detected RUB from price pattern: ${text}`); return originalCurrency; } // Special check for GBP - look for numbers ending with £ if (/£\s*\d+|\d+\s*£/i.test(text)) { originalCurrency = 'GBP'; console.log(`Detected GBP from price pattern: ${text}`); return originalCurrency; } // Special check for INR - look for numbers ending with ₹ if (/₹\s*\d+|\d+\s*₹/i.test(text)) { originalCurrency = 'INR'; console.log(`Detected INR from price pattern: ${text}`); return originalCurrency; } // Special check for JPY - look for numbers ending with ¥ if (/¥\s*\d+|\d+\s*¥/i.test(text)) { originalCurrency = 'JPY'; console.log(`Detected JPY from price pattern: ${text}`); return originalCurrency; } // Special check for CAD - look for numbers ending with CDN$ if (/CDN\$\s*\d+|\d+\s*CDN\$/i.test(text)) { originalCurrency = 'CAD'; console.log(`Detected CAD from price pattern: ${text}`); return originalCurrency; } // Special check for AUD - look for numbers ending with A$ if (/A\$\s*\d+|\d+\s*A\$/i.test(text)) { originalCurrency = 'AUD'; console.log(`Detected AUD from price pattern: ${text}`); return originalCurrency; } } } // Check entire page text for specific symbols const bodyText = document.body.innerText; // Check in order of priority (more specific first) if (bodyText.includes('₴')) { originalCurrency = 'UAH'; } else if (bodyText.includes('₽') || bodyText.includes('руб.')) { originalCurrency = 'RUB'; } else if (bodyText.includes('₺')) { originalCurrency = 'TRY'; } else if (bodyText.includes('₹')) { originalCurrency = 'INR'; } else if (bodyText.includes('¥')) { originalCurrency = 'JPY'; } else if (bodyText.includes('£')) { originalCurrency = 'GBP'; } else if (bodyText.includes('€')) { originalCurrency = 'EUR'; } else if (bodyText.includes('₸')) { originalCurrency = 'KZT'; } else if (bodyText.includes('Ft')) { originalCurrency = 'HUF'; } else if (bodyText.includes('lei')) { originalCurrency = 'RON'; } else if (bodyText.includes('元')) { originalCurrency = 'CNY'; } else if (bodyText.includes('zł')) { originalCurrency = 'PLN'; } else if (bodyText.includes('Kč')) { originalCurrency = 'CZK'; } else if (bodyText.includes('Br')) { originalCurrency = 'BYN'; } else if (bodyText.includes('CHF') || bodyText.includes('CHF ')) { originalCurrency = 'CHF'; } else if (bodyText.includes('$AR') || bodyText.includes('AR$')) { originalCurrency = 'ARS'; } else if (bodyText.includes('CA$')) { originalCurrency = 'CAD'; } else if (bodyText.includes('A$')) { originalCurrency = 'AUD'; } else if (bodyText.includes('$') || bodyText.includes('USD')) { originalCurrency = 'USD'; } else { originalCurrency = 'USD'; // Default to USD } console.log(`Final detected currency: ${originalCurrency} from body text analysis`); return originalCurrency; } // Saving original HTML of elements function saveOriginalPrices() { const priceSelectors = [ '.discount_final_price', '.discount_original_price', '.game_purchase_price', '[class*="price"]', '.market_listing_price', '.price', '.discount_prices', '.game_purchase_action', '.discount_block' ]; priceSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(el => { // Check if element hasn't been processed yet if (!priceElements.has(el) && !el.hasAttribute('data-price-converted')) { const originalHTML = el.innerHTML; const originalText = el.textContent.trim(); // Check if element contains a price (numbers + currency symbol) const hasPrice = /[\d.,]/.test(originalText) && (originalText.includes('$') || originalText.includes('€') || originalText.includes('₽') || originalText.includes('₴') || originalText.includes('₺') || originalText.includes('£') || originalText.includes('¥') || originalText.includes('CHF') || originalText.includes('zł') || originalText.includes('Kč') || originalText.includes('Ft') || originalText.includes('lei') || originalText.includes('元') || originalText.includes('₸') || originalText.includes('Br') || originalText.includes('₹') || originalText.includes('CA$') || originalText.includes('A$') || originalText.includes('$AR') || originalText.includes('AR$') || originalText.includes('руб.') || originalText.includes('руб') || originalText.includes('CDN$') || /\d+\s*[A-Z]{3}/i.test(originalText)); // For cases like "10 USD" if (originalText && hasPrice) { priceElements.set(el, { originalHTML: originalHTML, originalText: originalText, originalCurrency: detectOriginalCurrency() }); console.log(`Saved price element: ${originalText} (currency: ${detectOriginalCurrency()})`); } } }); }); } // Getting currency rates (called only when switching) async function getExchangeRate(fromCurrency, toCurrency) { if (fromCurrency === toCurrency) return 1; try { console.log(`Fetching exchange rate ${fromCurrency} -> ${toCurrency}`); const response = await new Promise((resolve, reject) => GM_xmlhttpRequest({ method: 'GET', url: `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/${fromCurrency.toLowerCase()}.json`, onload: resolve, onerror: reject, timeout: 10000 }) ); const data = JSON.parse(response.responseText); const rate = data[fromCurrency.toLowerCase()][toCurrency.toLowerCase()]; console.log(`Exchange rate ${fromCurrency} -> ${toCurrency}: ${rate}`); return rate || 1; } catch (error) { console.error('Error fetching exchange rate:', error); return 1; } } // Converting price in text function convertPriceInText(text, rate, targetCurrency) { // Check if text already contains target currency (indicating it's already converted) const targetSymbol = currencySymbols[targetCurrency] || (targetCurrency + ' '); // More accurate check - look for target symbol followed by a number const targetCurrencyPattern = new RegExp(`${targetSymbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*[\\d\\s,.]`, 'i'); if (targetCurrencyPattern.test(text)) { return text; // Already converted } // Find numbers preceding currency symbols in original currency const originalSymbol = currencySymbols[originalCurrency] || originalCurrency; // Special handling for different currencies let priceRegex; if (originalCurrency === 'USD') { // For USD, look for different variations: $10, 10$, $ 10, 10 $, 10 USD priceRegex = /(\$)\s*([\d\s,.]+)|([\d\s,.]+)\s*(\$)|([\d\s,.]+)\s*(USD)/gi; } else if (originalCurrency === 'RUB') { // For RUB, look for: 465 руб., 139 руб. priceRegex = /([\d\s,.]+)\s*(руб\.?)/gi; } else if (originalCurrency === 'GBP') { // For GBP, look for: £8.59, £3.43 priceRegex = /(£)\s*([\d\s,.]+)/gi; } else if (originalCurrency === 'INR') { // For INR, look for: ₹ 499, ₹ 199 priceRegex = /(₹)\s*([\d\s,.]+)/gi; } else if (originalCurrency === 'JPY') { // For JPY, look for: ¥ 1,320, ¥ 528 priceRegex = /(¥)\s*([\d\s,.]+)/gi; } else if (originalCurrency === 'CAD') { // For CAD, look for: CDN$ 13.49, CDN$ 5.39 priceRegex = /(CDN\$)\s*([\d\s,.]+)/gi; } else if (originalCurrency === 'AUD') { // For AUD, look for: A$ 14.95, A$ 5.98 priceRegex = /(A\$)\s*([\d\s,.]+)/gi; } else { // For other currencies, use standard approach const escapedSymbol = originalSymbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); priceRegex = new RegExp(`([\\d\\s,.]+)\\s*${escapedSymbol}|([\\d\\s,.]+)\\s*(${originalCurrency})`, 'gi'); } if (originalCurrency === 'USD') { let result = text.replace(priceRegex, (match, dollarBefore, amountBefore, amountAfter, dollarAfter, amountUSD, usdCode) => { const amount = amountBefore || amountAfter || amountUSD; const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining USD and $ anywhere result = result.replace(/USD/gi, '').replace(/\$/g, '').replace(/\s+/g, ' ').trim(); return result; } else if (originalCurrency === 'RUB') { const result = text.replace(priceRegex, (match, amount, rubSymbol) => { const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining руб. anywhere const cleanedResult = result.replace(/руб\.?/gi, '').replace(/\s+/g, ' ').trim(); return cleanedResult; } else if (originalCurrency === 'GBP') { const result = text.replace(priceRegex, (match, poundSymbol, amount) => { const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining £ anywhere const cleanedResult = result.replace(/£/gi, '').replace(/\s+/g, ' ').trim(); return cleanedResult; } else if (originalCurrency === 'INR') { const result = text.replace(priceRegex, (match, rupeeSymbol, amount) => { const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining ₹ anywhere const cleanedResult = result.replace(/₹/gi, '').replace(/\s+/g, ' ').trim(); return cleanedResult; } else if (originalCurrency === 'JPY') { const result = text.replace(priceRegex, (match, yenSymbol, amount) => { const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining ¥ anywhere const cleanedResult = result.replace(/¥/gi, '').replace(/\s+/g, ' ').trim(); return cleanedResult; } else if (originalCurrency === 'CAD') { const result = text.replace(priceRegex, (match, cadSymbol, amount) => { const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining CDN$ anywhere const cleanedResult = result.replace(/CDN\$/gi, '').replace(/\s+/g, ' ').trim(); return cleanedResult; } else if (originalCurrency === 'AUD') { const result = text.replace(priceRegex, (match, audSymbol, amount) => { const cleanNumber = amount.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining A$ anywhere const cleanedResult = result.replace(/A\$/gi, '').replace(/\s+/g, ' ').trim(); return cleanedResult; } else { let result = text.replace(priceRegex, (match, amount, symbol, amountCode, currencyCode) => { const amountValue = amount || amountCode; const cleanNumber = amountValue.replace(/[\s,]/g, '').replace(',', '.'); const price = parseFloat(cleanNumber); if (!isNaN(price) && price > 0) { const convertedPrice = (price * rate).toFixed(2); return targetSymbol + convertedPrice; } return match; }); // Remove remaining original currency symbol anywhere const escapedOriginalSymbol = originalSymbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); result = result.replace(new RegExp(`${escapedOriginalSymbol}`, 'gi'), '').replace(/\s+/g, ' ').trim(); return result; } } // Converting all saved prices using current rate function convertAllPrices() { if (isConverting) return; isConverting = true; const targetCurrency = getUserCurrency(); const fromCurrency = detectOriginalCurrency(); console.log(`Converting prices using rate: ${currentRate}`); if (fromCurrency === targetCurrency || currentRate === 1) { // Restore original HTML priceElements.forEach((data, element) => { if (element && element.parentNode) { element.innerHTML = data.originalHTML; element.removeAttribute('title'); element.removeAttribute('data-price-converted'); } }); } else { // Convert prices in each element priceElements.forEach((data, element) => { if (element && element.parentNode) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = data.originalHTML; // Special handling for discount cards if (element.classList.contains('discount_prices') || element.closest('.discount_block')) { // Process child elements with prices const priceElements = tempDiv.querySelectorAll('.discount_original_price, .discount_final_price'); priceElements.forEach(priceEl => { const originalText = priceEl.textContent; if (/[\d.,]/.test(originalText)) { // Check if it already contains target currency const targetSymbol = currencySymbols[targetCurrency] || (targetCurrency + ' '); if (!originalText.includes(targetSymbol)) { const convertedText = convertPriceInText(originalText, currentRate, targetCurrency); if (convertedText !== originalText) { priceEl.textContent = convertedText; priceEl.title = `Original: ${originalText}`; } } } }); } else { // Standard processing for regular elements const walker = document.createTreeWalker( tempDiv, NodeFilter.SHOW_TEXT, null, false ); let textNode; while (textNode = walker.nextNode()) { const originalText = textNode.textContent; if (/[\d.,]/.test(originalText)) { // Check if it already contains target currency const targetSymbol = currencySymbols[targetCurrency] || (targetCurrency + ' '); if (!originalText.includes(targetSymbol)) { const convertedText = convertPriceInText(originalText, currentRate, targetCurrency); if (convertedText !== originalText) { textNode.textContent = convertedText; } } } } } element.innerHTML = tempDiv.innerHTML; element.title = `Original: ${data.originalText}`; element.setAttribute('data-price-converted', 'true'); } }); } setTimeout(() => { isConverting = false; }, 100); } let isConverting = false; // Flag to prevent recursion // Processing new elements function processNewElements() { if (isConverting) return; // Avoid recursion const oldSize = priceElements.size; saveOriginalPrices(); const newSize = priceElements.size; // Convert only if new elements were added if (newSize > oldSize && getUserCurrency() !== detectOriginalCurrency() && currentRate !== 1) { isConverting = true; // Convert only new elements const newElements = Array.from(priceElements.entries()).slice(oldSize); newElements.forEach(([element, data]) => { if (element && element.parentNode && !element.hasAttribute('data-price-converted')) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = data.originalHTML; // Special handling for discount cards if (element.classList.contains('discount_prices') || element.closest('.discount_block')) { const priceElements = tempDiv.querySelectorAll('.discount_original_price, .discount_final_price'); priceElements.forEach(priceEl => { const originalText = priceEl.textContent; if (/[\d.,]/.test(originalText)) { // Check if it already contains target currency const targetSymbol = currencySymbols[getUserCurrency()] || (getUserCurrency() + ' '); if (!originalText.includes(targetSymbol)) { const convertedText = convertPriceInText(originalText, currentRate, getUserCurrency()); if (convertedText !== originalText) { priceEl.textContent = convertedText; priceEl.title = `Original: ${originalText}`; } } } }); } else { // Standard processing for regular elements const walker = document.createTreeWalker( tempDiv, NodeFilter.SHOW_TEXT, null, false ); let textNode; while (textNode = walker.nextNode()) { const originalText = textNode.textContent; if (/[\d.,]/.test(originalText)) { // Check if it already contains target currency const targetSymbol = currencySymbols[getUserCurrency()] || (getUserCurrency() + ' '); if (!originalText.includes(targetSymbol)) { const convertedText = convertPriceInText(originalText, currentRate, getUserCurrency()); if (convertedText !== originalText) { textNode.textContent = convertedText; } } } } } element.innerHTML = tempDiv.innerHTML; element.title = `Original: ${data.originalText}`; element.setAttribute('data-price-converted', 'true'); } }); setTimeout(() => { isConverting = false; }, 100); } } // Initialization async function init() { console.log('Initializing currency converter...'); // Reset currency detection on each initialization originalCurrency = null; priceElements.clear(); detectOriginalCurrency(); addCurrencyPanel(); saveOriginalPrices(); // Get initial rate if selected currency is different from original const targetCurrency = getUserCurrency(); const detectedCurrency = detectOriginalCurrency(); console.log(`Target currency: ${targetCurrency}, Detected currency: ${detectedCurrency}`); if (targetCurrency !== detectedCurrency) { currentRate = await getExchangeRate(detectedCurrency, targetCurrency); console.log(`Initial conversion rate: ${currentRate}`); convertAllPrices(); } else { console.log('No conversion needed - currencies match'); } } // Run after page load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { setTimeout(init, 100); } // Tracking URL changes to handle region changes let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; console.log('URL changed, re-initializing currency converter...'); setTimeout(init, 500); // Small delay to allow new content to load } }).observe(document, {subtree: true, childList: true}); // Observing new elements with protection against recursion let observerTimeout; const observer = new MutationObserver((mutations) => { // Ignore changes in our currency panel const relevantMutations = mutations.filter(mutation => { return !mutation.target.closest(`#${PANEL_ID}`) && mutation.target.id !== PANEL_ID && !isConverting; }); if (relevantMutations.length === 0) return; clearTimeout(observerTimeout); observerTimeout = setTimeout(processNewElements, 500); }); observer.observe(document.body, { childList: true, subtree: true, attributes: false // Do not track attribute changes }); })();