Toggle between showing pre-tax and post-tax prices in Torn Item Market (5% tax) with mobile refresh support
// ==UserScript==
// @name Torn IM After-Tax Price Toggle TEST BRANCH
// @namespace https://greasyfork.org/en/scripts/553584-torn-im-after-tax-price-toggle-test-branch
// @version 2.0
// @description Toggle between showing pre-tax and post-tax prices in Torn Item Market (5% tax) with mobile refresh support
// @author Felsync [3921027]
// @match https://www.torn.com/page.php?sid=ItemMarket*
// @match https://www.torn.com/page.php*sid=ItemMarket*
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-end
// ==/UserScript==
(() => {
'use strict';
// Configuration constants
const TAX_RATE = 0.05; // Torn Item Market tax rate (5%)
const BATCH_SIZE = 50; // Max elements to process per batch
const BATCH_DELAY_MS = 10; // Delay between batches (ms)
const DEBOUNCE_DELAY_MS = 100; // Debounce delay for DOM scanning (ms)
const MOBILE_FIX_INTERVAL_MS = 2000; // Interval for mobile price fix (ms)
let showAfterTax = GM_getValue('showAfterTax', false);
const prices = new WeakMap();
let debounceTimer = null;
// Add styles
document.head.insertAdjacentHTML('beforeend',
`<style>.tax-btn{background:#2c5aa0;color:#fff;border:1px solid #1a3a6b;padding:5px 12px;margin:0 0 10px;border-radius:3px;cursor:pointer;font-size:11px}.tax-btn.on{background:#28a745}</style>`);
const updatePrice = elem => {
const price = prices.get(elem);
if (price) {
elem.textContent = (showAfterTax ? '✓ $' : '$') + Math.floor(showAfterTax ? price * (1 - TAX_RATE) : price).toLocaleString();
}
};
const processNewPrices = () => {
// Only process prices within seller rows, not category headers
const elements = document.querySelectorAll('div[class*="sellerRow"] div[class*="price"]:not([data-tax])');
let count = 0;
for (const elem of elements) {
if (++count > BATCH_SIZE) {
// Process rest after a break to prevent freezing
setTimeout(processNewPrices, BATCH_DELAY_MS);
break;
}
const price = parseInt(elem.textContent.replace(/\D/g, ''));
if (price > 0) {
prices.set(elem, price);
elem.setAttribute('data-tax', '1');
updatePrice(elem);
}
}
};
const setupButtons = () => {
document.querySelectorAll('ul[class*="sellerList"]').forEach(list => {
const container = list.parentElement;
if (!container || container.querySelector('.tax-btn')) return;
const btn = document.createElement('button');
btn.className = 'tax-btn' + (showAfterTax ? ' on' : '');
btn.textContent = showAfterTax ? 'After-Tax ✓' : 'Pre-Tax';
btn.onclick = () => {
showAfterTax = !showAfterTax;
GM_setValue('showAfterTax', showAfterTax);
btn.textContent = showAfterTax ? 'After-Tax ✓' : 'Pre-Tax';
btn.classList.toggle('on');
// Update all tracked prices
document.querySelectorAll('div[class*="sellerRow"] div[class*="price"][data-tax]').forEach(updatePrice);
};
container.insertBefore(btn, list);
});
};
const scan = () => {
setupButtons();
processNewPrices();
};
const debouncedScan = () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(scan, DEBOUNCE_DELAY_MS);
};
// Watch for changes
new MutationObserver(mutations => {
const hasRelevantChange = mutations.some(m =>
Array.from(m.addedNodes).some(n =>
n.nodeType === 1 && (
n.querySelector?.('[class*="sellerList"]') ||
n.querySelector?.('[class*="sellerRow"]')
)
)
);
if (hasRelevantChange) debouncedScan();
}).observe(document.body, { childList: true, subtree: true });
// Initial setup
scan();
// Fix prices that get reset (mobile)
setInterval(() => {
if (showAfterTax) {
document.querySelectorAll('div[class*="sellerRow"] div[class*="price"][data-tax]').forEach(elem => {
if (!elem.textContent.includes('✓')) updatePrice(elem);
});
}
}, MOBILE_FIX_INTERVAL_MS);
})();