BazNav

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

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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