Steam Inventory Items Table

Shows you all your items in a cool way.

// ==UserScript==
// @name         Steam Inventory Items Table
// @namespace    https://github.com/Kostya12rus/steam_inventory_stack/
// @supportURL   https://github.com/Kostya12rus/steam_inventory_stack/issues
// @version      1.0.1
// @description  Shows you all your items in a cool way.
// @author       Kostya12rus
// @match        https://steamcommunity.com/profiles/*/inventory*
// @match        https://steamcommunity.com/id/*/inventory*
// @license      AGPL-3.0
// ==/UserScript==

class MarketDescription {
    constructor(descriptionDict = {}) {
        this.type = descriptionDict.type || '';
        this.value = descriptionDict.value || '';
    }
}
class MarketAssetDescription {
    constructor(assetDescriptionDict = {}) {
        this.appid = assetDescriptionDict.appid || 0;
        this.classid = assetDescriptionDict.classid || '';
        this.instanceid = assetDescriptionDict.instanceid || '';
        this.name = assetDescriptionDict.name || '';
        this.nameColor = assetDescriptionDict.name_color || '';
        this.marketName = assetDescriptionDict.market_name || '';
        this.marketHashName = assetDescriptionDict.market_hash_name || '';

        this.tradable = Boolean(assetDescriptionDict.tradable || false);
        this.marketable = Boolean(assetDescriptionDict.marketable || false);
        this.commodity = Boolean(assetDescriptionDict.commodity || false);

        this.marketTradableRestriction = assetDescriptionDict.market_tradable_restriction || -1;
        this.marketMarketableRestriction = assetDescriptionDict.market_marketable_restriction || -1;

        this.iconUrl = assetDescriptionDict.icon_url || '';
        this.iconUrlLarge = assetDescriptionDict.icon_url_large || '';

        this.currency = assetDescriptionDict.currency || 0;
        this.descriptions = (assetDescriptionDict.descriptions || []).map(d => new MarketDescription(d));
        this.type = assetDescriptionDict.type || '';
        this.backgroundColor = assetDescriptionDict.background_color || '';
    }
}
class MarketItem {
    constructor(itemDict = {}) {
        this.name = itemDict.name || ' ';
        this.hashName = itemDict.hash_name || '';

        this.sellListings = itemDict.sell_listings || 0;
        this.sellPrice = itemDict.sell_price || 0;
        this.sellPriceText = itemDict.sell_price_text || '';
        this.salePriceText = itemDict.sale_price_text || '';

        this.assetDescription = new MarketAssetDescription(itemDict.asset_description || {});

        this.appName = itemDict.app_name || '';
        this.appIcon = itemDict.app_icon || '';
    }

    loadSave(data) {
        this.name = data.name || ' ';
        this.hashName = data.hashName || '';

        this.sellListings = data.sellListings || 0;
        this.sellPrice = data.sellPrice || 0;
        this.sellPriceText = data.sellPriceText || '';
        this.salePriceText = data.salePriceText || '';

        this.assetDescription = new MarketAssetDescription(data.assetDescription || {});

        this.appName = data.appName || '';
        this.appName = data.appName || '';
        return this;
    }

    toString() {
        return `<${this.constructor.name}> name: ${this.name}, price: ${this.sellPriceText}, listings: ${this.sellPrice}`;
    }

    isBugItem() {
        return this.hashName !== this.assetDescription.marketHashName;
    }

    isEmpty() {
        return this.hashName === '';
    }

    iconUrl() {
        if (!this.assetDescription.iconUrl) return '';
        return `https://community.akamai.steamstatic.com/economy/image/${this.assetDescription.iconUrl}/330x192?allow_animated=1`;
    }

    marketUrl() {
        if (!this.assetDescription.appid || !this.assetDescription.marketHashName) return '';
        return `https://steamcommunity.com/market/listings/${this.assetDescription.appid}/${this.assetDescription.marketHashName}`;
    }

    marketHashName() {
        return this.assetDescription.marketHashName;
    }

    color() {
        return this.assetDescription.nameColor ? `#${this.assetDescription.nameColor}`.replace('##', '#') : '';
    }

    isCurrentGame(appId) {
        return String(this.assetDescription.appid) === String(appId);
    }

    replaceNumberInCurrency(newNumber) {
        return this.sellPriceText.replace(/\d{1,3}(?:\s?\d{3})*(?:[,.]\d+)?/, newNumber);
    }

