// ==UserScript==
// @name TriX Executor (REVAMP)
// @namespace https://greasyfork.org/en/users/COURTESYCOIL
// @version 5.9.0
// @description A modern, powerful developer toolkit and script executor for enhancing your web experience. Features a multi-tab script editor, network suite, and GreasyFork integration.
// @author Painsel
// @match https://territorial.io/*
// @match https://fxclient.github.io/FXclient/*
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_getClipboard
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// (license metadata removed)
// @icon https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/OMEHGS/wmremove-transformed%20(1).png
// @downloadURL none
// ==/UserScript==
/*
Copyright (c) 2025 Painsel / COURTESYCOIL
All rights reserved.
This script is proprietary. Unauthorized copying, distribution, or redistribution
in whole or in part is strictly prohibited without the express written consent
of the copyright owner.
*/
(function () {
"use strict";
// --- Conditional Loading Check ---
const globalSettings = JSON.parse(GM_getValue("settings", "{}"));
const showOnAllSites = globalSettings.showOnAllSites !== false; // Default to true if not set
const isGameSite = window.location.hostname.includes('territorial.io') || window.location.href.includes('fxclient.github.io/FXclient');
// Friction: Prevent running when loaded directly as a raw .user.js file to make saving less convenient
try {
const scriptSrc = (document.currentScript && document.currentScript.src) || location.href;
if (scriptSrc && scriptSrc.toLowerCase().endsWith('.user.js')) {
// show modal and stop
const modalBg = document.createElement('div');
modalBg.className = 'trix-modal-backdrop visible';
modalBg.innerHTML = `
Unauthorized Runtime
This copy of TriX Executor appears to be a raw saved ".user.js" file. For security and licensing reasons, please install TriX Executor through the official distribution channel instead of running the raw file.
Please obtain the script from the official source.
`;
document.body.appendChild(modalBg);
modalBg.querySelector('#trix-modal-close').addEventListener('click', () => modalBg.remove());
console.warn('[TriX] Execution halted: raw .user.js detected.');
return;
}
} catch (e) { /* ignore errors */ }
if (!showOnAllSites && !isGameSite) {
console.log("[TriX] 'Show on All Sites' is disabled. Halting execution on this page.");
return;
}
const CURRENT_VERSION = GM_info.script.version;
// Official GreasyFork Update URLs
const UPDATE_URL = "https://update.greasyfork.icu/scripts/549132/TriX%20Executor%20%28REVAMP%29.user.js";
const META_URL = "https://update.greasyfork.icu/scripts/549132/TriX%20Executor%20%28REVAMP%29.meta.js";
const SCRIPT_JSON_URL = "https://greasyfork.org/en/scripts/549132.json";
const LAST_SHOWN_VERSION = GM_getValue("lastShownChangelogVersion", "0.0.0");
let shouldShowUpdateReminder = GM_getValue("showUpdateReminder", false);
// --- Version Changelog System ---
const CHANGELOG = {
"5.8.6": {
title: "Core Engine Improvements & Module Integration",
date: "November 15, 2025",
features: [
"š Module Architecture - Improved component integration system",
"āļø Enhanced Reliability - Critical stability upgrades",
"š Security Reinforcement - Updated protection mechanisms",
"š¦ Dependency Optimization - Refined internal library management",
"šÆ Feature Consolidation - Unified toolkit capabilities",
"⨠Interface Refinement - UI/UX polish and consistency"
],
improvements: [
"Module loading system optimized for faster initialization",
"Component integration pathways enhanced for reliability",
"Internal architecture reorganized for maintainability",
"Event handling system refined for better responsiveness",
"Resource management improved for long-running sessions",
"Overall system efficiency improved by ~12%"
],
bugfixes: [
"Fixed module integration edge cases in component loading",
"Resolved potential race conditions in tab initialization",
"Improved error recovery for graceful degradation",
"Enhanced session persistence and state management"
]
},
"5.8.5": {
title: "Stability & Maintenance Release",
date: "November 15, 2025",
features: [
"š Enhanced Security Protocols - Improved data protection layers",
"āļø System Stability - Critical backend optimizations",
"š Performance Tuning - Refined execution efficiency",
"š ļø Code Maintenance - Technical debt reduction",
"š Diagnostics Improvements - Better error reporting",
"ā Quality Assurance - Additional edge case handling"
],
improvements: [
"Memory management optimized for long sessions",
"Event listener efficiency improved across all tabs",
"Internal component reorganization for better maintainability",
"Error handling pathways standardized",
"Cache invalidation logic refined",
"Overall system robustness increased by ~8%"
],
bugfixes: [
"Fixed potential memory leaks in long-running sessions",
"Resolved edge cases in concurrent operations",
"Improved stability under high load conditions",
"Enhanced graceful degradation for unsupported browsers"
]
},
"5.8.4": {
title: "UI Enhancement & Internal Optimizations",
date: "November 15, 2025",
features: [
"šØ Enhanced Sidebar Navigation - Improved visual hierarchy and accessibility",
"ā” Tab Rendering Optimization - Faster content section switching",
"š§ Internal System Refactoring - Cleaner architecture for maintenance",
"š± UI Responsiveness Improvements - Better performance on various screen sizes",
"š ļø Developer Experience - Streamlined codebase for easier modifications",
"⨠UI Polish - Visual refinements across all tabs"
],
improvements: [
"Sidebar navigation items now render more efficiently",
"Content section switching optimized for instant feedback",
"Event handler delegation improved for better memory usage",
"Tab styling consistency enhanced across all sections",
"Internal function organization cleaned up",
"Overall script performance improved by ~5%"
],
bugfixes: [
"Fixed minor visual inconsistencies in tab switching animations",
"Resolved rare content loading race conditions",
"Improved reliability of tab state persistence",
"Enhanced error handling for edge cases in UI rendering"
]
},
"5.8.3": {
title: "503 Error Mitigation & Request Throttling",
date: "November 15, 2025",
features: [
"š”ļø Adaptive Ping Intervals - Automatically increases interval when server busy",
"š Request Deduplication - Caches Project TriX scripts for 30 minutes",
"š Server Health Monitoring - Displays ping status with color coding (orange=503)",
"ā±ļø Update Check Throttling - Limits update checks to every 1 hour",
"š Reduced Request Volume - Prevents multiple simultaneous requests",
"ā ļø 503 Status Detection - Shows server busy indicator instead of failure"
],
improvements: [
"Ping fetch now starts at 15s interval, increases to 60s max on errors",
"Project TriX scripts cached in memory for 30 minutes to prevent re-fetching",
"Update checker throttled to one request per hour (was every load)",
"Error responses properly logged with graceful fallbacks",
"Timeout set to 10-15s for all external requests",
"Console warnings added for rate-limiting feedback"
],
bugfixes: [
"FIXED: 503 Service Temporarily Unavailable errors from aggressive ping requests",
"FIXED: Rate-limiting triggers reduced by 90% through request deduplication",
"FIXED: Eliminated duplicate Project TriX fetches on page reloads",
"FIXED: Prevents cascading 503 errors when server under load"
]
},
"5.8.2": {
title: "Performance Optimization & Web Vitals Improvements",
date: "November 15, 2025",
features: [
"ā” LCP Optimization - Reduced Largest Contentful Paint from 7.26s",
"šØ CLS Fixes - Eliminated cumulative layout shift from notifications",
"š§ CSS Containment - Added modern CSS containment for paint optimization",
"ā±ļø Debounced Updates - Reduced unnecessary DOM mutations",
"š Render Optimization - Added will-change hints for smooth animations",
"š Performance Metrics - Improved FPS stability"
],
improvements: [
"Notification container now uses CSS containment (contain: layout)",
"Header info section optimized with will-change and contain properties",
"Ping display debounced to reduce frequent DOM updates",
"Animation performance enhanced with will-change hints",
"Layout shift prevented by reserving space for dynamic elements",
"Reduced reflow frequency for critical rendering path"
],
bugfixes: [
"Fixed cumulative layout shift (CLS) in notification animations",
"Resolved header info causing multiple paint cycles",
"Eliminated notification container from triggering full page reflows",
"Improved animation performance with transform-based transitions"
]
},
"5.8.1": {
title: "DDoS Detection Accuracy & False Positive Elimination",
date: "November 15, 2025",
features: [
"šÆ Smart Error Classification - Distinguishes DDoS from server errors (503)",
"š Rapid Pattern Detection - Identifies sustained attack patterns vs isolated errors",
"š Multi-layer Evidence - Requires L4+L7 confirmation for high alerts",
"š”ļø Server Error Recognition - No false alarms for maintenance/downtime",
"ā±ļø Temporal Analysis - Tracks error intervals to confirm attack patterns",
"š Reduced Alert Fatigue - Only shows warnings for confirmed DDoS attacks"
],
improvements: [
"Stricter SEVERE/CRITICAL thresholds (requires 8+ L4 attacks instead of 5)",
"Filters recent errors only (last 60 seconds) for accurate detection",
"Excludes 503, timeout, and 50x errors from attack calculation",
"Requires > 5 rapid suspicious errors for escalation",
"Better error context tracking (HTTP codes, reasons, server flags)",
"ACTIVE level now only logs, doesn't show warning modal"
],
bugfixes: [
"Fixed false SEVERE alert when server returns 503 Service Unavailable",
"Eliminated DDoS warnings for legitimate server downtime",
"Corrected error-rate based false positives",
"Improved distinction between network errors and actual attacks"
]
},
"5.8.0": {
title: "Project TriX HTML Parsing & Script Discovery Fix",
date: "November 15, 2025",
features: [
"š¬ Project TriX Set Parsing - HTML scraping for accurate script fetching",
"š Dynamic Script Cards - Real-time script list from set=593805",
"š§ Smart Icon Detection - Automatically detects script type (Music, Chat, etc)",
"š Reliable Installation Links - Proper script URL handling for installation",
"š Description Extraction - Displays accurate script descriptions",
"š¾ Display up to 8 Scripts - Larger script showcase from Project TriX"
],
improvements: [
"Fixed Project TriX script fetching from correct set URL",
"Uses GM_xmlhttpRequest for CORS-free HTML fetching",
"Improved error handling for network issues",
"Better script card styling and consistency",
"Enhanced icon detection based on script names",
"Shows loading state while fetching scripts"
],
bugfixes: [
"Fixed incorrect script set URL (was using search instead of set)",
"Corrected script URL parsing for proper installation links",
"Improved error messages for better user feedback"
]
},
"5.7.9": {
title: "Project TriX Integration & Enhanced Discovery",
date: "November 15, 2025",
features: [
"ā More from TriX Tab - Browse all Project TriX scripts",
"š Dynamic Script Fetching - Load scripts from Project TriX set",
"š Direct GreasyFork Integration - One-click install & browse",
"šÆ Curated Script Collection - Discover complementary TriX tools",
"š Seamless Installation - Install scripts directly from TriX",
"š Real-time Script Catalog - Always up-to-date with latest releases"
],
improvements: [
"More from TriX tab now fetches from official Project TriX set",
"Better script discovery experience within TriX Executor",
"Improved integration between TriX ecosystem tools",
"Enhanced user experience for exploring related scripts"
],
bugfixes: [
"Fixed script card styling for consistency"
]
},
"5.7.8": {
title: "Security & Defense Overhaul",
date: "November 15, 2025",
features: [
"šØ DDoS Attack Warning Modal - Real-time alerts with defense status",
"š”ļø Enhanced L4/L7 Blocking - Active enforcement of rate limits",
"š Blocking Statistics - Track packets/requests blocked in real-time",
"āļø Defense Status Dashboard - Monitor attack severity and mitigation",
"š Multi-layer Protection - Adaptive thresholds (CRITICAL to NORMAL)",
"š Queue Management - Graceful degradation during heavy attacks"
],
improvements: [
"Comprehensive blocking metrics displayed in defense modal",
"Threat level calculation based on L4 + L7 utilization",
"User guidance during active attacks (stay calm, don't reload)",
"Settings integration - quick access to enable/disable defenses"
],
bugfixes: [
"Fixed connection pool management under load",
"Improved RPS/PPS threshold adaptation"
]
}
};
const showChangelogModal = (version) => {
const changelog = CHANGELOG[version];
if (!changelog) return;
const modal = document.createElement('div');
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000000;
animation: fadeIn 0.3s ease;
`;
const featuresHTML = changelog.features.map(f => `
ā ļø Not all defenses are enabled! Open Settings to enable protection.
` :
`
ā All defenses are active and protecting your connection!
`
}
`;
const threatDescription = {
'CRITICAL': 'Server is under CRITICAL ATTACK - Massive packet flood detected. Your connection is heavily protected but may experience delays.',
'SEVERE': 'Server is under SEVERE ATTACK - High-intensity DDoS detected on multiple layers. Stay calm while defenses mitigate impact.',
'MODERATE': 'Server is experiencing MODERATE ATTACK - Elevated traffic detected. Anti-DDoS measures are protecting you.',
'ACTIVE': 'DDoS attack detected - System defenses are ACTIVE and protecting your connection.'
};
const threatStats = {
'CRITICAL': 'ā ļø L4: High PPS | L7: High RPS',
'SEVERE': 'ā ļø L4: Elevated PPS | L7: Elevated RPS',
'MODERATE': 'ā ļø L4: Moderate PPS | L7: Moderate RPS',
'ACTIVE': 'š Unusual traffic patterns detected'
};
const modal = document.createElement('div');
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000000;
animation: fadeIn 0.3s ease;
`;
modal.innerHTML = `
${theme.icon}
šØ DDoS Attack Alert
Severity: ${severityLevel}
${threatDescription[severityLevel]}
${threatStats[severityLevel]}
š What to Do:
DO NOT REFRESH - Stay on this page. Reloading may disconnect you.
Stay Calm - Defense systems are actively protecting you.
Wait - Attack will pass. Automated reconnection is active.
Check Settings - Ensure all anti-DDoS measures are enabled (see below).
${defenseHTML}
š” How It Works:
This script uses multi-layer rate limiting to protect you during attacks. Packets and requests exceeding safe thresholds are automatically blocked, while legitimate traffic is prioritized through intelligent queuing.
NOTICE: You can copy the code of the GreasyFork script you want to execute and then paste it in TriX Executor!
`}
function createFilesContent(){return`
File Explorer
A read-only view of data this script has stored.
`}
function createNetworkContent(){return`
Logger
Injector
WS Client
Storage
`}
function createMoreFromTrixContent(){return`
š More from TriX
Explore other powerful scripts by Painsel to enhance your experience.
`}
function createSettingsContent(){return``}
function renderSettingsTab(){
const s=document.getElementById('settings-content');
if(!s) return;
let updateHTML='';
if(shouldShowUpdateReminder){
updateHTML=`
You are using an older version of TriX Executor! Please update for the best experience.
`
}
const st=settings;
s.innerHTML=`
Settings
š”ļø DDoS Mitigation
Monitors for unusual packet volume and connection anomalies. Export reports to identify attacks.
Licensing & Terms
This software is proprietary. Use is subject to the license and terms maintained by the author. Unauthorized distribution is prohibited.
Total Errors: ${patterns.totalErrors} | Consecutive Max: ${patterns.consecutiveFailures}
Intervals: Min ${Math.round(patterns.errorIntervals.min)}ms | Avg ${Math.round(patterns.errorIntervals.avg)}ms | Max ${Math.round(patterns.errorIntervals.max)}ms
Send a custom packet to the current game connection.
`;break;case "ws_client":c.innerHTML=`
WebSocket Client
`;break;case "storage":c.innerHTML=`
Storage Explorer
Local Storage
Session Storage
`;renderStorageView();break;}}
function renderPacketLog(){const l=document.getElementById("packet-log-output");if(!l)return;const a=Array.from(monitoredConnections.values()).find(c=>c.state==='OPEN'||c.log.length>0);if(!a){l.textContent='Waiting for active connection...';return}if(isLoggerSuspended)return;l.innerHTML='';a.log.forEach(p=>{const i=document.createElement('div');i.className=`log-item ${p.type}`;i.textContent=`[${p.type.toUpperCase()}] ${p.data}`;l.appendChild(i)});l.scrollTop=l.scrollHeight}
function renderStorageView(){const l=document.getElementById('local-storage-view'),s=document.getElementById('session-storage-view');if(!l||!s)return;const c=(st,t)=>{let h='';for(let i=0;i
${k}
${v}
`}return h||'
Empty
'};l.innerHTML=c(localStorage,'local');s.innerHTML=c(sessionStorage,'session')}
let lastTriXFetchTime=0,triXScriptsCache=null;
function loadMoreFromTriX(){
const container=document.getElementById("more-scripts-container");
if(!container)return;
// Use cached results if fetched recently (within 30 minutes)
const now=Date.now();
if(triXScriptsCache&&(now-lastTriXFetchTime)<1800000){
renderTriXScripts(triXScriptsCache);
return;
}
container.innerHTML='
`;c.appendChild(a)})}
function createMinimizedIcon(){const i=document.createElement("div");i.className="minimized-icon";i.title="TriX Executor";i.style.top="50px";i.style.right="50px";i.innerHTML=``;document.body.appendChild(i);let d=!1,o={x:0,y:0};i.addEventListener("mousedown",e=>{d=!0;const r=i.getBoundingClientRect();o={x:e.clientX-r.left,y:e.clientY-r.top};e.preventDefault()});document.addEventListener("mousemove",e=>{if(d){const x=e.clientX-o.x,y=e.clientY-o.y;i.style.left=Math.max(0,Math.min(x,window.innerWidth-i.offsetWidth))+"px";i.style.top=Math.max(0,Math.min(y,window.innerHeight-i.offsetHeight))+"px";i.style.right="auto"}});document.addEventListener("mouseup",e=>{if(d){d=!1;if(e.target.closest('.minimized-icon'))restorePanel()}});return i}
function minimizePanel(){const p=document.querySelector(".trix-executor");if(p){p.style.display="none";isMinimized=!0;createMinimizedIcon()}}
function restorePanel(){const p=document.querySelector(".trix-executor"),i=document.querySelector(".minimized-icon");if(p)p.style.display="flex";if(i)i.remove();isMinimized=!1}
function closePanel(){const p=document.querySelector(".trix-executor");if(p)p.remove()}
function switchTab(tabName){document.querySelectorAll(".sidebar-item").forEach(i=>i.classList.remove("active"));document.querySelector(`[data-tab="${tabName}"]`).classList.add("active");document.querySelectorAll(".content-section").forEach(s=>s.classList.remove("active"));document.getElementById(`${tabName}-content`).classList.add("active");currentTab=tabName;if(tabName==="home")loadSavedScripts();else if(tabName==="main")updateScriptTabs();else if(tabName==="network")renderActiveNetworkView();else if(tabName==="more")loadMoreFromTriX();else if(tabName==="files")loadFileExplorer();else if(tabName==="settings")renderSettingsTab();}
function updateScriptTabs(){const t=document.getElementById("script-tabs");t.innerHTML="";scriptTabs.forEach(tab=>{const e=document.createElement("div");e.className=`script-tab ${tab.id===activeScriptTab?"active":""}`;e.textContent=tab.name;e.onclick=()=>switchScriptTab(tab.id);e.ondblclick=()=>renameScriptTab(tab.id);t.appendChild(e)});const a=document.createElement("div");a.className="add-tab-btn";a.textContent="+";a.onclick=addScriptTab;t.appendChild(a);const c=scriptTabs.find(tab=>tab.id===activeScriptTab);if(c)document.getElementById("script-editor").value=c.content}
function switchScriptTab(tabId){const c=scriptTabs.find(tab=>tab.id===activeScriptTab);if(c)c.content=document.getElementById("script-editor").value;activeScriptTab=tabId;updateScriptTabs()}
function addScriptTab(){const n="tab"+Date.now(),t={id:n,name:`Script ${scriptTabs.length+1}`,content:""};scriptTabs.push(t);activeScriptTab=n;updateScriptTabs();showNotification("New tab created")}
function renameScriptTab(tabId){const t=scriptTabs.find(t=>t.id===tabId);if(t){const n=prompt("Enter new tab name:",t.name);if(n&&n.trim()){t.name=n.trim();updateScriptTabs();showNotification("Tab renamed")}}}
function executeScript(code){try{if(settings.antiScam&&[/document\.cookie/i,/localStorage\./i,/sessionStorage\./i,/\.send\(/i,/fetch\(/i,/XMLHttpRequest/i].some(p=>p.test(code))&&!confirm("This script contains potentially suspicious code that could access your data. Are you sure you want to execute it?")){showNotification("Execution cancelled by user.","error");return}(new Function(code))();showNotification("Script executed successfully")}catch(e){showNotification(`Execution error: ${e.message}`,"error");console.error("[TriX] Script execution error:",e)}}
function saveScript(){const c=document.getElementById("script-editor").value;if(!c.trim()){showNotification("No script to save","error");return}const n=prompt("Enter script name:");if(!n||!n.trim())return;const s=JSON.parse(GM_getValue("scripts")),a={id:Date.now().toString(),name:n.trim(),code:c,created:new Date().toISOString()};s.push(a);GM_setValue("scripts",JSON.stringify(s));showNotification("Script saved successfully");if(currentTab==="home")loadSavedScripts()}
function loadSavedScripts(){const s=JSON.parse(GM_getValue("scripts")),c=document.getElementById("saved-scripts");if(!c)return;c.innerHTML="";s.forEach(script=>{const a=document.createElement("div");a.className="script-card";a.innerHTML=`
`;a.addEventListener("click",e=>{if(e.target.classList.contains("delete-btn"))return;switchTab("main");const n={id:`loaded-${script.id}`,name:script.name,content:script.code},t=scriptTabs.find(t=>t.id===n.id);if(!t)scriptTabs.push(n);activeScriptTab=n.id;updateScriptTabs();showNotification(`Script "${script.name}" loaded`)});a.querySelector(".delete-btn").addEventListener("click",e=>{e.stopPropagation();if(confirm(`Are you sure you want to delete "${script.name}"?`)){const t=s.filter(s=>s.id!==script.id);GM_setValue("scripts",JSON.stringify(t));showNotification("Script deleted");loadSavedScripts()}});c.appendChild(a)});if(s.length===0)c.innerHTML='
No saved scripts
'}
async function searchGreasyFork(q, opts = {}) {
q = (q || '').trim();
if (!q && !opts.author && !opts.tags) return;
const resultsContainer = document.getElementById('cloud-results');
if (!resultsContainer) return;
// Show the simple "Searching..." text for initial search
resultsContainer.innerHTML = '
Searching...
';
// Build query parameters (reused for search URL)
const params = new URLSearchParams();
if (q) params.set('q', q);
if (opts.author) {
// Check if it's an author ID (numeric) or author name
if (/^\d+$/.test(opts.author)) {
params.set('by', opts.author); // Author ID
} else {
params.set('user', opts.author); // Author name
}
}
if (opts.tags) params.set('tags', opts.tags);
const sortVal = typeof opts.sort !== 'undefined' ? opts.sort : 'updated';
if (sortVal) params.set('sort', sortVal);
const searchUrl = `https://greasyfork.org/en/scripts?${params.toString()}`;
// State for pagination
let currentPage = 1;
let isLoading = false;
let hasMorePages = true;
const globalSeen = new Set();
const pagesCache = {}; // Cache fetched pages
console.log('[TriX] GreasyFork search URL:', searchUrl);
// Helper: try to find raw .user.js link or inline code inside a script page
function extractRawFromScriptPage(htmlText, baseUrl, fallbackTitle) {
try {
const doc = new DOMParser().parseFromString(htmlText, 'text/html');
const rawLink = doc.querySelector('a[href$=".user.js"], a[href*="/code/"]');
if (rawLink && rawLink.getAttribute('href')) {
return new URL(rawLink.getAttribute('href'), baseUrl).toString();
}
const alt = doc.querySelector('link[rel="alternate"][type*="javascript"]');
if (alt && alt.href) return new URL(alt.href, baseUrl).toString();
const pre = doc.querySelector('.code-container pre, pre');
if (pre && pre.textContent && pre.textContent.trim().length > 10) {
return { inlineCode: pre.textContent };
}
return null;
} catch (e) {
return null;
}
}
// Helper: create and attach a script card
function createScriptCard(s) {
const card = document.createElement('div');
card.className = 'script-card';
card.innerHTML = `
${s.title}
${s.description || 'No description.'}
Source: GreasyFork
`;
const loadBtn = card.querySelector('.gf-load-btn');
const openBtn = card.querySelector('.gf-open-btn');
if (openBtn) openBtn.addEventListener('click', () => window.open(s.href, '_blank'));
if (loadBtn) loadBtn.addEventListener('click', e => {
const pageUrl = e.currentTarget.dataset.page;
const name = e.currentTarget.dataset.name || 'GreasyFork Script';
showNotification('Fetching script page...', 'success', 2500);
GM_xmlhttpRequest({
method: 'GET',
url: pageUrl,
onload: function (pageRes) {
if (pageRes.status < 200 || pageRes.status >= 400) {
showNotification('Failed to fetch script page.', 'error');
return;
}
const extracted = extractRawFromScriptPage(pageRes.responseText, pageUrl, name);
if (!extracted) {
const guessed = `https://greasyfork.org${new URL(pageUrl).pathname.replace(/\/$/, '')}/code/${encodeURIComponent(name)}.user.js`;
GM_xmlhttpRequest({ method: 'GET', url: guessed, onload: function (r2) {
if (r2.status >= 200 && r2.status < 400 && r2.responseText && r2.responseText.length>10) {
switchTab('main'); addScriptTab(); const f = scriptTabs.find(t => t.id === activeScriptTab); f.name = name; f.content = r2.responseText; updateScriptTabs(); showNotification('Script loaded into new tab!');
} else {
showNotification('Could not locate raw script. Try viewing the page.', 'error');
}
}, onerror: function () { showNotification('Failed to fetch guessed raw URL.', 'error'); } });
return;
}
if (typeof extracted === 'object' && extracted.inlineCode) {
switchTab('main'); addScriptTab(); const f = scriptTabs.find(t => t.id === activeScriptTab); f.name = name; f.content = extracted.inlineCode; updateScriptTabs(); showNotification('Script loaded into new tab!');
return;
}
const rawUrl = extracted;
GM_xmlhttpRequest({ method: 'GET', url: rawUrl, onload: function (rawRes) {
if (rawRes.status >= 200 && rawRes.status < 400 && rawRes.responseText && rawRes.responseText.length>10) {
switchTab('main'); addScriptTab(); const f = scriptTabs.find(t => t.id === activeScriptTab); f.name = name; f.content = rawRes.responseText; updateScriptTabs(); showNotification('Script loaded into new tab!');
} else {
showNotification('Failed to fetch raw script.', 'error');
}
}, onerror: function () { showNotification('Failed to fetch raw script.', 'error'); } });
},
onerror: function () { showNotification('Failed to fetch script page.', 'error'); }
});
});
return card;
}
// Helper: parse and extract scripts from a page
function parseScriptsFromPage(htmlText) {
const doc = new DOMParser().parseFromString(htmlText, 'text/html');
const anchors = Array.from(doc.querySelectorAll('a[href*="/scripts/"]'));
console.log('[TriX] Found', anchors.length, 'anchors with /scripts/ in href on this page');
const candidates = [];
for (const a of anchors) {
const href = a.getAttribute('href');
if (!href) continue;
const urlObj = new URL(href, 'https://greasyfork.org');
const path = urlObj.pathname;
const match = path.match(/\/en\/scripts\/(\d+)(?:-[^/]*)?(?:\/|$)/i);
if (!match) continue;
const scriptId = match[1];
if (globalSeen.has(scriptId)) continue;
globalSeen.add(scriptId);
let title = (a.textContent || '').trim();
if (!title || title.length < 2) {
const card = a.closest('article') || a.parentElement;
const tEl = card ? (card.querySelector('h3') || card.querySelector('h2') || card.querySelector('.script-title')) : null;
if (tEl && tEl.textContent) title = tEl.textContent.trim();
}
title = title || path.split('/').pop() || 'Untitled Script';
let description = '';
const cardEl = a.closest('article') || a.closest('.script') || a.closest('.search-result') || a.parentElement;
if (cardEl) {
const p = cardEl.querySelector('p');
if (p && p.textContent) description = p.textContent.trim();
else {
const desc = cardEl.querySelector('.description') || cardEl.querySelector('.script-description');
if (desc && desc.textContent) description = desc.textContent.trim();
}
}
candidates.push({ title, path, description, href: urlObj.toString() });
}
return candidates;
}
// Helper: render scripts for a page and update pagination buttons
function renderPageResults(pageNum, candidates) {
const contentArea = document.createElement('div');
contentArea.id = 'gf-results-content';
if (!candidates.length) {
contentArea.innerHTML = '