xd2shuffle

ARS to USD converter for shuffle.com (text + numbers)

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         xd2shuffle
// @namespace    http://tampermonkey.net/
// @version      7.7.7.8
// @description  ARS to USD converter for shuffle.com (text + numbers)
// @author       fusi | @loficat on tg | codedfusi on github | Modified by user
// @match        *://*.shuffle.com/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    // ==========================================
    // SET THE EXCHANGE RATE HERE
    // Example: 1 USD = 1400 ARS
    // ==========================================
    const USD_RATE = 1400;  // <-- CHANGE THIS NUMBER to the current rate

    // Store modified nodes to avoid double processing
    const processedTextNodes = new WeakSet();
    const processedImages = new WeakSet();

    // Helper: Convert ARS number string to USD formatted string
    function convertARStextToUSD(text) {
        // Match numbers that could be ARS amounts (including commas and decimals)
        // This looks for patterns like: 1,234,567.89 or 1.234.567,89 or just 1234
        const arsNumberPattern = /(\b[\d,]+(?:\.\d+)?\b)|(\b[\d.]+(?:,\d+)?\b)/g;

        return text.replace(arsNumberPattern, (match) => {
            // Clean the match: remove commas (for US style) or dots (for EU style)
            let cleanNumber = match.replace(/,/g, '');
            // If it has a dot and then exactly 2 digits at the end, it might be decimal
            // For EU style like 1.234,56 -> replace dot with empty and comma with dot
            if (cleanNumber.includes('.') && cleanNumber.includes(',')) {
                cleanNumber = cleanNumber.replace(/\./g, '').replace(/,/, '.');
            } else if (cleanNumber.includes(',')) {
                // If only commas, treat as decimal separator (EU)
                cleanNumber = cleanNumber.replace(/,/, '.');
            }

            const arsValue = parseFloat(cleanNumber);
            if (isNaN(arsValue)) return match;

            const usdValue = arsValue / USD_RATE;
            // Format USD with 2 decimal places, using US notation
            return usdValue.toLocaleString('en-US', {
                minimumFractionDigits: 2,
                maximumFractionDigits: 2
            });
        });
    }

    function processTextNode(node) {
        if (node.nodeType === 3 && node.nodeValue && !processedTextNodes.has(node)) {
            let original = node.nodeValue;
            let modified = false;
            let newValue = original;

            // Replace ARS symbol with $
            if (newValue.includes("ARS")) {
                newValue = newValue.replace(/ARS/g, "$");
                modified = true;
            }

            // Convert ARS numbers to USD (if text contains numbers)
            if (/\d/.test(newValue)) {
                const converted = convertARStextToUSD(newValue);
                if (converted !== newValue) {
                    newValue = converted;
                    modified = true;
                }
            }

            if (modified) {
                node.nodeValue = newValue;
                processedTextNodes.add(node);
            }
        }
    }

    function processImageElement(el) {
        if (el.nodeType === 1 && el.hasAttribute("src") && !processedImages.has(el)) {
            const src = el.getAttribute("src");
            if (src && src.includes("/icons/fiat/ARS.svg")) {
                el.setAttribute("src", src.replace("/icons/fiat/ARS.svg", "/icons/fiat/USD.svg"));
                processedImages.add(el);
            }
        }
    }

    function walkAndProcess(root) {
        if (!root) return;

        // Process text nodes
        const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
        let node;
        while ((node = walker.nextNode())) {
            processTextNode(node);
        }

        // Process image elements
        const images = root.querySelectorAll('img[src*="/icons/fiat/ARS.svg"]');
        images.forEach(img => processImageElement(img));
    }

    // MutationObserver callback to handle dynamically added content
    function onMutation(mutations) {
        for (const mutation of mutations) {
            if (mutation.type === 'childList') {
                for (const addedNode of mutation.addedNodes) {
                    if (addedNode.nodeType === 1) {
                        walkAndProcess(addedNode);
                    } else if (addedNode.nodeType === 3) {
                        processTextNode(addedNode);
                    }
                }
            }
        }
        // Also periodically re-check text nodes that might have changed value
        // (We use a weak set so we only reprocess if the node was garbage collected)
    }

    // Start the script once the DOM is ready
    document.addEventListener("DOMContentLoaded", () => {
        walkAndProcess(document.body);

        const observer = new MutationObserver(onMutation);
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
})();