    generateNumberInCurrency(newNumber) {
        return this.replaceNumberInCurrency((newNumber / 100).toFixed(2));
    }

    multiplyPriceInCurrency(count) {
        return this.generateNumberInCurrency(this.sellPrice * count);
    }

    calculateCommission(price = null) {
        return this.generateNumberInCurrency(this.calculateCommissionInteger(price));
    }

    calculateCommissionInteger(price = null) {
        if (!price) price = this.sellPrice;
        const commission = Math.abs(price - (price / 115 * 100));
        return price - commission;
    }
}

class InventoryDescription {
    constructor(descriptionDict = {}) {
        this.type = descriptionDict.type || '';
        this.value = descriptionDict.value || '';
    }
}
class InventoryTag {
    constructor(tagDict = {}) {
        this.category = tagDict.category || '';
        this.internalName = tagDict.internal_name || '';
        this.categoryName = tagDict.category_name || '';
        this.name = tagDict.name || '';
    }
}
class InventoryItem {
    constructor(itemDict = {}) {
        this.classid = itemDict.classid || '';
        this.instanceid = itemDict.instanceid || '';
        this.amount = itemDict.amount || '1';

        const rgDescriptions = itemDict.rgDescriptions || {};
        this.rgDescriptions = {
            appid: rgDescriptions.appid || '',
            classid: rgDescriptions.classid || '',
            instanceid: rgDescriptions.instanceid || '',
            iconUrl: rgDescriptions.icon_url || '',
            iconUrlLarge: rgDescriptions.icon_url_large || '',
            iconDragUrl: rgDescriptions.icon_drag_url || '',
            name: rgDescriptions.name || '',
            marketHashName: rgDescriptions.market_hash_name || '',
            marketName: rgDescriptions.market_name || '',
            nameColor: rgDescriptions.name_color || '',
            backgroundColor: rgDescriptions.background_color || '',
            type: rgDescriptions.type || '',
            tradable: Boolean(rgDescriptions.tradable || false),
            marketable: Boolean(rgDescriptions.marketable || false),
            commodity: Boolean(rgDescriptions.commodity || false),
            marketTradableRestriction: rgDescriptions.market_tradable_restriction || '-1',
            marketMarketableRestriction: rgDescriptions.market_marketable_restriction || '7',
            descriptions: (rgDescriptions.descriptions || []).map(desc => new InventoryDescription(desc)),
            tags: (rgDescriptions.tags || []).map(tag => new InventoryTag(tag))
        };
    }

    updateRgDescriptions(rgDescriptions) {
        this.rgDescriptions = {
            appid: rgDescriptions.appid || '',
            classid: rgDescriptions.classid || '',
            instanceid: rgDescriptions.instanceid || '',
            iconUrl: rgDescriptions.icon_url || '',
            iconUrlLarge: rgDescriptions.icon_url_large || '',
            iconDragUrl: rgDescriptions.icon_drag_url || '',
            name: rgDescriptions.name || '',
            marketHashName: rgDescriptions.market_hash_name || '',
            marketName: rgDescriptions.market_name || '',
            nameColor: rgDescriptions.name_color || '',
            backgroundColor: rgDescriptions.background_color || '',
            type: rgDescriptions.type || '',
            tradable: Boolean(rgDescriptions.tradable || false),
            marketable: Boolean(rgDescriptions.marketable || false),
            commodity: Boolean(rgDescriptions.commodity || false),
            marketTradableRestriction: rgDescriptions.market_tradable_restriction || '-1',
            marketMarketableRestriction: rgDescriptions.market_marketable_restriction || '7',
            descriptions: (rgDescriptions.descriptions || []).map(desc => new InventoryDescription(desc)),
            tags: (rgDescriptions.tags || []).map(tag => new InventoryTag(tag))
        };
    }

    name() {
        if (!this.rgDescriptions.name) return '';
        return this.rgDescriptions.name;
    }

    iconUrl() {
        if (!this.rgDescriptions.iconUrl) return '';
        return `https://community.akamai.steamstatic.com/economy/image/${this.rgDescriptions.iconUrl}/330x192?allow_animated=1`;
    }

    marketUrl() {
        if (!this.rgDescriptions.appid || !this.rgDescriptions.marketHashName) return '';
        return `https://steamcommunity.com/market/listings/${this.rgDescriptions.appid}/${this.rgDescriptions.marketHashName}`;
    }

