BazNav

An enhanced Torn City bazaar navigator with customization options to make this tool YOURS

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         BazNav
// @namespace    http://tampermonkey.net/
// @version      2.5
// @description  An enhanced Torn City bazaar navigator with customization options to make this tool YOURS
// @author       J4C
// @match        https://www.torn.com/*
// @grant        GM_addStyle
// ==/UserScript==

(() => {
    'use strict';

    const defaultSettings = {
        // UI Colors
        gradientStart: '#000000', gradientEnd: '#ffffff',
        textColor: '#ffffff', fontFamily: 'Arial, sans-serif',
        fontSize: 14, opacity: 1, width: 200,
        borderRadius: 8, borderWidth: 1, borderColor: '#000000',
        shadowBlur: 5, shadowColor: 'rgba(0,0,0,0.2)',

        // Buttons
        buttonBgColor: '#ffffff', buttonTextColor: '#000000',
        buttonBorderColor: '#000000', buttonBorderWidth: 1,
        buttonHoverColor: '#8F4729',

        // Counter
        counterBgColor: '#252525', counterTextColor: '#fff',
        counterBorderColor: '#fff',

        // Settings Panel
        panelBgColor: '#ffffff', panelTextColor: '#000',
        panelBorderColor: '#cccccc',

        // Loading Bar
        loadingBarColor: '#8F4729'
    };

    if (window.bazNavInitialized) {
        console.warn('BazNav is already initialized!');
        return;
    }
    window.bazNavInitialized = true;

    // User IDs
    const userIds = [
        1010587, 3199521, 1281694, 1286142, 1601153, 1821105, 1826175, 1853324, 1927218, 1962806, 2018311,
        2018693, 2029519, 2093595, 2144418, 2145030, 2150517, 2157199, 2163550, 2176411, 2202548,
        2203576, 2214797, 2215721, 2256247, 2263400, 2271357, 2321305, 2327316, 2329817, 2332873,
        2334174, 2349680, 2352900, 2373781, 2418443, 2459465, 2462160, 2466069, 2470308, 2515770,
        2531848, 2541678, 2557282, 2561006, 2570451, 2587064, 2596546, 2599031, 2601828, 2631792,
        2637146, 2649236, 2656557, 2658357, 2659552, 2664822, 2668560, 2675624, 2676295, 2693254,
        2693850, 2700933, 2706250, 2718606, 2733754, 2746056, 2749015, 2759415, 2768219, 2769269,
        2810688, 2812113, 2821007, 2855343, 2858099, 286232, 2865837, 2871891, 2905897, 2930086,
        2954100, 2954103, 2962007, 3025664, 3050251, 3060802, 3108759, 3118416, 3145984, 3146495,
        3152626, 3166857, 3169682, 3170980, 3181495, 3182441, 3185895, 3186599, 3186796, 3187167,
        3187441, 3192720, 3198458, 3200247, 3218273, 3220837, 3237207, 3244939, 3248078,
        3249592, 3253085, 3253836, 3256697, 3259246, 3272175, 3276048, 3284969, 3303003, 3304611,
        3304959, 3306975, 3325064, 3325774, 3327900, 333493, 3338586, 3340959, 3347008, 3348231,
        3351015, 3355475, 3357431, 3369647, 3372209, 3384244, 3385583, 3388276, 3390097, 3392180,
        3394866, 3399085, 3400186, 3405216, 3407522, 3409934, 3424078, 3443006, 3444185, 3445243,
        3447101, 3452004, 3455398, 3455607, 3455822, 3462112, 3465149, 3466754, 3468210, 3469314,
        3474044, 3476656, 3480404, 3484482, 3484674, 3485561, 3488406, 3492821, 3493798, 3499388,
        3504237, 3506751, 3528214, 3532145, 3532830, 3535815, 3546645, 3552837, 3554441, 3555668,
        3561791, 3562619, 3570456, 3571449, 3582325, 3584826, 3588663, 3595133, 3602729, 360330,
        3603393, 3615849, 3617287, 3621114, 3625719, 3626448, 3626655, 3653078, 3657829, 3661330,
        3665796, 3676143, 3738196, 830027, 1145056, 1403609, 1441750, 1496324, 1636350
];

const uniqueUserIds = [...new Set(userIds)];
const originalLinks = uniqueUserIds.map(id => `https://www.torn.com/bazaar.php?userId=${id}`);

const links = new Proxy(originalLinks, {
    get(target, prop) {
        if (prop === 'length') {
            console.log('Links array length accessed:', target.length);
        } else if (prop === 'map') {
            return function(...args) {
                console.log('Links array map called, length:', target.length);
                return Array.prototype.map.apply(target, args);
            };
        }
        return target[prop];
    },
    set() {
        console.error('Attempted to modify frozen links array! Stack:', new Error().stack);
        return false; // Prevent modifications
    }
});

console.log(`Total User IDs before deduplication: ${userIds.length}`);
console.log(`Total Unique User IDs: ${uniqueUserIds.length}`);
console.log(`Total Links: ${links.length}`);
console.log('Links array created at:', new Error().stack);

let index = Math.min(+localStorage.getItem('bazaarLinkIndex') || 0, links.length - 1);

const settings = JSON.parse(localStorage.getItem('bazaarNavSettings')) || defaultSettings;

    // Initialize GM.addStyle if not available
    if (typeof GM === 'undefined') window.GM = {};
    GM.addStyle ||= css => document.head.appendChild(Object.assign(document.createElement('style'), {textContent: css}));

    // Create UI elements
    const container = document.createElement('div');
    container.id = 'bazaarNavFloat';

    const counter = document.createElement('div');
    counter.id = 'bazaarCounter';
    counter.textContent = `${index + 1} / ${links.length}`;
console.log('Initial links length when creating counter:', links.length);
    const cogIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    cogIcon.setAttribute("id", "cogIcon");
    cogIcon.setAttribute("viewBox", "0 0 24 24");
    cogIcon.setAttribute("aria-label", "Settings");
    cogIcon.setAttribute("role", "button");
    cogIcon.setAttribute("tabindex", "0");
    cogIcon.innerHTML = `<path d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 00.12-.63l-1.92-3.32a.5.5 0 00-.6-.22l-2.39.96a7.027 7.027 0 00-1.63-.94l-.36-2.54a.5.5 0 00-.5-.42h-3.84a.5.5 0 00-.5.42l-.36 2.54a6.94 6.94 0 00-1.63.94l-2.39-.96a.5.5 0 00-.6.22l-1.92 3.32a.5.5 0 00.12.63l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.63l1.92 3.32a.5.5 0 00.6.22l2.39-.96c.5.38 1.04.7 1.63.94l.36 2.54a.5.5 0 00.5.42h3.84a.5.5 0 00.5-.42l.36-2.54c.59-.24 1.13-.56 1.63-.94l2.39.96a.5.5 0 00.6-.22l1.92-3.32a.5.5 0 00-.12-.63l-2.03-1.58zM12 15.5a3.5 3.5 0 110-7 3.5 3.5 0 010 7z"/>`;

    const nextBtn = document.createElement('button');
    nextBtn.textContent = 'Next Bazaar';
    const backToFirstBtn = document.createElement('button');
    backToFirstBtn.textContent = 'Back to First';
    const settingsPanel = document.createElement('div');
    settingsPanel.id = 'settingsPanel';

    // Resize handle
    const resizeHandle = document.createElement('div');
    resizeHandle.id = 'resizeHandle';
    resizeHandle.innerHTML = '↔';
    resizeHandle.title = "Drag to resize";

    // Build UI
    counter.appendChild(cogIcon);
    [counter, nextBtn, backToFirstBtn, settingsPanel, resizeHandle].forEach(el => container.appendChild(el));
    document.body.appendChild(container);

    const savedPos = JSON.parse(localStorage.getItem('bazaarNavPosition'));
    if (savedPos?.x !== undefined && savedPos?.y !== undefined) {
        container.style.left = `${savedPos.x}px`;
        container.style.top = `${savedPos.y}px`;
    }

    const updateStyles = () => {
        GM.addStyle(`
            #bazaarNavFloat {
                z-index: 999999; position: fixed; top: 120px; left: 10px; display: flex;
                flex-direction: column; align-items: center; font-weight: bold;
                overflow: hidden; user-select: none; cursor: grab;
                background: linear-gradient(135deg, ${settings.gradientStart}, ${settings.gradientEnd});
                color: ${settings.textColor}; font-family: ${settings.fontFamily};
                font-size: ${settings.fontSize}px; border-radius: ${settings.borderRadius}px;
                border: ${settings.borderWidth}px solid ${settings.borderColor};
                box-shadow: 0 2px ${settings.shadowBlur}px ${settings.shadowColor};
                opacity: ${settings.opacity}; width: ${settings.width}px;
            }
            #bazaarNavFloat button {
                background: ${settings.buttonBgColor}; color: ${settings.buttonTextColor};
                border: ${settings.buttonBorderWidth}px solid ${settings.buttonBorderColor};
                padding: 8px 16px; cursor: pointer; font-weight: bold;
                font-size: ${settings.fontSize}px; width: calc(100% - 16px);
                text-shadow: 0 1px 1px #fff; border-radius: ${settings.borderRadius}px;
                transition: background 0.3s; margin: 4px 8px 0;
            }
            #bazaarNavFloat button:hover { background: ${settings.buttonHoverColor}; }
            #bazaarCounter {
                padding: 10px 14px; background: ${settings.counterBgColor};
                color: ${settings.counterTextColor}; width: calc(100% - 28px);
                text-align: center; border-bottom: 1px solid ${settings.counterBorderColor};
                position: relative; user-select: none; display: flex;
                align-items: center; justify-content: center; gap: 6px;
            }
            #bazaarCounter.loading::after {
                content: ''; position: absolute; bottom: 0; left: 0; height: 3px; width: 100%;
                background: ${settings.loadingBarColor}; animation: loadingBar 3s linear forwards;
                border-radius: 0 0 ${settings.borderRadius}px ${settings.borderRadius}px;
            }
            @keyframes loadingBar { from { width: 0; } to { width: 100%; } }
            #settingsPanel {
                display: none; background: ${settings.panelBgColor};
                color: ${settings.panelTextColor}; width: calc(100% - 16px);
                padding: 8px; box-sizing: border-box; border-top: 1px solid ${settings.panelBorderColor};
                font-size: 12px; max-height: 240px; overflow-y: auto; margin: 0 8px;
            }
            #settingsPanel label { display: block; margin: 8px 0 2px; font-weight: bold; }
            #settingsPanel input, #settingsPanel select {
                width: 100%; padding: 2px 4px; box-sizing: border-box; font-size: 13px;
                border-radius: 4px; border: 1px solid #ccc;
            }
            #settingsPanel .section {
                margin: 10px 0; padding-bottom: 10px;
                border-bottom: 1px dashed #ddd;
            }
            #settingsPanel .section-title {
                font-weight: bold; margin-bottom: 8px; color: #555;
            }
            #cogIcon {
                cursor: pointer; width: 18px; height: 18px; fill: ${settings.textColor};
                user-select: none; flex-shrink: 0;
            }
            #resizeHandle {
                position: absolute; right: 0; bottom: 0; width: 16px; height: 16px;
                background: rgba(0,0,0,0.1); cursor: nwse-resize; display: flex;
                align-items: center; justify-content: center; font-size: 12px;
                border-radius: 4px 0 0 0;
            }
            #resizeHandle:hover { background: rgba(0,0,0,0.2); }
            @media screen and (max-width: 1000px) {
                #bazaarNavFloat { top: 140px; }
            }
        `);
    };

    updateStyles();
    container.style.width = `${settings.width}px`;
container.style.opacity = settings.opacity;

    // Dragging function
    let isDragging = false, dragStartX, dragStartY, elemStartX, elemStartY;
    container.addEventListener('mousedown', e => {
        if (e.target.closest('#settingsPanel, #cogIcon, #resizeHandle')) return;
        isDragging = true;
        [dragStartX, dragStartY] = [e.clientX, e.clientY];
        const rect = container.getBoundingClientRect();
        [elemStartX, elemStartY] = [rect.left, rect.top];
        container.style.cursor = 'grabbing';
        e.preventDefault();
    });

    // Resize function
    let isResizing = false, startWidth, startX;
    resizeHandle.addEventListener('mousedown', e => {
        isResizing = true;
        startWidth = container.offsetWidth;
        startX = e.clientX;
        e.preventDefault();
        e.stopPropagation();
    });

    const handleMove = e => {
        if (isDragging) {
            let newX = Math.max(0, Math.min(window.innerWidth - container.offsetWidth, elemStartX + e.clientX - dragStartX));
            let newY = Math.max(0, Math.min(window.innerHeight - container.offsetHeight, elemStartY + e.clientY - dragStartY));
            container.style.left = `${newX}px`;
            container.style.top = `${newY}px`;
        } else if (isResizing) {
            const newWidth = Math.max(120, Math.min(400, startWidth + (e.clientX - startX)));
            settings.width = newWidth;
            container.style.width = `${newWidth}px`;
            saveSettings();
        }
    };

    const handleUp = () => {
        if (isDragging) {
            isDragging = false;
            container.style.cursor = 'grab';
            const rect = container.getBoundingClientRect();
            localStorage.setItem('bazaarNavPosition', JSON.stringify({x: rect.left, y: rect.top}));
        } else if (isResizing) {
            isResizing = false;
        }
    };

    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', handleUp);

    // Navigation function
   const openLinkAtIndex = i => {
    console.log('openLinkAtIndex - links length:', links.length, 'index:', i);
    if (i < 0 || i >= links.length) {
        console.error('Invalid index or links array is empty');
        return;
    }
    index = i;
    localStorage.setItem('bazaarLinkIndex', index);
    counter.textContent = `${index + 1} / ${links.length}`;
    counter.appendChild(cogIcon);
    window.location.href = links[index];
};

    nextBtn.addEventListener('click', () => openLinkAtIndex((index + 1) % links.length));
    backToFirstBtn.addEventListener('click', () => openLinkAtIndex(0));

    // Settings management
    const saveSettings = () => {
        localStorage.setItem('bazaarNavSettings', JSON.stringify(settings));
        updateStyles();
    };

    const resetToDefaults = () => {
        Object.assign(settings, defaultSettings);
        saveSettings();
        populateSettingsPanel();
    };

    // Settings panel
    const populateSettingsPanel = () => {
        settingsPanel.innerHTML = `
            <div class="section">
                <div class="section-title">Presets</div>
                <button id="defaultPreset" style="width:100%;margin:4px 0;padding:4px">Default</button>
                <button id="darkPreset" style="width:100%;margin:4px 0;padding:4px">Dark Theme</button>
                <button id="lightPreset" style="width:100%;margin:4px 0;padding:4px">Light Theme</button>
                <button id="resetDefaults" style="width:100%;margin:4px 0;padding:4px">Reset All to Defaults</button>
            </div>

            <div class="section">
                <div class="section-title">Container</div>
                <label for="widthInput">Width:</label>
                <input type="range" id="widthInput" min="120" max="400" step="1" value="${settings.width}">

                <label for="opacityInput">Opacity:</label>
                <input type="range" id="opacityInput" min="0.5" max="1" step="0.05" value="${settings.opacity}">

                <label for="gradientStart">Gradient Start:</label>
                <input type="color" id="gradientStart" value="${settings.gradientStart}">

                <label for="gradientEnd">Gradient End:</label>
                <input type="color" id="gradientEnd" value="${settings.gradientEnd}">

                <label for="borderRadius">Border Radius:</label>
                <input type="number" id="borderRadius" min="0" max="50" value="${settings.borderRadius}">

                <label for="borderWidth">Border Width:</label>
                <input type="number" id="borderWidth" min="0" max="10" value="${settings.borderWidth}">

                <label for="borderColor">Border Color:</label>
                <input type="color" id="borderColor" value="${settings.borderColor}">

                <label for="shadowColor">Shadow Color:</label>
                <input type="color" id="shadowColor" value="${settings.shadowColor}">

                <label for="shadowBlur">Shadow Blur:</label>
                <input type="number" id="shadowBlur" min="0" max="20" value="${settings.shadowBlur}">
            </div>

            <div class="section">
                <div class="section-title">Text</div>
                <label for="textColor">Text Color:</label>
                <input type="color" id="textColor" value="${settings.textColor}">

                <label for="fontFamily">Font Family:</label>
                <select id="fontFamily">
                    <option value="Arial, sans-serif">Arial</option>
                    <option value="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif">Segoe UI</option>
                    <option value="'Courier New', Courier, monospace">Courier New</option>
                    <option value="'Georgia', serif">Georgia</option>
                    <option value="'Comic Sans MS', cursive, sans-serif">Comic Sans MS</option>
                    <option value="'Trebuchet MS', sans-serif">Trebuchet MS</option>
                </select>

                <label for="fontSize">Font Size:</label>
                <input type="number" id="fontSize" min="10" max="20" value="${settings.fontSize}">
            </div>

            <div class="section">
                <div class="section-title">Buttons</div>
                <label for="buttonBgColor">Background:</label>
                <input type="color" id="buttonBgColor" value="${settings.buttonBgColor}">

                <label for="buttonTextColor">Text Color:</label>
                <input type="color" id="buttonTextColor" value="${settings.buttonTextColor}">

                <label for="buttonHoverColor">Hover Color:</label>
                <input type="color" id="buttonHoverColor" value="${settings.buttonHoverColor}">

                <label for="buttonBorderColor">Border Color:</label>
                <input type="color" id="buttonBorderColor" value="${settings.buttonBorderColor}">

                <label for="buttonBorderWidth">Border Width:</label>
                <input type="number" id="buttonBorderWidth" min="0" max="5" value="${settings.buttonBorderWidth}">
            </div>

            <div class="section">
                <div class="section-title">Counter</div>
                <label for="counterBgColor">Background:</label>
                <input type="color" id="counterBgColor" value="${settings.counterBgColor}">

                <label for="counterTextColor">Text Color:</label>
                <input type="color" id="counterTextColor" value="${settings.counterTextColor}">

                <label for="counterBorderColor">Border Color:</label>
                <input type="color" id="counterBorderColor" value="${settings.counterBorderColor}">

                <label for="loadingBarColor">Loading Bar Color:</label>
                <input type="color" id="loadingBarColor" value="${settings.loadingBarColor}">
            </div>

            <div class="section">
                <div class="section-title">Settings Panel</div>
                <label for="panelBgColor">Background:</label>
                <input type="color" id="panelBgColor" value="${settings.panelBgColor}">

                <label for="panelTextColor">Text Color:</label>
                <input type="color" id="panelTextColor" value="${settings.panelTextColor}">

                <label for="panelBorderColor">Border Color:</label>
                <input type="color" id="panelBorderColor" value="${settings.panelBorderColor}">
            </div>
        `;

        settingsPanel.querySelectorAll('input, select').forEach(el => {
    el.addEventListener('input', () => {
        if (el.id === 'widthInput') {
            settings.width = parseInt(el.value);
            container.style.width = `${settings.width}px`;
        } else if (el.id === 'opacityInput') {
            settings.opacity = parseFloat(el.value);
            container.style.opacity = settings.opacity;
        } else if (el.type === 'color') {
            settings[el.id] = el.value;
        } else if (el.type === 'number' || el.type === 'range') {
            settings[el.id] = parseInt(el.value);
        } else {
            settings[el.id] = el.value;
        }
        saveSettings();
    });
});

        // Preset buttons
        settingsPanel.querySelector('#defaultPreset').addEventListener('click', () => {
            Object.assign(settings, defaultSettings);
            saveSettings();
            populateSettingsPanel();
        });

        settingsPanel.querySelector('#darkPreset').addEventListener('click', () => {
            Object.assign(settings, {
                gradientStart: '#2d2d2d', gradientEnd: '#1a1a1a',
                textColor: '#e0e0e0', fontFamily: 'Arial, sans-serif',
                buttonBgColor: '#444444', buttonTextColor: '#000000',
                buttonHoverColor: '#666666', counterBgColor: '#333333',
                counterTextColor: '#ffffff', panelBgColor: '#222222',
                panelTextColor: '#dddddd', borderColor: '#444444',
                panelBorderColor: '#444444', counterBorderColor: '#555555',
                buttonBorderColor: '#666666', loadingBarColor: '#4a90e2'
            });
            saveSettings();
            populateSettingsPanel();
        });

        settingsPanel.querySelector('#lightPreset').addEventListener('click', () => {
            Object.assign(settings, {
                gradientStart: '#f5f5f5', gradientEnd: '#e0e0e0',
                textColor: '#000000', fontFamily: 'Arial, sans-serif',
                buttonBgColor: '#ffffff', buttonTextColor: '#000000',
                buttonHoverColor: '#8F4729', counterBgColor: '#f0f0f0',
                counterTextColor: '#000000', panelBgColor: '#ffffff',
                panelTextColor: '#000000',
                borderColor: '#000000',      // Border color
                borderWidth: 2,              // Border thickness (in pixels)
                panelBorderColor: '#000000',
                counterBorderColor: '#000000',
                buttonBorderColor: '#000000',
                buttonBorderWidth: 2,        // Button border thickness
                loadingBarColor: '#8F4729'
            });
            saveSettings();
            populateSettingsPanel();
        });

        settingsPanel.querySelector('#resetDefaults').addEventListener('click', resetToDefaults);
    };

    populateSettingsPanel();

    // Toggle settings panel
    cogIcon.addEventListener('click', () => settingsPanel.style.display = settingsPanel.style.display === 'block' ? 'none' : 'block');
    cogIcon.addEventListener('keydown', e => (e.key === 'Enter' || e.key === ' ') && (e.preventDefault(), cogIcon.click()));
})();