Torn Item Safety

Displays effects of items in Torn's various item shops with hover tooltips and disables items with warnings when toggle is active.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Torn Item Safety
// @namespace    http://tampermonkey.net/
// @version      1.1.1
// @license      GNU GPLv3
// @description  Displays effects of items in Torn's various item shops with hover tooltips and disables items with warnings when toggle is active.
// @author       Vassilios [2276659]
// @match        https://www.torn.com/item.php*
// @match        https://www.torn.com/bigalgunshop.php*
// @match        https://www.torn.com/shops.php?step=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @connect      api.torn.com
// ==/UserScript==


(function() {
    'use strict';

    // Register Tampermonkey menu command to enter API key
    GM_registerMenuCommand('Enter API key.', () => {
        const newApiKey = prompt('Please enter your Torn API key:', '');
        if (newApiKey && newApiKey.trim() !== '') {
            localStorage.setItem(CONFIG.STORAGE_KEYS.API_KEY, newApiKey.trim());
            // Clear cached data to force a fresh fetch with the new key
            localStorage.removeItem(CONFIG.STORAGE_KEYS.ITEM_DATA);
            localStorage.removeItem(CONFIG.STORAGE_KEYS.LAST_REQUEST_TIME);
            // Trigger a refresh of item data
            ItemEffectsApp.init();
        }
    });

    // =====================================================================
    // CONFIGURATION
    // =====================================================================

    const CONFIG = {
        getApiKey: () => localStorage.getItem('tornItemEffects_apiKey') || "", // Dynamically retrieve API key
        API_BASE_URL: 'https://api.torn.com/v2/torn/items',
        ITEM_CATEGORIES: ['Tool', 'Enhancer'],
        CACHE_DURATION: 24 * 60 * 60 * 1000, // 24 hours in milliseconds
        OBSERVER_CONFIG: { childList: true, subtree: true },
        ELEMENT_IDS: {
            WARNINGS_TOGGLE: 'warnings-toggle',
            WARNINGS_STATUS: 'warnings-status'
        },
        SELECTORS: {
            ALL_ITEMS: '#all-items',
            SELL_ITEMS_WRAP: '.sell-items-wrap',
            CONTENT_TITLE_LINKS: '.content-title-links',
            ITEM_NAME: '.name',
            WARNING_SIGN: '.warning-sign'
        },
        STORAGE_KEYS: {
            WARNING_STATE: 'tornItemEffects_warningState',
            ITEM_DATA: 'tornItemEffects_itemData',
            LAST_REQUEST_TIME: 'tornItemEffects_lastRequestTime',
            API_KEY: 'tornItemEffects_apiKey'
        },
        STYLES: {
            WARNING_SIGN: {
                color: '#ff9900',
                fontWeight: 'bold',
                marginLeft: '5px',
                cursor: 'help'
            },
            TOOLTIP: {
                position: 'absolute',
                backgroundColor: '#191919',
                color: '#F7FAFC',
                padding: '6px 10px',
                borderRadius: '4px',
                fontSize: '12px',
                zIndex: '9999999',
                display: 'none',
                boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
                maxWidth: '250px',
                textAlign: 'left',
                fontFamily: 'Segoe UI, Arial, sans-serif',
                lineHeight: '1.3',
                border: '1px solid #4A5568',
                wordWrap: 'break-word',
                overflowWrap: 'break-word'
            },
            STATUS_INDICATOR: {
                ON: { text: 'ON', color: '#4CAF50' },
                OFF: { text: 'OFF', color: '#F44336' }
            }
        }
    };

    // =====================================================================
    // STATE MANAGEMENT
    // =====================================================================

    const State = {
        itemData: [],
        observers: { items: null, body: null, disabledInputs: null },
        disabledInputs: new Map() // Store references to disabled inputs
    };

    // =====================================================================
    // API INTERFACE
    // =====================================================================

    const ApiService = {
        fetchItemCategory: function(category) {
            return new Promise((resolve, reject) => {
                if (typeof GM_xmlhttpRequest === 'undefined') {
                    console.error('GM_xmlhttpRequest is not available');
                    reject(new Error('GM_xmlhttpRequest is not defined'));
                    return;
                }

                const apiKey = CONFIG.getApiKey();
                const url = `${CONFIG.API_BASE_URL}?cat=${category}&sort=ASC`;
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    headers: {
                        'accept': 'application/json',
                        'Authorization': `ApiKey ${apiKey}`
                    },
                    onload: function(response) {
                        try {
                            const data = JSON.parse(response.responseText);
                            if (data && data.items) {
                                resolve(data.items);
                            } else {
                                console.error('API response does not contain items:', response.responseText);
                                reject(new Error('Invalid data format from API'));
                            }
                        } catch (error) {
                            console.error('Error parsing API response:', response.responseText, error);
                            reject(error);
                        }
                    },
                    onerror: function(error) {
                        console.error('GM_xmlhttpRequest error:', error);
                        reject(error);
                    }
                });
            });
        },

        fetchAllItemData: function() {
            // Check if API key is set
            const apiKey = CONFIG.getApiKey();
            if (!apiKey) {
                const cachedData = localStorage.getItem(CONFIG.STORAGE_KEYS.ITEM_DATA);
                if (cachedData) {
                    try {
                        State.itemData = JSON.parse(cachedData);
                        return Promise.resolve(State.itemData);
                    } catch (error) {
                        console.error('Error parsing cached data:', error);
                        return Promise.resolve([]);
                    }
                }
                return Promise.resolve([]);
            }

            // Check for cached data
            const cachedData = localStorage.getItem(CONFIG.STORAGE_KEYS.ITEM_DATA);
            const lastRequestTime = localStorage.getItem(CONFIG.STORAGE_KEYS.LAST_REQUEST_TIME);
            const currentTime = Date.now();

            if (cachedData && lastRequestTime && cachedData !== "[]") {
                const timeSinceLastRequest = currentTime - parseInt(lastRequestTime, 10);
                if (timeSinceLastRequest < CONFIG.CACHE_DURATION) {
                    try {
                        State.itemData = JSON.parse(cachedData);
                        return Promise.resolve(State.itemData);
                    } catch (error) {
                        console.error('Error parsing cached data:', error);
                    }
                }
            }

            // Fetch new data
            const fetchPromises = CONFIG.ITEM_CATEGORIES.map(category =>
                this.fetchItemCategory(category)
                    .then(items => {
                        const simplifiedItems = items.map(item => ({
                            name: item.name,
                            effect: item.effect
                        }));
                        State.itemData = [...State.itemData, ...simplifiedItems];
                        return simplifiedItems;
                    })
                    .catch(error => {
                        console.error(`Error fetching ${category} items:`, error);
                        return [];
                    })
            );

            return Promise.all(fetchPromises).then(() => {
                // Save to localStorage
                try {
                    localStorage.setItem(CONFIG.STORAGE_KEYS.ITEM_DATA, JSON.stringify(State.itemData));
                    localStorage.setItem(CONFIG.STORAGE_KEYS.LAST_REQUEST_TIME, currentTime.toString());
                } catch (error) {
                    console.error('Error saving to localStorage:', error);
                }
                return State.itemData;
            });
        }
    };

    // =====================================================================
    // DOM UTILITIES
    // =====================================================================

    const DomUtils = {
        find: {
            itemContainers: function() {
                const containers = [];
                const allItemsList = document.querySelector(CONFIG.SELECTORS.ALL_ITEMS);
                if (allItemsList) containers.push(allItemsList);
                const sellItemsWrap = document.querySelector(CONFIG.SELECTORS.SELL_ITEMS_WRAP);
                if (sellItemsWrap) containers.push(sellItemsWrap);
                return containers;
            },

            listItems: function() {
                const containers = this.itemContainers();
                let items = [];
                containers.forEach(container => {
                    const containerItems = Array.from(container.getElementsByTagName('li'));
                    items = [...items, ...containerItems];
                });
                return items;
            },

            navigationContainer: function() {
                return document.querySelector(CONFIG.SELECTORS.CONTENT_TITLE_LINKS);
            }
        },

        create: {
            warningSign: function(effectText) {
                const warningSign = document.createElement('span');
                warningSign.className = 'warning-sign';
                Object.assign(warningSign.style, CONFIG.STYLES.WARNING_SIGN);
                warningSign.innerHTML = '⚠️';

                const tooltip = this.tooltip(effectText);
                warningSign.appendChild(tooltip);

                warningSign.addEventListener('mouseenter', function() {
                    tooltip.style.display = 'block';
                    const rect = warningSign.getBoundingClientRect();
                    const isLongText = (tooltip.textContent || '').length > 50;
                    tooltip.style.left = '0px';
                    tooltip.style.top = isLongText ? '-45px' : '-30px';

                    setTimeout(() => {
                        const tooltipRect = tooltip.getBoundingClientRect();
                        if (tooltipRect.left < 0) tooltip.style.left = '5px';
                        if (tooltipRect.top < 0) tooltip.style.top = '20px';
                    }, 0);
                });

                warningSign.addEventListener('mouseleave', function() {
                    tooltip.style.display = 'none';
                });

                return warningSign;
            },

            tooltip: function(content) {
                const tooltip = document.createElement('div');
                tooltip.className = 'item-effect-tooltip';
                tooltip.setAttribute('role', 'tooltip');
                Object.assign(tooltip.style, CONFIG.STYLES.TOOLTIP);
                tooltip.textContent = content;
                return tooltip;
            },

            toggleButton: function() {
                const toggleButton = document.createElement('a');
                toggleButton.id = CONFIG.ELEMENT_IDS.WARNINGS_TOGGLE;
                toggleButton.className = 'warnings-toggle t-clear h c-pointer m-icon line-h24 right';
                toggleButton.setAttribute('aria-labelledby', 'warnings-toggle-label');

                const savedState = localStorage.getItem(CONFIG.STORAGE_KEYS.WARNING_STATE);
                const isActive = savedState !== null ? savedState === 'true' : true;

                if (isActive) {
                    toggleButton.classList.add('top-page-link-button--active');
                    toggleButton.classList.add('active');
                }

                toggleButton.innerHTML = `
                    <span class="icon-wrap svg-icon-wrap">
                        <span class="link-icon-svg warnings-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 16">
                                <defs>
                                    <style>.cls-1{opacity:0.35;}.cls-2{fill:#fff;}.cls-3{fill:#777;}</style>
                                </defs>
                                <g>
                                    <g>
                                        <g class="cls-1">
                                            <path class="cls-2" d="M7.5,1 L15,15 L0,15 Z"></path>
                                        </g>
                                        <path class="cls-3" d="M7.5,0 L15,14 L0,14 Z"></path>
                                        <path class="cls-3" d="M7,6 L8,6 L8,10 L7,10 Z" style="fill:#fff"></path>
                                        <circle class="cls-3" cx="7.5" cy="12" r="1" style="fill:#fff"></circle>
                                    </g>
                                </g>
                            </svg>
                        </span>
                    </span>
                    <span id="warnings-toggle-label">Item Safety:</span>
                `;

                const statusIndicator = document.createElement('span');
                statusIndicator.id = CONFIG.ELEMENT_IDS.WARNINGS_STATUS;
                statusIndicator.style.marginLeft = '5px';
                statusIndicator.style.fontWeight = 'bold';
                statusIndicator.textContent = isActive ? CONFIG.STYLES.STATUS_INDICATOR.ON.text : CONFIG.STYLES.STATUS_INDICATOR.OFF.text;
                statusIndicator.style.color = isActive ? CONFIG.STYLES.STATUS_INDICATOR.ON.color : CONFIG.STYLES.STATUS_INDICATOR.OFF.color;
                toggleButton.appendChild(statusIndicator);

                return toggleButton;
            }
        }
    };

    // =====================================================================
    // INPUT PROTECTION FUNCTIONALITY
    // =====================================================================

    const InputProtection = {
        setupDisabledInputsObserver: function() {
            // Create a MutationObserver to watch for changes to disabled inputs
            const observerConfig = {
                attributes: true,
                attributeFilter: ['disabled', 'value'],
                subtree: true
            };

            State.observers.disabledInputs = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    const element = mutation.target;

                    // Check if warnings are enabled before applying protection
                    const warningsEnabled = localStorage.getItem(CONFIG.STORAGE_KEYS.WARNING_STATE) !== 'false';
                    if (!warningsEnabled) return;

                    // Handle disabled attribute changes
                    if (mutation.attributeName === 'disabled') {
                        if (!element.disabled && element.dataset.disabledByWarning === 'true') {
                            // Element was disabled by our script but something tried to enable it
                            // Re-disable it
                            element.disabled = true;
                        }
                    }

                    // Handle value changes on disabled inputs
                    if (mutation.attributeName === 'value' && element.disabled && element.dataset.disabledByWarning === 'true') {
                        // Reset value to 0 if it was changed while disabled
                        if (element.value !== '0') {
                            element.value = '0';
                        }
                    }
                });
            });

            document.addEventListener('input', function(e) {
                // Check if warnings are enabled before applying protection
                const warningsEnabled = localStorage.getItem(CONFIG.STORAGE_KEYS.WARNING_STATE) !== 'false';
                if (!warningsEnabled) return;

                // For any input events on disabled inputs
                if (e.target.disabled && e.target.dataset.disabledByWarning === 'true') {
                    e.preventDefault();
                    e.stopPropagation();
                    e.target.value = '0';
                }
            }, true);

            // Start observing the entire document
            State.observers.disabledInputs.observe(document.documentElement, observerConfig);
        },

        // Proxy for input properties to intercept changes to disabled inputs
        setupInputPropertyProxy: function() {
            // Store original property descriptors
            const originalValueDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
            const originalDisabledDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'disabled');

            // Override the value property
            Object.defineProperty(HTMLInputElement.prototype, 'value', {
                set: function(newValue) {
                    // Check if warnings are enabled before applying protection
                    const warningsEnabled = localStorage.getItem(CONFIG.STORAGE_KEYS.WARNING_STATE) !== 'false';

                    if (warningsEnabled && this.disabled && this.dataset.disabledByWarning === 'true') {
                        originalValueDescriptor.set.call(this, '0');
                        return '0';
                    } else {
                        return originalValueDescriptor.set.call(this, newValue);
                    }
                },
                get: function() {
                    return originalValueDescriptor.get.call(this);
                },
                configurable: true
            });

            // Override the disabled property
            Object.defineProperty(HTMLInputElement.prototype, 'disabled', {
                set: function(value) {
                    // Check if warnings are enabled before applying protection
                    const warningsEnabled = localStorage.getItem(CONFIG.STORAGE_KEYS.WARNING_STATE) !== 'false';

                    if (warningsEnabled && !value && this.dataset.disabledByWarning === 'true') {
                        return originalDisabledDescriptor.set.call(this, true);
                    } else {
                        return originalDisabledDescriptor.set.call(this, value);
                    }
                },
                get: function() {
                    return originalDisabledDescriptor.get.call(this);
                },
                configurable: true
            });
        },

        // Method to track new disabled inputs
        trackDisabledInput: function(input) {
            if (input.type === 'text' || input.type === 'number') {
                // Store original value
                input.dataset.originalValue = input.value || '';

                // Set value to 0 for numerical inputs
                if (input.type === 'number' || !isNaN(parseFloat(input.value))) {
                    input.value = '0';
                }
            }

            // Add to our tracking map
            State.disabledInputs.set(input, {
                originalDisabled: input.disabled,
                originalValue: input.dataset.originalValue
            });
        },

        // Method to untrack and restore inputs when warnings are disabled
        restoreInput: function(input) {
            // Restore original value if it exists
            if (input.dataset.originalValue !== undefined) {
                input.value = input.dataset.originalValue;
                delete input.dataset.originalValue;
            }

            // Restore original state
            input.disabled = false;
            input.style.opacity = '';
            input.style.cursor = '';

            delete input.dataset.disabledByWarning;

            if (input.type === 'text' && input.dataset.originalBg !== undefined) {
                input.style.backgroundColor = input.dataset.originalBg;
                delete input.dataset.originalBg;
            }

            // Remove from tracking
            State.disabledInputs.delete(input);
        },

        // Initialize input protection
        init: function() {
            this.setupDisabledInputsObserver();
            this.setupInputPropertyProxy();
        }
    };

    // =====================================================================
    // CORE FUNCTIONALITY
    // =====================================================================

    const ItemEffectsApp = {
        init: function() {
            this.initializeToggleButton();

            // Initialize input protection first
            InputProtection.init();

            ApiService.fetchAllItemData()
                .then(() => {
                    this.processItems();
                    this.setupObservers();
                    this.applyWarningState();  // Changed from applyInitialWarningState to be more general
                })
                .catch(error => {
                    console.error('Failed to fetch item data:', error);
                    this.processItems();
                    this.setupObservers();
                    this.applyWarningState();  // Changed from applyInitialWarningState to be more general
                });
        },

        applyWarningState: function() {
            const savedState = localStorage.getItem(CONFIG.STORAGE_KEYS.WARNING_STATE);
            const isActive = savedState !== null ? savedState === 'true' : true;
            const warningElements = document.querySelectorAll(CONFIG.SELECTORS.WARNING_SIGN);

            warningElements.forEach(warning => {
                const listItem = warning.closest('li[data-item]');
                if (!listItem) return;

                const isItemPage = window.location.href.includes('item.php');

                if (isItemPage) {
                    const deleteButtons = listItem.querySelectorAll('.option-delete');
                    deleteButtons.forEach(button => {
                        if (isActive) {
                            button.disabled = true;
                            button.style.opacity = '0.5';
                            button.style.cursor = 'not-allowed';
                            button.dataset.disabledByWarning = 'true';
                        } else {
                            button.disabled = false;
                            button.style.opacity = '';
                            button.style.cursor = '';
                            delete button.dataset.disabledByWarning;
                        }
                    });
                } else {
                    const inputElements = listItem.querySelectorAll('input, button, select, textarea, a.buy');
                    inputElements.forEach(input => {
                        if (isActive) {
                            if (input.tagName.toLowerCase() === 'a') {
                                input.dataset.originalHref = input.href;
                                input.href = 'javascript:void(0)';
                                input.style.opacity = '0.5';
                                input.style.pointerEvents = 'none';
                            } else {
                                // Store original value before disabling
                                if (input.type === 'text' || input.type === 'number') {
                                    input.dataset.originalValue = input.value || '';
                                }

                                input.disabled = true;
                                input.style.opacity = '0.5';
                                input.style.cursor = 'not-allowed';
                                input.dataset.disabledByWarning = 'true';

                                // Track and protect this disabled input
                                InputProtection.trackDisabledInput(input);

                                if (input.type === 'text') {
                                    input.dataset.originalBg = input.style.backgroundColor;
                                    input.style.backgroundColor = '#e0e0e0';
                                }
                            }
                        } else {
                            if (input.tagName.toLowerCase() === 'a') {
                                if (input.dataset.originalHref) {
                                    input.href = input.dataset.originalHref;
                                }
                                input.style.opacity = '';
                                input.style.pointerEvents = '';
                            } else {
                                // Use the dedicated restore method
                                InputProtection.restoreInput(input);
                            }
                        }
                    });
                }
            });
        },

        processItems: function() {
            const listItems = DomUtils.find.listItems();

            if (listItems.length === 0) return;

            listItems.forEach(item => this.processItemElement(item));

            // After processing items, apply the warning state based on toggle setting
            setTimeout(() => this.applyWarningState(), 0);
        },

        processItemElement: function(itemElement) {
            const nameElement = itemElement.querySelector(CONFIG.SELECTORS.ITEM_NAME);
            if (!nameElement || !nameElement.textContent) return;

            const itemName = nameElement.textContent.trim();
            const matchingItem = State.itemData.find(item => item.name === itemName);

            if (matchingItem && matchingItem.effect && !nameElement.querySelector(CONFIG.SELECTORS.WARNING_SIGN)) {
                const warningSign = DomUtils.create.warningSign(matchingItem.effect);
                nameElement.appendChild(warningSign);
            }
        },

        initializeToggleButton: function() {
            if (document.getElementById(CONFIG.ELEMENT_IDS.WARNINGS_TOGGLE)) {
                return;
            }

            const navContainer = DomUtils.find.navigationContainer();
            if (!navContainer) {
                if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
                    document.addEventListener('DOMContentLoaded', () => this.initializeToggleButton());
                }
                return;
            }

            const toggleButton = DomUtils.create.toggleButton();
            toggleButton.addEventListener('click', this.toggleWarnings);
            navContainer.appendChild(toggleButton);
        },

        toggleWarnings: function() {
            this.classList.toggle('top-page-link-button--active');
            this.classList.toggle('active');

            const warningsEnabled = this.classList.contains('active');
            localStorage.setItem(CONFIG.STORAGE_KEYS.WARNING_STATE, warningsEnabled);

            const statusIndicator = document.getElementById(CONFIG.ELEMENT_IDS.WARNINGS_STATUS);
            statusIndicator.textContent = warningsEnabled ? CONFIG.STYLES.STATUS_INDICATOR.ON.text : CONFIG.STYLES.STATUS_INDICATOR.OFF.text;
            statusIndicator.style.color = warningsEnabled ? CONFIG.STYLES.STATUS_INDICATOR.ON.color : CONFIG.STYLES.STATUS_INDICATOR.OFF.color;

            // Use the general applyWarningState method instead of duplicating logic here
            ItemEffectsApp.applyWarningState();
        },

        setupObservers: function() {
            const itemContainers = DomUtils.find.itemContainers();
            State.observers.items = new MutationObserver(mutations => {
                let newItemsAdded = false;
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                        newItemsAdded = true;
                    }
                });
                if (newItemsAdded) {
                    this.processItems();
                    // Apply warning state after new items are processed
                    this.applyWarningState();
                }
            });

            if (itemContainers.length > 0) {
                itemContainers.forEach(container => {
                    State.observers.items.observe(container, CONFIG.OBSERVER_CONFIG);
                });
            } else {
                this.setupBodyObserver();
            }
        },

        setupBodyObserver: function() {
            State.observers.body = new MutationObserver(mutations => {
                const itemContainers = DomUtils.find.itemContainers();
                if (itemContainers.length > 0) {
                    itemContainers.forEach(container => {
                        State.observers.items.observe(container, CONFIG.OBSERVER_CONFIG);
                    });
                    this.processItems();
                    this.initializeToggleButton();
                    // Apply warning state after container is found
                    this.applyWarningState();
                    State.observers.body.disconnect();
                }
            });
            State.observers.body.observe(document.body, CONFIG.OBSERVER_CONFIG);
        }
    };

    // =====================================================================
    // INITIALIZATION
    // =====================================================================

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        ItemEffectsApp.init();
    } else {
        document.addEventListener('DOMContentLoaded', () => ItemEffectsApp.init());
    }

    window.TornItemEffects = {
        processItems: () => ItemEffectsApp.processItems(),
        toggleWarnings: () => {
            const warningToggleButton = document.getElementById(CONFIG.ELEMENT_IDS.WARNINGS_TOGGLE);
            if (warningToggleButton) warningToggleButton.click();
        }
    };
})();