    color() {
        return this.rgDescriptions.nameColor ? `#${this.rgDescriptions.nameColor}`.replace('##', '#') : '';
    }
}
class InventoryManager {
    constructor(items = {}) {
        this.rgDescriptions = items.descriptions || {};
        if (typeof this.rgDescriptions !== 'object') {
            this.rgDescriptions = {};
        }
        this.rgInventory = items.assets || [];
        if (typeof this.rgInventory !== 'object') {
            this.rgInventory = [];
        }
        this.success = Boolean(items.success || false);
        this.inventory = [];

        this.parseInventory();
    }

    loadSaveInventory(oldData) {
        this.rgDescriptions = oldData.rgDescriptions || {};
        this.rgInventory = oldData.rgInventory || [];
        this.inventory = oldData.inventory || [];
        this.parseInventory();
        return this;
    }

    addNextInvent(nextInventory) {
        if (!(nextInventory instanceof InventoryManager)) return;
        this.rgInventory.push(...nextInventory.rgInventory);
        for (const [key, value] of Object.entries(nextInventory.rgDescriptions)) {
            this.rgDescriptions[key] = value;
        }
        this.parseInventory();
    }

    parseInventory() {
        this.inventory = [];

        for (const [key, item] of Object.entries(this.rgInventory)) {
            const inventoryItem = new InventoryItem(item);
            this.inventory.push(inventoryItem);
        }

        for (const item of this.inventory) {
            const classid = item.classid || 0;
            if (classid === 0) continue;

            const instanceid = item.instanceid || 0;
            for (const [key, itemDescription] of Object.entries(this.rgDescriptions)) {
                const classidD = itemDescription.classid || 0;
                if (classid !== classidD) continue;

                const instanceidD = itemDescription.instanceid || 0;
                if (instanceid !== instanceidD) continue;

                item.updateRgDescriptions(itemDescription);
                break;
            }
        }
    }
}

class TotalItem {
    constructor(classid) {
        this.classid = classid;
        this.items = [];
        this.marketData = null
    }
    addItem(item) {
        this.items.push(item);
    }
    setMarketData(marketData) {
        this.marketData = marketData;
    }

    getCount() {
        return this.items.reduce((total, _item) => total + parseInt(_item.amount), 0);
    }
    getConsolePrice(){
        if (!this.marketData) return 0;
        return this.marketData.sellPrice * this.getCount();
    }
    getConsolePriceOne(){
        if (!this.marketData) return 0;
        return this.marketData.sellPrice;
    }
    getOtherPrice(price = 0){
        if (!this.marketData) return null;
        return this.marketData.generateNumberInCurrency(price);
    }
    getPrice() {
        if (!this.marketData) return '';
        return this.marketData.multiplyPriceInCurrency(this.getCount());
    }
    getOnePrice() {
        if (!this.marketData) return '';
        return this.marketData.multiplyPriceInCurrency(1);
    }
    getIconUrl() {
        if (this.marketData)
        {
            return this.marketData.iconUrl();
        }
        else
        {
            const item = this.items[0];
            if (item)
            {
                return item.iconUrl();
            }
        }
        return '';
    }
    getMarketUrl() {
        if (this.marketData)
        {
            return this.marketData.marketUrl();
        }
        else
        {
            const item = this.items[0];
            if (item)
            {
                return item.marketUrl();
            }
        }
        return '';
    }
    getName() {
        if (this.marketData)
        {
            return this.marketData.name;
        }
        else
        {
            const item = this.items[0];
            if (item)
            {
                return item.name();
            }
        }
        return '';
    }
    getColor() {
        if (this.marketData)
        {
            return this.marketData.color();
        }
        else
        {
            const item = this.items[0];
            if (item)
            {
                return item.color();
            }
        }
        return '';
    }
}
class TotalItemsManager {
    constructor() {
        this.items = {};
        this.inventoryManager = new InventoryManager();
        this.marketItems = [];
        this.cachedData = {};
    }
    async loadTotalItems() {
        const { m_appid, m_contextid, m_steamid } = g_ActiveInventory;

        this.appid = m_appid;
        this.contextid = m_contextid;
        this.steamid = m_steamid;

        if (!this.cachedData) { this.cachedData = {}; }

        const cacheKey = `inventory_${this.steamid}_${this.appid}`;
        const now = new Date().getTime();

        if (this.cachedData[cacheKey] && this.cachedData[cacheKey].expiry > now) {
            this.inventoryManager = this.cachedData[cacheKey].inventoryManager;
            this.marketItems = this.cachedData[cacheKey].marketItems;
            this.parseItems();
            return;
        }

        this.inventoryManager = await this.getFullInventory();
        this.marketItems = await this.getGameMarketList();
        this.parseItems();

        if (!this.cachedData[cacheKey]) { this.cachedData[cacheKey] = {}; }

        this.cachedData[cacheKey].inventoryManager = this.inventoryManager;
        this.cachedData[cacheKey].marketItems = this.marketItems;
        this.cachedData[cacheKey].expiry = now + 10 * 60 * 1000;
    }
    parseItems() {
        this.items = [];
        for (const item of this.inventoryManager.inventory) {
            const classid = item.classid || 0;
            if (!this.items[classid]) {
                this.items[classid] = new TotalItem(classid);
            }
            this.items[classid].addItem(item);
        }
        for (const item of this.marketItems) {
            const classid = item.assetDescription.classid || 0;
            if (!this.items[classid]) {
                continue;
            }
            this.items[classid].setMarketData(item);
        }
    }

