Torn Item Market Helper

Items market 2.0 helper

当前为 2024-12-07 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn Item Market Helper
// @namespace    Nurv.IronNerd.me
// @version      0.1
// @description  Items market 2.0 helper
// @author       Nurv [669537]
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @icon         https://img.icons8.com/?size=100&id=NrG0tlrTAiph&format=png&color=000000
// @license      Copyright IronNerd.me
// ==/UserScript==

(function () {
    'use strict';

    const API_BASE_URL = "https://api.torn.com/v2/user";
    const CACHE_DURATION = 30000;
    const dataCache = {};
    let previousURL = window.location.href;

    let currentPopup = null;
    let currentIcon = null;
    const URL_CHECK_INTERVAL = 5000;

    function setCookie(name, value, days) {
        const d = new Date();
        d.setTime(d.getTime() + (days*24*60*60*1000));
        const expires = "expires="+ d.toUTCString();
        document.cookie = name + "=" + (value || "") + "; " + expires + "; path=/";
    }

    function getCookie(name) {
        const nameEQ = name + "=";
        const ca = document.cookie.split(';');
        for(let i=0; i<ca.length; i++) {
            let c = ca[i].trim();
            if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
        }
        return null;
    }

    function addGlobalStyles() {
        const css = `
        .mugButton {
            cursor: pointer;
            margin-right: 10px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            color: white;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            z-index: 1000 !important;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .mugButton svg {
            width: 25px;
            height: 25px;
        }
        .mugPanel {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 300px;
            background: linear-gradient(to bottom right, #ffffff, #f7f7f7);
            border: 1px solid #ccc;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 8px 20px rgba(0,0,0,0.2);
            z-index: 3000;
            font-size: 14px;
            font-family: Arial, sans-serif;
            color: #333;
        }
        .mugPanel label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #222;
        }
        .mugPanel input {
            width: 100%;
            margin-bottom: 15px;
            padding: 6px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 13px;
        }
        .mugPanel .closeButton {
            position: absolute;
            top: 10px;
            right: 10px;
            background: #d9534f;
            color: white;
            border: none;
            border-radius: 50%;
            width: 25px;
            height: 25px;
            cursor: pointer;
            font-size: 16px;
            line-height: 25px;
            text-align: center;
        }
        .mugPanel button.saveSettings {
            background: #28a745;
            color: white;
            padding: 8px 12px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-top: 10px;
            font-weight: bold;
        }
        .mugPanel button.saveSettings:hover {
            background: #218838;
        }
        .infoIcon {
            margin-left: 5px;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            background: #007bff;
            color: white;
            border-radius: 50%;
            width: 16px;
            height: 16px;
            font-size: 12px;
            text-align: center;
            line-height: 16px;
            z-index: 1000 !important;
        }
        .infoPopup {
            position: absolute;
            color: black;
            border: 1px solid #ccc;
            padding: 10px;
            border-radius: 5px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            font-size: 12px;
            z-index: 2000;
            display: none;
        }
        .infoPopup.visible {
            display: block !important;
        }`;
        const styleElement = document.createElement('style');
        styleElement.textContent = css;
        document.head.appendChild(styleElement);
    }

    function createMugButtonAndPanel() {
        const mugButton = document.createElement('div');
        mugButton.className = 'mugButton';
        mugButton.innerHTML = `<svg fill="#ffffff" height="256px" width="256px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-51.2 -51.2 614.40 614.40" xml:space="preserve" transform="matrix(-1, 0, 0, 1, 0, 0)"><g id="SVGRepo_bgCarrier" stroke-width="0" transform="translate(0,0), scale(1)"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#ff0505" stroke-width="13.311974000000001"> <g> <g> <path d="M182.705,70.577c-1.157-7.628-8.271-12.872-15.91-11.717c-3.187,0.484-6.377,0.844-9.566,1.137l-3.845-33.66 c-0.32-2.804-2.805-4.849-5.618-4.625l-16.658,1.326c-4.652,0.37-9.326-0.326-13.668-2.036l-15.549-6.124 c-2.626-1.034-5.599,0.199-6.723,2.787L81.681,48.743c-2.966-1.209-5.913-2.485-8.82-3.875c-6.962-3.33-15.302-0.389-18.633,6.571 c-3.33,6.96-0.387,15.301,6.572,18.632c5.719,2.736,11.545,5.157,17.451,7.294c-0.463,1.6-0.836,3.245-1.09,4.937 c-3.109,20.68,11.135,39.963,31.815,43.071c20.68,3.109,39.963-11.135,43.071-31.815c0.264-1.751,0.392-3.49,0.413-5.212 c6.175-0.328,12.356-0.927,18.529-1.864C178.616,85.327,183.862,78.205,182.705,70.577z"></path> </g> </g> <g> <g> <path d="M293.065,163.552c-7.081-0.336-22.746-1.078-38.84-1.841v-8.307c0-2.279-1.847-4.126-4.127-4.126h-8.74 c-2.279,0-4.127,1.848-4.127,4.126c0,2.777,0,4.715,0,7.501c-0.061-0.002-0.122-0.006-0.184-0.009 c0.068-0.023-1.659-0.05-2.815,0.164c-8.75,1.382-14.32,10.182-11.813,18.777l-65.756,23.648l-39.853-38.6l29.359,16.761 l0.75-4.992c2.261-15.041-7.307-28.948-21.37-31.062l-33.314-5.663c-5.397-0.918-12.278-1.024-18.744,3.721L6.488,196.082 c-5.079,3.974-7.455,10.491-6.124,16.802l18.452,87.497c1.678,7.959,8.702,13.42,16.524,13.42c6.01,0,11.756-3.192,14.827-8.782 l9.589,77.436L33.05,469.738c-3.468,11.334,2.91,23.333,14.243,26.8c11.333,3.468,23.333-2.907,26.801-14.244l28.044-91.658 c0.882-2.884,1.147-5.921,0.777-8.916l-8.74-70.583l2.063,0.342l27.352,71.832l-4.97,91.54 c-0.642,11.835,8.432,21.951,20.267,22.593c11.716,0.666,21.946-8.344,22.593-20.265l5.217-96.088 c0.162-2.997-0.305-5.996-1.373-8.801c-25.131-65.865-34.333-89.652-34.333-89.652l4.297-28.584l-41.994-64.881l47.417,45.926 c4.665,4.52,11.455,5.932,17.482,3.765l84.144-30.263c6.118-2.201,10.177-7.566,11.017-13.586c2.235,2.491,5.89,3.169,8.883,1.542 c3.535-1.919,4.843-6.34,2.924-9.875l-3.049-5.613c2.083-0.099,4.132-0.196,6.134-0.29c10.621-0.504,19.843-0.941,24.817-1.177 c2.202-0.105,3.931-1.915,3.931-4.119v-7.815C296.996,165.469,295.264,163.657,293.065,163.552z M46.079,265.805l-10.458-49.587 l20.392-15.957L46.079,265.805z"></path> </g> </g> <g> <g> <path d="M490.36,141.071c-2.138-13.934-14.156-23.324-26.841-20.975l-42.061,7.788c-12.685,2.349-21.235,15.549-19.096,29.483 l4.07,26.512l8.565-1.089l10.237-13.234l-14.362,29.01l-36.041,4.174c1.474-3.934-1.448-8.044-5.568-8.044h-27.181 c-4.306,0-7.194,4.442-5.444,8.378l2.728,6.137c-5.724,4.003-9.127,10.938-8.266,18.363c0.508,4.393,2.431,8.276,5.258,11.252 c-3.445,3.809-7.005,7.992-10.375,12.378c-9.169,11.931-16.91,25.327-16.91,36.572c0,27.595,20.863,49.964,46.6,49.964 s46.6-22.37,46.6-49.964c0-9.52-5.547-20.582-12.811-30.989c-3.634-5.206-7.695-10.244-11.711-14.848l48.242-5.587 c6.612-0.766,12.386-4.828,15.34-10.794c3.401-6.871,28.329-57.221,31.892-64.417l-17.012,70.989 c-2.054,10.326-10.459,18.678-19.509,19.845c-8.37,1.057,0,0-19.441,2.473c1.962,12.783-0.95-6.186,6.563,42.761l-0.102,178.021 c0,12.292,9.965,22.256,22.257,22.256s22.257-9.965,22.257-22.256V290.155l17.312-3.206v188.279 c0,7.379-2.026,14.283-5.543,20.197c2.835,1.31,5.982,2.061,9.309,2.061c12.292,0,22.257-9.965,22.257-22.256l0.426-193.178 L490.36,141.071z M358.695,306.836v1.844c0,0.889-1.207,1.716-2.414,1.716c-1.399,0-2.416-0.827-2.416-1.716v-1.589 c-7.627-0.255-13.855-4.195-13.855-8.262c0-2.161,1.906-5.338,4.322-5.338c2.669,0,4.83,3.75,9.533,4.576V287.77 c-5.847-2.225-12.711-4.958-12.711-13.092c0-8.071,5.974-11.948,12.711-12.901v-1.779c0-0.89,1.017-1.716,2.416-1.716 c1.207,0,2.414,0.827,2.414,1.716v1.588c3.941,0.128,11.377,1.145,11.377,5.53c0,1.716-1.144,5.212-3.941,5.212 c-2.097,0-3.305-2.034-7.436-2.351v9.278c5.784,2.161,12.521,5.148,12.521,13.728 C371.216,300.862,366.131,305.628,358.695,306.836z"></path> </g> </g> <g> <g> <circle cx="398.348" cy="78.006" r="37.885"></circle> </g> </g> <g> <g> <path d="M358.06,289.486v8.516c2.161-0.508,3.876-1.716,3.876-4.004C361.938,291.901,360.348,290.566,358.06,289.486z"></path> </g> </g> <g> <g> <path d="M350.434,273.724c0,1.844,1.652,2.987,4.068,4.004v-7.564C351.641,270.737,350.434,272.199,350.434,273.724z"></path> </g> </g> </g><g id="SVGRepo_iconCarrier"> <g> <g> <path d="M182.705,70.577c-1.157-7.628-8.271-12.872-15.91-11.717c-3.187,0.484-6.377,0.844-9.566,1.137l-3.845-33.66 c-0.32-2.804-2.805-4.849-5.618-4.625l-16.658,1.326c-4.652,0.37-9.326-0.326-13.668-2.036l-15.549-6.124 c-2.626-1.034-5.599,0.199-6.723,2.787L81.681,48.743c-2.966-1.209-5.913-2.485-8.82-3.875c-6.962-3.33-15.302-0.389-18.633,6.571 c-3.33,6.96-0.387,15.301,6.572,18.632c5.719,2.736,11.545,5.157,17.451,7.294c-0.463,1.6-0.836,3.245-1.09,4.937 c-3.109,20.68,11.135,39.963,31.815,43.071c20.68,3.109,39.963-11.135,43.071-31.815c0.264-1.751,0.392-3.49,0.413-5.212 c6.175-0.328,12.356-0.927,18.529-1.864C178.616,85.327,183.862,78.205,182.705,70.577z"></path> </g> </g> <g> <g> <path d="M293.065,163.552c-7.081-0.336-22.746-1.078-38.84-1.841v-8.307c0-2.279-1.847-4.126-4.127-4.126h-8.74 c-2.279,0-4.127,1.848-4.127,4.126c0,2.777,0,4.715,0,7.501c-0.061-0.002-0.122-0.006-0.184-0.009 c0.068-0.023-1.659-0.05-2.815,0.164c-8.75,1.382-14.32,10.182-11.813,18.777l-65.756,23.648l-39.853-38.6l29.359,16.761 l0.75-4.992c2.261-15.041-7.307-28.948-21.37-31.062l-33.314-5.663c-5.397-0.918-12.278-1.024-18.744,3.721L6.488,196.082 c-5.079,3.974-7.455,10.491-6.124,16.802l18.452,87.497c1.678,7.959,8.702,13.42,16.524,13.42c6.01,0,11.756-3.192,14.827-8.782 l9.589,77.436L33.05,469.738c-3.468,11.334,2.91,23.333,14.243,26.8c11.333,3.468,23.333-2.907,26.801-14.244l28.044-91.658 c0.882-2.884,1.147-5.921,0.777-8.916l-8.74-70.583l2.063,0.342l27.352,71.832l-4.97,91.54 c-0.642,11.835,8.432,21.951,20.267,22.593c11.716,0.666,21.946-8.344,22.593-20.265l5.217-96.088 c0.162-2.997-0.305-5.996-1.373-8.801c-25.131-65.865-34.333-89.652-34.333-89.652l4.297-28.584l-41.994-64.881l47.417,45.926 c4.665,4.52,11.455,5.932,17.482,3.765l84.144-30.263c6.118-2.201,10.177-7.566,11.017-13.586c2.235,2.491,5.89,3.169,8.883,1.542 c3.535-1.919,4.843-6.34,2.924-9.875l-3.049-5.613c2.083-0.099,4.132-0.196,6.134-0.29c10.621-0.504,19.843-0.941,24.817-1.177 c2.202-0.105,3.931-1.915,3.931-4.119v-7.815C296.996,165.469,295.264,163.657,293.065,163.552z M46.079,265.805l-10.458-49.587 l20.392-15.957L46.079,265.805z"></path> </g> </g> <g> <g> <path d="M490.36,141.071c-2.138-13.934-14.156-23.324-26.841-20.975l-42.061,7.788c-12.685,2.349-21.235,15.549-19.096,29.483 l4.07,26.512l8.565-1.089l10.237-13.234l-14.362,29.01l-36.041,4.174c1.474-3.934-1.448-8.044-5.568-8.044h-27.181 c-4.306,0-7.194,4.442-5.444,8.378l2.728,6.137c-5.724,4.003-9.127,10.938-8.266,18.363c0.508,4.393,2.431,8.276,5.258,11.252 c-3.445,3.809-7.005,7.992-10.375,12.378c-9.169,11.931-16.91,25.327-16.91,36.572c0,27.595,20.863,49.964,46.6,49.964 s46.6-22.37,46.6-49.964c0-9.52-5.547-20.582-12.811-30.989c-3.634-5.206-7.695-10.244-11.711-14.848l48.242-5.587 c6.612-0.766,12.386-4.828,15.34-10.794c3.401-6.871,28.329-57.221,31.892-64.417l-17.012,70.989 c-2.054,10.326-10.459,18.678-19.509,19.845c-8.37,1.057,0,0-19.441,2.473c1.962,12.783-0.95-6.186,6.563,42.761l-0.102,178.021 c0,12.292,9.965,22.256,22.257,22.256s22.257-9.965,22.257-22.256V290.155l17.312-3.206v188.279 c0,7.379-2.026,14.283-5.543,20.197c2.835,1.31,5.982,2.061,9.309,2.061c12.292,0,22.257-9.965,22.257-22.256l0.426-193.178 L490.36,141.071z M358.695,306.836v1.844c0,0.889-1.207,1.716-2.414,1.716c-1.399,0-2.416-0.827-2.416-1.716v-1.589 c-7.627-0.255-13.855-4.195-13.855-8.262c0-2.161,1.906-5.338,4.322-5.338c2.669,0,4.83,3.75,9.533,4.576V287.77 c-5.847-2.225-12.711-4.958-12.711-13.092c0-8.071,5.974-11.948,12.711-12.901v-1.779c0-0.89,1.017-1.716,2.416-1.716 c1.207,0,2.414,0.827,2.414,1.716v1.588c3.941,0.128,11.377,1.145,11.377,5.53c0,1.716-1.144,5.212-3.941,5.212 c-2.097,0-3.305-2.034-7.436-2.351v9.278c5.784,2.161,12.521,5.148,12.521,13.728 C371.216,300.862,366.131,305.628,358.695,306.836z"></path> </g> </g> <g> <g> <circle cx="398.348" cy="78.006" r="37.885"></circle> </g> </g> <g> <g> <path d="M358.06,289.486v8.516c2.161-0.508,3.876-1.716,3.876-4.004C361.938,291.901,360.348,290.566,358.06,289.486z"></path> </g> </g> <g> <g> <path d="M350.434,273.724c0,1.844,1.652,2.987,4.068,4.004v-7.564C351.641,270.737,350.434,272.199,350.434,273.724z"></path> </g> </g> </g></svg>`;

        const mugPanel = document.createElement('div');
        mugPanel.className = 'mugPanel';
        mugPanel.innerHTML = `
            <button class="closeButton">&times;</button>
            <label>Enter Torn API Key:</label>
            <input type="text" id="apiKeyInput" placeholder="API Key" />
            <label>Mug Merits (0-10):</label>
            <input type="number" id="mugMeritsInput" placeholder="0 to 10" min="0" max="10" />
            <button class="saveSettings">Save</button>
        `;

        mugPanel.querySelector('.closeButton').addEventListener('click', () => {
            mugPanel.style.display = 'none';
        });

        const savedApiKey = getCookie('tornBuyMugApiKey');
        if (savedApiKey) mugPanel.querySelector('#apiKeyInput').value = savedApiKey;

        const savedMerits = getCookie('tornBuyMugMerits');
        if (savedMerits) mugPanel.querySelector('#mugMeritsInput').value = savedMerits;

        mugPanel.querySelector('.saveSettings').addEventListener('click', function () {
            const apiKeyVal = mugPanel.querySelector('#apiKeyInput').value.trim();
            if (apiKeyVal) setCookie('tornBuyMugApiKey', apiKeyVal, 365);

            let mugMeritsVal = parseInt(mugPanel.querySelector('#mugMeritsInput').value.trim(),10);
            if (isNaN(mugMeritsVal) || mugMeritsVal < 0) mugMeritsVal = 0;
            if (mugMeritsVal > 10) mugMeritsVal = 10;
            setCookie('tornBuyMugMerits', mugMeritsVal, 365);

            mugPanel.style.display = 'none';
        });

        mugButton.addEventListener('click', () => {
            mugPanel.style.display = mugPanel.style.display === 'none' ? 'block' : 'none';
        });

        const appHeader = document.querySelector('.appHeaderWrapper___uyPti .linksContainer___LiOTN');
        if (appHeader) {
            appHeader.prepend(mugPanel);
            appHeader.prepend(mugButton);
        }
    }

    function waitForElements(selector, callback, maxAttempts = 10, interval = 500) {
        let attempts = 0;
        const check = () => {
            attempts++;
            const elems = document.querySelectorAll(selector);
            if (elems.length > 0) {
                callback(elems);
            } else if (attempts < maxAttempts) {
                setTimeout(check, interval);
            }
        };
        check();
    }

    function fetchUserData(apiKey, playerId) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `${API_BASE_URL}?key=${apiKey}&selections=basic,profile&id=${playerId}&cat=all`,
                onload: (response) => {
                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        resolve(data);
                    } else {
                        reject();
                    }
                },
                onerror: () => {
                    reject();
                },
            });
        });
    }

    function determineBackgroundColor(data) {
        const companyType = data.job?.company_type;
        const state = data.status?.state;

        if (companyType === 5) return '#ffe6e6';
        if (state === "Traveling" || state === "Abroad") return '#e6f2ff';
        if (state === "Hospital") return '#c4c278';
        if (state === "Federal") return '#db1f1f';
        return '#7fc986';
    }

    function createInfoPopup(data, totalMoney, mugMerits, isAnon = false) {
        let level = data.level || "Unknown";
        let status = data.status || {state:"Unknown", description:"No status info", until:0};

        let mugPercentage = 5 + (mugMerits * 0.25);
        let adjustedMoney = totalMoney;
        let clothingNote = "";
        const companyType = data.job?.company_type;

        if (companyType === 5) {
            adjustedMoney = totalMoney * 0.25;
            clothingNote = "(Clothing Store: 75% protection)";
        }

        const potentialMug = Math.round(adjustedMoney * (mugPercentage / 100));
        const now = Math.floor(Date.now() / 1000);
        const secondsLeft = status.until - now;
        const hospitalTime = status.state === "Hospital" ? `Time left: ${formatTime(secondsLeft)}` : "Not in hospital";

        const popup = document.createElement("div");
        popup.className = "infoPopup";

        let anonMessage = "";
        if (isAnon) {
            anonMessage = "<br><em>Being anonymous have saved them from your cunning mug attempt!</em>";
        }

        popup.innerHTML = `
            <strong>Level:</strong> ${level}<br>
            <strong>Status:</strong> ${status.description}<br>
            <strong>Hospital:</strong> ${hospitalTime}<br>
            <strong>Total Money:</strong> $${(totalMoney || 0).toLocaleString()}<br>
            ${clothingNote ? `<strong>${clothingNote}</strong><br>` : ""}
            <strong>Min Potential Mug:</strong> ~${mugPercentage.toFixed(2)}% ≈ $${potentialMug.toLocaleString()}
            ${anonMessage}
        `;
        popup.style.backgroundColor = determineBackgroundColor(data);

        return popup;
    }

    function formatTime(seconds) {
        if (seconds < 0) seconds = 0;
        const h = Math.floor(seconds / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);
        return `${h}h ${m}m ${s}s`;
    }

    function positionPopup(icon, popup) {
        const rect = icon.getBoundingClientRect();
        popup.style.top = `${rect.bottom + window.scrollY + 5}px`;
        popup.style.left = `${rect.left + window.scrollX}px`;
    }

    function extractUserId(href) {
        const match = href.match(/XID=(\d+)|\/profiles\.php\?XID=(\d+)/);
        return match ? (match[1] || match[2]) : null;
    }

    function extractMarketInfo(row) {
        let priceElement = row.querySelector(".price___Uwiv2") || row.querySelector(".price___v8rRx");
        let availableElement = row.querySelector(".available___xegv_") || row.querySelector(".available___jtANf");

        const price = priceElement ? parseInt(priceElement.textContent.replace("$", "").replace(/,/g, "")) : 0;
        const available = availableElement ? parseInt(availableElement.textContent.replace(" available", "")) : 0;
        return { price, available };
    }

    async function attachInfoIconForRow(row) {
        const oldIcon = row.querySelector('.infoIcon');
        if (oldIcon) oldIcon.remove();

        const honorElem = row.querySelector('.honorWrap___BHau4 a.linkWrap___ZS6r9');
        const anonElem = row.querySelector('.anonymous___P3s5s');
        if (!honorElem && !anonElem) return;

        let priceElement = row.querySelector(".price___Uwiv2") || row.querySelector(".price___v8rRx");
        if (!priceElement) return;

        const infoIcon = document.createElement("div");
        infoIcon.className = "infoIcon";
        infoIcon.textContent = "i";

        priceElement.parentNode.insertBefore(infoIcon, priceElement);

        infoIcon.addEventListener("click", async (e) => {
            e.stopPropagation();
            if (currentPopup && currentIcon === infoIcon) {
                currentPopup.remove();
                currentPopup = null;
                currentIcon = null;
                return;
            }
            if (currentPopup) {
                currentPopup.remove();
                currentPopup = null;
                currentIcon = null;
            }

            const apiKey = getCookie("tornBuyMugApiKey");
            const mugMerits = parseInt(getCookie("tornBuyMugMerits")||"0",10);
            if (!apiKey) return;

            const { price, available } = extractMarketInfo(row);
            const totalMoney = price * available;
            let data;
            let isAnon = false;
            if (anonElem && !honorElem) {
                data = {
                    level: "N/A",
                    status: {state:"N/A", description:"Anonymous Seller", until:0},
                    job: {}
                };
                isAnon = true;
            } else {
                const playerId = extractUserId(honorElem.href);
                if (!playerId) return;

                const now = Date.now();
                if (dataCache[playerId] && (now - dataCache[playerId].timestamp < CACHE_DURATION)) {
                    data = dataCache[playerId].data;
                } else {
                    try {
                        data = await fetchUserData(apiKey, playerId);
                        dataCache[playerId] = { data, timestamp: now };
                    } catch {
                        return;
                    }
                }
            }

            const popup = createInfoPopup(data, totalMoney, mugMerits, isAnon);
            document.body.appendChild(popup);
            positionPopup(infoIcon, popup);
            popup.classList.add("visible");
            currentPopup = popup;
            currentIcon = infoIcon;

            const status = data.status;
            if (status && status.state === "Hospital") {
                const now = Math.floor(Date.now() / 1000);
                const secondsLeft = status.until - now;
                if (secondsLeft <= 300) {
                    infoIcon.style.backgroundColor = 'green';
                }
            }
        });
    }

    function processAllSellers() {
        const allRows = document.querySelectorAll('.rowWrapper___me3Ox, .sellerRow___Ca2pK');
        allRows.forEach(row => attachInfoIconForRow(row));
    }

    function observeNewSellers() {
        const container = document.querySelector('.sellerListWrapper___PN32N');
        if (!container) return;

        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === 1) {
                            if (node.matches('.rowWrapper___me3Ox, .sellerRow___Ca2pK')) {
                                attachInfoIconForRow(node);
                            } else {
                                const newRows = node.querySelectorAll?.('.rowWrapper___me3Ox, .sellerRow___Ca2pK');
                                newRows?.forEach(r => attachInfoIconForRow(r));
                            }
                        }
                    });
                }
            }
        });

        observer.observe(container, {childList: true, subtree: true});
    }

    function monitorURLChanges() {
        setInterval(() => {
            if (window.location.href !== previousURL) {
                previousURL = window.location.href;
                processAllSellers();
            }
        }, URL_CHECK_INTERVAL);
    }

    document.addEventListener('click', (e) => {
        if (currentPopup && currentIcon && !currentPopup.contains(e.target) && e.target !== currentIcon) {
            currentPopup.remove();
            currentPopup = null;
            currentIcon = null;
        }
    });

    function initialize() {
        addGlobalStyles();
        createMugButtonAndPanel();

        waitForElements('.rowWrapper___me3Ox, .sellerRow___Ca2pK', () => {
            processAllSellers();
            observeNewSellers();
            monitorURLChanges();
        });
    }

    window.addEventListener('load', initialize);
})();