Torn Inventory Management

Manage your Torn inventory with custom categories

目前為 2025-07-27 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Torn Inventory Management
// @namespace    http://tampermonkey.net/
// @version      1.3.0
// @description  Manage your Torn inventory with custom categories
// @author       TornUser
// @match        https://www.torn.com/item.php*
// @match        https://www.torn.com/index.php?page=items*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const STORAGE_KEY = 'torn_inventory_management_categories';
    const ITEMS_KEY = 'torn_inventory_management_items_mapping';
    
    // Global variables
    let categories = {};
    let itemsMapping = {};
    let isInitialized = false;

    // Initialize the script
    function init() {
        if (isInitialized) return;
        
        // Check if we're on the correct page
        if (!isInventoryPage()) {
            console.log('[Torn Inventory] Not on inventory page, skipping initialization');
            return;
        }
        
        console.log('[Torn Inventory] Initializing on inventory page...');
        
        loadData();
        createCategoryInterface();
        
        // Add a small delay before trying to find items
        setTimeout(() => {
            console.log('[Torn Inventory] Looking for inventory items...');
            setupInventoryObserver();
            addInventoryControlsToItems();
        }, 1000);
        
        isInitialized = true;
        
        console.log('[Torn Inventory] Inventory Management loaded successfully');
    }

    // Check if current page is inventory
    function isInventoryPage() {
        return (window.location.href.includes('item.php') || 
               window.location.href.includes('page=items')) &&
               (document.querySelector('.items-wrap, .inventory-wrap, #inventory, .item-list') !== null);
    }

    // Load saved data from storage
    function loadData() {
        try {
            const savedCategories = GM_getValue(STORAGE_KEY, '{}');
            const savedItems = GM_getValue(ITEMS_KEY, '{}');
            
            categories = JSON.parse(savedCategories);
            itemsMapping = JSON.parse(savedItems);
            
            console.log('[Torn Inventory] Loaded data:', {
                categories: Object.keys(categories).length,
                items: Object.keys(itemsMapping).length
            });
            
            // Initialize with default category if empty
            if (Object.keys(categories).length === 0) {
                categories = {
                    'default': {
                        id: 'default',
                        name: 'Uncategorized',
                        parent: null,
                        children: [],
                        collapsed: false,
                        order: 0
                    }
                };
                saveData();
            }
            
            // Add order property to existing categories if missing
            Object.values(categories).forEach((category, index) => {
                if (category.order === undefined) {
                    category.order = index;
                }
            });
            
        } catch (error) {
            console.error('[Torn Inventory] Error loading data:', error);
            categories = {
                'default': {
                    id: 'default',
                    name: 'Uncategorized',
                    parent: null,
                    children: [],
                    collapsed: false,
                    order: 0
                }
            };
            itemsMapping = {};
        }
    }

    // Save data to storage
    function saveData() {
        try {
            GM_setValue(STORAGE_KEY, JSON.stringify(categories));
            GM_setValue(ITEMS_KEY, JSON.stringify(itemsMapping));
            console.log('[Torn Inventory] Data saved successfully');
        } catch (error) {
            console.error('[Torn Inventory] Error saving data:', error);
        }
    }

    // Create the category interface
    function createCategoryInterface() {
        // Find inventory container
        const inventoryContainer = findInventoryContainer();
        if (!inventoryContainer) {
            console.warn('[Torn Inventory] Inventory container not found');
            return;
        }

        // Create categories panel
        const categoriesPanel = createCategoriesPanel();
        
        // Insert categories panel before inventory
        inventoryContainer.parentNode.insertBefore(categoriesPanel, inventoryContainer);
        
        // Render categories
        renderCategories();
    }

    // Find the inventory container
    function findInventoryContainer() {
        console.log('[Torn Inventory] Searching for inventory container...');
        
        // Try multiple selectors for different inventory layouts
        const selectors = [
            '.items-wrap',
            '.inventory-wrap', 
            '#inventory',
            '.item-list',
            '.items-cont',
            '.item-list-wrap',
            '.your-items',
            '[class*="items"]'
        ];
        
        for (const selector of selectors) {
            const element = document.querySelector(selector);
            if (element) {
                console.log('[Torn Inventory] Found inventory container with selector:', selector);
                return element;
            }
        }
        
        // Fallback: look for ul.all-items specifically
        const allItems = document.querySelector('ul.all-items');
        if (allItems) {
            console.log('[Torn Inventory] Found ul.all-items container');
            return allItems.parentElement || allItems;
        }
        
        console.warn('[Torn Inventory] No inventory container found');
        return null;
    }

    // Create the categories panel
    function createCategoriesPanel() {
        const panel = document.createElement('div');
        panel.id = 'torn-inventory-management-panel';
        panel.innerHTML = `
            <div class="inventory-management-header">
                <h3>Inventory Categories</h3>
                <div class="inventory-management-controls">
                    <button id="add-category-btn" class="torn-btn">+ Add Category</button>
                    <button id="reset-categories-btn" class="torn-btn" style="background: #d32f2f;">Reset All</button>
                    <button id="toggle-categories-btn" class="torn-btn">Toggle</button>
                </div>
            </div>
            <div id="categories-container" class="categories-container">
                <!-- Categories will be rendered here -->
            </div>
        `;
        
        // Add styles
        addStyles();
        
        // Add event listeners
        panel.querySelector('#add-category-btn').addEventListener('click', () => {
            showAddCategoryDialog();
        });
        panel.querySelector('#reset-categories-btn').addEventListener('click', resetAllCategories);
        panel.querySelector('#toggle-categories-btn').addEventListener('click', toggleCategoriesPanel);
        
        return panel;
    }

    // Add CSS styles
    function addStyles() {
        const styles = `
            #torn-inventory-management-panel {
                background: #2e2e2e;
                border: 1px solid #444;
                border-radius: 5px;
                margin: 10px 0;
                padding: 15px;
                color: #ddd;
            }
            
            .inventory-management-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 15px;
                border-bottom: 1px solid #444;
                padding-bottom: 10px;
            }
            
            .inventory-management-header h3 {
                margin: 0;
                color: #fff;
            }
            
            .inventory-management-controls {
                display: flex;
                gap: 10px;
            }
            
            .torn-btn {
                background: #4a4a4a;
                border: 1px solid #666;
                color: #ddd;
                padding: 5px 10px;
                border-radius: 3px;
                cursor: pointer;
                font-size: 12px;
            }
            
            .torn-btn:hover {
                background: #555;
            }
            
            .categories-container {
                max-height: 300px;
                overflow-y: auto;
            }
            
            .category-item {
                background: #3a3a3a;
                border: 1px solid #555;
                border-radius: 3px;
                margin: 5px 0;
                padding: 10px;
                position: relative;
            }
            
            .category-item.collapsed .category-children,
            .category-item.collapsed .category-items {
                display: none;
            }
            
            .category-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                cursor: pointer;
            }
            
            .category-name {
                font-weight: bold;
                color: #fff;
            }
            
            .category-controls {
                display: flex;
                gap: 5px;
            }
            
            .category-controls button {
                background: #555;
                border: none;
                color: #ddd;
                padding: 2px 6px;
                border-radius: 2px;
                cursor: pointer;
                font-size: 10px;
            }
            
            .category-controls button:hover {
                background: #666;
            }
            
            .category-reorder-controls {
                display: flex;
                gap: 2px;
                margin-right: 5px;
            }
            
            .reorder-btn {
                background: #666;
                border: none;
                color: #ddd;
                padding: 1px 4px;
                border-radius: 2px;
                cursor: pointer;
                font-size: 10px;
                width: 16px;
                height: 16px;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            
            .reorder-btn:hover {
                background: #777;
            }
            
            .reorder-btn:disabled {
                background: #444;
                color: #666;
                cursor: not-allowed;
            }
            
            .category-children {
                margin-left: 20px;
                margin-top: 10px;
            }
            
            /* Item category controls */
            .torn-inventory-control {
                margin: 5px 0;
                padding: 3px;
                background: rgba(0, 0, 0, 0.3);
                border-radius: 3px;
                display: flex;
                gap: 5px;
                align-items: center;
            }
            
            .category-selector {
                background: #4a4a4a;
                border: 1px solid #666;
                color: #ddd;
                padding: 2px 4px;
                border-radius: 2px;
                font-size: 11px;
                flex: 1;
                max-width: 150px;
            }
            
            .category-quick-btn {
                background: #555;
                border: 1px solid #666;
                color: #ddd;
                padding: 2px 6px;
                border-radius: 2px;
                cursor: pointer;
                font-size: 10px;
            }
            
            .category-quick-btn:hover {
                background: #666;
            }
            
            /* Quick categorize menu */
            .torn-quick-category-menu {
                background: #2e2e2e;
                border: 1px solid #444;
                border-radius: 3px;
                padding: 5px;
                box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
                min-width: 120px;
            }
            
            .quick-menu-title {
                font-size: 11px;
                font-weight: bold;
                color: #fff;
                padding: 3px 0;
                border-bottom: 1px solid #444;
                margin-bottom: 3px;
            }
            
            .quick-category-btn {
                display: block;
                width: 100%;
                background: #4a4a4a;
                border: 1px solid #666;
                color: #ddd;
                padding: 4px 8px;
                margin: 2px 0;
                border-radius: 2px;
                cursor: pointer;
                font-size: 11px;
                text-align: left;
            }
            
            .quick-category-btn:hover {
                background: #555;
            }
            
            .quick-category-btn.remove-btn {
                background: #d32f2f;
                border-color: #f44336;
            }
            
            .quick-category-btn.remove-btn:hover {
                background: #f44336;
            }
            
            .quick-category-btn.close-btn {
                background: #666;
                margin-top: 5px;
                border-top: 1px solid #777;
            }
            
            .category-items {
                margin-top: 10px;
            }
            
            .category-item-preview {
                background: #4a4a4a;
                border: 1px solid #666;
                padding: 5px;
                margin: 2px 0;
                border-radius: 2px;
                font-size: 11px;
                display: flex;
                justify-content: space-between;
                align-items: center;
                cursor: pointer;
                transition: background-color 0.2s ease;
            }
            
            .category-item-preview:hover {
                background: #555;
            }
            
            /* Green flasher animation for highlighting items */
            @keyframes greenFlash {
                0% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); }
                50% { box-shadow: 0 0 0 10px rgba(76, 175, 80, 0.3); }
                100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); }
            }
            
            .inventory-item-highlighted {
                animation: greenFlash 2s ease-out;
                border: 2px solid #4CAF50 !important;
                background: rgba(76, 175, 80, 0.1) !important;
            }
            
            .remove-item-btn {
                background: #d32f2f;
                border: none;
                color: white;
                padding: 1px 4px;
                border-radius: 2px;
                cursor: pointer;
                font-size: 10px;
            }
            
            .remove-item-btn:hover {
                background: #f44336;
            }
            
            /* Modal styles */
            .category-modal {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.7);
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 10000;
            }
            
            .category-modal-content {
                background: #2e2e2e;
                border: 1px solid #444;
                border-radius: 5px;
                padding: 20px;
                max-width: 400px;
                width: 90%;
                color: #ddd;
            }
            
            .category-modal input, .category-modal select {
                width: 100%;
                padding: 8px;
                margin: 10px 0;
                background: #4a4a4a;
                border: 1px solid #666;
                border-radius: 3px;
                color: #ddd;
            }
            
            .category-modal-buttons {
                display: flex;
                gap: 10px;
                justify-content: flex-end;
                margin-top: 15px;
            }
        `;
        
        const styleSheet = document.createElement('style');
        styleSheet.textContent = styles;
        document.head.appendChild(styleSheet);
    }

    // Make inventory items have category controls
    function addInventoryControlsToItems() {
        console.log('[Torn Inventory] Starting to add category controls to items...');
        
        const inventoryItems = findInventoryItems();
        
        console.log(`[Torn Inventory] Found ${inventoryItems.length} inventory items to process`);
        
        inventoryItems.forEach((item, index) => {
            console.log(`[Torn Inventory] Processing item ${index + 1}:`, item);
            if (!item.querySelector('.torn-inventory-control')) {
                addInventoryControlToItem(item);
            } else {
                console.log('[Torn Inventory] Item already has category control, skipping');
            }
        });
    }

    // Find actual inventory items
    function findInventoryItems() {
        const items = [];
        
        const inventoryContainer = findInventoryContainer();
        if (!inventoryContainer) {
            console.warn('[Torn Inventory] No inventory container found');
            return items;
        }
        
        console.log('[Torn Inventory] Looking for items in container:', inventoryContainer);
        
        const itemSelectors = [
            'ul.all-items li[data-item]',
            'ul.items-cont li[data-item]',
            'ul.itemsList li[data-item]',
            'li[data-item]',
            'li[data-equipped]',
            '.all-items li',
            '.items-cont li'
        ];
        
        itemSelectors.forEach(selector => {
            const elements = inventoryContainer.querySelectorAll(selector);
            console.log(`[Torn Inventory] Found ${elements.length} items with selector: ${selector}`);
            
            elements.forEach(element => {
                if (isValidInventoryItem(element) && !items.includes(element)) {
                    items.push(element);
                    console.log('[Torn Inventory] Added valid inventory item:', element.getAttribute('data-item'), getItemName(element));
                }
            });
        });
        
        console.log(`[Torn Inventory] Total inventory items found: ${items.length}`);
        return items;
    }

    // Validate if an element is actually an inventory item
    function isValidInventoryItem(element) {
        if (element.querySelector('.torn-inventory-control')) {
            return false;
        }
        
        const isCorrectElement = element.tagName === 'LI' && element.hasAttribute('data-item');
        
        if (!isCorrectElement) {
            return false;
        }
        
        const hasItemContent = element.querySelector('.name, .title-wrap, img[alt]');
        const isVisible = element.offsetWidth > 0 && element.offsetHeight > 0;
        const inInventoryArea = element.closest('.all-items, .items-cont, .itemsList');
        
        const isValid = hasItemContent && isVisible && inInventoryArea;
        
        if (isValid) {
            console.log('[Torn Inventory] Valid inventory item found:', {
                element: element,
                dataItem: element.getAttribute('data-item'),
                itemName: getItemName(element),
                tagName: element.tagName
            });
        }
        
        return isValid;
    }

    // Add category control to a single item
    function addInventoryControlToItem(item) {
        const itemId = getItemId(item);
        const itemName = getItemName(item);
        
        // Create control container
        const controlContainer = document.createElement('div');
        controlContainer.className = 'torn-inventory-control';
        controlContainer.innerHTML = `
            <select class="category-selector" data-item-id="${itemId}" data-item-name="${itemName}">
                <option value="">Select Category...</option>
                ${generateCategoryOptions()}
            </select>
            <button class="category-quick-btn" data-item-id="${itemId}" data-item-name="${itemName}" title="Quick categorize">📁</button>
        `;
        
        // Set current category if item is already categorized
        const currentCategory = itemsMapping[itemId];
        if (currentCategory) {
            const selector = controlContainer.querySelector('.category-selector');
            selector.value = currentCategory;
        }
        
        // Add event listeners
        const selector = controlContainer.querySelector('.category-selector');
        selector.addEventListener('change', (e) => {
            const newCategoryId = e.target.value;
            const itemId = e.target.getAttribute('data-item-id');
            const itemName = e.target.getAttribute('data-item-name');
            
            if (newCategoryId) {
                addItemToCategory(itemId, itemName, newCategoryId);
            } else {
                removeItemFromCategory(itemId);
            }
        });
        
        const quickBtn = controlContainer.querySelector('.category-quick-btn');
        quickBtn.addEventListener('click', (e) => {
            const itemId = e.target.getAttribute('data-item-id');
            const itemName = e.target.getAttribute('data-item-name');
            showQuickCategorizeMenu(e.target, itemId, itemName);
        });
        
        insertInventoryControl(item, controlContainer);
    }

    // Generate category options HTML
    function generateCategoryOptions() {
        let options = '';
        
        function addCategoryOptions(categoryList, indent = '') {
            categoryList.forEach(category => {
                options += `<option value="${category.id}">${indent}${category.name}</option>`;
                
                if (category.children && category.children.length > 0) {
                    const children = category.children.map(childId => categories[childId]).filter(Boolean);
                    addCategoryOptions(children, indent + '→ ');
                }
            });
        }
        
        const rootCategories = Object.values(categories).filter(cat => 
            cat.parent === null || cat.parent === undefined || cat.parent === ''
        );
        addCategoryOptions(rootCategories);
        
        return options;
    }

    // Insert category control into item element
    function insertInventoryControl(item, controlContainer) {
        console.log('[Torn Inventory] Inserting control into item:', getItemName(item));
        
        const insertionTargets = [
            item.querySelector('.cont-wrap'),
            item.querySelector('.actions'),
            item.querySelector('.title-wrap'),
            item.querySelector('.outside-actions'),
            item
        ];
        
        for (const target of insertionTargets) {
            if (target && target !== item) {
                const wrapper = document.createElement('div');
                wrapper.style.cssText = 'clear: both; margin: 5px 0;';
                wrapper.appendChild(controlContainer);
                
                target.parentNode.insertBefore(wrapper, target.nextSibling);
                console.log('[Torn Inventory] Added control after:', target.className || target.tagName);
                return;
            }
        }
        
        if (item) {
            const wrapper = document.createElement('div');
            wrapper.style.cssText = 'clear: both; margin: 5px 0; padding: 5px; background: rgba(0,0,0,0.1); border-radius: 3px;';
            wrapper.appendChild(controlContainer);
            item.appendChild(wrapper);
            console.log('[Torn Inventory] Added control to item directly');
        } else {
            console.warn('[Torn Inventory] Could not find suitable location to insert control');
        }
    }

    // Show quick categorize menu
    function showQuickCategorizeMenu(button, itemId, itemName) {
        document.querySelectorAll('.torn-quick-category-menu').forEach(menu => menu.remove());
        
        const menu = document.createElement('div');
        menu.className = 'torn-quick-category-menu';
        
        const rootCategories = Object.values(categories).filter(cat => 
            cat.parent === null || cat.parent === undefined || cat.parent === ''
        );
        
        let menuHTML = '<div class="quick-menu-title">Quick Categorize</div>';
        
        rootCategories.slice(0, 5).forEach(category => {
            menuHTML += `<button class="quick-category-btn" data-category-id="${category.id}" data-item-id="${itemId}" data-item-name="${itemName}">${category.name}</button>`;
        });
        
        menuHTML += `<button class="quick-category-btn remove-btn" data-item-id="${itemId}">Remove from category</button>`;
        menuHTML += `<button class="quick-category-btn close-btn">Close</button>`;
        
        menu.innerHTML = menuHTML;
        
        const rect = button.getBoundingClientRect();
        menu.style.position = 'fixed';
        menu.style.top = (rect.bottom + 5) + 'px';
        menu.style.left = rect.left + 'px';
        menu.style.zIndex = '10001';
        
        menu.addEventListener('click', (e) => {
            const categoryId = e.target.getAttribute('data-category-id');
            const itemId = e.target.getAttribute('data-item-id');
            const itemName = e.target.getAttribute('data-item-name');
            
            if (e.target.classList.contains('remove-btn')) {
                removeItemFromCategory(itemId);
                menu.remove();
            } else if (e.target.classList.contains('close-btn')) {
                menu.remove();
            } else if (categoryId) {
                addItemToCategory(itemId, itemName, categoryId);
                menu.remove();
            }
        });
        
        document.body.appendChild(menu);
        
        setTimeout(() => {
            document.addEventListener('click', function closeMenu(e) {
                if (!menu.contains(e.target) && e.target !== button) {
                    menu.remove();
                    document.removeEventListener('click', closeMenu);
                }
            });
        }, 100);
    }

    // Get item ID from element
    function getItemId(item) {
        const id = item.getAttribute('data-item') || 
                   item.getAttribute('data-id') ||
                   item.querySelector('[data-item]')?.getAttribute('data-item') ||
                   item.id ||
                   'item_' + Math.random().toString(36).substr(2, 9);
        return id;
    }

    // Get item name from element
    function getItemName(item) {
        const nameSelectors = [
            '.name',
            '.item-name',
            '.title',
            'img[alt]',
            '.desc'
        ];
        
        for (const selector of nameSelectors) {
            const element = item.querySelector(selector);
            if (element) {
                if (element.tagName === 'IMG') {
                    return element.alt || 'Unknown Item';
                }
                return element.textContent.trim() || 'Unknown Item';
            }
        }
        
        return item.textContent.trim().substring(0, 50) || 'Unknown Item';
    }

    // Highlight an item in the actual inventory
    function highlightInventoryItem(itemId) {
        console.log('[Torn Inventory] Attempting to highlight item:', itemId);
        
        // Find the item in the inventory
        const inventoryContainer = document.querySelector('#all-items, ul.all-items, .all-items');
        if (!inventoryContainer) {
            console.warn('[Torn Inventory] Could not find inventory container for highlighting');
            return;
        }
        
        // Look for the item by data-item attribute
        const inventoryItem = inventoryContainer.querySelector(`[data-item="${itemId}"]`);
        if (!inventoryItem) {
            console.warn('[Torn Inventory] Could not find item in inventory:', itemId);
            return;
        }
        
        // Remove any existing highlights
        document.querySelectorAll('.inventory-item-highlighted').forEach(item => {
            item.classList.remove('inventory-item-highlighted');
        });
        
        // Add highlight class
        inventoryItem.classList.add('inventory-item-highlighted');
        
        // Scroll to the item
        inventoryItem.scrollIntoView({ 
            behavior: 'smooth', 
            block: 'center' 
        });
        
        // Remove highlight after animation
        setTimeout(() => {
            inventoryItem.classList.remove('inventory-item-highlighted');
        }, 3000);
        
        console.log('[Torn Inventory] Successfully highlighted item:', itemId);
    }

    // Move category up in order
    function moveCategoryUp(categoryId) {
        const category = categories[categoryId];
        if (!category) return;
        
        // Get sibling categories
        const siblingCategories = category.parent 
            ? categories[category.parent]?.children || []
            : Object.values(categories).filter(cat => 
                cat.parent === null || cat.parent === undefined || cat.parent === ''
            ).map(cat => cat.id);
        
        const currentIndex = siblingCategories.indexOf(categoryId);
        if (currentIndex <= 0) return; // Already at top
        
        // Swap order values
        const currentCategory = categories[categoryId];
        const previousCategory = categories[siblingCategories[currentIndex - 1]];
        
        const tempOrder = currentCategory.order || currentIndex;
        currentCategory.order = previousCategory.order || (currentIndex - 1);
        previousCategory.order = tempOrder;
        
        saveData();
        renderCategories();
        updateItemCategoryControls();
        console.log('[Torn Inventory] Moved category up:', category.name);
    }

    // Move category down in order
    function moveCategoryDown(categoryId) {
        const category = categories[categoryId];
        if (!category) return;
        
        // Get sibling categories
        const siblingCategories = category.parent 
            ? categories[category.parent]?.children || []
            : Object.values(categories).filter(cat => 
                cat.parent === null || cat.parent === undefined || cat.parent === ''
            ).map(cat => cat.id);
        
        const currentIndex = siblingCategories.indexOf(categoryId);
        if (currentIndex >= siblingCategories.length - 1) return; // Already at bottom
        
        // Swap order values
        const currentCategory = categories[categoryId];
        const nextCategory = categories[siblingCategories[currentIndex + 1]];
        
        const tempOrder = currentCategory.order || currentIndex;
        currentCategory.order = nextCategory.order || (currentIndex + 1);
        nextCategory.order = tempOrder;
        
        saveData();
        renderCategories();
        updateItemCategoryControls();
        console.log('[Torn Inventory] Moved category down:', category.name);
    }

    // Add item to category
    function addItemToCategory(itemId, itemName, categoryId) {
        itemsMapping[itemId] = categoryId;
        saveData();
        renderCategories();
        updateItemCategoryControls();
        console.log(`[Torn Inventory] Added item ${itemName} to category ${categories[categoryId].name}`);
    }

    // Remove item from category
    function removeItemFromCategory(itemId) {
        delete itemsMapping[itemId];
        saveData();
        renderCategories();
        updateItemCategoryControls();
        console.log(`[Torn Inventory] Removed item from category`);
    }

    // Update all item category controls
    function updateItemCategoryControls() {
        console.log('[Torn Inventory] Updating item category controls...');
        
        addInventoryControlsToItems();
        
        // Update all existing selectors with new category options
        document.querySelectorAll('.category-selector').forEach(selector => {
            const itemId = selector.getAttribute('data-item-id');
            const currentCategory = itemsMapping[itemId];
            
            // Update the options
            selector.innerHTML = `
                <option value="">Select Category...</option>
                ${generateCategoryOptions()}
            `;
            
            // Restore the selected value
            selector.value = currentCategory || '';
            console.log('[Torn Inventory] Updated control for item:', itemId, 'category:', currentCategory);
        });
    }

    // Render categories
    function renderCategories() {
        const container = document.getElementById('categories-container');
        if (!container) {
            console.error('[Torn Inventory] Categories container not found');
            return;
        }
        
        container.innerHTML = '';
        
        console.log('[Torn Inventory] Rendering categories:', categories);
        
        const rootCategories = Object.values(categories).filter(cat => 
            cat.parent === null || cat.parent === undefined || cat.parent === ''
        ).sort((a, b) => (a.order || 0) - (b.order || 0));
        console.log('[Torn Inventory] Root categories found:', rootCategories.length);
        
        if (rootCategories.length === 0) {
            container.innerHTML = '<div style="color: #999; padding: 20px; text-align: center;">No categories found. Click "Add Category" to create one.</div>';
            return;
        }
        
        rootCategories.forEach(category => {
            console.log('[Torn Inventory] Rendering category:', category.name);
            container.appendChild(renderCategory(category));
        });
    }

    // Render a single category
    function renderCategory(category) {
        console.log('[Torn Inventory] Rendering single category:', category);
        
        const categoryDiv = document.createElement('div');
        categoryDiv.className = `category-item ${category.collapsed ? 'collapsed' : ''}`;
        categoryDiv.setAttribute('data-category-id', category.id);
        
        const header = document.createElement('div');
        header.className = 'category-header';
        
        // Get sibling categories for reorder buttons
        const siblingCategories = category.parent 
            ? categories[category.parent]?.children || []
            : Object.values(categories).filter(cat => 
                cat.parent === null || cat.parent === undefined || cat.parent === ''
            ).map(cat => cat.id);
        
        const currentIndex = siblingCategories.indexOf(category.id);
        const isFirst = currentIndex === 0;
        const isLast = currentIndex === siblingCategories.length - 1;
        
        header.innerHTML = `
            <span class="category-name">${category.name}</span>
            <div class="category-controls">
                <div class="category-reorder-controls">
                    <button class="reorder-btn" data-action="move-up" data-category-id="${category.id}" ${isFirst ? 'disabled' : ''} title="Move up">↑</button>
                    <button class="reorder-btn" data-action="move-down" data-category-id="${category.id}" ${isLast ? 'disabled' : ''} title="Move down">↓</button>
                </div>
                <button data-action="add-sub" data-category-id="${category.id}">+</button>
                <button data-action="edit" data-category-id="${category.id}">✎</button>
                <button data-action="delete" data-category-id="${category.id}">×</button>
            </div>
        `;
        
        header.addEventListener('click', (e) => {
            const action = e.target.getAttribute('data-action');
            const categoryId = e.target.getAttribute('data-category-id');
            
            if (action === 'add-sub') {
                e.stopPropagation();
                addSubCategory(categoryId);
            } else if (action === 'edit') {
                e.stopPropagation();
                editCategory(categoryId);
            } else if (action === 'delete') {
                e.stopPropagation();
                deleteCategory(categoryId);
            } else if (action === 'move-up') {
                e.stopPropagation();
                moveCategoryUp(categoryId);
            } else if (action === 'move-down') {
                e.stopPropagation();
                moveCategoryDown(categoryId);
            } else if (!e.target.matches('button')) {
                toggleCategory(category.id);
            }
        });
        
        categoryDiv.appendChild(header);
        
        // Category items display
        const itemsDiv = document.createElement('div');
        itemsDiv.className = 'category-items';
        
        const categoryItems = Object.entries(itemsMapping).filter(([itemId, catId]) => catId === category.id);
        categoryItems.forEach(([itemId, catId]) => {
            const itemPreview = document.createElement('div');
            itemPreview.className = 'category-item-preview';
            itemPreview.innerHTML = `
                <span>${itemId}</span>
                <button class="remove-item-btn" data-item-id="${itemId}">×</button>
            `;
            
            // Add click event to highlight item in inventory
            itemPreview.addEventListener('click', (e) => {
                if (!e.target.classList.contains('remove-item-btn')) {
                    highlightInventoryItem(itemId);
                }
            });
            
            itemPreview.querySelector('.remove-item-btn').addEventListener('click', (e) => {
                e.stopPropagation(); // Prevent highlighting when removing
                const itemId = e.target.getAttribute('data-item-id');
                removeItemFromCategory(itemId);
            });
            
            itemsDiv.appendChild(itemPreview);
        });
        
        categoryDiv.appendChild(itemsDiv);
        
        // Children categories
        if (category.children && category.children.length > 0) {
            const childrenDiv = document.createElement('div');
            childrenDiv.className = 'category-children';
            
            // Sort children by order
            const sortedChildren = category.children
                .map(childId => categories[childId])
                .filter(Boolean)
                .sort((a, b) => (a.order || 0) - (b.order || 0));
            
            sortedChildren.forEach(childCategory => {
                childrenDiv.appendChild(renderCategory(childCategory));
            });
            
            categoryDiv.appendChild(childrenDiv);
        }
        
        return categoryDiv;
    }

    // Show add category dialog
    function showAddCategoryDialog(parentId) {
        console.log('[Torn Inventory] showAddCategoryDialog called with parentId:', parentId);
        
        const modal = document.createElement('div');
        modal.className = 'category-modal';
        
        const isSubcategory = parentId && parentId !== '' && parentId !== 'undefined';
        console.log('[Torn Inventory] Is subcategory:', isSubcategory, 'parentId:', parentId);
        
        modal.innerHTML = `
            <div class="category-modal-content">
                <h3>${isSubcategory ? 'Add Subcategory' : 'Add Category'}</h3>
                <input type="text" id="category-name-input" placeholder="Category name" maxlength="50">
                ${isSubcategory ? '' : `
                <select id="parent-category-select">
                    <option value="">No parent (root level)</option>
                    ${Object.values(categories).map(cat => 
                        `<option value="${cat.id}">${cat.name}</option>`
                    ).join('')}
                </select>
                `}
                <div class="category-modal-buttons">
                    <button class="torn-btn" id="cancel-category-btn">Cancel</button>
                    <button class="torn-btn" id="create-category-btn">Create</button>
                </div>
            </div>
        `;
        
        document.body.appendChild(modal);
        document.getElementById('category-name-input').focus();
        
        // Add event listeners for buttons
        document.getElementById('cancel-category-btn').addEventListener('click', () => {
            modal.remove();
        });
        
        document.getElementById('create-category-btn').addEventListener('click', () => {
            createCategory(isSubcategory ? parentId : null);
        });
        
        // Handle Enter key
        document.getElementById('category-name-input').addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                createCategory(isSubcategory ? parentId : null);
            }
        });
        
        // Handle Escape key
        document.addEventListener('keydown', function escapeHandler(e) {
            if (e.key === 'Escape') {
                modal.remove();
                document.removeEventListener('keydown', escapeHandler);
            }
        });
        
        // Handle click outside modal
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.remove();
            }
        });
    }

    // Create new category
    function createCategory(parentId) {
        console.log('[Torn Inventory] createCategory called with parentId:', parentId);
        
        const nameInput = document.getElementById('category-name-input');
        const parentSelect = document.getElementById('parent-category-select');
        
        const name = nameInput ? nameInput.value.trim() : '';
        if (!name) {
            alert('Please enter a category name');
            return;
        }
        
        // Determine the actual parent ID
        let actualParentId = null;
        
        if (parentId && parentId !== '' && parentId !== 'null' && parentId !== 'undefined') {
            actualParentId = parentId;
            console.log('[Torn Inventory] Creating subcategory with parent:', actualParentId);
        } else if (parentSelect && parentSelect.value && parentSelect.value !== '') {
            actualParentId = parentSelect.value;
            console.log('[Torn Inventory] Creating category with selected parent:', actualParentId);
        } else {
            actualParentId = null;
            console.log('[Torn Inventory] Creating root level category');
        }
        
        const categoryId = 'cat_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
        
        const newCategory = {
            id: categoryId,
            name: name,
            parent: actualParentId,
            children: [],
            collapsed: false,
            order: getNextOrderValue(actualParentId)
        };
        
        categories[categoryId] = newCategory;
        
        // Add to parent's children if parent exists
        if (actualParentId && categories[actualParentId]) {
            if (!categories[actualParentId].children) {
                categories[actualParentId].children = [];
            }
            categories[actualParentId].children.push(categoryId);
        }
        
        saveData();
        renderCategories();
        updateItemCategoryControls();
        
        // Remove the modal
        const modal = document.querySelector('.category-modal');
        if (modal) {
            modal.remove();
        }
        
        console.log(`[Torn Inventory] Created category: ${name} (ID: ${categoryId}), Parent: ${actualParentId || 'none (root level)'}`);
    }

    // Get next order value for a parent category
    function getNextOrderValue(parentId) {
        const siblingCategories = parentId 
            ? categories[parentId]?.children.map(id => categories[id]).filter(Boolean) || []
            : Object.values(categories).filter(cat => 
                cat.parent === null || cat.parent === undefined || cat.parent === ''
            );
        
        if (siblingCategories.length === 0) return 0;
        
        const maxOrder = Math.max(...siblingCategories.map(cat => cat.order || 0));
        return maxOrder + 1;
    }

    // Add subcategory
    function addSubCategory(parentId) {
        showAddCategoryDialog(parentId);
    }

    // Edit category
    function editCategory(categoryId) {
        const category = categories[categoryId];
        if (!category) return;
        
        const newName = prompt('Enter new category name:', category.name);
        if (newName && newName.trim()) {
            category.name = newName.trim();
            saveData();
            renderCategories();
            updateItemCategoryControls();
        }
    }

    // Delete category
    function deleteCategory(categoryId) {
        if (categoryId === 'default') {
            alert('Cannot delete the default category');
            return;
        }
        
        const category = categories[categoryId];
        if (!category) return;
        
        if (!confirm(`Delete category "${category.name}" and all its subcategories?`)) {
            return;
        }
        
        // Move items to default category
        Object.keys(itemsMapping).forEach(itemId => {
            if (itemsMapping[itemId] === categoryId) {
                itemsMapping[itemId] = 'default';
            }
        });
        
        // Delete recursively
        deleteCategoryRecursive(categoryId);
        
        saveData();
        renderCategories();
        updateItemCategoryControls();
    }

    // Delete category and all children recursively
    function deleteCategoryRecursive(categoryId) {
        const category = categories[categoryId];
        if (!category) return;
        
        // Delete children first
        if (category.children) {
            category.children.forEach(childId => {
                deleteCategoryRecursive(childId);
            });
        }
        
        // Remove from parent's children
        if (category.parent && categories[category.parent]) {
            const parentChildren = categories[category.parent].children;
            const index = parentChildren.indexOf(categoryId);
            if (index > -1) {
                parentChildren.splice(index, 1);
            }
        }
        
        // Delete the category
        delete categories[categoryId];
    }

    // Toggle category collapse
    function toggleCategory(categoryId) {
        if (categories[categoryId]) {
            categories[categoryId].collapsed = !categories[categoryId].collapsed;
            saveData();
            renderCategories();
        }
    }

    // Reset all categories
    function resetAllCategories() {
        if (!confirm('Are you sure you want to delete ALL categories and item assignments? This cannot be undone!')) {
            return;
        }
        
        // Reset to default state
        categories = {
            'default': {
                id: 'default',
                name: 'Uncategorized',
                parent: null,
                children: [],
                collapsed: false,
                order: 0
            }
        };
        itemsMapping = {};
        
        saveData();
        renderCategories();
        updateItemCategoryControls();
        
        console.log('[Torn Inventory] All categories and assignments have been reset');
        alert('All categories have been reset! You now have a clean "Uncategorized" category to start with.');
    }

    // Toggle categories panel
    function toggleCategoriesPanel() {
        const container = document.getElementById('categories-container');
        if (container) {
            container.style.display = container.style.display === 'none' ? 'block' : 'none';
        }
    }

    // Setup observer for inventory page
    function setupInventoryObserver() {
        const observer = new MutationObserver((mutations) => {
            let shouldUpdate = false;
            
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === 1) { // Element node
                        // Check if new inventory items were added
                        if (node.matches && (node.matches('.item') || node.querySelector('.item'))) {
                            shouldUpdate = true;
                        }
                    }
                });
            });
            
            if (shouldUpdate) {
                setTimeout(() => {
                    addInventoryControlsToItems();
                }, 100);
            }
        });
        
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // Initialize when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
    
    // Also try to initialize after a short delay for dynamic content
    setTimeout(init, 1000);

})();