Torn Plushies & Flowers Tracker

Track plushies and flowers in Torn inventory and calculate missing items

当前为 2025-04-30 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn Plushies & Flowers Tracker
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Track plushies and flowers in Torn inventory and calculate missing items
// @author       You
// @match        https://www.torn.com/item.php*
// @match        https://www.torn.com/preferences.php*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      api.torn.com
// @run-at       document-idle
// ==/UserScript==
(() => {
    'use strict';
    // Enable debug logging
    const DEBUG = true;
    // Default configuration
    const DEFAULT_CONFIG = {
        apiKey: '',
        useMarketPrices: true,
        cacheDuration: 24,
        lastCacheUpdate: 0
    };
    // Load configuration from GM storage
    const loadConfig = () => {
        try {
            const savedConfig = GM_getValue('plushiesFlowersConfig');
            return savedConfig ? JSON.parse(savedConfig) : DEFAULT_CONFIG;
        }
        catch (e) {
            log('Error loading configuration, using defaults', e);
            return DEFAULT_CONFIG;
        }
    };
    // Save configuration to GM storage
    const saveConfig = (config) => {
        try {
            GM_setValue('plushiesFlowersConfig', JSON.stringify(config));
            log('Configuration saved', config);
        }
        catch (e) {
            log('Error saving configuration', e);
        }
    };
    // Load cached market prices
    const loadCachedPrices = () => {
        try {
            const cachedData = GM_getValue('plushiesFlowersPrices');
            if (!cachedData) {
                log('No cached price data found');
                return {};
            }
            const parsedData = JSON.parse(cachedData);
            log('Loaded cached prices data:', parsedData);
            // Log the structure of the prices object for debugging
            if (parsedData.prices) {
                log(`Cache contains prices for ${Object.keys(parsedData.prices).length} items`);
                log('First few cached prices:', Object.entries(parsedData.prices).slice(0, 5));
            }
            else {
                log('Cache does not contain a valid prices object');
            }
            return parsedData;
        }
        catch (e) {
            log('Error loading cached prices, using empty cache', e);
            return {};
        }
    };
    // Save market prices to cache
    const saveCachedPrices = (prices) => {
        try {
            const cacheData = {
                timestamp: Date.now(),
                prices: prices
            };
            GM_setValue('plushiesFlowersPrices', JSON.stringify(cacheData));
            log('Market prices cached', cacheData);
        }
        catch (e) {
            log('Error caching market prices', e);
        }
    };
    // Check if cache is valid (within cacheDuration)
    const isCacheValid = (config) => {
        try {
            const cachedData = GM_getValue('plushiesFlowersPrices');
            if (!cachedData)
                return false;
            const data = JSON.parse(cachedData);
            const now = Date.now();
            const cacheAge = (now - data.timestamp) / (1000 * 60 * 60); // hours
            return cacheAge < config.cacheDuration;
        }
        catch (e) {
            log('Error checking cache validity', e);
            return false;
        }
    };
    // Clear the price cache
    const clearCache = () => {
        try {
            GM_setValue('plushiesFlowersPrices', '');
            log('Price cache cleared');
        }
        catch (e) {
            log('Error clearing cache', e);
        }
    };
    // Current configuration
    let config = loadConfig();
    // Helper function for logging
    const log = (message, data) => {
        if (DEBUG) {
            if (data) {
                console.log(`[Plushies & Flowers Tracker] ${message}`, data);
            }
            else {
                console.log(`[Plushies & Flowers Tracker] ${message}`);
            }
        }
    };
    log('Script initialized');
    // Configuration - Update these values if the total numbers change
    const TOTAL_PLUSHIES = 13; // Total unique plushies in a complete set
    const TOTAL_FLOWERS = 11; // Total unique flowers in a complete set
    // Plushie names for reference
    const PLUSHIE_NAMES = [
        'Teddy Bear', 'Camel', 'Chamois', 'Jaguar', 'Kitten', 'Lion',
        'Monkey', 'Nessie', 'Panda', 'Red Fox', 'Sheep', 'Stingray', 'Wolverine'
    ];
    // Flower names for reference
    const FLOWER_NAMES = [
        'Dahlia', 'Orchid', 'African Violet', 'Cherry Blossom', 'Peony',
        'Ceibo Flower', 'Edelweiss', 'Crocus', 'Heather', 'Tribulus Omanense', 'Banana Orchid'
    ];
    // Collections to store found items and prices
    const plushiesFound = new Map();
    const flowersFound = new Map();
    const plushiePrices = new Map();
    const flowerPrices = new Map();
    // Item IDs for plushies and flowers (used for both market links and images)
    const PLUSHIE_IDS = {
        'Teddy Bear': 187,
        'Camel': 384,
        'Chamois': 273,
        'Jaguar': 258,
        'Kitten': 215,
        'Lion': 281,
        'Monkey': 269,
        'Nessie': 266,
        'Panda': 274,
        'Red Fox': 268,
        'Sheep': 186,
        'Stingray': 618,
        'Wolverine': 261
    };
    const FLOWER_IDS = {
        'Dahlia': 260,
        'Orchid': 264,
        'African Violet': 282,
        'Cherry Blossom': 277,
        'Peony': 276,
        'Ceibo Flower': 271,
        'Edelweiss': 272,
        'Crocus': 263,
        'Heather': 267,
        'Tribulus Omanense': 385,
        'Banana Orchid': 617
    };
    // Function to create and add the tracker button
    const addTrackerButton = () => {
        // Only run on inventory pages
        if (!window.location.href.includes('item.php'))
            return;
        // Check if our container already exists (to avoid duplicates)
        if (document.getElementById('plushies-flowers-tracker')) {
            log('Tracker already exists, not adding again');
            return;
        }
        // Create a container similar to the weapon ID script
        const container = document.createElement('div');
        container.className = 'tutorial-cont';
        container.id = 'plushies-flowers-tracker';
        const titleContainer = document.createElement('div');
        titleContainer.className = 'title-gray top-round';
        titleContainer.setAttribute('role', 'heading');
        titleContainer.setAttribute('aria-level', '5');
        const title = document.createElement('span');
        title.className = 'tutorial-title';
        title.innerHTML = 'Plushies & Flowers Collection Tracker';
        titleContainer.appendChild(title);
        container.appendChild(titleContainer);
        const description = document.createElement('div');
        description.className = 'tutorial-desc bottom-round cont-gray p10';
        description.innerHTML = `
            <p>Track your plushies and flowers collections to see what you're missing!</p>
            <p>Make sure to scroll down completely on each page to load all items before analyzing.</p>
        `;
        const buttonWrapper = document.createElement('div');
        buttonWrapper.style.display = 'flex';
        buttonWrapper.style.justifyContent = 'space-around';
        buttonWrapper.style.marginTop = '10px';
        const plushiesButton = document.createElement('div');
        plushiesButton.className = 'torn-btn';
        plushiesButton.innerHTML = 'Analyze Plushies';
        plushiesButton.style.width = '150px';
        plushiesButton.style.display = 'flex';
        plushiesButton.style.alignItems = 'center';
        plushiesButton.style.justifyContent = 'center';
        const flowersButton = document.createElement('div');
        flowersButton.className = 'torn-btn';
        flowersButton.innerHTML = 'Analyze Flowers';
        flowersButton.style.width = '150px';
        flowersButton.style.display = 'flex';
        flowersButton.style.alignItems = 'center';
        flowersButton.style.justifyContent = 'center';
        buttonWrapper.appendChild(plushiesButton);
        buttonWrapper.appendChild(flowersButton);
        description.appendChild(buttonWrapper);
        container.appendChild(description);
        const delimiter = document.createElement('hr');
        delimiter.className = 'delimiter-999 m-top10 m-bottom10';
        // Find the last item list in the page to add our container after it
        // This ensures we don't add it multiple times and it's positioned correctly
        const itemLists = document.querySelectorAll('ul.items-cont');
        const lastItemList = itemLists[itemLists.length - 1];
        if (lastItemList && lastItemList.parentElement) {
            // Add some spacing
            const spacer = document.createElement('div');
            spacer.style.height = '20px';
            lastItemList.parentElement.insertAdjacentElement('afterend', spacer);
            spacer.insertAdjacentElement('afterend', container);
        }
        else {
            // Fallback to category-wrap if we can't find item lists
            const categoryWrap = document.getElementById('category-wrap');
            if (categoryWrap) {
                categoryWrap.insertAdjacentElement('afterend', delimiter);
                categoryWrap.insertAdjacentElement('afterend', container);
            }
        }
        // Add click events
        plushiesButton.addEventListener('click', () => analyzePlushies());
        flowersButton.addEventListener('click', () => analyzeFlowers());
    };
    // Function to directly scan the inventory for plushies and flowers
    const scanInventory = () => {
        log('Directly scanning inventory for plushies and flowers');
        // Clear existing collections
        plushiesFound.clear();
        flowersFound.clear();
        log('Cleared existing collections');
    };
    // Function to scan plushies
    const scanPlushies = () => {
        log('Scanning plushies...');
        // Find the plushies list
        const plushiesList = document.getElementById('plushies-items');
        if (!plushiesList) {
            log('Plushies list not found');
            return;
        }
        // Get all list items in the plushies section
        const plushieItems = Array.from(plushiesList.children);
        log(`Found ${plushieItems.length} plushie items`, plushieItems);
        // Process each plushie item
        plushieItems.forEach((item) => {
            processPlushieItem(item);
        });
    };
    // Function to scan flowers
    const scanFlowers = () => {
        log('Scanning flowers...');
        // Find the flowers list
        const flowersList = document.getElementById('flowers-items');
        if (!flowersList) {
            log('Flowers list not found');
            return;
        }
        // Get all list items in the flowers section
        const flowerItems = Array.from(flowersList.children);
        log(`Found ${flowerItems.length} flower items`, flowerItems);
        // Process each flower item
        flowerItems.forEach((item) => {
            processFlowerItem(item);
        });
    };
    // Function to process a plushie item from the inventory
    const processPlushieItem = (item) => {
        var _a;
        try {
            // Extract the name from the item
            const nameElement = item.querySelector('.name');
            if (!nameElement)
                return;
            // Get the name without 'Plushie' suffix
            let name = ((_a = nameElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '';
            name = name.replace(' Plushie', '');
            // Extract the quantity
            const quantityElement = item.querySelector('.qty');
            const quantity = quantityElement ? parseInt(quantityElement.textContent || '0') : 1;
            // Extract the price
            const priceElement = item.querySelector('.price');
            let price = 0;
            if (priceElement) {
                const priceText = priceElement.textContent || '';
                price = parseInt(priceText.replace(/[^0-9]/g, '')) || 0;
            }
            // Add to collections
            plushiesFound.set(name, quantity);
            plushiePrices.set(name, price);
            log(`Found plushie: ${name}, Quantity: ${quantity}, Price: $${price}`);
        }
        catch (e) {
            log('Error processing plushie item', e);
        }
    };
    // Function to process a flower item from the inventory
    const processFlowerItem = (item) => {
        var _a;
        try {
            // Extract the name from the item
            const nameElement = item.querySelector('.name');
            if (!nameElement)
                return;
            // Get the name without 'Flower' suffix
            let name = ((_a = nameElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '';
            name = name.replace(' Flower', '');
            // Extract the quantity
            const quantityElement = item.querySelector('.qty');
            const quantity = quantityElement ? parseInt(quantityElement.textContent || '0') : 1;
            // Extract the price
            const priceElement = item.querySelector('.price');
            let price = 0;
            if (priceElement) {
                const priceText = priceElement.textContent || '';
                price = parseInt(priceText.replace(/[^0-9]/g, '')) || 0;
            }
            // Add to collections
            flowersFound.set(name, quantity);
            flowerPrices.set(name, price);
            log(`Found flower: ${name}, Quantity: ${quantity}, Price: $${price}`);
        }
        catch (e) {
            log('Error processing flower item', e);
        }
    };
    // Function to display a popup with results
    const showResultPopup = (content) => {
        // Remove any existing popup
        const existingPopup = document.getElementById('plushies-flowers-result');
        if (existingPopup) {
            existingPopup.remove();
        }
        // Create the popup container
        const popup = document.createElement('div');
        popup.id = 'plushies-flowers-result';
        popup.style.position = 'fixed';
        popup.style.top = '50px';
        popup.style.left = '50%';
        popup.style.transform = 'translateX(-50%)';
        popup.style.width = '800px';
        popup.style.maxWidth = '90%';
        popup.style.maxHeight = '80vh';
        popup.style.backgroundColor = '#1a1a1a';
        popup.style.border = '1px solid #444';
        popup.style.borderRadius = '5px';
        popup.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
        popup.style.zIndex = '9999';
        popup.style.overflow = 'hidden';
        popup.style.display = 'flex';
        popup.style.flexDirection = 'column';
        // Create the header
        const header = document.createElement('div');
        header.style.padding = '10px';
        header.style.backgroundColor = '#333';
        header.style.borderBottom = '1px solid #444';
        header.style.display = 'flex';
        header.style.justifyContent = 'space-between';
        header.style.alignItems = 'center';
        header.style.cursor = 'move';
        const title = document.createElement('div');
        title.textContent = 'Torn Plushies & Flowers Tracker';
        title.style.fontWeight = 'bold';
        title.style.color = '#ffb502';
        const closeButton = document.createElement('div');
        closeButton.textContent = '×';
        closeButton.style.fontSize = '24px';
        closeButton.style.color = '#fff';
        closeButton.style.cursor = 'pointer';
        closeButton.addEventListener('click', () => popup.remove());
        header.appendChild(title);
        header.appendChild(closeButton);
        // Create the content area
        const contentArea = document.createElement('div');
        contentArea.style.padding = '15px';
        contentArea.style.overflowY = 'auto';
        contentArea.style.maxHeight = 'calc(80vh - 50px)';
        contentArea.innerHTML = content;
        popup.appendChild(header);
        popup.appendChild(contentArea);
        // Add to the page
        document.body.appendChild(popup);
        // Make the popup draggable
        let isDragging = false;
        let offsetX = 0;
        let offsetY = 0;
        header.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - popup.getBoundingClientRect().left;
            offsetY = e.clientY - popup.getBoundingClientRect().top;
        });
        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                popup.style.left = (e.clientX - offsetX) + 'px';
                popup.style.top = (e.clientY - offsetY) + 'px';
                popup.style.transform = 'none';
            }
        });
        document.addEventListener('mouseup', () => {
            isDragging = false;
        });
    };
    // Function to display flowers results
    const displayFlowersResults = () => {
        // Check if we found any flowers
        log(`Found ${flowersFound.size} flowers in inventory`, Array.from(flowersFound.entries()));
        if (flowersFound.size === 0) {
            log('No flowers found, showing error popup');
            showResultPopup('No flowers found in your inventory. Make sure you have clicked the Flowers tab and scrolled through your inventory to load all items.');
            return;
        }
        // Count unique flowers
        const uniqueFlowersCount = flowersFound.size;
        // Calculate total flowers
        let totalFlowersCount = 0;
        flowersFound.forEach(qty => totalFlowersCount += qty);
        // Calculate missing flower types (unique flowers missing)
        const missingFlowerTypes = TOTAL_FLOWERS - uniqueFlowersCount;
        // Find the flower with the highest quantity to use as target for complete sets
        let maxQuantity = 0;
        flowersFound.forEach(qty => {
            if (qty > maxQuantity)
                maxQuantity = qty;
        });
        // Default target sets is the maximum quantity (can be adjusted by user)
        let targetSets = maxQuantity;
        // Calculate total missing flowers count (how many flowers needed to reach potential maximum)
        let totalMissingFlowers = 0;
        // For each flower, calculate how many are needed to reach the target sets
        FLOWER_NAMES.forEach(name => {
            const quantity = flowersFound.get(name) || 0;
            const missing = targetSets - quantity;
            if (missing > 0) {
                totalMissingFlowers += missing;
            }
        });
        // Prepare missing flowers list
        const missingFlowers = FLOWER_NAMES.filter(name => !flowersFound.has(name));
        // Generate table rows for each flower
        let tableRows = '';
        let totalInvestment = 0;
        // First add the flowers the user has
        FLOWER_NAMES.forEach(name => {
            const quantity = flowersFound.get(name) || 0;
            const missing = maxQuantity - quantity;
            const itemId = FLOWER_IDS[name] || 0;
            // Try to get price from API first, then fall back to DOM-extracted price
            let price = 0;
            if (config.useMarketPrices && config.apiKey) {
                price = getMarketPrice(itemId);
            }
            // If no API price, use DOM-extracted price
            if (price === 0) {
                price = flowerPrices.get(name) || 0;
            }
            log(`Price for ${name} Flower (ID: ${FLOWER_IDS[name]}): $${price}`);
            const totalPrice = price * missing;
            // Add to total investment if there are missing items
            if (missing > 0 && price > 0) {
                totalInvestment += totalPrice;
            }
            // Create market link
            const encodedName = encodeURIComponent(`${name} Flower`);
            const marketLink = `https://www.torn.com/page.php?sid=ItemMarket#/market/view=search&itemID=${itemId}&itemName=${encodedName}&itemType=Flower`;
            // Use the item ID for the image
            const imageId = itemId;
            // Format price with commas
            const formattedPrice = price > 0 ? `$${price.toLocaleString()}` : '-';
            const formattedTotalPrice = totalPrice > 0 ? `$${totalPrice.toLocaleString()}` : '-';
            tableRows += `
                <tr>
                    <td style="vertical-align: middle; text-align: center;"><img src="https://www.torn.com/images/items/${imageId}/small.png" style="width: 30px; height: 30px; object-fit: contain;" alt="${name} Flower" /></td>
                    <td style="vertical-align: middle; color: #fff;">${name} Flower</td>
                    <td style="vertical-align: middle; text-align: center; color: #fff;">${quantity}</td>
                    <td style="vertical-align: middle; text-align: center; color: #fff;">${missing}</td>
                    <td style="vertical-align: middle; text-align: center; color: #fff;">${formattedPrice}</td>
                    <td style="vertical-align: middle; text-align: center; color: #fff;">${formattedTotalPrice}</td>
                    <td style="vertical-align: middle; text-align: center;">${missing > 0 ? `<a href="${marketLink}" target="_blank" class="t-blue">Buy</a>` : '-'}</td>
                </tr>
            `;
        });
        // Add total row
        if (totalInvestment > 0) {
            tableRows += `
                <tr>
                    <td colspan="5" style="vertical-align: middle; text-align: right; color: #ffb502; font-weight: bold;">Total Investment:</td>
                    <td style="vertical-align: middle; text-align: center; color: #ffb502; font-weight: bold;">$${totalInvestment.toLocaleString()}</td>
                    <td></td>
                </tr>
            `;
        }
        // Calculate how many complete sets can be made
        const completeSets = uniqueFlowersCount < TOTAL_FLOWERS ? 0 : Math.min(...Array.from(flowersFound.values(), v => v || 0).filter(v => v > 0));
        const potentialCompleteSets = maxQuantity;
        // Show results with table
        const resultMessage = `
            <div style="color: #ffb502; font-size: 18px; font-weight: bold; margin-bottom: 10px;">Flowers Collection Progress</div>
            <div style="margin-bottom: 15px; padding: 10px; background-color: #222; border-radius: 5px;">
                <p style="color: #fff; margin-bottom: 5px;">Target number of sets: <input type="number" id="flower-target-sets" value="${targetSets}" min="1" max="${maxQuantity}" style="width: 80px; padding: 5px; background-color: #333; color: #fff; border: 1px solid #555;"> <button id="update-flower-calc" style="padding: 5px 10px; background-color: #ffb502; color: #000; border: none; cursor: pointer;">Update</button></p>
                <p style="color: #aaa; font-size: 12px;">Adjust the target number of sets to calculate how many flowers you need to collect.</p>
            </div>
            <p style="color: #fff;">Unique flowers: ${uniqueFlowersCount}/${TOTAL_FLOWERS}</p>
            <p style="color: #fff;">Total flowers owned: ${totalFlowersCount}</p>
            <p style="color: #fff;">Complete sets: ${completeSets} (potential: ${potentialCompleteSets})</p>
            <p style="color: #fff;">Missing flower types: ${missingFlowerTypes}</p>
            <p style="color: #fff;">Total flowers needed: ${totalMissingFlowers}</p>
            
            <div style="height: 100%; overflow: auto; margin-top: 10px;">
                <table class="torn-table" width="100%" style="border-collapse: collapse;">
                    <thead>
                        <tr>
                            <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;"></th>
                            <th style="padding: 8px; text-align: left; background-color: #333; color: #ffb502;">Name</th>
                            <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Owned</th>
                            <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Missing</th>
                            <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Unit Price</th>
                            <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Total</th>
                            <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${tableRows}
                    </tbody>
                </table>
            </div>
        `;
        showResultPopup(resultMessage);
    };
    // Function to get market price for an item
    const getMarketPrice = (itemId) => {
        try {
            const cachedData = GM_getValue('plushiesFlowersPrices');
            if (!cachedData) {
                log(`No cached price data found for item ${itemId}`);
                return 0;
            }
            const data = JSON.parse(cachedData);
            // Convert itemId to string since API returns string keys
            const itemIdStr = itemId.toString();
            const price = data.prices && data.prices[itemIdStr] ? Number(data.prices[itemIdStr]) : 0;
            if (price > 0) {
                log(`Found cached price for item ${itemId}: $${price}`);
            }
            else {
                log(`No price found in cache for item ${itemId}`);
            }
            return price;
        }
        catch (e) {
            log('Error getting market price', e);
            return 0;
        }
    };
    // Function to fetch market prices from Torn API v2
    const fetchMarketPrices = (callback, forceUpdate = false) => {
        // Combine plushie and flower IDs for the API request
        const itemIds = [];
        // Add plushie IDs
        for (const name in PLUSHIE_IDS) {
            itemIds.push(PLUSHIE_IDS[name]);
        }
        // Add flower IDs
        for (const name in FLOWER_IDS) {
            itemIds.push(FLOWER_IDS[name]);
        }
        log(`Fetching market prices for ${itemIds.length} items...`);
        // Check if we have valid cached data and it's not too old
        const cachedData = loadCachedPrices();
        if (!forceUpdate && isCacheValid(config)) {
            log('Using cached market prices');
            callback(true);
            return;
        }
        // Build the API URL using the more efficient v2 torn/items endpoint
        // This endpoint provides just the item information we need with less data to process
        const apiUrl = `https://api.torn.com/v2/torn/items?ids=${itemIds.join(',')}&key=${config.apiKey}`;
        log(`Fetching item prices from API: ${apiUrl}`);
        // Make the API request
        GM_xmlhttpRequest({
            method: 'GET',
            url: apiUrl,
            onload: (response) => {
                try {
                    const data = JSON.parse(response.responseText);
                    // Check for API errors
                    if (data.error) {
                        log('API Error:', data.error);
                        callback(false);
                        return;
                    }
                    // Check if we got a valid response
                    if (!data.items) {
                        log('Invalid API response - no items data:', data);
                        callback(false);
                        return;
                    }
                    // Process the items data
                    const prices = {};
                    // Extract market values for each item
                    for (const itemId in data.items) {
                        const item = data.items[itemId];
                        // The market price is nested inside value.market_price
                        if (item && item.id && item.value && item.value.market_price) {
                            // Ensure we're storing numeric values as numbers
                            const marketValue = Number(item.value.market_price);
                            // Use the actual item ID from the API response
                            const actualItemId = item.id.toString();
                            prices[actualItemId] = marketValue;
                            log(`Fetched market value for item ${actualItemId} (${item.name}): $${marketValue.toLocaleString()}`);
                        }
                        else {
                            log(`No market value found for item ${itemId} in API response`);
                        }
                    }
                    // Log the total number of prices fetched
                    log(`Fetched prices for ${Object.keys(prices).length} items out of ${itemIds.length} requested`);
                    // Debug log the prices object before caching
                    log('Prices object to be cached:', prices);
                    // Cache the prices
                    saveCachedPrices(prices);
                    // Update the config with the last cache update time
                    config.lastCacheUpdate = Date.now();
                    saveConfig(config);
                    // Force a reload of the cached prices to verify they were stored correctly
                    const verifiedCache = loadCachedPrices();
                    log('Verified cached prices after saving:', verifiedCache);
                    callback(true);
                }
                catch (e) {
                    log('Error processing API response', e);
                    callback(false);
                }
            },
            onerror: (error) => {
                log('API request error', error);
                callback(false);
            }
        });
    };
    // Function to analyze plushies
    const analyzePlushies = () => {
        log('Analyzing plushies...');
        // Fetch market prices if needed
        if (config.useMarketPrices && config.apiKey) {
            fetchMarketPrices((success) => {
                if (success) {
                    log('Market prices updated successfully');
                }
                continueAnalyzePlushies();
            });
        }
        else {
            continueAnalyzePlushies();
        }
    };
    // Continue with plushie analysis after price fetch
    const continueAnalyzePlushies = () => {
        // Make sure we're on the plushies tab
        const plushiesTab = document.querySelector('a[data-category="plushies"]');
        if (plushiesTab) {
            // Click the plushies tab to ensure items are loaded
            log('Clicking plushies tab to ensure items are loaded');
            plushiesTab.click();
            // Give a moment for the tab to load
            setTimeout(() => {
                // Scan for plushies directly
                scanPlushies();
                displayPlushiesResults();
            }, 500);
        }
        else {
            // Try to scan anyway
            scanPlushies();
            displayPlushiesResults();
        }
    };
    // Function to display plushies results
    const displayPlushiesResults = () => {
        // Check if we found any plushies
        log(`Found ${plushiesFound.size} plushies in inventory`, Array.from(plushiesFound.entries()));
        if (plushiesFound.size === 0) {
            log('No plushies found, showing error popup');
            showResultPopup('No plushies found in your inventory. Make sure you have clicked the Plushies tab and scrolled through your inventory to load all items.');
            return;
        }
        // Count unique plushies
        const uniquePlushiesCount = plushiesFound.size;
        // Calculate total plushies
        let totalPlushiesCount = 0;
        plushiesFound.forEach(qty => totalPlushiesCount += qty);
        // Calculate missing plushies types (unique plushies missing)
        const missingPlushiesTypes = TOTAL_PLUSHIES - uniquePlushiesCount;
        // Find the plushie with the highest quantity to use as target for complete sets
        let maxQuantity = 0;
        plushiesFound.forEach(qty => {
            if (qty > maxQuantity)
                maxQuantity = qty;
        });
        // Default target sets is the maximum quantity (can be adjusted by user)
        let targetSets = maxQuantity;
        // Calculate total missing plushies count (how many plushies needed to reach potential maximum)
        let totalMissingPlushies = 0;
        // For each plushie, calculate how many are needed to reach the target sets
        PLUSHIE_NAMES.forEach(name => {
            const quantity = plushiesFound.get(name) || 0;
            const missing = targetSets - quantity;
            if (missing > 0) {
                totalMissingPlushies += missing;
            }
        });
        // Prepare missing plushies list
        const missingPlushies = PLUSHIE_NAMES.filter(name => !plushiesFound.has(name));
        // Generate table rows for each plushie
        let tableRows = '';
        let totalInvestment = 0;
        let singleSetValue = 0; // Track the market value of a single complete set
        // First add the plushies the user has
        PLUSHIE_NAMES.forEach(name => {
            const quantity = plushiesFound.get(name) || 0;
            const missing = maxQuantity - quantity;
            const itemId = PLUSHIE_IDS[name] || 0;
            // Try to get price from API first, then fall back to DOM-extracted price
            let price = 0;
            if (config.useMarketPrices && config.apiKey) {
                price = getMarketPrice(itemId);
            }
            // If no API price, use DOM-extracted price
            if (price === 0) {
                price = plushiePrices.get(name) || 0;
            }
            log(`Price for ${name} Plushie (ID: ${PLUSHIE_IDS[name]}): $${price}`);
            const totalPrice = price * missing;
            // Add to total investment if there are missing items
            if (missing > 0 && price > 0) {
                totalInvestment += totalPrice;
            }
            // Add to single set value (one of each plushie)
            if (price > 0) {
                singleSetValue += price;
            }
            // Create market link
            const encodedName = encodeURIComponent(`${name} Plushie`);
            const marketLink = `https://www.torn.com/page.php?sid=ItemMarket#/market/view=search&itemID=${itemId}&itemName=${encodedName}&itemType=Plushie`;
            // Use the item ID for the image
            const imageId = itemId;
            // Format price with commas
            const formattedPrice = price > 0 ? `$${price.toLocaleString()}` : '-';
            const formattedTotalPrice = totalPrice > 0 ? `$${totalPrice.toLocaleString()}` : '-';
            tableRows += `
            <tr>
                <td style="vertical-align: middle; text-align: center;"><img src="https://www.torn.com/images/items/${imageId}/small.png" style="width: 30px; height: 30px; object-fit: contain;" alt="${name} Plushie" /></td>
                <td style="vertical-align: middle; color: #fff;">${name} Plushie</td>
                <td style="vertical-align: middle; text-align: center; color: #fff;">${quantity}</td>
                <td style="vertical-align: middle; text-align: center; color: #fff;">${missing}</td>
                <td style="vertical-align: middle; text-align: center; color: #fff;">${formattedPrice}</td>
                <td style="vertical-align: middle; text-align: center; color: #fff;">${formattedTotalPrice}</td>
                <td style="vertical-align: middle; text-align: center;">${missing > 0 ? `<a href="${marketLink}" target="_blank" class="t-blue">Buy</a>` : '-'}</td>
            </tr>
        `;
        });
        // Add total investment row
        if (totalInvestment > 0) {
            tableRows += `
            <tr>
                <td colspan="5" style="vertical-align: middle; text-align: right; color: #ffb502; font-weight: bold;">Total Investment:</td>
                <td style="vertical-align: middle; text-align: center; color: #ffb502; font-weight: bold;">$${totalInvestment.toLocaleString()}</td>
                <td></td>
            </tr>
        `;
        }
        // Add single set value row
        if (singleSetValue > 0) {
            tableRows += `
            <tr>
                <td colspan="5" style="vertical-align: middle; text-align: right; color: #ffb502; font-weight: bold;">Market Value of Single Set:</td>
                <td style="vertical-align: middle; text-align: center; color: #ffb502; font-weight: bold;">$${singleSetValue.toLocaleString()}</td>
                <td></td>
            </tr>
        `;
        }
        // Calculate how many complete sets can be made
        // If any plushie is missing (uniquePlushiesCount < TOTAL_PLUSHIES), then no complete sets can be made
        const completeSets = uniquePlushiesCount < TOTAL_PLUSHIES ? 0 : Math.min(...Array.from(plushiesFound.values(), v => v || 0).filter(v => v > 0));
        const potentialCompleteSets = maxQuantity;
        // Show results with table
        const resultMessage = `
        <div style="color: #ffb502; font-size: 18px; font-weight: bold; margin-bottom: 10px;">Plushies Collection Progress</div>
        <div style="margin-bottom: 15px; padding: 10px; background-color: #222; border-radius: 5px;">
            <p style="color: #fff; margin-bottom: 5px;">Target number of sets: <input type="number" id="plushie-target-sets" value="${targetSets}" min="1" max="${maxQuantity}" style="width: 80px; padding: 5px; background-color: #333; color: #fff; border: 1px solid #555;"> <button id="update-plushie-calc" style="padding: 5px 10px; background-color: #ffb502; color: #000; border: none; cursor: pointer;">Update</button></p>
            <p style="color: #aaa; font-size: 12px;">Adjust the target number of sets to calculate how many plushies you need to collect.</p>
        </div>
        <div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px; padding: 15px; background-color: #222; border-radius: 5px;">
            <div style="flex: 1; min-width: 200px; background-color: #333; padding: 12px; border-radius: 5px; border-left: 3px solid #ffb502;">
                <div style="color: #aaa; font-size: 12px; margin-bottom: 5px;">Unique Plushies</div>
                <div style="color: #fff; font-size: 16px; font-weight: bold;">${uniquePlushiesCount}/${TOTAL_PLUSHIES}</div>
            </div>
            <div style="flex: 1; min-width: 200px; background-color: #333; padding: 12px; border-radius: 5px; border-left: 3px solid #ffb502;">
                <div style="color: #aaa; font-size: 12px; margin-bottom: 5px;">Total Plushies Owned</div>
                <div style="color: #fff; font-size: 16px; font-weight: bold;">${totalPlushiesCount.toLocaleString()}</div>
            </div>
            <div style="flex: 1; min-width: 200px; background-color: #333; padding: 12px; border-radius: 5px; border-left: 3px solid #ffb502;">
                <div style="color: #aaa; font-size: 12px; margin-bottom: 5px;">Complete Sets</div>
                <div style="color: #fff; font-size: 16px; font-weight: bold;">${completeSets} <span style="color: #aaa; font-size: 12px;">(potential: ${potentialCompleteSets})</span></div>
            </div>
            <div style="flex: 1; min-width: 200px; background-color: #333; padding: 12px; border-radius: 5px; border-left: 3px solid #ffb502;">
                <div style="color: #aaa; font-size: 12px; margin-bottom: 5px;">Missing Plushie Types</div>
                <div style="color: #fff; font-size: 16px; font-weight: bold;">${missingPlushiesTypes}</div>
            </div>
            <div style="flex: 1; min-width: 200px; background-color: #333; padding: 12px; border-radius: 5px; border-left: 3px solid #ffb502;">
                <div style="color: #aaa; font-size: 12px; margin-bottom: 5px;">Total Plushies Needed</div>
                <div style="color: #fff; font-size: 16px; font-weight: bold;">${totalMissingPlushies.toLocaleString()}</div>
            </div>
        </div>
        
        <div style="height: 100%; overflow: auto; margin-top: 10px;">
            <table class="torn-table" width="100%" style="border-collapse: collapse;">
                <thead>
                    <tr>
                        <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;"></th>
                        <th style="padding: 8px; text-align: left; background-color: #333; color: #ffb502;">Name</th>
                        <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Owned</th>
                        <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Missing</th>
                        <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Unit Price</th>
                        <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Total</th>
                        <th style="padding: 8px; text-align: center; background-color: #333; color: #ffb502;">Action</th>
                    </tr>
                </thead>
                <tbody>
                    ${tableRows}
                </tbody>
            </table>
        </div>
        
        <div style="margin-top: 20px; display: flex; justify-content: flex-end;">
            <button id="export-plushies-data" style="padding: 10px 15px; background-color: #ffb502; color: #000; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; display: flex; align-items: center;">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" style="margin-right: 8px; fill: currentColor;"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
                Export Missing Plushies
            </button>
        </div>
    `;
        showResultPopup(resultMessage);
        // Function to export missing plushies data
        const exportMissingPlushiesData = () => {
            // Create a data object with missing plushies
            const missingPlushiesData = {
                timestamp: new Date().toISOString(),
                targetSets: targetSets,
                totalMissingPlushies: totalMissingPlushies,
                totalInvestment: totalInvestment,
                items: []
            };
            // Add each missing plushie to the data
            PLUSHIE_NAMES.forEach(name => {
                const quantity = plushiesFound.get(name) || 0;
                const itemId = PLUSHIE_IDS[name] || 0;
                const price = getMarketPrice(itemId);
                const missing = Math.max(0, targetSets - quantity);
                if (missing > 0) {
                    missingPlushiesData.items.push({
                        name: `${name} Plushie`,
                        itemId: itemId,
                        missing: missing,
                        unitPrice: price,
                        totalCost: missing * price
                    });
                }
            });
            // Convert to JSON and create a downloadable file
            const dataStr = JSON.stringify(missingPlushiesData, null, 2);
            const dataUri = `data:application/json;charset=utf-8,${encodeURIComponent(dataStr)}`;
            // Create a temporary link element and trigger download
            const exportLink = document.createElement('a');
            exportLink.setAttribute('href', dataUri);
            exportLink.setAttribute('download', `torn-plushies-shopping-list-${new Date().toISOString().split('T')[0]}.json`);
            document.body.appendChild(exportLink);
            exportLink.click();
            document.body.removeChild(exportLink);
            // Also copy a simplified version to clipboard for easy sharing
            const clipboardText = missingPlushiesData.items.map(item => `${item.name}: ${item.missing} needed`).join('\n');
            const clipboardHeader = `Torn Plushies Shopping List (${new Date().toLocaleDateString()})\n\n`;
            navigator.clipboard.writeText(clipboardHeader + clipboardText)
                .then(() => {
                alert('Shopping list exported! A JSON file has been downloaded and a text version copied to your clipboard.');
            })
                .catch(err => {
                console.error('Could not copy to clipboard:', err);
                alert('Shopping list exported! A JSON file has been downloaded.');
            });
        };
        // Add event handlers for the buttons
        setTimeout(() => {
            // Export button event handler
            const exportButton = document.getElementById('export-plushies-data');
            if (exportButton) {
                exportButton.addEventListener('click', exportMissingPlushiesData);
            }
            // Update button event handler
            const updateButton = document.getElementById('update-plushie-calc');
            if (updateButton) {
                updateButton.addEventListener('click', () => {
                    const targetSetsInput = document.getElementById('plushie-target-sets');
                    if (targetSetsInput) {
                        const newTargetSets = parseInt(targetSetsInput.value, 10);
                        if (!isNaN(newTargetSets) && newTargetSets > 0 && newTargetSets <= maxQuantity) {
                            // Recalculate missing plushies with new target
                            let newTotalMissingPlushies = 0;
                            let newTableRows = '';
                            let newTotalInvestment = 0;
                            let newSingleSetValue = 0; // Track the market value of a single complete set
                            // Update the table rows with new calculations
                            PLUSHIE_NAMES.forEach(name => {
                                const quantity = plushiesFound.get(name) || 0;
                                // Get the item ID for the plushie
                                const itemId = PLUSHIE_IDS[name] || 0;
                                // Get the market price using the item ID
                                const price = getMarketPrice(itemId);
                                const missing = Math.max(0, newTargetSets - quantity);
                                if (missing > 0) {
                                    newTotalMissingPlushies += missing;
                                    const total = missing * price;
                                    newTotalInvestment += total;
                                }
                                // Add to single set value (one of each plushie)
                                if (price > 0) {
                                    newSingleSetValue += price;
                                }
                                // Format price with commas
                                const formattedPrice = price.toLocaleString();
                                const formattedTotal = (missing * price).toLocaleString();
                                newTableRows += `
                                <tr>
                                    <td style="vertical-align: middle; text-align: center;"><img src="https://www.torn.com/images/items/${itemId}/small.png" style="width: 30px; height: 30px; object-fit: contain;" alt="${name} Plushie" /></td>
                                    <td style="vertical-align: middle; color: #fff;">${name} Plushie</td>
                                    <td style="vertical-align: middle; text-align: center; color: #fff;">${quantity}</td>
                                    <td style="vertical-align: middle; text-align: center; color: #fff;">${missing}</td>
                                    <td style="vertical-align: middle; text-align: center; color: #fff;">$${formattedPrice}</td>
                                    <td style="vertical-align: middle; text-align: center; color: #fff;">${missing > 0 ? '$' + formattedTotal : '-'}</td>
                                    <td style="vertical-align: middle; text-align: center;">${missing > 0 ? `<a href="https://www.torn.com/imarket.php#/p=shop&step=shop&type=10&searchname=${encodeURIComponent(name + ' Plushie')}" target="_blank" style="color: #ffb502; text-decoration: none;">Buy</a>` : ''}</td>
                                </tr>
                            `;
                            });
                            // Create footer rows with the updated total investment and single set value
                            const totalInvestmentRow = `
                            <tr>
                                <td colspan="5" style="text-align: right; padding: 10px; color: #ffb502; font-weight: bold;">Total Investment:</td>
                                <td style="text-align: center; padding: 10px; color: #ffb502; font-weight: bold;">$${newTotalInvestment.toLocaleString()}</td>
                                <td></td>
                            </tr>
                        `;
                            const singleSetValueRow = `
                            <tr>
                                <td colspan="5" style="text-align: right; padding: 10px; color: #ffb502; font-weight: bold;">Market Value of Single Set:</td>
                                <td style="text-align: center; padding: 10px; color: #ffb502; font-weight: bold;">$${newSingleSetValue.toLocaleString()}</td>
                                <td></td>
                            </tr>
                        `;
                            // Update the table with new rows including the footer rows
                            const tableBody = document.querySelector('.torn-table tbody');
                            if (tableBody) {
                                tableBody.innerHTML = newTableRows + totalInvestmentRow + singleSetValueRow;
                            }
                            // Update the summary information in the grid layout
                            const summaryElements = document.querySelectorAll('div[style*="flex: 1"]');
                            summaryElements.forEach(element => {
                                const labelElement = element.querySelector('div:first-child');
                                const valueElement = element.querySelector('div:last-child');
                                if (labelElement && valueElement) {
                                    const label = labelElement.textContent || '';
                                    if (label.includes('Total Plushies Needed')) {
                                        valueElement.innerHTML = `${newTotalMissingPlushies.toLocaleString()}`;
                                    }
                                    else if (label.includes('Missing Plushie Types')) {
                                        valueElement.innerHTML = `${missingPlushiesTypes}`;
                                    }
                                    else if (label.includes('Complete Sets')) {
                                        valueElement.innerHTML = `${completeSets} <span style="color: #aaa; font-size: 12px;">(potential: ${potentialCompleteSets})</span>`;
                                    }
                                }
                            });
                        }
                    }
                });
            }
        }, 500);
    };
    // Function to analyze flowers
    const analyzeFlowers = () => {
        log('Analyzing flowers...');
        // Fetch market prices if needed
        if (config.useMarketPrices && config.apiKey) {
            fetchMarketPrices((success) => {
                if (success) {
                    log('Market prices updated successfully');
                }
                continueAnalyzeFlowers();
            });
        }
        else {
            continueAnalyzeFlowers();
        }
    };
    // Continue with flower analysis after price fetch
    const continueAnalyzeFlowers = () => {
        // Make sure we're on the flowers tab
        const flowersTab = document.querySelector('a[data-category="flowers"]');
        if (flowersTab) {
            // Click the flowers tab to ensure items are loaded
            log('Clicking flowers tab to ensure items are loaded');
            flowersTab.click();
            // Give a moment for the tab to load
            setTimeout(() => {
                // Scan for flowers directly
                scanFlowers();
                displayFlowersResults();
            }, 500);
        }
        else {
            // Try to scan anyway
            scanFlowers();
            displayFlowersResults();
        }
    };
    // Function to create the configuration UI
    const createConfigUI = () => {
        log('Creating configuration UI');
        // Check if our settings panel already exists to prevent duplicates
        if (document.getElementById('plushies-flowers-settings')) {
            log('Settings panel already exists, not creating another one');
            return;
        }
        // Find the preferences container
        const prefsContainer = document.querySelector('.preferences-container');
        if (!prefsContainer) {
            log('Preferences container not found');
            return;
        }
        // Create our config container
        const configContainer = document.createElement('div');
        configContainer.className = 'preferences-container-wrap';
        configContainer.id = 'plushies-flowers-settings';
        // Create the title
        const titleDiv = document.createElement('div');
        titleDiv.className = 'title-black top-round';
        titleDiv.textContent = 'Plushies & Flowers Tracker Settings';
        configContainer.appendChild(titleDiv);
        // Create the content container
        const content = document.createElement('div');
        content.className = 'cont-gray bottom-round';
        content.style.padding = '10px';
        // Create the API key input
        const apiKeyLabel = document.createElement('label');
        apiKeyLabel.textContent = 'Torn API Key (requires v2 access):';
        apiKeyLabel.style.display = 'block';
        apiKeyLabel.style.marginBottom = '5px';
        content.appendChild(apiKeyLabel);
        const apiKeyContainer = document.createElement('div');
        apiKeyContainer.style.display = 'flex';
        apiKeyContainer.style.marginBottom = '15px';
        apiKeyContainer.style.alignItems = 'center';
        const apiKeyInput = document.createElement('input');
        apiKeyInput.type = 'password';
        apiKeyInput.value = config.apiKey;
        apiKeyInput.style.flex = '1';
        apiKeyInput.style.marginRight = '10px';
        apiKeyInput.style.padding = '5px';
        apiKeyContainer.appendChild(apiKeyInput);
        const showApiKeyButton = document.createElement('button');
        showApiKeyButton.className = 'torn-btn';
        showApiKeyButton.textContent = 'Show API Key';
        showApiKeyButton.addEventListener('click', () => {
            if (apiKeyInput.type === 'password') {
                apiKeyInput.type = 'text';
                showApiKeyButton.textContent = 'Hide API Key';
            }
            else {
                apiKeyInput.type = 'password';
                showApiKeyButton.textContent = 'Show API Key';
            }
        });
        apiKeyContainer.appendChild(showApiKeyButton);
        content.appendChild(apiKeyContainer);
        // Create the use market prices checkbox
        const useMarketContainer = document.createElement('div');
        useMarketContainer.style.marginBottom = '15px';
        const useMarketCheck = document.createElement('input');
        useMarketCheck.type = 'checkbox';
        useMarketCheck.id = 'use-market-prices';
        useMarketCheck.checked = config.useMarketPrices;
        useMarketContainer.appendChild(useMarketCheck);
        const useMarketLabel = document.createElement('label');
        useMarketLabel.htmlFor = 'use-market-prices';
        useMarketLabel.textContent = ' Use market prices from Torn API';
        useMarketLabel.style.marginLeft = '5px';
        useMarketContainer.appendChild(useMarketLabel);
        content.appendChild(useMarketContainer);
        // Create the cache duration input
        const cacheContainer = document.createElement('div');
        cacheContainer.style.marginBottom = '15px';
        const cacheLabel = document.createElement('label');
        cacheLabel.textContent = 'Cache duration (hours): ';
        cacheContainer.appendChild(cacheLabel);
        const cacheInput = document.createElement('input');
        cacheInput.type = 'number';
        cacheInput.min = '1';
        cacheInput.max = '72';
        cacheInput.value = config.cacheDuration.toString();
        cacheInput.style.width = '60px';
        cacheInput.style.marginLeft = '5px';
        cacheContainer.appendChild(cacheInput);
        content.appendChild(cacheContainer);
        const lastUpdateDiv = document.createElement('div');
        lastUpdateDiv.style.marginBottom = '15px';
        let lastUpdateText = 'Cache status: ';
        try {
            const cachedData = GM_getValue('plushiesFlowersPrices');
            if (cachedData) {
                const data = JSON.parse(cachedData);
                const date = new Date(data.timestamp);
                lastUpdateText += `Last updated on ${date.toLocaleString()}`;
            }
            else {
                lastUpdateText += 'No cached data';
            }
        }
        catch (e) {
            lastUpdateText += 'Error reading cache';
        }
        lastUpdateDiv.textContent = lastUpdateText;
        content.appendChild(lastUpdateDiv);
        // Buttons row
        const buttonsDiv = document.createElement('div');
        buttonsDiv.style.display = 'flex';
        buttonsDiv.style.gap = '10px';
        buttonsDiv.style.flexWrap = 'wrap';
        // Save button
        const saveButton = document.createElement('button');
        saveButton.className = 'torn-btn';
        saveButton.textContent = 'Save Settings';
        saveButton.addEventListener('click', () => {
            config.apiKey = apiKeyInput.value.trim();
            config.useMarketPrices = useMarketCheck.checked;
            config.cacheDuration = parseInt(cacheInput.value) || 24;
            saveConfig(config);
            alert('Settings saved!');
        });
        buttonsDiv.appendChild(saveButton);
        // Clear cache button
        const clearButton = document.createElement('button');
        clearButton.className = 'torn-btn';
        clearButton.textContent = 'Clear Price Cache';
        clearButton.addEventListener('click', () => {
            clearCache();
            alert('Price cache cleared!');
            // Update the last update text
            lastUpdateDiv.textContent = 'Cache status: No cached data (cleared)';
        });
        buttonsDiv.appendChild(clearButton);
        // Update prices button
        const updateButton = document.createElement('button');
        updateButton.className = 'torn-btn';
        updateButton.textContent = 'Update Prices Now';
        updateButton.addEventListener('click', () => {
            // Check if API key is set
            if (!apiKeyInput.value.trim()) {
                alert('Please enter an API key first!');
                return;
            }
            // Disable button during update
            updateButton.disabled = true;
            updateButton.textContent = 'Updating...';
            // Save current settings first
            config.apiKey = apiKeyInput.value.trim();
            config.useMarketPrices = useMarketCheck.checked;
            config.cacheDuration = parseInt(cacheInput.value) || 24;
            saveConfig(config);
            // Clear existing cache first
            log('Clearing existing price cache before update');
            GM_setValue('plushiesFlowersPrices', '');
            // Force fetch new prices with forceUpdate=true
            fetchMarketPrices((success) => {
                updateButton.disabled = false;
                updateButton.textContent = 'Update Prices Now';
                if (success) {
                    // Verify the cache was updated properly
                    const cachedData = GM_getValue('plushiesFlowersPrices');
                    if (cachedData) {
                        try {
                            const parsedData = JSON.parse(cachedData);
                            const priceCount = parsedData.prices ? Object.keys(parsedData.prices).length : 0;
                            log(`Cache verification: Found ${priceCount} prices in cache`);
                            alert(`Market prices updated successfully! Cached ${priceCount} item prices.`);
                        }
                        catch (e) {
                            log('Error verifying cache after update', e);
                            alert('Market prices updated but there may be an issue with the cache.');
                        }
                    }
                    else {
                        log('No cache data found after update');
                        alert('Market prices update failed - no cache data found.');
                    }
                    // Update the last update text
                    try {
                        const cachedData = GM_getValue('plushiesFlowersPrices');
                        if (cachedData) {
                            const data = JSON.parse(cachedData);
                            const date = new Date(data.timestamp);
                            lastUpdateDiv.textContent = `Cache status: Last updated on ${date.toLocaleString()}`;
                        }
                    }
                    catch (e) {
                        log('Error updating cache status text', e);
                    }
                }
                else {
                    alert('Failed to update market prices. Make sure your API key has access to the market endpoint in API v2.');
                }
            }, true); // Force update
        });
        buttonsDiv.appendChild(updateButton);
        content.appendChild(buttonsDiv);
        configContainer.appendChild(content);
        // Add our config section to the page
        prefsContainer.appendChild(configContainer);
    };
    // Initialize the script
    const init = () => {
        log('Initializing script...');
        // Check if we're on the preferences page
        if (window.location.href.includes('preferences.php')) {
            createConfigUI();
            return;
        }
        // We're on the item page, add the tracker button
        addTrackerButton();
        log('Tracker button added');
        // Add event listeners to the inventory tabs to ensure we can detect when tabs are changed
        const plushiesTab = document.querySelector('a[data-category="plushies"]');
        const flowersTab = document.querySelector('a[data-category="flowers"]');
        if (plushiesTab) {
            plushiesTab.addEventListener('click', () => {
                log('Plushies tab clicked');
                setTimeout(scanPlushies, 500);
            });
        }
        if (flowersTab) {
            flowersTab.addEventListener('click', () => {
                log('Flowers tab clicked');
                setTimeout(scanFlowers, 500);
            });
        }
        // Initial scan of inventory
        setTimeout(() => {
            log('Performing initial inventory scan...');
            scanInventory();
        }, 1000);
    };
    // Run the script when the page is fully loaded
    window.addEventListener('load', init);
    // Also run when DOM content is loaded (as a backup)
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    }
    else {
        log('Document already loaded, initializing immediately');
        init();
    }
})();