您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
An enhanced Torn City bazaar navigator with customization options to make this tool YOURS
// ==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())); })();