    async getGameMarketList(start = 0, count = 100) {
        const searchParams = new URLSearchParams({
            start: start,
            count: count,
            search_descriptions: 0,
            sort_column: 'popular',
            sort_dir: 'desc',
            appid: this.appid,
            norender: 1
        });

        const searchUrl = `https://steamcommunity.com/market/search/render/?${searchParams.toString()}`;
        let _marketItems = [];

        try {
            const marketResponse = await fetch(searchUrl, { method: 'GET', timeout: 10000 });
            if (marketResponse.ok) {
                const responseData = await marketResponse.json();
                if (responseData.success) {
                    const items = responseData.results.map(itemData => new MarketItem(itemData));
                    _marketItems = _marketItems.concat(items);
                    const totalItemsAvailable = responseData.total_count || 0;
                    if (totalItemsAvailable > start + count && start + count < 1000) {
                        start += count;
                        const additionalItems = await this.getGameMarketList(start, count);
                        _marketItems = _marketItems.concat(additionalItems);
                    }
                    return _marketItems;
                }
            }
        } catch (e) {
            console.error(`getGameMarketList failed: ${e.message}`);
        }
        return _marketItems;
    }
    async  getFullInventory() {
        try {
            const inventoryManager = new InventoryManager();
            return await this.getInventoryItems(inventoryManager);
        } catch (error) {
            console.error("Ошибка при получении предметов инвентаря:", error);
        }
        return this.inventoryManager;
    }
    getInventoryItems(inventoryManager, start_assetid = null) {
        const searchParams = new URLSearchParams({
            count: 2000,
        });
        if (start_assetid) {
            searchParams.set('start_assetid', start_assetid);
        }
        const url = `https://steamcommunity.com/inventory/${this.steamid}/${this.appid}/${this.contextid}?${searchParams.toString()}`;
        return fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        })
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json();
        })
        .then(data => {
            if (!data.success) {
                throw new Error("Не удалось получить данные инвентаря.");
            }
            inventoryManager.addNextInvent(new InventoryManager(data));
            const more_items = data.more_items;
            if (Number.isInteger(more_items) && more_items > 0) {
                return this.getInventoryItems(inventoryManager, data.last_assetid);
            }
            return inventoryManager;
        })
        .catch(error => {
            console.error("Ошибка проверки инвентаря:", error);
            throw error;
        });
    }
}

class ModalWindow {
    constructor(userNickname, userAvatar) {
        this.userNickname = userNickname
        this.userAvatar = userAvatar
        this.items = [];

        this.overlay = document.createElement('div');
        this.modal = document.createElement('div');
        this.closeButton = document.createElement('button');

        this.settingModal();
    }

