您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add/edit personal notes and colour tags to items in Torn inventory, faction armory, and the trade screen with shared keys.
// ==UserScript== // @name Torn Item & Armory Notes // @version 1.2 // @description Add/edit personal notes and colour tags to items in Torn inventory, faction armory, and the trade screen with shared keys. // @match https://www.torn.com/item.php* // @match https://www.torn.com/factions.php?step=your&type=1* // @match https://www.torn.com/trade.php* // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @grant GM_registerMenuCommand // @run-at document-idle // @author aquagloop // @license MIT // @namespace https://greasyfork.runtimutd.eu.org/users/1476871 // ==/UserScript== (function () { 'use strict'; let deletionMode = false; function makeNoteButton() { const btn = document.createElement('button'); btn.textContent = '📝'; btn.title = 'Click to add/edit note & colour'; btn.style.cssText = ` background: none; border: none; padding: 0; margin-left: 4px; cursor: pointer; font-size: 90%; `; btn.classList.add('torn-note-btn'); return btn; } function makecolourBox(colour) { const box = document.createElement('span'); box.classList.add('torn-colour-box'); box.style.cssText = ` display: ${colour ? 'inline-block' : 'none'}; width: 10px; height: 10px; margin-left: 4px; vertical-align: middle; background: ${colour || 'transparent'}; border: 1px solid #000; `; return box; } function loadData(key) { const raw = GM_getValue(key, ''); if (!raw) return { note: '', colour: '' }; try { return JSON.parse(raw); } catch { return { note: raw, colour: '' }; } } function saveData(key, data) { GM_setValue(key, JSON.stringify(data)); } function deleteData(key, li, colourBox) { GM_deleteValue(key); li.title = ''; colourBox.style.background = 'transparent'; colourBox.style.display = 'none'; } function handleNoteButtonClick(key, li, colourBox, label) { if (deletionMode) { if (confirm(`Delete note and colour for ${label}?`)) { deleteData(key, li, colourBox); } return; } const current = loadData(key); const note = prompt(`Note for ${label}:`, current.note); if (note === null) return; setTimeout(() => { const colour = prompt('Colour tag (CSS/hex):', current.colour); if (colour === null) return; const newData = { note: note.trim(), colour: colour.trim() }; saveData(key, newData); li.title = newData.note || ''; colourBox.style.background = newData.colour || 'transparent'; colourBox.style.display = newData.colour ? 'inline-block' : 'none'; }, 100); } function generateSharedKey(name, stats) { return `shared|${name}|${stats}`; } function processInventory() { document.querySelectorAll('li[data-item]').forEach(li => { if (li.querySelector('.torn-note-btn')) return; const nameEl = li.querySelector('.name'); if (!nameEl) return; const name = nameEl.textContent.trim(); const nums = li.innerText.match(/\d+\.\d+/g) || []; const stats = nums.slice(0, 2).join('_') || 'nostats'; const key = generateSharedKey(name, stats); const data = loadData(key); const btn = makeNoteButton(); const colourBox = makecolourBox(data.colour); nameEl.appendChild(btn); nameEl.appendChild(colourBox); if (data.note) li.title = data.note; btn.addEventListener('click', e => { e.stopPropagation(); handleNoteButtonClick(key, li, colourBox, `"${name}" (Inventory)`); }); }); } function processArmory() { const cat = getArmoryCategory(); document.querySelectorAll('ul.item-list > li').forEach(li => { const nameEl = li.querySelector('.name'); if (!nameEl || li.querySelector('.torn-note-btn')) return; const name = nameEl.textContent.trim(); const nums = li.innerText.match(/\d+\.\d+/g) || []; const stats = nums.slice(0, 2).join('_') || 'nostats'; const key = generateSharedKey(name, stats); const data = loadData(key); const btn = makeNoteButton(); const colourBox = makecolourBox(data.colour); nameEl.appendChild(btn); nameEl.appendChild(colourBox); if (data.note) li.title = data.note; btn.addEventListener('click', e => { e.stopPropagation(); handleNoteButtonClick(key, li, colourBox, `"${name}" (Armory - ${cat})`); }); }); } function getArmoryCategory() { const hash = window.location.hash.substring(1); return new URLSearchParams(hash).get('sub') || 'armoury'; } // ─────────────────────────────────────────────────────────────────────────── // New: Process Torn Trade “Add Item” screen function processTradeAdd() { const tradeItems = document.querySelectorAll( 'li.clearfix[data-group="child"], .item___jLJcf' ); tradeItems.forEach(elem => { if (elem.querySelector('.torn-note-btn')) return; // Find the item name element in either legacy or new UI const nameEl = elem.querySelector('.t-overflow, .desc___VJSNQ span b'); if (!nameEl) return; const name = nameEl.textContent.trim(); const nums = elem.innerText.match(/\d+\.\d+/g) || []; const stats = nums.slice(0, 2).join('_') || 'nostats'; const key = generateSharedKey(name, stats); const data = loadData(key); const btn = makeNoteButton(); const colourBox = makecolourBox(data.colour); // Append next to the displayed name const container = nameEl.parentElement; container.appendChild(btn); container.appendChild(colourBox); if (data.note) elem.title = data.note; btn.addEventListener('click', e => { e.stopPropagation(); handleNoteButtonClick(key, elem, colourBox, `"${name}" (Trade Add)`); }); }); } function initAll() { processInventory(); processArmory(); processTradeAdd(); } GM_registerMenuCommand('🗑️ Clear All Notes & colours', async () => { if (!confirm('Are you sure you want to delete ALL notes and colour tags?')) return; const keys = await GM_listValues(); keys.filter(k => k.startsWith('shared|')) .forEach(k => GM_deleteValue(k)); alert('All shared notes and colour tags have been cleared. Reload page to see changes.'); }); GM_registerMenuCommand('🧨 Toggle Deletion Mode (OFF)', function toggleDeletionMode() { deletionMode = !deletionMode; alert(`Deletion Mode is now ${deletionMode ? 'ON' : 'OFF'}.`); }); initAll(); new MutationObserver(initAll).observe(document.body, { childList: true, subtree: true }); })();