您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display Top 60 Power Racers on NT Race UI
当前为
// ==UserScript== // @name Nitro Type NT Comps Top 60 Power Racers // @version 1.5 // @description Display Top 60 Power Racers on NT Race UI // @author TensorFlow - Dvorak // @match *://*.nitrotype.com/race // @match *://*.nitrotype.com/race/* // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/dexie/3.2.1/dexie.min.js#sha512-ybuxSW2YL5rQG/JjACOUKLiosgV80VUfJWs4dOpmSWZEGwdfdsy2ldvDSQ806dDXGmg9j/csNycIbqsrcqW6tQ== // @license MIT // @namespace https://greasyfork.org/users/1331131-tensorflow-dvorak // ==/UserScript== /* globals Dexie */ const findReact = (dom, traverseUp = 0) => { const key = Object.keys(dom).find(key => key.startsWith("__reactFiber$")); const domFiber = dom[key]; if (!domFiber) return null; const getCompFiber = (fiber) => { let parentFiber = fiber?.return; while (parentFiber && typeof parentFiber.type === "string") { parentFiber = parentFiber.return; } return parentFiber; }; let compFiber = getCompFiber(domFiber); for (let i = 0; i < traverseUp && compFiber; i++) { compFiber = getCompFiber(compFiber); } return compFiber?.stateNode || null; }; const createLogger = (namespace) => { const logPrefix = (prefix = "") => { const formatMessage = `%c[${namespace}]${prefix ? `%c[${prefix}]` : ""}`; let args = [console, `${formatMessage}%c`, "background-color: #4285f4; color: #fff; font-weight: bold"]; if (prefix) { args = args.concat("background-color: #4f505e; color: #fff; font-weight: bold"); } return args.concat("color: unset"); }; const bindLog = (logFn, prefix) => Function.prototype.bind.apply(logFn, logPrefix(prefix)); return { info: (prefix) => bindLog(console.info, prefix), warn: (prefix) => bindLog(console.warn, prefix), error: (prefix) => bindLog(console.error, prefix), log: (prefix) => bindLog(console.log, prefix), debug: (prefix) => bindLog(console.debug, prefix), }; }; const logging = createLogger("Top 60 Power Racers"); // Config storage const db = new Dexie("PowerRacers"); db.version(1).stores({ users: "id, &username, team, displayName, status, league", }); db.open().catch(function (e) { logging.error("Init")("Failed to open up the config database", e); }); //Race if (window.location.pathname === "/race" || window.location.pathname.startsWith("/race/")) { const raceContainer = document.getElementById("raceContainer"); const raceObj = raceContainer ? findReact(raceContainer) : null; if (!raceContainer || !raceObj) { logging.error("Init")("Could not find the race container or race object"); return; } if (!raceObj.props.user.loggedIn) { logging.error("Init")("Extractor is not available for Guest Racing"); return; } function createPowerRacersUI() { const powerRacersContainer = document.createElement("div"); powerRacersContainer.id = "power-racers-container"; powerRacersContainer.style.zIndex = "1000"; powerRacersContainer.style.backgroundColor = "rgba(0, 0, 0, 0.85)"; powerRacersContainer.style.color = "#fff"; powerRacersContainer.style.padding = "15px"; powerRacersContainer.style.borderRadius = "8px"; powerRacersContainer.style.fontFamily = "'Nunito', sans-serif"; powerRacersContainer.style.fontSize = "14px"; powerRacersContainer.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.1)"; powerRacersContainer.style.width = "100%"; powerRacersContainer.style.overflowY = "auto"; powerRacersContainer.style.maxHeight = "70vh"; powerRacersContainer.innerHTML = ` <style> #power-racers-container::-webkit-scrollbar { width: 8px; } #power-racers-container::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.5); } #power-racers-container::-webkit-scrollbar-thumb { background: #00008B; /* Dark blue */ border-radius: 4px; } </style> <h2 style="margin: 0 0 10px; font-size: 18px; border-bottom: 1px solid #ccc; padding-bottom: 5px;">Top 60 Power Racers</h2> <table id='power-racers-table' style="width: 100%; border-collapse: collapse; text-align: left;"> <thead> <tr style="border-bottom: 1px solid #444;"> <th style="padding: 5px; color: #f4b400;">Rank</th> <th style="padding: 5px; color: #e74c3c;">Name</th> <th style="padding: 5px; color: #0f9d58;">Races Completed</th> <th style="padding: 5px; color: #4285f4;">Team</th> </tr> </thead> <tbody id='power-racers-list' style="color: #ddd;"></tbody> </table> `; const targetElement = document.querySelector("#raceContainer"); if (targetElement) { targetElement.appendChild(powerRacersContainer); } else { document.body.appendChild(powerRacersContainer); } } function fetchPowerRacers() { const proxyUrl = 'https://api.allorigins.win/get?url='; const targetUrl = `https://www.ntcomps.com/leaderboards/power_racers?tp=60m`; fetch(`${proxyUrl}${encodeURIComponent(targetUrl)}`) .then(response => response.json()) .then(data => { const parser = new DOMParser(); const doc = parser.parseFromString(data.contents, "text/html"); const racers = Array.from(doc.querySelectorAll('table tr')).slice(1, 61); // Get top 60 racers const powerRacersList = document.getElementById('power-racers-list'); powerRacersList.innerHTML = ''; // Clear any existing content racers.forEach((racer, index) => { const cols = racer.querySelectorAll('td'); if (cols.length < 4) return; // Skip rows that don't have enough columns const racerName = cols[1]?.textContent.split('Go to')[0].trim() || 'N/A'; // Extract name and remove unwanted text const listItem = document.createElement("tr"); listItem.style.borderBottom = "1px solid #444"; listItem.style.fontWeight = "bold"; listItem.innerHTML = ` <td style="padding: 5px; color: #f4b400;">${index + 1}</td> <td style="padding: 5px; color: #e74c3c;">${racerName}</td> <td style="padding: 5px; color: #0f9d58;">${cols[3]?.textContent?.trim() || 'N/A'}</td> <td style="padding: 5px; color: #4285f4;">${cols[2]?.textContent?.trim() || 'N/A'}</td> `; powerRacersList.appendChild(listItem); }); }) .catch(error => { console.error('Error fetching power racers data:', error); }); } function initializePowerRacersUI() { const raceContainer = document.getElementById("raceContainer"); if (raceContainer) { createPowerRacersUI(); fetchPowerRacers(); const resultObserver = new MutationObserver(([mutation], observer) => { for (const node of mutation.addedNodes) { if (node.classList?.contains("race-results")) { observer.disconnect(); logging.info("Update")("Race Results received"); const powerRacersContainer = document.getElementById("power-racers-container"); const targetElement = document.querySelector("#raceContainer").parentElement; if (powerRacersContainer && targetElement) { targetElement.appendChild(powerRacersContainer); } break; } } }); resultObserver.observe(raceContainer, { childList: true, subtree: true }); } else { logging.error("Init")("Race container not found, retrying..."); setTimeout(initializePowerRacersUI, 1000); } } window.addEventListener("load", initializePowerRacersUI); }