    settingModal() {
        this.overlay.style.position = 'fixed';
        this.overlay.style.top = '0';
        this.overlay.style.left = '0';
        this.overlay.style.width = '100%';
        this.overlay.style.height = '100%';
        this.overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        this.overlay.style.zIndex = '9999';
        this.overlay.style.display = 'flex';
        this.overlay.style.justifyContent = 'center';
        this.overlay.style.alignItems = 'center';
        this.overlay.style.opacity = '0';
        this.overlay.style.transition = 'opacity 0.3s ease-in-out';
        this.overlay.addEventListener('click', this.closeModal.bind(this));

        this.modal.style.padding = '30px';
        this.modal.style.backgroundColor = '#242424';
        this.modal.style.borderRadius = '12px';
        this.modal.style.boxShadow = '0 8px 16px rgba(0, 0, 0, 0.5)';
        this.modal.style.color = '#e0e0e0';
        this.modal.style.width = '800px';
        this.modal.style.maxHeight = '90vh';
        this.modal.style.overflowY = 'auto';
        this.modal.style.position = 'relative';
        this.modal.style.transform = 'scale(0.9)';
        this.modal.style.opacity = '0';
        this.modal.style.transition = 'transform 0.3s ease-in-out, opacity 0.3s ease-in-out';
        this.modal.addEventListener('click', function(event) { event.stopPropagation(); });

        this.closeButton.innerText = '✖';
        this.closeButton.style.position = 'absolute';
        this.closeButton.style.top = '10px';
        this.closeButton.style.right = '10px';
        this.closeButton.style.background = 'none';
        this.closeButton.style.border = 'none';
        this.closeButton.style.color = '#fff';
        this.closeButton.style.fontSize = '20px';
        this.closeButton.style.cursor = 'pointer';
        this.closeButton.addEventListener('click', this.closeModal.bind(this));

        this.modal.appendChild(this.closeButton);

        this.addProfileInfo();
        this.addInventoryTable();

        this.overlay.appendChild(this.modal);
        document.body.appendChild(this.overlay);

        requestAnimationFrame(() => {
            this.overlay.style.opacity = '1';
            this.modal.style.transform = 'scale(1)';
            this.modal.style.opacity = '1';
        });
    }
    addProfileInfo() {
        this.profileContainer = document.createElement('div');
        this.profileContainer.style.display = 'flex';
        this.profileContainer.style.alignItems = 'center';
        this.profileContainer.style.justifyContent = 'center';
        this.profileContainer.style.marginBottom = '10px';
        this.profileContainer.style.color = '#ffffff';

        this.avatar = document.createElement('img');
        this.avatar.src = this.userAvatar;
        this.avatar.alt = 'Profile Avatar';
        this.avatar.style.width = '60px';
        this.avatar.style.height = '60px';
        this.avatar.style.borderRadius = '50%';
        this.avatar.style.marginRight = '15px';

        this.infoContainer = document.createElement('div');
        this.infoContainer.style.display = 'flex';
        this.infoContainer.style.alignItems = 'center';

        this.nickname = document.createElement('h2');
        this.nickname.innerText = this.userNickname;
        this.nickname.style.margin = '0 20px 0 0';
        this.nickname.style.fontSize = '20px';
        this.nickname.style.fontWeight = 'bold';
        this.nickname.style.color = '#6a5acd';

        this.detailsContainer = document.createElement('div');
        this.detailsContainer.style.display = 'flex';
        this.detailsContainer.style.alignItems = 'center';

        const itemCountContainer = document.createElement('div');
        itemCountContainer.style.marginRight = '20px';
        const itemCountLabel = document.createElement('p');
        itemCountLabel.innerText = 'Количество вещей';
        itemCountLabel.style.fontSize = '14px';
        itemCountLabel.style.color = '#b0c4de';
        itemCountLabel.style.margin = '0';
        itemCountLabel.style.textAlign = 'center';
        this.itemCountValue = document.createElement('p');
        this.itemCountValue.innerText = `123`;
        this.itemCountValue.style.margin = '0';
        this.itemCountValue.style.fontSize = '16px';
        this.itemCountValue.style.fontWeight = 'bold';
        this.itemCountValue.style.textAlign = 'center';
        this.itemCountValue.style.color = '#ffffff';

        itemCountContainer.appendChild(itemCountLabel);
        itemCountContainer.appendChild(this.itemCountValue);

        const inventoryValueContainer = document.createElement('div');
        const inventoryValueLabel = document.createElement('p');
        inventoryValueLabel.innerText = 'Сумма инвентаря';
        inventoryValueLabel.style.fontSize = '14px';
        inventoryValueLabel.style.color = '#b0c4de';
        inventoryValueLabel.style.margin = '0';
        inventoryValueLabel.style.textAlign = 'center';
        this.inventoryValue = document.createElement('p');
        this.inventoryValue.innerText = ``;
        this.inventoryValue.style.margin = '0';
        this.inventoryValue.style.fontSize = '16px';
        this.inventoryValue.style.fontWeight = 'bold';
        this.inventoryValue.style.textAlign = 'center';
        this.inventoryValue.style.color = '#ffffff';

        inventoryValueContainer.appendChild(inventoryValueLabel);
        inventoryValueContainer.appendChild(this.inventoryValue);

        this.detailsContainer.appendChild(itemCountContainer);
        this.detailsContainer.appendChild(inventoryValueContainer);

        this.infoContainer.appendChild(this.nickname);
        this.infoContainer.appendChild(this.detailsContainer);

        this.profileContainer.appendChild(this.avatar);
        this.profileContainer.appendChild(this.infoContainer);

        this.modal.appendChild(this.profileContainer);
    }
    addInventoryTable() {
        this.table = document.createElement('table');
        this.table.style.width = '100%';
        this.table.style.borderCollapse = 'separate';
        this.table.style.borderSpacing = '0';
        this.table.style.borderRadius = '8px';
        this.table.style.overflow = 'hidden';
        this.table.style.boxShadow = '0 4px 8px rgba(0,0,0,0.1)';

        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');

        const headers = [' ', 'Название предмета', 'Количество', 'Цена за штуку', 'Цена за все'];
        const textAligns = ['left', 'left', 'center', 'right', 'right'];

        headers.forEach((headerText, index) => {
            const th = document.createElement('th');
            th.innerText = headerText;
            th.style.borderBottom = '1px solid #ccc';
            th.style.textAlign = textAligns[index];
            th.style.cursor = 'pointer';
            th.setAttribute('data-order', 'asc');
            th.addEventListener('click', () => this.sortTableByColumn(index));
            headerRow.appendChild(th);
        });

        thead.appendChild(headerRow);
        this.table.appendChild(thead);

        this.tbody = document.createElement('tbody');
        this.table.appendChild(this.tbody);

        this.modal.appendChild(this.table);
    }
    sortTableByColumn(columnIndex) {
        const rows = Array.from(this.tbody.querySelectorAll('tr'));
        const isNumeric = columnIndex !== 1;
        const header = this.table.rows[0].cells[columnIndex];

        const order = header.getAttribute('data-order') === 'desc' ? 'asc' : 'desc';
        header.setAttribute('data-order', order);

        rows.sort((a, b) => {
            let aValue = a.cells[columnIndex].dataset.sort;
            let bValue = b.cells[columnIndex].dataset.sort;

            if (isNumeric) {
                aValue = parseFloat(aValue);
                bValue = parseFloat(bValue);
            }

            if (order === 'asc') {
                return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
            } else {
                return aValue < bValue ? 1 : aValue > bValue ? -1 : 0;
            }
        });

        while (this.tbody.firstChild) {
            this.tbody.removeChild(this.tbody.firstChild);
        }

        rows.forEach(row => this.tbody.appendChild(row));
    }
    addItemToTable(item) {
        const row = document.createElement('tr');

        const imgCell = document.createElement('td');
        const img = document.createElement('img');
        img.src = item.getIconUrl();
        img.alt = item.getName();
        img.style.height = '30px';
        img.style.width = 'auto';
        imgCell.appendChild(img);
        row.appendChild(imgCell);

        const nameCell = document.createElement('td');
        nameCell.style.color = item.getColor();
        nameCell.dataset.sort = item.getName();

        const nameLink = document.createElement('a');
        nameLink.href = item.getMarketUrl();
        nameLink.innerText = item.getName();
        nameLink.style.color = 'inherit';
        nameLink.style.textDecoration = 'none';
        nameLink.target = "_blank";
        nameCell.appendChild(nameLink);
        row.appendChild(nameCell);

        const quantityCell = document.createElement('td');
        quantityCell.innerText = item.getCount();
        quantityCell.style.textAlign = 'center';
        quantityCell.dataset.sort = item.getCount();
        row.appendChild(quantityCell);

        const pricePerItemCell = document.createElement('td');
        pricePerItemCell.innerText = item.getOnePrice();
        pricePerItemCell.style.textAlign = 'right';
        pricePerItemCell.dataset.sort = item.getConsolePriceOne();
        row.appendChild(pricePerItemCell);

        const totalPriceCell = document.createElement('td');
        totalPriceCell.innerText = item.getPrice();
        totalPriceCell.style.textAlign = 'right';
        totalPriceCell.dataset.sort = item.getConsolePrice();
        row.appendChild(totalPriceCell);

        this.tbody.appendChild(row);
    }

