Ghost Trade Helper

Adds subtraction buttons for quick money adjustments within trades on Torn.

// ==UserScript==
// @name         Ghost Trade Helper
// @namespace    GreasyDiddy
// @version      1.1
// @description  Adds subtraction buttons for quick money adjustments within trades on Torn.
// @license      MIT
// @author       GreasyDiddy[3455236]
// @match        https://www.torn.com/trade.php*
// @grant        none
// ==/UserScript==

(() => {
    'use strict';

    const normalizeInput = (str) => {
        let cleaned = str.toLowerCase().replace(/,/g, '').trim();
        const units = { k: 1e3, m: 1e6, b: 1e9 };
        const unit = cleaned.slice(-1);
        const multiplier = units[unit] || 1;
        if (units[unit]) cleaned = cleaned.slice(0, -1);
        const num = parseFloat(cleaned);
        return isNaN(num) || num <= 0 ? null : Math.floor(num * multiplier);
    };

    const locateMoneyFields = () => {
        const inputs = document.querySelectorAll(".user-id.input-money");
        return inputs.length === 2 ? [...inputs] : [null, null];
    };

    const updateMoney = (diff, isSet = false) => {
        const [visible, hidden] = locateMoneyFields();
        if (!visible || !hidden) return;
        const current = parseInt(hidden.value) || 0;
        const result = Math.max(0, isSet ? diff : current + diff);
        visible.value = result;
        visible.dispatchEvent(new Event("input", { bubbles: true }));
    };

    const fabricateButton = (label, action) => {
        const btn = document.createElement("input");
        btn.type = "button";
        btn.value = label;
        btn.className = "torn-btn";
        btn.addEventListener("click", action);
        return btn;
    };

    const buildPresetButtons = (container) => {
        container.appendChild(fabricateButton("-1m", () => updateMoney(-1000000)));
        container.appendChild(fabricateButton("-5m", () => updateMoney(-5000000)));
        container.appendChild(fabricateButton("-10m", () => updateMoney(-10000000)));
    };

    const buildCustomButton = (container) => {
        const customBtn = fabricateButton("Custom", () => {
            const userVal = prompt("Enter amount to subtract:");
            const parsed = normalizeInput(userVal || "");
            parsed !== null ? updateMoney(-parsed) : alert("Invalid amount.");
        });
        container.appendChild(customBtn);
    };

    const buildPasteButton = (container) => {
        const pasteBtn = fabricateButton("Paste", async () => {
            try {
                const raw = await navigator.clipboard.readText();
                const clean = raw.replace(/[, $]/g, "");
                const parsed = normalizeInput(clean);
                parsed !== null
                    ? updateMoney(-parsed)
                    : alert("Clipboard doesn't contain a valid amount.");
            } catch (e) {
                console.error("Clipboard error:", e);
                alert("Clipboard access failed. Paste manually.");
            }
        });
        container.appendChild(pasteBtn);
    };

    const injectControls = () => {
        const inputGroup = document.querySelector("div.input-money-group");
        if (!inputGroup) return;

        const refButton = document.querySelector("span.btn-wrap.silver");
        if (!refButton) return;

        const btnWrapper = document.createElement("div");
        const clonedSpacer = refButton.previousElementSibling?.cloneNode();
        if (clonedSpacer) btnWrapper.appendChild(clonedSpacer);

        buildPresetButtons(btnWrapper);
        buildCustomButton(btnWrapper);
        buildPasteButton(btnWrapper);

        inputGroup.parentNode.insertBefore(btnWrapper, inputGroup.nextSibling);
    };

    const watchTradePage = () => {
        if (!window.location.href.includes("trade.php#step=addmoney")) return;

        clearInterval(window.GhostTradeWatch);
        window.GhostTradeWatch = setInterval(() => {
            const moneyFieldExists = document.querySelector(".user-id.input-money");
            const tooFewInputs = document.querySelectorAll("ul.inputs > li > div").length < 3;

            if (moneyFieldExists && tooFewInputs) {
                clearInterval(window.GhostTradeWatch);
                injectControls();
            }
        }, 100);
    };

    window.addEventListener("hashchange", watchTradePage);
    watchTradePage();
})();