Infinite Craft More Pins & Colored Tabs (Updated)

Create tabs to group items and color them for organization. Updated to work with "Helper: Not-so-budget Edition".

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Infinite Craft More Pins & Colored Tabs (Updated)
// @version      2.0.3
// @namespace    https://github.com/ChessScholar
// @description  Create tabs to group items and color them for organization. Updated to work with "Helper: Not-so-budget Edition".
// @author       ChessScholar (updated for compatibility by AI)
// @match        https://neal.fun/infinite-craft/
// @icon         https://neal.fun/favicons/infinite-craft.png
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-idle
// @compatible   chrome
// @compatible   firefox
// @license      MIT
// @credits      adrianmgg for original "tweaks" script, Natasquare for the helper script.
// ==/UserScript==

(function() {
    'use strict';

    class GMValue {
        constructor(key, defaultValue) { this.key = key; this.defaultValue = defaultValue; }
        async set(value) { await GM_setValue(this.key, value); }
        async get() { return await GM_getValue(this.key, this.defaultValue); }
    }

    const VAL_PINNED_SETS = new GMValue('infinitecraft_pinned_sets_v2', {});
    const VAL_PINNED_SETS_ORDER = new GMValue('infinitecraft_pinned_sets_order_v1', []);
    const VAL_SETTINGS = new GMValue('infinitecraft_pinned_sets_settings_v1', {
        expandContainer: false,
        combineColors: true,
    });

    async function initialize(ICHelper, v_container, v_sidebar) {
        let settings = await VAL_SETTINGS.get();
        let zIndexCounter = 1;

        const el = {
            setup(elem, options) {
                const { style, attrs, dataset, events, classList, children, parent, insertBefore, ...props } = options;
                Object.assign(elem.style, style);
                Object.entries(style?.vars || {}).forEach(([k, v]) => elem.style.setProperty(k, v));
                Object.entries(attrs || {}).forEach(([k, v]) => elem.setAttribute(k, v));
                Object.entries(dataset || {}).forEach(([k, v]) => elem.dataset[k] = v);
                Object.entries(events || {}).forEach(([k, v]) => elem.addEventListener(k, v));
                elem.classList.add(...(classList || []));
                Object.assign(elem, props);
                (children || []).forEach(c => elem.appendChild(c));
                if (parent) { if (insertBefore) parent.insertBefore(elem, insertBefore); else parent.appendChild(elem); }
                return elem;
            },
            create(tagName, options = {}) { return this.setup(document.createElement(tagName), options); },
        };

        const css = `
            .pinned-set {
                margin-left: -40px; /* Overlap by 33% of 120px width */
                transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out, margin-left 0.1s ease-in-out;
            }
            .pinned-set:hover {
                transform: translateY(-3px);
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            }
            .pinned-set.dragging { opacity: 0.4; }
            .drop-placeholder { background: rgba(128, 128, 128, 0.2); border: 1px dashed #888; border-radius: 5px; }
        `;
        el.create('style', { parent: document.head, textContent: css });

        const pinnedTabsContainer = el.create('div', {
            style: {
                display: 'flex', flexDirection: 'row', alignItems: 'flex-start',
                position: 'relative', background: 'var(--sidebar-bg)',
                width: '100%', borderBottom: '1px solid var(--border-color)',
                zIndex: '1', padding: '5px'
            },
        });

        const pinnedTabsList = el.create('div', {
            parent: pinnedTabsContainer,
            style: {
                display: 'flex', flexDirection: 'row', alignItems: 'center',
                gap: '5px', flexWrap: 'wrap',
                flex: '1 1 auto'
            }
        });

        const pinnedItemsContainer = el.create('div', {
            style: {
                borderBottom: '1px solid var(--border-color)', zIndex: '1',
                padding: '5px', background: 'var(--sidebar-bg)',
                maxHeight: '40%', overflowY: 'auto',
                transition: 'max-height 0.2s ease-in-out'
            },
        });

        const combinedItemsContainer = el.create('div', {
            parent: pinnedItemsContainer,
            style: { display: 'none', flexWrap: 'wrap' }
        });

        const sidebarInner = v_sidebar.$el.querySelector('.sidebar-inner');
        sidebarInner.insertBefore(pinnedItemsContainer, sidebarInner.firstChild);
        sidebarInner.insertBefore(pinnedTabsContainer, sidebarInner.firstChild);

        function getDialogBaseStyle() {
            const isDark = document.body.classList.contains('dark-mode');
            return {
                background: isDark ? '#2d2d2d' : '#f0f0f0', color: 'var(--text-color)',
                padding: '20px', border: '1px solid var(--border-color)',
                borderRadius: '8px', boxShadow: '0 5px 15px rgba(0,0,0,0.3)',
                margin: 'auto',
            };
        }

        function generateRandomColor() {
            const hue = Math.floor(Math.random() * 360);
            return `hsl(${hue}, 75%, 60%)`;
        }

        function addPinnedElementInternal(element, setname) {
            const setItemContainer = pinnedItemsContainer.querySelector(`[data-set-items="${CSS.escape(setname)}"]`);
            if (!setItemContainer) return;
            const elementExists = Array.from(setItemContainer.children).some(child => child.querySelector(".item")?.textContent.trim() === element.text.trim());

            if (!elementExists) {
                const elementWrapper = el.create('div', { parent: setItemContainer, classList: ['item-wrapper'], style: { display: 'inline-block' }, dataset: { originSet: setname } });
                const elementElement = ICHelper.createItemElement(element);
                elementWrapper.appendChild(elementElement);
                el.setup(elementElement, {
                    events: {
                        mousedown: async (e) => {
                            if (e.button === 1 || (e.button === 0 && e.altKey)) {
                                e.preventDefault(); e.stopPropagation();
                                elementWrapper.remove();
                                const sets = await VAL_PINNED_SETS.get();
                                sets[setname].elements = sets[setname].elements.filter(eText => eText !== element.text);
                                await VAL_PINNED_SETS.set(sets);
                            }
                        },
                    },
                });
            }
        }

        async function addElementToSelectedTabs(element) {
            const selectedSetNames = Array.from(pinnedTabsList.querySelectorAll('.selected-set')).map(set => set.dataset.pinnedSet);
            if (selectedSetNames.length === 0) { alert("No tab selected. Click a tab to select it for pinning."); return; }
            const sets = await VAL_PINNED_SETS.get();
            let changed = false;
            for (const setname of selectedSetNames) {
                if (sets[setname] && !sets[setname].elements.includes(element.text)) {
                    addPinnedElementInternal(element, setname);
                    sets[setname].elements.push(element.text);
                    changed = true;
                }
            }
            if (changed) await VAL_PINNED_SETS.set(sets);
        }

        v_sidebar.$el.addEventListener("mousedown", e => { if (e.altKey && e.button === 0) { const item = e.target.closest(".item"); if (!item || e.target.closest('.items-pinned-inner') || e.target.closest('.set-items')) return; e.preventDefault(); e.stopPropagation(); const element = { text: item.getAttribute("data-item-text"), emoji: item.getAttribute("data-item-emoji") }; addElementToSelectedTabs(element); } }, true);
        window.addEventListener("mousedown", e => { if (e.button === 1) { const instance = e.target.closest(".instance"); if (!instance) return; e.preventDefault(); e.stopPropagation(); const element = { text: instance.textContent.trim().split(" ").slice(1).join(" "), emoji: instance.querySelector(".instance-emoji")?.textContent }; addElementToSelectedTabs(element); } }, true);

        async function updatePinDisplay() {
            Array.from(combinedItemsContainer.children).forEach(itemWrapper => {
                const originSet = itemWrapper.dataset.originSet;
                if (originSet) {
                    const originalContainer = pinnedItemsContainer.querySelector(`[data-set-items="${CSS.escape(originSet)}"]`);
                    if (originalContainer) originalContainer.appendChild(itemWrapper);
                }
            });

            const selectedTabs = Array.from(pinnedTabsList.querySelectorAll('.selected-set'));
            const useCombinedView = settings.combineColors && selectedTabs.length > 1;

            if (settings.expandContainer && selectedTabs.length > 0) {
                pinnedItemsContainer.style.maxHeight = 'none';
                pinnedItemsContainer.style.overflowY = 'visible';
            } else {
                pinnedItemsContainer.style.maxHeight = '40%';
                pinnedItemsContainer.style.overflowY = 'auto';
            }

            combinedItemsContainer.style.display = 'none';
            pinnedItemsContainer.querySelectorAll('.set-items').forEach(c => c.style.display = 'none');

            if (selectedTabs.length === 0) {
                pinnedItemsContainer.style.background = 'var(--sidebar-bg)';
                return;
            }

            if (useCombinedView) {
                const displayedItems = new Set();
                combinedItemsContainer.replaceChildren();
                selectedTabs.forEach(tab => {
                    const setname = tab.dataset.pinnedSet;
                    const individualContainer = pinnedItemsContainer.querySelector(`[data-set-items="${CSS.escape(setname)}"]`);
                    if (individualContainer) {
                        Array.from(individualContainer.children).forEach(itemWrapper => {
                            const itemText = itemWrapper.querySelector('.item').textContent.trim();
                            if (!displayedItems.has(itemText)) {
                                displayedItems.add(itemText);
                                combinedItemsContainer.appendChild(itemWrapper);
                            }
                        });
                    }
                });
                const colors = selectedTabs.map(tab => tab.style.backgroundColor);
                pinnedItemsContainer.style.background = `linear-gradient(to bottom right, ${colors.join(', ')})`;
                combinedItemsContainer.style.display = 'block';
            } else {
                pinnedItemsContainer.style.background = 'var(--sidebar-bg)';
                selectedTabs.forEach(tab => {
                    const setname = tab.dataset.pinnedSet;
                    const container = pinnedItemsContainer.querySelector(`[data-set-items="${CSS.escape(setname)}"]`);
                    if (container) {
                        container.style.background = tab.style.backgroundColor;
                        container.style.display = 'block';
                    }
                });
            }
        }

        function hsvToRgb(h, s, v) { let r, g, b; const i = Math.floor(h / 60); const f = h / 60 - i; const p = v * (1 - s); const q = v * (1 - f * s); const t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; }
        function createColorWheel(setname, setContainer) {
            const dialog = el.create('dialog', { style: { ...getDialogBaseStyle(), borderRadius: '50%', padding: '0', overflow: 'hidden' }, parent: document.body, events: { close: (e) => e.target.remove() } });
            dialog.showModal();
            const canvas = el.create('canvas', { attrs: { width: '150', height: '150' }, parent: dialog });
            const ctx = canvas.getContext('2d'), radius = 75, image = ctx.createImageData(150, 150), data = image.data;
            for (let x = -radius; x < radius; x++) for (let y = -radius; y < radius; y++) { const dist = Math.sqrt(x * x + y * y); if (dist > radius) continue; const angle = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360; const [r, g, b] = hsvToRgb(angle, dist / radius, 1); const index = (x + radius + (y + radius) * 150) * 4; data[index] = r; data[index + 1] = g; data[index + 2] = b; data[index + 3] = 255; }
            ctx.putImageData(image, 0, 0);
            canvas.addEventListener('click', async (e) => {
                const rect = canvas.getBoundingClientRect(), x = e.clientX - rect.left - radius, y = e.clientY - rect.top - radius, dist = Math.sqrt(x * x + y * y);
                if (dist <= radius) {
                    const angle = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360, [r, g, b] = hsvToRgb(angle, dist / radius, 1), color = `rgb(${r}, ${g}, ${b})`;
                    const sets = await VAL_PINNED_SETS.get(); sets[setname].color = color;
                    setContainer.style.backgroundColor = color;
                    await VAL_PINNED_SETS.set(sets); await updatePinDisplay();
                }
                dialog.close();
            });
        }

        function showNameDialog(title, currentName = '', onConfirm) {
            const dialog = el.create('dialog', { parent: document.body, style: getDialogBaseStyle(), events: { close: e => e.target.remove() }, children: [ el.create('h3', { textContent: title, style: { marginTop: '0', textAlign: 'center' } }), el.create('input', { attrs: { type: 'text', value: currentName, placeholder: 'Set name...' }, dataset: { nameInput: '' }, style: { width: '200px' } }), el.create('div', { style: { display: 'flex', justifyContent: 'space-around', marginTop: '15px' }, children: [ el.create('button', { textContent: 'Cancel', events: { click: () => dialog.close() } }), el.create('button', { textContent: 'Confirm', events: { click: () => { const newName = dialog.querySelector('[data-name-input]').value.trim(); onConfirm(newName); dialog.close(); } } }) ] }) ] });
            dialog.showModal();
            dialog.querySelector('input').focus();
        }

        async function loadPinnedSets() {
            const sets = await VAL_PINNED_SETS.get();
            let setsOrder = await VAL_PINNED_SETS_ORDER.get();

            const selectedBefore = Array.from(pinnedTabsList.querySelectorAll('.selected-set')).map(t => t.dataset.pinnedSet);

            pinnedTabsList.replaceChildren();
            pinnedItemsContainer.querySelectorAll('.set-items').forEach(c => c.remove());

            if (setsOrder.length !== Object.keys(sets).length) {
                const existingOrder = setsOrder.filter(name => sets[name]);
                const newKeys = Object.keys(sets).filter(name => !existingOrder.includes(name));
                setsOrder = [...existingOrder, ...newKeys];
                await VAL_PINNED_SETS_ORDER.set(setsOrder);
            }

            const btnStyle = { fontWeight: 'bold', cursor: 'pointer', userSelect: 'none', padding: '5px', borderRadius: '5px', border: '1px solid var(--border-color)', zIndex: '11', flexShrink: 0 };

            const controlsContainer = el.create('div', {style: {display: 'flex', flexDirection: 'row', gap: '5px', alignItems: 'center', marginRight: '50px', flexShrink: 0}});
            el.create('button', { parent: controlsContainer, textContent: 'All', style: btnStyle, events: { click: () => { document.querySelectorAll('.pinned-set:not(.selected-set)').forEach(sc => sc.click()); } } });
            el.create('button', { parent: controlsContainer, textContent: 'None', style: btnStyle, events: { click: () => { document.querySelectorAll('.pinned-set.selected-set').forEach(sc => sc.click()); } } });
            const addButton = el.create('div', { parent: controlsContainer, textContent: '➕', style: { ...btnStyle } });
            addButton.addEventListener('click', () => { showNameDialog("Create New Set", '', async (newName) => { if (newName) { const s = await VAL_PINNED_SETS.get(); if (s[newName]) { alert("A set with this name already exists."); return; } s[newName] = { elements: [], color: generateRandomColor() }; await VAL_PINNED_SETS.set(s); let order = await VAL_PINNED_SETS_ORDER.get(); order.push(newName); await VAL_PINNED_SETS_ORDER.set(order); await loadPinnedSets(); } }); });
            const settingsButton = el.create('div', { parent: controlsContainer, textContent: '⚙️', style: { ...btnStyle } });
            settingsButton.addEventListener('click', () => { createSettingsDialog().showModal(); });
            pinnedTabsList.appendChild(controlsContainer);

            for (const [index, setname] of setsOrder.entries()) {
                if(!sets[setname]) continue;

                const renameButton = el.create('span', { textContent: '✏️', style: { cursor: 'pointer', zIndex: '12', fontSize: '12px' } });
                renameButton.addEventListener('click', (e) => { e.stopPropagation(); showNameDialog("Rename Set", setname, async (newName) => { if (newName && newName !== setname) { const s = await VAL_PINNED_SETS.get(); if (s[newName]) { alert("A set with this name already exists."); return; } s[newName] = s[setname]; delete s[setname]; await VAL_PINNED_SETS.set(s); let order = await VAL_PINNED_SETS_ORDER.get(); const orderIndex = order.indexOf(setname); if(orderIndex > -1) order[orderIndex] = newName; await VAL_PINNED_SETS_ORDER.set(order); await loadPinnedSets(); } }); });

                const tabTitleStyle = { fontWeight: 'bold', userSelect: 'none', zIndex: '11', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', textAlign: 'center', width: '100%', textShadow: '-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000' };
                const setContainer = el.create('div', { draggable: true, dataset: { pinnedSet: setname }, classList: ['pinned-set'], style: { backgroundColor: sets[setname].color || 'var(--sidebar-bg)', padding: '5px', borderRadius: '5px', display: 'flex', flexDirection: 'column', alignItems: 'center', position: 'relative', flexShrink: '0', width: '120px', cursor: 'grab', zIndex: index }, children: [ el.create('div', { textContent: setname, style: tabTitleStyle }), el.create('div', { style: { display: 'flex', flexDirection: 'row', alignItems: 'center', marginTop: '5px', gap: '8px' }, children: [ el.create('span', { textContent: '❌', style: { cursor: 'pointer', zIndex: '12', fontSize: '12px' }, events: { click: async (e) => { e.stopPropagation(); if (confirm(`Delete set "${setname}"?`)) { const s = await VAL_PINNED_SETS.get(); delete s[setname]; await VAL_PINNED_SETS.set(s); let order = await VAL_PINNED_SETS_ORDER.get(); await VAL_PINNED_SETS_ORDER.set(order.filter(name => name !== setname)); await loadPinnedSets(); } } } }), renameButton, el.create('span', { textContent: '🎨', style: { cursor: 'pointer', zIndex: '12', fontSize: '12px' }, events: { click: (e) => { e.stopPropagation(); createColorWheel(setname, setContainer); } } }) ] }) ] });
                if(index === 0) setContainer.style.marginLeft = '0';

                setContainer.addEventListener('click', (e) => {
                    e.stopPropagation();
                    const isSelected = setContainer.classList.toggle('selected-set');
                    setContainer.style.outline = isSelected ? '2px solid #55aaff' : 'none';
                    if (isSelected) {
                        setContainer.style.zIndex = ++zIndexCounter + setsOrder.length;
                    } else {
                        setContainer.style.zIndex = index;
                    }
                    updatePinDisplay();
                });

                setContainer.addEventListener('dragstart', (e) => { e.dataTransfer.setData('text/plain', setname); e.dataTransfer.effectAllowed = 'move'; setTimeout(() => e.target.classList.add('dragging'), 0); });
                setContainer.addEventListener('dragend', (e) => e.target.classList.remove('dragging'));
                setContainer.addEventListener('dragover', (e) => e.preventDefault());

                let placeholder = null;
                setContainer.addEventListener('dragenter', (e) => { e.preventDefault(); if (e.target.closest('.pinned-set') && e.target.closest('.pinned-set') !== placeholder) { placeholder = e.target.closest('.pinned-set'); placeholder.classList.add('drop-placeholder'); } });
                setContainer.addEventListener('dragleave', (e) => { e.preventDefault(); if(placeholder && !placeholder.contains(e.relatedTarget)) { placeholder.classList.remove('drop-placeholder'); placeholder = null; } });

                setContainer.addEventListener('drop', async (e) => {
                    e.preventDefault();
                    if(placeholder) placeholder.classList.remove('drop-placeholder');
                    const draggedSetName = e.dataTransfer.getData('text/plain');
                    const targetSetName = e.target.closest('.pinned-set').dataset.pinnedSet;
                    if (draggedSetName !== targetSetName) {
                        let order = await VAL_PINNED_SETS_ORDER.get();
                        const draggedIndex = order.indexOf(draggedSetName);
                        order.splice(draggedIndex, 1);
                        const targetIndex = order.indexOf(targetSetName);
                        order.splice(targetIndex, 0, draggedSetName);
                        await VAL_PINNED_SETS_ORDER.set(order);
                        await loadPinnedSets();
                    }
                });

                pinnedTabsList.appendChild(setContainer);
                const setItemContainer = el.create('div', { parent: pinnedItemsContainer, dataset: { setItems: setname }, classList: ['set-items'], style: { display: 'none', flexWrap: 'wrap' } });
                sets[setname].elements.forEach((elementName) => { const element = v_container.items.find(e => e.text === elementName); if (element) addPinnedElementInternal(element, setname); });
            }

            selectedBefore.forEach(setName => {
                const tab = pinnedTabsList.querySelector(`[data-pinned-set="${CSS.escape(setName)}"]`);
                if (tab) {
                    tab.classList.add('selected-set');
                    tab.style.outline = '2px solid #55aaff';
                    tab.style.zIndex = ++zIndexCounter + setsOrder.length;
                }
            });

            updatePinDisplay();
        }

        function createSettingsDialog() {
            const createCheckbox = (id, labelText, settingKey) => {
                const checkbox = el.create('input', { attrs: { type: 'checkbox', id }, checked: settings[settingKey] });
                checkbox.addEventListener('change', async () => { settings[settingKey] = checkbox.checked; await VAL_SETTINGS.set(settings); await updatePinDisplay(); });
                const label = el.create('label', { attrs: { for: id }, children: [checkbox, document.createTextNode(` ${labelText}`)] });
                return el.create('div', { children: [label], style: { textAlign: 'left', margin: '8px 0', cursor: 'pointer', display: 'flex', alignItems: 'center' } });
            };

            const resetButton = el.create('button', {
                textContent: 'Reset All Tabs',
                style: { marginTop: '15px', color: '#fff', backgroundColor: '#d9534f', border: '1px solid #d43f3a', borderRadius: '4px', padding: '5px 10px', display: 'block', margin: '20px auto 0 auto' }
            });
            resetButton.addEventListener('click', async () => {
                if(confirm("Are you sure you want to delete ALL tabs and their contents? This action cannot be undone.")){
                    await VAL_PINNED_SETS.set({});
                    await VAL_PINNED_SETS_ORDER.set([]);
                    await loadPinnedSets();
                    dialog.close();
                }
            });

            const dialog = el.create('dialog', { parent: document.body, style: { ...getDialogBaseStyle(), minWidth: '280px' }, events: { close: e => e.target.remove() }, children: [ el.create('h3', { textContent: 'Settings', style: { marginTop: '0', textAlign: 'center' } }), createCheckbox('setting-expand-container', 'Expand container to show all items', 'expandContainer'), createCheckbox('setting-combine-colors', 'Combine selected tabs into one box', 'combineColors'), resetButton, el.create('button', { textContent: 'Close', style: { marginTop: '15px', display: 'block', margin: '0 auto' }, events: { click: () => dialog.close() } }) ] });
            return dialog;
        }

        loadPinnedSets();
    }

    function waitForReady() {
        let attempts = 0;
        const interval = setInterval(() => {
            const v_container = document.querySelector(".container")?.__vue__;
            const v_sidebar = document.querySelector("#sidebar")?.__vue__;
            const ICHelper = unsafeWindow.ICHelper;
            if (v_container && v_sidebar && ICHelper) { clearInterval(interval); initialize(ICHelper, v_container, v_sidebar); }
            else if (++attempts > 100) { clearInterval(interval); console.warn('More Pins & Colored Tabs failed to load: Helper script not found.'); }
        }, 100);
    }

    waitForReady();
})();