    addItem(newItem) {
        if (!newItem || newItem.getName() === '') { return; }

        this.items.push(newItem);
        this.addItemToTable(newItem);

        const totalCount = this.items.reduce((total, item) => total + item.getCount(), 0);
        this.itemCountValue.innerText = `${totalCount}`;

        const totalPriceFloat = this.items.reduce((total, item) => total + item.getConsolePrice(), 0);
        const itemWithMarketData = this.items.find(_item => _item.marketData);
        if (itemWithMarketData) {
            this.inventoryValue.innerText = `${itemWithMarketData.getOtherPrice(totalPriceFloat)}`;
        }
    }

    closeModal() {
        this.overlay.style.opacity = '0';
        this.modal.style.transform = 'scale(0.9)';
        this.modal.style.opacity = '0';

        setTimeout(() => {
            document.body.removeChild(this.overlay);
        }, 300);
    }
}

(function() {
    'use strict';
    const appInventoryManager = new TotalItemsManager();
    createButton();

    async function localAppData() {
        await appInventoryManager.loadTotalItems()
        if (Object.keys(appInventoryManager.items).length === 0) {
            alert('Не удалось получить список предметов. Пожалуйста, попробуйте позже');
            return;
        }

        const nickNameElement = document.querySelector('.profile_small_header_name > a');
        const avatarUrlElement = document.querySelector('.profile_small_header_avatar .playerAvatar > img');

        const nickName = nickNameElement ? nickNameElement.textContent.trim() : '';
        const avatarUrl = avatarUrlElement ? avatarUrlElement.src : '';

        const modalWindow = new ModalWindow(nickName, avatarUrl);
        const sortedItemsList = Object.entries(appInventoryManager.items)
            .sort(([keyA], [keyB]) => keyA - keyB)
            .map(([key, value]) => value);

        sortedItemsList.forEach(item => modalWindow.addItem(item));

    }

    function createButton() {
        const button = document.createElement("button");
        button.innerText = "All Items Table";
        button.classList.add("btn_darkblue_white_innerfade");
        button.style.width = "100%";
        button.style.height = "30px";
        button.style.lineHeight = "30px";
        button.style.fontSize = "15px";
        button.style.position = "relative";
        button.style.zIndex = "2";

        button.addEventListener("click", async function() {
            if (button.disabled) return;
            button.disabled = true;
            try { await localAppData(); }
            catch (error) { console.error(error); }
            button.disabled = false;
        });
        function updateButtonText() {
            const gameNameElement = document.querySelector('.name_game');
            if (gameNameElement) {
                button.disabled = true;
                let remainingTime = 5;
                const gameName = gameNameElement.textContent.trim();

                button.innerText = `All Items Table in ${gameName} (wait ${remainingTime} sec)`;
                const timer = setInterval(() => {
                    if (gameName !== gameNameElement.textContent.trim()) {
                        clearInterval(timer);
                        return;
                    }

                    remainingTime--;
                    button.innerText = `All Items Table in ${gameName} (wait ${remainingTime} sec)`;

                    if (remainingTime <= 0) {
                        clearInterval(timer);
                        button.innerText = `All Items Table in ${gameName}`;
                        button.disabled = false;
                    }
                }, 1000);
            }
        }
        function waitForElement(selector) {
            return new Promise((resolve) => {
                const observer = new MutationObserver((mutations, observer) => {
                    if (document.querySelector(selector)) {
                        observer.disconnect();
                        resolve(document.querySelector(selector));
                    }
                });
                observer.observe(document.body, { childList: true, subtree: true });
            });
        }
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList' || mutation.type === 'characterData') {
                    updateButtonText();
                }
            });
        });
        waitForElement('.name_game').then((target) => {
            observer.observe(target, { childList: true, subtree: true, characterData: true });
            updateButtonText();
        });
        const referenceElement = document.querySelector('#tabcontent_inventory');
        if (referenceElement) {
            referenceElement.parentNode.insertBefore(button, referenceElement);
            updateButtonText();
        }
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址