Quickly sell weapons to the game
// ==UserScript==
// @name Torn Quick Sell Weapons to Game
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Quickly sell weapons to the game
// @author PedroXimenez
// @match https://*.torn.com/item.php*
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Class name cache and lookup for CSS modules compatibility
const classCache = {};
function findClass(prefix) {
if (classCache[prefix]) return classCache[prefix];
// Plain class fallback (for classes not yet using CSS modules)
if (document.querySelector(`.${prefix}`)) {
classCache[prefix] = prefix;
return prefix;
}
// Search through all elements for a matching hashed class
const el = document.querySelector(`[class*="${prefix}___"]`);
if (el) {
const match = el.className.match(new RegExp(`${prefix}___\\w+`));
if (match) {
classCache[prefix] = match[0];
return match[0];
}
}
// Fallback: search stylesheets
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules || []) {
if (rule.selectorText && rule.selectorText.includes(`${prefix}___`)) {
const match = rule.selectorText.match(new RegExp(`${prefix}___\\w+`));
if (match) {
classCache[prefix] = match[0];
return match[0];
}
}
}
} catch (e) {
// Cross-origin stylesheets will throw
}
}
return null;
}
// Inject styles once we can resolve the option-sell class
let stylesInjected = false;
function injectStyles() {
if (stylesInjected) return;
const optionSellClass = findClass('option-sell');
if (!optionSellClass) return;
stylesInjected = true;
GM_addStyle(`
button.${optionSellClass}.quick-sell-confirm::after {
content: "?";
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
color: white;
background: rgba(0, 0, 0, 0.7);
border-radius: 4px;
}
button.${optionSellClass}.quick-sell-close::after {
content: "\\2715";
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: bold;
color: white;
background: rgba(0, 0, 0, 0.7);
border-radius: 4px;
}
button.${optionSellClass} {
position: relative;
}
`);
}
// Track the currently modified button
let activeButton = null;
// Two-layer protection against mutation cycles
const enqueueUpdate = typeof window.requestAnimationFrame === 'function'
? (cb) => window.requestAnimationFrame(cb)
: (cb) => setTimeout(cb, 50);
let isUpdating = false;
let updateScheduled = false;
function scheduleUpdate() {
if (updateScheduled) {
return;
}
updateScheduled = true;
enqueueUpdate(() => {
updateScheduled = false;
doUpdate();
});
}
function doUpdate() {
if (isUpdating) return;
isUpdating = true;
try {
injectStyles();
// Clear previous state
if (activeButton) {
activeButton.classList.remove('quick-sell-confirm', 'quick-sell-close');
activeButton = null;
}
const sellActClass = findClass('sell-act');
const actClass = findClass('act');
const optionSellClass = findClass('option-sell');
const nextActClass = findClass('next-act');
const closeActClass = findClass('close-act');
if (!sellActClass || !actClass || !optionSellClass) return;
const sellAct = document.querySelector(`.${sellActClass}`);
if (sellAct) {
// Find the active item's sell button
const activeLi = document.querySelector(`li.${actClass}[data-item]`);
if (activeLi) {
const sellButton = activeLi.querySelector(`button.${optionSellClass}`);
if (sellButton) {
activeButton = sellButton;
// Determine which state we're in
if (nextActClass && sellAct.querySelector(`a.${nextActClass}`)) {
// Confirmation dialog - show question mark
sellButton.classList.add('quick-sell-confirm');
} else if (closeActClass && sellAct.querySelector(`a.${closeActClass}`)) {
// Success dialog - show close icon
sellButton.classList.add('quick-sell-close');
}
}
}
}
} finally {
isUpdating = false;
}
}
// Watch for DOM changes to detect dialog state
const observer = new MutationObserver(() => scheduleUpdate());
function startObserver() {
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class']
});
scheduleUpdate();
}
if (document.body) {
startObserver();
} else {
document.addEventListener('DOMContentLoaded', startObserver);
}
// Click handler for sell button
document.addEventListener('click', function(e) {
const optionSellClass = findClass('option-sell');
if (!optionSellClass) return;
const sellButton = e.target.closest(`button.${optionSellClass}`);
if (!sellButton) {
return;
}
const sellActClass = findClass('sell-act');
const nextActClass = findClass('next-act');
const closeActClass = findClass('close-act');
if (!sellActClass) return;
// Check if sell confirmation dialog is visible (Yes/No prompt)
if (nextActClass) {
const yesLink = document.querySelector(`.${sellActClass} a.${nextActClass}`);
if (yesLink) {
e.preventDefault();
e.stopPropagation();
yesLink.click();
return;
}
}
// Check if sell success dialog is visible (Close prompt)
if (closeActClass) {
const closeLink = document.querySelector(`.${sellActClass} a.${closeActClass}`);
if (closeLink) {
e.preventDefault();
e.stopPropagation();
closeLink.click();
return;
}
}
// No dialog visible, let the click proceed normally
}, true);
})();