Torn Plushies & Flowers Tracker

Track plushies and flowers in Torn inventory and calculate missing items

// ==UserScript==
// @name         Torn Plushies & Flowers Tracker
// @namespace    http://tampermonkey.net/
// @version      1.4.2
// @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 marketLink = `https://www.torn.com/page.php?sid=ItemMarket#/market/view=search&itemID=${itemId}`;
            // 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 marketLink = `https://www.torn.com/page.php?sid=ItemMarket#/market/view=search&itemID=${itemId}`;
            // 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/page.php?sid=ItemMarket#/market/view=search&itemID=${itemId}" 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();
    }
})();