您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Core logic library for the TriX Executor. Not intended for direct installation.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/541461/1618069/TriX%20Core%20Library.js
// ==UserScript== // @name TriX Core Library // @namespace https://github.com/YourUsername/TriX-Executor // @version 1.5.0 // @description Core logic library for the TriX Executor. Not intended for direct installation. // @author You // @license MIT // ==/UserScript== const TriX_Core = (function() { 'use strict'; // --- Configuration & Constants --- const SCRIPT_PREFIX = 'trix_script_'; const BROADCAST_CHANNEL = 'trix_broadcast_channel'; const TAB_LIST_KEY = 'trix_active_tabs'; const HEARTBEAT_INTERVAL = 5000; const STALE_TAB_TIMEOUT = 15000; const TAB_ID = `tab_${Date.now().toString(36)}_${Math.random().toString(36).substring(2)}`; const LOAD_TIME = Date.now(); // --- TabManager Module --- const TabManager = { tabs:[], myTabInfo:{}, uiInitialized:false, isMaster:false, init(username){this.myTabInfo={id:TAB_ID,username:username,loadTime:LOAD_TIME,lastSeen:Date.now()};GM_addValueChangeListener(TAB_LIST_KEY,(name,old_value,new_value,remote)=>{if(remote)this.pruneAndRefresh(new_value)});this.register();setInterval(()=>this.register(),HEARTBEAT_INTERVAL);window.addEventListener('beforeunload',()=>this.unregister())}, async register(){let currentTabs=await GM_getValue(TAB_LIST_KEY,[]);const now=Date.now();currentTabs=currentTabs.filter(tab=>now-tab.lastSeen<STALE_TAB_TIMEOUT);this.myTabInfo.lastSeen=now;const myIndex=currentTabs.findIndex(tab=>tab.id===TAB_ID);if(myIndex>-1){currentTabs[myIndex]=this.myTabInfo}else{currentTabs.push(this.myTabInfo)}await GM_setValue(TAB_LIST_KEY,currentTabs);this.pruneAndRefresh(currentTabs)}, async unregister(){let currentTabs=await GM_getValue(TAB_LIST_KEY,[]);currentTabs=currentTabs.filter(tab=>tab.id!==TAB_ID);await GM_setValue(TAB_LIST_KEY,currentTabs)}, pruneAndRefresh(tabList){this.tabs=tabList;if(this.uiInitialized)TriX_UI.updateTabCountUI(tabList.length);this.checkMasterStatus()}, checkMasterStatus(){this.isMaster=this.amIMaster();if(this.isMaster&&!this.uiInitialized){this.initUI()}}, amIMaster(){if(!this.myTabInfo.username||this.myTabInfo.username.startsWith('Guest_'))return true;const competingTabs=this.tabs.filter(tab=>tab.username===this.myTabInfo.username);if(competingTabs.length===0)return true;const earliestLoadTime=Math.min(...competingTabs.map(tab=>tab.loadTime));return this.myTabInfo.loadTime===earliestLoadTime}, initUI(){ if(this.uiInitialized)return; console.log('[TriX Core] This tab is the master. Initializing UI.'); this.uiInitialized=true; // FIX: Call the globally exposed UI library object, not a private one. TriX_UI.init(); MultiTab.init(); } }; // --- ScriptManager, Executor, MultiTab Modules --- const ScriptManager = { async saveScriptFromEditor() { const name = document.getElementById('trix-save-name').value.trim(); const code = document.getElementById('trix-editor').value; if (!name) return TriX_UI.log('Cannot save: Name is required.', 'error'); if (!code) return TriX_UI.log('Cannot save: Editor is empty.', 'warn'); await GM_setValue(SCRIPT_PREFIX + name, code); TriX_UI.log(`Script '${name}' saved.`, 'info'); this.populateScriptList(SCRIPT_PREFIX + name); }, async loadScriptToEditor(key) { if (!key) return TriX_UI.log('Invalid script key.', 'error'); const code = await GM_getValue(key, ''); const name = key.replace(SCRIPT_PREFIX, ''); document.getElementById('trix-editor').value = code; document.getElementById('trix-save-name').value = name; TriX_UI.log(`Loaded script: ${name}`, 'info'); TriX_UI.currentScriptName = name; TriX_UI.setActiveScriptItem(key); }, async deleteCurrentScript() { const name = TriX_UI.currentScriptName; if (!name) return TriX_UI.log('No script loaded to delete.', 'warn'); if (confirm(`Are you sure you want to delete '${name}'?`)) { await GM_deleteValue(SCRIPT_PREFIX + name); TriX_UI.log(`Script '${name}' deleted.`, 'info'); document.getElementById('trix-editor').value = ''; document.getElementById('trix-save-name').value = ''; TriX_UI.currentScriptName = ''; this.populateScriptList(); } }, async populateScriptList(activeKey = null) { const listEl = document.getElementById('trix-script-list'); listEl.innerHTML = ''; const allKeys = (await GM_listValues()).filter(k => k.startsWith(SCRIPT_PREFIX)); allKeys.sort().forEach(key => { const item = document.createElement('div'); item.className = 'trix-script-item'; item.textContent = key.replace(SCRIPT_PREFIX, ''); item.dataset.scriptKey = key; listEl.appendChild(item); }); TriX_UI.setActiveScriptItem(activeKey || TriX_UI.currentScriptName ? SCRIPT_PREFIX + TriX_UI.currentScriptName : null); } }; const Executor = { execute(code) { if (!code.trim()) return TriX_UI.log('Execution skipped: script is empty.', 'warn'); TriX_UI.log('Executing script...', 'info'); try { const TriX = this.createAPI(); const scriptFunction = new Function('TriX', code); scriptFunction(TriX); } catch (e) { TriX_UI.log(`Execution Error: ${e.message}`, 'error'); console.error("TriX Executor Error:", e); } }, createAPI: () => ({ log: (message, type = 'log') => { const msgStr = typeof message === 'object' ? JSON.stringify(message) : String(message); TriX_UI.log(msgStr, type); }, broadcast: (payload) => { MultiTab.broadcast(payload); }, query: (selector, dataType = 'text') => { const element = document.querySelector(selector); if (!element) return null; switch (dataType) { case 'html': return element.innerHTML; case 'value': return element.value; case 'element': return element; case 'text': default: return element.textContent; } }, queryAll: (selector, dataType = 'text') => { const elements = document.querySelectorAll(selector); return Array.from(elements).map(el => { switch (dataType) { case 'html': return el.innerHTML; case 'value': return el.value; case 'element': return el; case 'text': default: return el.textContent; } }); } }) }; const MultiTab = { init() { GM_addValueChangeListener(BROADCAST_CHANNEL, this.listener); }, listener(key, oldValue, newValue, remote) { if (remote && newValue.senderId !== TAB_ID) { TriX_UI.log(`Received broadcast: ${JSON.stringify(newValue.payload)}`, 'broadcast'); } }, broadcast(payload) { const message = { senderId: TAB_ID, timestamp: Date.now(), payload: payload }; GM_setValue(BROADCAST_CHANNEL, message); TriX_UI.log(`Broadcast sent: ${JSON.stringify(payload)}`, 'broadcast'); } }; return { TabManager, ScriptManager, Executor, MultiTab }; })();