Steam Multi-currency Live Converter

Auto-detect and convert Steam prices to any currency with live exchange rates

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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
    });

})();