Visual larp: Changes ARS to $ everywhere (bets, balance, everything) - keeps numbers the same
// ==UserScript==
// @name xd2shuffle
// @namespace http://tampermonkey.net/
// @version 7.7.8.1
// @description Visual larp: Changes ARS to $ everywhere (bets, balance, everything) - keeps numbers the same
// @author fusi | @loficat on tg | codedfusi on github
// @match *://*.shuffle.com/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
(function () {
const processedTextNodes = new WeakSet();
const processedImages = new WeakSet();
// Force update a text node
function updateTextNode(node) {
if (node.nodeType !== 3 || !node.nodeValue) return false;
let original = node.nodeValue;
let newValue = original;
let changed = false;
// Replace ARS with $ (case insensitive just in case)
if (newValue.includes("ARS")) {
newValue = newValue.replace(/ARS/g, "$");
changed = true;
}
// Replace lowercase "ars" if it appears
if (newValue.includes("ars")) {
newValue = newValue.replace(/ars/g, "$");
changed = true;
}
// Remove space after the new $
if (changed) {
newValue = newValue.replace(/\$\s+/g, '$');
}
if (changed && newValue !== original) {
node.nodeValue = newValue;
return true;
}
return false;
}
function processTextNode(node) {
if (node.nodeType === 3 && node.nodeValue && !processedTextNodes.has(node)) {
if (updateTextNode(node)) {
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 all text nodes
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
let node;
while ((node = walker.nextNode())) {
processTextNode(node);
}
// Process all ARS flag images
const images = root.querySelectorAll('img[src*="/icons/fiat/ARS.svg"]');
images.forEach(img => processImageElement(img));
}
// More aggressive mutation handling
function onMutation(mutations) {
let needsFullScan = false;
for (const mutation of mutations) {
// If attributes changed (like a bet amount updating), scan the target
if (mutation.type === 'attributes') {
if (mutation.target.nodeType === 1) {
// Re-scan this element and its children
walkAndProcess(mutation.target);
// Also scan its parent in case the change bubbled
if (mutation.target.parentNode) {
walkAndProcess(mutation.target.parentNode);
}
}
needsFullScan = true;
}
// If child nodes were added, process them
if (mutation.type === 'childList') {
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType === 1) {
walkAndProcess(addedNode);
} else if (addedNode.nodeType === 3) {
processTextNode(addedNode);
}
}
needsFullScan = true;
}
// If text itself changed (characterData mutation)
if (mutation.type === 'characterData') {
if (mutation.target.nodeType === 3) {
// Re-process this text node even if it was processed before
const node = mutation.target;
if (node.nodeValue && (node.nodeValue.includes("ARS") || node.nodeValue.includes("ars"))) {
updateTextNode(node);
}
}
needsFullScan = true;
}
}
// Do a quick partial scan of the body periodically to catch anything missed
if (needsFullScan) {
setTimeout(() => {
// Scan the whole body but skip nodes we've already processed to save performance
const allTextNodes = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
let tn;
while ((tn = allTextNodes.nextNode())) {
if (tn.nodeValue && (tn.nodeValue.includes("ARS") || tn.nodeValue.includes("ars"))) {
if (updateTextNode(tn)) {
processedTextNodes.add(tn);
}
}
}
}, 100);
}
}
// Start when DOM is ready
document.addEventListener("DOMContentLoaded", () => {
walkAndProcess(document.body);
// Watch for attribute changes (like bet amounts), text changes, AND child lists
const observer = new MutationObserver(onMutation);
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true, // <-- CRITICAL: catches bet amount updates
attributeFilter: ['textContent', 'innerHTML', 'value'], // Focus on text-related attributes
characterData: true // <-- CRITICAL: catches direct text changes
});
// Also run every second as a backup (heavy but ensures bets get caught)
setInterval(() => {
const allTextNodes = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
let tn;
while ((tn = allTextNodes.nextNode())) {
if (tn.nodeValue && (tn.nodeValue.includes("ARS") || tn.nodeValue.includes("ars"))) {
updateTextNode(tn);
}
}
}, 1000);
});
})();