Torn Inventory Management

Manage your Torn inventory with custom categories

当前为 2025-07-27 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==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);

})();