Greasy Fork 支持简体中文。

Torn Item Market Highlighter

Highlight items in the item market/bazaars that are at or below Arson Warehouse Pricelist and Market Value

// ==UserScript==
// @name         Torn Item Market Highlighter
// @namespace    http://tampermonkey.net/
// @version      2.16
// @description  Highlight items in the item market/bazaars that are at or below Arson Warehouse Pricelist and Market Value
// @author       You
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @match        https://www.torn.com/bazaar.php*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @grant        GM.xmlHttpRequest
// ==/UserScript==

(function() {
    'use strict';

    GM_addStyle(`
         /* Existing styles */
         .price-indicators-row {
             display: inline-flex;
             gap: 4px;
             margin-left: 4px;
             font-size: 10px;
             vertical-align: middle;
         }

         .price-indicator {
             padding: 1px 3px;
             border-radius: 3px;
             font-weight: bold;
             white-space: nowrap;
             display: inline-flex;
             align-items: center;
             justify-content: center;
             gap: 2px;
             min-width: 44px;
             max-width: fit-content;
             text-align: center;
         }

         .diff-90-100 {
             background: #004d00;
             color: white;
         }
         .diff-60-90 {
             background: #006700;
             color: white;
         }
         .diff-30-60 {
             background: #008100;
             color: white;
         }
         .diff-0-30 {
             background: #009b00;
             color: white;
         }
         .diff0-30 {
             background: #cc0000;
             color: white;
             width: fit-content;
             padding: 1px 4px;
         }
         .diff30-60 {
             background: #b30000;
             color: white;
             width: fit-content;
             padding: 1px 4px;
         }
         .diff60-90 {
             background: #990000;
             color: white;
             width: fit-content;
             padding: 1px 4px;
         }
         .diff90-plus {
             background: #800000;
             color: white;
             width: fit-content;
             padding: 1px 4px;
         }
         .diff-equal {
             background: #666666;
             color: white;
             width: fit-content;
             padding: 1px 4px;
         }

         .icon-exchange {
             display: inline-block;
             width: 12px;
             height: 12px;
             background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='white' d='M0 168v-16c0-13.255 10.745-24 24-24h360V80c0-21.367 25.899-32.042 40.971-16.971l80 80c9.372 9.373 9.372 24.569 0 33.941l-80 80C409.956 271.982 384 261.456 384 240v-48H24c-13.255 0-24-10.745-24-24zm488 152H128v-48c0-21.314-25.862-32.08-40.971-16.971l-80 80c-9.372 9.373-9.372 24.569 0 33.941l80 80C102.057 463.997 128 453.437 128 432v-48h360c13.255 0 24-10.745 24-24v-16c0-13.255-10.745-24-24-24z'/%3E%3C/svg%3E");
         }

         .icon-store {
             display: inline-block;
             width: 12px;
             height: 12px;
             background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 616 512'%3E%3Cpath fill='white' d='M602 118.6L537.1 15C531.3 5.7 521 0 510 0H106C95 0 84.7 5.7 78.9 15L14 118.6c-33.5 53.5-3.8 127.9 58.8 136.4 4.5.6 9.1.9 13.7.9 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18.1 20.1 44.3 33.1 73.8 33.1 4.7 0 9.2-.3 13.7-.9 62.8-8.4 92.6-82.8 59-136.4zM529.5 288c-10 0-19.9-1.5-29.5-3.8V384H116v-99.8c-9.6 2.2-19.5 3.8-29.5 3.8-6 0-12.1-.4-18-1.2-5.6-.8-11.1-2.1-16.4-3.6V480c0 17.7 14.3 32 32 32h448c17.7 0 32-14.3 32-32V283.2c-5.4 1.6-10.8 2.9-16.4 3.6-6.1.8-12.1 1.2-18.2 1.2z'/%3E%3C/svg%3E");
         }

         .icon-exchange, .icon-store {
             display: inline-block;
             width: 10px;
             height: 10px;
             background-size: contain;
             background-repeat: no-repeat;
             background-position: center;
             vertical-align: middle;
             margin-right: 2px;
         }

         /* Desktop layout improvements */
         @media (min-width: 785px) {
             .sellerRow___AI0m6 {
                 padding: 4px 4px !important;
                 display: flex !important;
                 align-items: center !important;
                 gap: 2px !important;
                 width: 100% !important;
             }

             .thumbnail___M_h9v {
                 flex-shrink: 0;
                 width: 40px !important;
                 margin-right: 4px !important;
             }

             .userInfoWrapper___B2a2P {
                 flex-shrink: 0;
                 min-width: 110px;
                 margin-right: 4px !important;
             }

             /* Stack indicators only in seller rows */
             .sellerRow___AI0m6 .price-indicators-row {
                 display: inline-flex !important;
                 flex-direction: column !important;
                 gap: 2px !important;
                 margin-left: 2px !important;
                 margin-right: 0 !important;
             }

             .price___Uwiv2 {
                 display: flex !important;
                 align-items: center !important;
                 flex-shrink: 0;
                 min-width: 85px;
                 margin-right: 0 !important;
             }

             .available___xegv_ {
                 flex-shrink: 0;
                 min-width: 55px;
                 text-align: right;
                 margin-right: 2px !important;
             }

             .buyControlsInRow___GVAKp {
                 flex-shrink: 0;
             }

             .buyControls___MxiIN {
                 display: flex !important;
                 align-items: center !important;
                 gap: 2px !important;
             }

             .amountInputWrapper___a4BMt {
                 min-width: 55px !important;
                 width: 55px !important;
                 flex-shrink: 0;
             }

             .input-money {
                 min-width: 45px !important;
                 width: 100% !important;
                 padding: 0 2px !important;
             }

             .buyButton___Flkhg {
                 flex-shrink: 0;
                 min-width: 65px;
                 padding-left: 8px !important;
                 padding-right: 8px !important;
             }

             .price-indicator {
                 padding: 1px 4px !important;
                 min-width: 0 !important;
             }

             .space___qCLQp {
                 display: none !important;
             }
         }

         /* Mobile-specific styles */
         @media (max-width: 784px) {
             .sellerRow___Ca2pK {
                 display: grid !important;
                 grid-template-columns: minmax(80px, 1fr) auto auto auto !important;
                 align-items: center !important;
                 gap: 8px !important;
                 padding: 8px 12px !important;
             }

             .sellerRow___Ca2pK:first-child {
                 font-weight: bold;
                 background-color: rgba(0, 0, 0, 0.1);
             }

             .userInfoWrapper___B2a2P {
                 min-width: 80px;
                 max-width: 120px;
             }

             .price___v8rRx {
                 position: relative;
                 display: flex;
                 flex-direction: column;
                 align-items: center;
                 gap: 2px;
                 min-width: 85px;
             }

             .price-indicators-row {
                 position: static !important;
                 display: flex !important;
                 flex-direction: column !important;
                 gap: 2px !important;
                 margin-top: 2px !important;
                 font-size: 9px !important;
                 align-items: center !important;
             }

             .price-indicator {
                 padding: 1px 4px !important;
                 white-space: nowrap !important;
                 text-align: center !important;
                 justify-content: center !important;
                 width: fit-content !important;
                 min-width: 0 !important;
                 margin: 0 auto !important;
                 display: inline-flex !important;
                 align-items: center !important;
             }

             .available___jtANf {
                 text-align: center;
                 min-width: 30px;
             }

             .showBuyControlsButton___K8f72 {
                 padding: 6px !important;
                 display: flex !important;
                 align-items: center !important;
                 justify-content: center !important;
             }

             .userInfoHead___LXxjB,
             .priceHead___Yo8ku,
             .availableHead___BkcpB,
             .showBuyControlsHead___SczEn {
                 text-align: center !important;
             }

             .icon-exchange,
             .icon-store {
                 width: 8px !important;
                 height: 8px !important;
                 margin: 0 2px 0 0 !important;
                 display: inline-flex !important;
                 align-items: center !important;
                 justify-content: center !important;
             }
         }

/* Styles for the floating container */
#floating-container {
    position: fixed;
    top: 100px;
    left: -200px; /* Adjust this value to the negative width of the container */
    z-index: 1000;
    background-color: rgba(0, 0, 0, 0.7);
    padding: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    color: white;
    transition: left 0.3s ease;
}

#floating-container.expanded {
    left: 0;
}

#floating-container button {
    display: block;
    width: 100%;
    margin-bottom: 5px;
    background-color: #333;
    color: white;
    border: none;
    padding: 8px;
    border-radius: 5px;
    cursor: pointer;
}

#floating-container button:hover {
    background-color: #555;
}

/* Styles for the toggle button */
#toggle-button {
    position: fixed;
    top: 100px;
    left: 0;
    background-color: rgba(0,0,0,0.7);
    border-top-right-radius: 5px;
    border-bottom-right-radius: 5px;
    width: 25px;
    height: 50px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-size: 18px;
    z-index: 1001;
}

.toast {
    position: fixed;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    background-color: rgba(0,0,0,0.7);
    color: white;
    padding: 10px;
    border-radius: 5px;
    z-index: 9999; /* Ensure it's above other elements */
    opacity: 0;
    transition: opacity 0.5s ease;
}

.toast.show {
    opacity: 1;
}

    `);

    let item_prices = {};
    let torn_market_values = {};

    try {
        item_prices = JSON.parse(GM_getValue("AWH_Prices", "{}"));
        torn_market_values = JSON.parse(GM_getValue("Torn_Market_Values", "{}"));
    } catch (e) {}

    function getTornIDFromPage() {
        const tornUserInput = document.getElementById('torn-user');
        if (tornUserInput) {
            try {
                const userData = JSON.parse(tornUserInput.value);
                return userData.id;
            } catch (e) {
                console.error('Error parsing torn-user data:', e);
                return null;
            }
        }
        return null;
    }

function createFloatingContainer() {
    const container = document.createElement('div');
    container.id = 'floating-container';
    // Start collapsed by default (left position is negative in CSS)

    const toggleButton = document.createElement('div');
    toggleButton.id = 'toggle-button';
    toggleButton.innerHTML = '☰'; // Hamburger icon

    toggleButton.addEventListener('click', () => {
        if (container.classList.contains('expanded')) {
            container.classList.remove('expanded');
        } else {
            container.classList.add('expanded');
        }
    });

    const buttonsWrapper = document.createElement('div');

    const addAWHButton = document.createElement('button');
    addAWHButton.textContent = GM_getValue("AWH_Key", "") ? 'Edit AWH API key' : 'Add AWH API key';
    addAWHButton.addEventListener('click', () => {
        let AWH_Key = GM_getValue("AWH_Key", "");
        AWH_Key = prompt("Enter your AWH API key", AWH_Key);
        if (AWH_Key !== null) {  // Only proceed if user didn't press Cancel
            if (AWH_Key.trim() === "") {
                // If field was cleared, remove the key and clear AWH prices
                GM_setValue("AWH_Key", "");
                GM_setValue("AWH_Prices", "{}");
                item_prices = {};  // Clear the current prices in memory
                showToast("AWH API key and prices removed successfully!", 'success');
                addAWHButton.textContent = 'Add AWH API key';
            } else {
                // If new key provided, save it
                GM_setValue("AWH_Key", AWH_Key);
                showToast("AWH API key saved successfully!", 'success');
                addAWHButton.textContent = 'Edit AWH API key';
                checkAndUpdatePrices();
            }
            updateButtonsVisibility();
            processElements();  // Refresh the display
        }
    });

    const addTornButton = document.createElement('button');
    addTornButton.textContent = GM_getValue("Torn_API_Key", "") ? 'Edit Torn API key' : 'Add Torn API key';
    addTornButton.addEventListener('click', () => {
        let tornApiKey = GM_getValue("Torn_API_Key", "");
        tornApiKey = prompt("Enter your Torn API key", tornApiKey);
        if (tornApiKey !== null) {  // Only proceed if user didn't press Cancel
            if (tornApiKey.trim() === "") {
                // If field was cleared, remove the key and clear market values
                GM_setValue("Torn_API_Key", "");
                GM_setValue("Torn_Market_Values", "{}");
                torn_market_values = {};  // Clear the current values in memory
                showToast("Torn API key and market values removed successfully!", 'success');
                addTornButton.textContent = 'Add Torn API key';
            } else {
                // If new key provided, save it
                GM_setValue("Torn_API_Key", tornApiKey);
                showToast("Torn API key saved successfully!", 'success');
                addTornButton.textContent = 'Edit Torn API key';
                getTornMarketValues();
            }
            updateButtonsVisibility();
            processElements();  // Refresh the display
        }
    });


    const getAWHPricesButton = document.createElement('button');
    getAWHPricesButton.textContent = 'Get AWH Prices Now';
    getAWHPricesButton.addEventListener('click', getAWHPrices);

    const getMarketValuesButton = document.createElement('button');
    getMarketValuesButton.textContent = 'Get Market Values Now';
    getMarketValuesButton.addEventListener('click', getTornMarketValues);

    buttonsWrapper.appendChild(addAWHButton);
    buttonsWrapper.appendChild(addTornButton);
    buttonsWrapper.appendChild(getAWHPricesButton);
    buttonsWrapper.appendChild(getMarketValuesButton);

    container.appendChild(buttonsWrapper);

    document.body.appendChild(container);
    document.body.appendChild(toggleButton);

    // Function to update button visibility based on API keys
    function updateButtonsVisibility() {
        const AWH_Key = GM_getValue("AWH_Key", "");
        const tornApiKey = GM_getValue("Torn_API_Key", "");

        addAWHButton.textContent = AWH_Key ? 'Edit AWH API key' : 'Add AWH API key';
        addTornButton.textContent = tornApiKey ? 'Edit Torn API key' : 'Add Torn API key';

        if (!AWH_Key) {
            getAWHPricesButton.style.display = 'none';
            // Ensure AWH prices are cleared if key is removed
            if (Object.keys(item_prices).length > 0) {
                item_prices = {};
                GM_setValue("AWH_Prices", "{}");
            }
        } else {
            getAWHPricesButton.style.display = '';
        }

        if (!tornApiKey) {
            getMarketValuesButton.style.display = 'none';
            // Ensure market values are cleared if key is removed
            if (Object.keys(torn_market_values).length > 0) {
                torn_market_values = {};
                GM_setValue("Torn_Market_Values", "{}");
            }
        } else {
            getMarketValuesButton.style.display = '';
        }
    }


    updateButtonsVisibility(); // Set initial visibility
}



function showToast(message, type = 'info') {
    const toast = document.createElement('div');
    toast.className = 'toast';
    toast.textContent = message;

    if (type === 'success') {
        toast.style.backgroundColor = 'green';
    } else if (type === 'error') {
        toast.style.backgroundColor = 'red';
    } else {
        toast.style.backgroundColor = 'rgba(0,0,0,0.7)';
    }

    document.body.appendChild(toast);
    setTimeout(() => {
        toast.classList.add('show');
    }, 100);

    setTimeout(() => {
        toast.classList.remove('show');
        setTimeout(() => {
            toast.remove();
        }, 500);
    }, 3000); // Show for 3 seconds
}


    function checkAndUpdatePrices() {
        const stored_torn_id = GM_getValue("AWH_TornID", "");
        const page_torn_id = getTornIDFromPage();
        const AWH_Key = GM_getValue("AWH_Key", "");

        // Update stored Torn ID if we found one on the page
        if (page_torn_id && page_torn_id !== stored_torn_id) {
            GM_setValue("AWH_TornID", page_torn_id);
        }

        // Use page Torn ID if available, fall back to stored ID
        const torn_id = page_torn_id || stored_torn_id;

        if (AWH_Key) {
            getAWHPrices();
        }
    }

    function scheduleNextUpdate() {
        const now = new Date();
        const target = new Date(now);
        target.setUTCHours(20, 15, 0, 0); // 8:15 PM UTC

        if (now > target) {
            target.setDate(target.getDate() + 1);
        }

        const msUntilUpdate = target - now;
        setTimeout(() => {
            getAWHPrices();
            getTornMarketValues();
            scheduleNextUpdate();
        }, msUntilUpdate);
    }

function getTornMarketValues() {
    const tornApiKey = GM_getValue("Torn_API_Key", "");

    if (!tornApiKey) {
        // Torn API key not set, skipping
        return;
    }

    GM.xmlHttpRequest({
        method: "GET",
        url: `https://api.torn.com/torn/?key=${tornApiKey}&selections=items`,
        onload: function(response) {
            try {
                const data = JSON.parse(response.responseText);
                if (data.items) {
                    Object.entries(data.items).forEach(([itemId, item]) => {
                        torn_market_values[itemId] = item.market_value || 0;
                    });
                    GM_setValue("Torn_Market_Values", JSON.stringify(torn_market_values));
                    GM_setValue("lastMarketUpdate", Date.now());
                    showToast('Market values updated successfully!', 'success');
                    processElements();
                } else {
                    showToast('No market value data received. Please check your API key.', 'error');
                }
            } catch (e) {
                showToast('Error updating market values. Please check your API key.', 'error');
            }
        },
        onerror: function() {
            showToast('Failed to connect to Torn API. Please try again later.', 'error');
        }
    });
}


function getAWHPrices() {
    const AWH_Key = GM_getValue("AWH_Key", "");
    const torn_id = getTornIDFromPage() || GM_getValue("AWH_TornID", "");

    if (!AWH_Key) {
        // AWH API key not set, skipping
        return;
    }

    item_prices = {};
    GM.xmlHttpRequest({
        method: "GET",
        url: `https://arsonwarehouse.com/api/v1/bids/${torn_id}`,
        headers: {
            "Authorization": "Basic " + btoa(AWH_Key + ':')
        },
        onload: function(response) {
            try {
                const items = JSON.parse(response.responseText);
                if (items.bids?.length > 0) {
                    items.bids.forEach(bid => {
                        if (bid.item_id && bid.bids?.length > 0) {
                            item_prices[bid.item_id] = bid.bids[0].price || 0;
                        }
                    });
                    GM_setValue("AWH_Prices", JSON.stringify(item_prices));
                    GM_setValue("lastUpdate", Date.now());
                    showToast('Prices updated successfully!', 'success');
                    processElements();
                } else {
                    showToast('No price data received. Please check your credentials.', 'error');
                }
            } catch (e) {
                showToast('Error updating prices. Please check your credentials.', 'error');
            }
        },
        onerror: function() {
            showToast('Failed to connect to AWH. Please try again later.', 'error');
        }
    });
}


    function addPriceIndicator(itemId, itemPrice, container) {
        // Remove any existing indicators row
        const existingRow = container.nextElementSibling;
        if (existingRow?.classList.contains('price-indicators-row')) {
            existingRow.remove();
        }

        // Create new indicators row
        const indicatorsRow = document.createElement('div');
        indicatorsRow.classList.add('price-indicators-row');

        // Get quantity if we're in a seller row
        let quantity = 1;
        if (container.closest('.sellerRow___AI0m6')) {
            const quantityElement = container.closest('.sellerRow___AI0m6').querySelector('.available___xegv_');
            if (quantityElement) {
                // Extract number from "X available" text
                const match = quantityElement.textContent.match(/(\d+)\s+available/);
                quantity = match ? parseInt(match[1]) : 1;
            }
        }

        // AWH Price comparison (using exchange icon)
        if (item_prices[itemId]) {
            const awhPrice = item_prices[itemId];
            const awhPriceDiff = Math.round(((awhPrice - itemPrice) / awhPrice) * 100 * 100) / 100;
            const potentialProfit = (awhPrice - itemPrice) * quantity;

            const awhIndicator = document.createElement('span');
            awhIndicator.classList.add('price-indicator');
            awhIndicator.title = `Potential profit: $${potentialProfit.toLocaleString()}` +
                                (quantity > 1 ? ` (${quantity}x)` : '');
            const icon = document.createElement('span');
            icon.classList.add('icon-exchange');
            awhIndicator.appendChild(icon);
            awhIndicator.appendChild(document.createTextNode(
                ` ${awhPriceDiff > 0 ? '-' : '+'}${Math.abs(Math.round(awhPriceDiff))}%`
            ));

            if (Math.abs(awhPriceDiff) < 0.5) {
                awhIndicator.classList.add('diff-equal');
            } else if (awhPriceDiff > 0) {
                if (awhPriceDiff >= 90) awhIndicator.classList.add('diff-90-100');
                else if (awhPriceDiff >= 60) awhIndicator.classList.add('diff-60-90');
                else if (awhPriceDiff >= 30) awhIndicator.classList.add('diff-30-60');
                else awhIndicator.classList.add('diff-0-30');
            } else {
                if (awhPriceDiff <= -90) awhIndicator.classList.add('diff90-plus');
                else if (awhPriceDiff <= -60) awhIndicator.classList.add('diff60-90');
                else if (awhPriceDiff <= -30) awhIndicator.classList.add('diff30-60');
                else awhIndicator.classList.add('diff0-30');
            }

            indicatorsRow.appendChild(awhIndicator);
        }

        // Market Value comparison (using store icon)
        if (torn_market_values[itemId]) {
            const marketValue = torn_market_values[itemId];
            const marketPriceDiff = Math.round(((marketValue - itemPrice) / marketValue) * 100 * 100) / 100;
            const potentialProfit = (marketValue - itemPrice) * quantity;

            const marketIndicator = document.createElement('span');
            marketIndicator.classList.add('price-indicator');
            marketIndicator.title = `Potential profit: $${potentialProfit.toLocaleString()}` +
                                   (quantity > 1 ? ` (${quantity}x)` : '');
            const icon = document.createElement('span');
            icon.classList.add('icon-store');
            marketIndicator.appendChild(icon);
            marketIndicator.appendChild(document.createTextNode(
                ` ${marketPriceDiff > 0 ? '-' : '+'}${Math.abs(Math.round(marketPriceDiff))}%`
            ));

            if (Math.abs(marketPriceDiff) < 0.5) {
                marketIndicator.classList.add('diff-equal');
            } else if (marketPriceDiff > 0) {
                if (marketPriceDiff >= 90) marketIndicator.classList.add('diff-90-100');
                else if (marketPriceDiff >= 60) marketIndicator.classList.add('diff-60-90');
                else if (marketPriceDiff >= 30) marketIndicator.classList.add('diff-30-60');
                else marketIndicator.classList.add('diff-0-30');
            } else {
                if (marketPriceDiff <= -90) marketIndicator.classList.add('diff90-plus');
                else if (marketPriceDiff <= -60) marketIndicator.classList.add('diff60-90');
                else if (marketPriceDiff <= -30) marketIndicator.classList.add('diff30-60');
                else marketIndicator.classList.add('diff0-30');
            }

            indicatorsRow.appendChild(marketIndicator);
        }

        // Only add the row if we have at least one indicator
        if (indicatorsRow.children.length > 0) {
            container.after(indicatorsRow);
        }
    }

    function updateSingleElement(element) {
        let itemId, priceElement;

        // Check if we're in mobile view
        const isMobileView = window.innerWidth < 785;

        if (isMobileView) {
            // Find item ID from info button's aria-controls
            const infoButton = document.querySelector('button[aria-controls^="wai-itemInfo-"]');
            if (infoButton) {
                const ariaControls = infoButton.getAttribute('aria-controls');
                const match = ariaControls.match(/wai-itemInfo-(\d+)/);
                if (match) itemId = match[1];
            }

            if (element.classList.contains('price___v8rRx')) {
                priceElement = element;
            }
        } else {
            let container = element;
            while (container && !itemId) {
                const img = container.querySelector('img[src*="/images/items/"]');
                if (img) {
                    const idMatch = img.src.match(/\/images\/items\/(\d+)\//);
                    if (idMatch) itemId = idMatch[1];
                }
                container = container.parentElement;
            }

            if (element.classList.contains('priceAndTotal___eEVS7') ||
                element.classList.contains('price___Uwiv2') ||
                element.className.includes('price_')) {
                priceElement = element;
            }
        }

        if (!itemId || !priceElement) return;

        const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/);
        if (priceMatch) {
            const itemPrice = parseInt(priceMatch[1].replace(/,/g, ''));
            addPriceIndicator(itemId, itemPrice, priceElement);
        }
    }

    function processElements() {
        const isMobileView = window.innerWidth < 785;

        if (document.URL.includes('sid=ItemMarket')) {
            // Item tiles - keep original handling for both mobile and desktop
            document.querySelectorAll('.itemTile___cbw7w').forEach(tile => {
                const img = tile.querySelector('img.torn-item');
                if (!img) return;

                const idMatch = img.src.match(/\/images\/items\/(\d+)\//);
                if (!idMatch) return;

                const itemId = idMatch[1];
                const priceElement = tile.querySelector('.priceAndTotal___eEVS7');

                if (priceElement) {
                    const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/);
                    if (priceMatch) {
                        const itemPrice = parseInt(priceMatch[1].replace(/,/g, ''));
                        addPriceIndicator(itemId, itemPrice, priceElement);
                    }
                }
            });

            // Seller rows - handle differently for mobile vs desktop
            if (isMobileView) {
                const infoButton = document.querySelector('button[aria-controls^="wai-itemInfo-"]');
                if (infoButton) {
                    const ariaControls = infoButton.getAttribute('aria-controls');
                    const match = ariaControls.match(/wai-itemInfo-(\d+)/);
                    if (match) {
                        const itemId = match[1];
                        document.querySelectorAll('.sellerRow___Ca2pK').forEach(row => {
                            const priceElement = row.querySelector('.price___v8rRx');
                            if (priceElement) {
                                const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/);
                                if (priceMatch) {
                                    const itemPrice = parseInt(priceMatch[1].replace(/,/g, ''));
                                    addPriceIndicator(itemId, itemPrice, priceElement);
                                    // Restructure mobile layout
                                    if (!row.querySelector('.userInfoHead___LXxjB')) {  // Skip header row
                                        const indicatorsRow = row.querySelector('.price-indicators-row');
                                        if (indicatorsRow) {
                                            priceElement.appendChild(indicatorsRow);
                                        }
                                    }
                                }
                            }
                        });
                    }
                }
            } else {
                document.querySelectorAll('.sellerRow___AI0m6').forEach(row => {
                    const img = row.querySelector('.thumbnail___M_h9v img');
                    if (!img) return;

                    const idMatch = img.src.match(/\/images\/items\/(\d+)\//);
                    if (!idMatch) return;

                    const itemId = idMatch[1];
                    const priceElement = row.querySelector('.price___Uwiv2');

                    if (priceElement) {
                        const priceText = priceElement.textContent;
                        const priceMatch = priceText.match(/\$([0-9,]+)/);
                        if (priceMatch) {
                            const itemPrice = parseInt(priceMatch[1].replace(/,/g, ''));
                            addPriceIndicator(itemId, itemPrice, priceElement);
                        }
                    }
                });
            }
        }
        else if (document.URL.includes('bazaar.php')) {
            document.querySelectorAll('img[src*="/images/items/"][src*="/large.png"]').forEach(img => {
                if (!img.parentElement?.parentElement?.parentElement) return;

                const idMatch = img.src.match(/\/images\/items\/(\d+)\//);
                if (!idMatch) return;

                const itemId = idMatch[1];
                const container = img.parentElement.parentElement.parentElement;
                const priceElement = container.querySelector('[class*="price_"]');

                if (priceElement) {
                    const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/);
                    if (priceMatch) {
                        const itemPrice = parseInt(priceMatch[1].replace(/,/g, ''));
                        addPriceIndicator(itemId, itemPrice, priceElement);
                    }
                }
            });
        }
    }

    function initialize() {
        const lastUpdate = GM_getValue("lastUpdate", 0);
        const lastMarketUpdate = GM_getValue("lastMarketUpdate", 0);
        const now = Date.now();

        if (now - lastUpdate > 24 * 60 * 60 * 1000) {
            getAWHPrices();
        }

        if (now - lastMarketUpdate > 24 * 60 * 60 * 1000) {
            getTornMarketValues();
        }

        try {
            torn_market_values = JSON.parse(GM_getValue("Torn_Market_Values", "{}"));
        } catch (e) {}

        scheduleNextUpdate();

        setTimeout(() => {
            observer.observe(document.body, {
                childList: true,
                subtree: true,
                characterData: true,
                characterDataOldValue: true,
                attributes: true,
                attributeFilter: ['class']
            });
            processElements();
        }, 1000);

        createFloatingContainer();
    }

    const observer = new MutationObserver(mutations => {
        let affected = new Set();

        for (const mutation of mutations) {
            if (mutation.type === 'characterData') {
                let parentElement = mutation.target.parentElement;
                while (parentElement) {
                    if (parentElement.classList) {
                        if (parentElement.classList.contains('priceAndTotal___eEVS7') ||
                            parentElement.classList.contains('price___Uwiv2') ||
                            [...parentElement.classList].some(c => c.includes('price_'))) {
                            affected.add(parentElement);
                            break;
                        }
                    }
                    parentElement = parentElement.parentElement;
                }
            }
            else if (mutation.addedNodes.length) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.classList?.contains('itemTile___cbw7w') ||
                            node.classList?.contains('sellerRow___AI0m6') ||
                            node.querySelector?.('.itemTile___cbw7w, .sellerRow___AI0m6, [class*="price_"]')) {
                            processElements();
                            return;
                        }
                    }
                }
            }
        }

        affected.forEach(element => updateSingleElement(element));
    });

    // Start the script
    initialize();

})();