您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows the racing skill of drivers in your race as long as they have this script installed as well
// ==UserScript== // @name Racing Skill Display // @namespace https://github.com/zim312/raceskill // @version 0.0.1 // @description Shows the racing skill of drivers in your race as long as they have this script installed as well // @originalauthor Sulsay [2173590] // @author Zim312 [1984387] // @match https://www.torn.com/loader.php?sid=racing* // @grant GM_xmlhttpRequest // ==/UserScript== const arsonBaseApiUrl = 'https://cs.etmc.org/torn/api/v1'; const racingSkillCacheByDriverId = new Map(); (async function () { const racingSkillElm = document.querySelector('.skill'); await saveRacingSkill(getUserIdFromCookie(), racingSkillElm.innerText); insertRacingSkillsIntoCurrentDriversList(); // On change race tab, (re-)insert the racing skills if applicable: new MutationObserver(insertRacingSkillsIntoCurrentDriversList).observe(document.getElementById('racingAdditionalContainer'), {childList: true}); })(); async function insertRacingSkillsIntoCurrentDriversList() { const driversList = document.getElementById('leaderBoard'); if (driversList === null) { return; } watchForDriversListContentChanges(driversList); const racingSkills = await getRacingSkillForDrivers(getDriverIds(driversList)); for (let driver of driversList.querySelectorAll('.driver-item')) { const driverId = getDriverId(driver); if (! racingSkills[driverId]) { continue; } const nameDiv = driver.querySelector('.name'); nameDiv.style.position = 'relative'; nameDiv.insertAdjacentHTML('beforeend', `<span style="position:absolute;right:5px">${racingSkills[driverId]}</span>`); } } function watchForDriversListContentChanges(driversList) { if (driversList.dataset.hasWatcher !== undefined) { return; } // The content of #leaderBoard is rebuilt periodically so watch for changes: new MutationObserver(insertRacingSkillsIntoCurrentDriversList).observe(driversList, {childList: true}); driversList.dataset.hasWatcher = 'true'; } function getDriverIds(driversList) { return Array.from(driversList.querySelectorAll('.driver-item')).map(driver => getDriverId(driver)); } function getDriverId(driverUl) { return +driverUl.closest('li').id.substr(4); } async function getRacingSkillForDrivers(driverIds) { const driverIdsToFetchSkillFor = driverIds.filter(driverId => ! racingSkillCacheByDriverId.has(driverId)); if (driverIdsToFetchSkillFor.length > 0) { const skills = await fetchRacingSkillForDrivers(driverIdsToFetchSkillFor); for (let [driverId, skill] of Object.entries(skills)) { racingSkillCacheByDriverId.set(+driverId, skill); } } const resultHash = {}; for (let driverId of driverIds) { resultHash[driverId] = racingSkillCacheByDriverId.get(driverId); } return resultHash; } function getUserIdFromCookie() { const userIdString = document.cookie.split(';') .map(entry => entry.trim()) .find(entry => entry.indexOf('uid=') === 0) .replace('uid=', ''); return parseInt(userIdString, 10); } function fetchRacingSkillForDrivers(driverIds) { return new Promise(resolve => { GM_xmlhttpRequest({ method: 'GET', url: `${arsonBaseApiUrl}/racing-skills?expect_strings&drivers=${driverIds.join(',')}`, onload: ({responseText}) => resolve(JSON.parse(responseText)), }); }); } function saveRacingSkill(userId, racingSkillString) { return new Promise(resolve => { GM_xmlhttpRequest({ method: 'POST', url: `${arsonBaseApiUrl}/players/${userId}/racing-skill`, data: JSON.stringify({racing_skill: racingSkillString}), headers: {'Content-Type': 'application/json'}, onload: resolve, }); }); }