raceData

Race data

此脚本不应直接安装,它是供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/514399/1476019/raceData.js

  1. // ==UserScript==
  2. // @name Session Info
  3. // @namespace https://greasyfork.org/users/1331131-tensorflow-dvorak
  4. // @version 2024-10-26
  5. // @description Race data
  6. // @match *://*.nitrotype.com/race
  7. // @match *://*.nitrotype.com/race/*
  8. // @require https://update.greasyfork.org/scripts/501960/1418069/findReact.js
  9. // @license MIT
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. let sessionStartTime;
  17. let raceTimes = [];
  18. let averageWPM = 0;
  19. let cardColor = localStorage.getItem("nt_cardColor") || "#1a1a2e";
  20. let buttonColor = localStorage.getItem("nt_buttonColor") || "#5a67d8";
  21.  
  22. function initializeSessionData() {
  23. const persistData = localStorage.getItem("persist:nt");
  24. if (persistData) {
  25. const parsedData = JSON.parse(JSON.parse(persistData).user);
  26. sessionStartTime =
  27. parseInt(localStorage.getItem("sessionStartTime")) || Date.now();
  28. raceTimes = JSON.parse(localStorage.getItem("raceTimes")) || [];
  29. averageWPM = parsedData.avgSpeed || 0;
  30.  
  31. if (!localStorage.getItem("sessionStartTime")) {
  32. localStorage.setItem("sessionStartTime", sessionStartTime);
  33. }
  34.  
  35. return parsedData.sessionRaces || 0;
  36. }
  37. return 0;
  38. }
  39.  
  40. let sessionRaces = initializeSessionData();
  41.  
  42. function createPostRaceInfo() {
  43. const dashElement = document.querySelector(".dash");
  44. const settingsElement = document.querySelector(
  45. ".nitro-monkey__settings-container"
  46. );
  47.  
  48. if (dashElement && settingsElement) {
  49. const postRaceContainer = document.createElement("div");
  50. postRaceContainer.classList.add("nitro-monkey__pr-container");
  51.  
  52. postRaceContainer.style.display = "flex";
  53. postRaceContainer.style.justifyContent = "center";
  54. postRaceContainer.style.alignItems = "center";
  55. postRaceContainer.style.padding = "0.5rem";
  56. postRaceContainer.style.backgroundColor = cardColor;
  57. postRaceContainer.style.borderRadius = "8px";
  58. postRaceContainer.style.boxShadow = "0 2px 8px rgba(0, 0, 0, 0.15)";
  59. postRaceContainer.style.width = "auto";
  60. postRaceContainer.style.height = "fit-content";
  61. postRaceContainer.style.color = "#e0e0e0";
  62. postRaceContainer.style.fontSize = "0.9rem";
  63. postRaceContainer.style.marginBottom = "1rem";
  64.  
  65. postRaceContainer.innerHTML = `
  66. <table style="width: auto; border-collapse: collapse; font-size: 0.9rem;">
  67. <tbody>
  68. <tr>
  69. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Session Races:</td>
  70. <td id="sessionValue" style="padding: 0.4rem; text-align: right;">${sessionRaces}</td>
  71. </tr>
  72. <tr>
  73. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Races/hr:</td>
  74. <td id="hourlyRaces" style="padding: 0.4rem; text-align: right;">${calculateRacesLastHour()}</td>
  75. </tr>
  76. <tr>
  77. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Est. Races/hr:</td>
  78. <td id="estimatedRaces" style="padding: 0.4rem; text-align: right;">${
  79. localStorage.getItem("estimatedRaces") || 0
  80. }</td>
  81. </tr>
  82. <tr>
  83. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Average WPM:</td>
  84. <td id="averageWPM" style="padding: 0.4rem; text-align: right;">${averageWPM}</td>
  85. </tr>
  86. <tr>
  87. <td colspan="2" style="text-align: center; padding: 0.5rem;">
  88. <button id="resetButton" style="padding: 0.3rem 0.8rem; font-size: 0.85rem; border: none; border-radius: 5px; background-color: ${buttonColor}; color: #1a1a2e; cursor: pointer;">Reset</button>
  89. </td>
  90. </tr>
  91. </tbody>
  92. </table>
  93. `;
  94.  
  95. settingsElement.insertBefore(
  96. postRaceContainer,
  97. settingsElement.firstChild
  98. );
  99.  
  100. document
  101. .getElementById("resetButton")
  102. .addEventListener("click", resetSessionData);
  103.  
  104. return true;
  105. }
  106. return false;
  107. }
  108.  
  109. const getTypedCharacterCount = () => {
  110. return document.querySelectorAll(".dash-letter.is-correct.is-typed")
  111. ?.length;
  112. };
  113.  
  114. const getTotalCharacters = () => {
  115. return document.querySelectorAll(".dash-letter").length - 1;
  116. };
  117.  
  118. function calculateRacesLastHour() {
  119. const oneHourAgo = Date.now() - 1000 * 60 * 60;
  120.  
  121. const recentRaces = raceTimes.filter(
  122. (raceTime) => raceTime.timestamp >= oneHourAgo
  123. );
  124.  
  125. return recentRaces.length;
  126. }
  127.  
  128. function calculateEstimatedRacesPerHour() {
  129. if (raceTimes.length === 0) return 0;
  130.  
  131. const avgRaceTimeInHours =
  132. raceTimes.reduce((a, b) => a + b.duration, 0) /
  133. raceTimes.length /
  134. (1000 * 60 * 60);
  135. const estimatedRacesPerHour = (1 / avgRaceTimeInHours).toFixed(1);
  136.  
  137. localStorage.setItem("estimatedRaces", estimatedRacesPerHour);
  138. return estimatedRacesPerHour;
  139. }
  140.  
  141. function resetSessionData() {
  142. sessionStartTime = Date.now();
  143. raceTimes = [];
  144.  
  145. document.getElementById("estimatedRaces").textContent = 0;
  146. document.getElementById("hourlyRaces").textContent = 0;
  147.  
  148. localStorage.setItem("sessionStartTime", sessionStartTime);
  149. localStorage.setItem("raceTimes", JSON.stringify(raceTimes));
  150. localStorage.setItem("estimatedRaces", 0);
  151.  
  152. console.log("Session data reset.");
  153. }
  154.  
  155. const updateSessionInfo = () => {
  156. const typedCharacters = getTypedCharacterCount();
  157. const totalCharactersInRace = getTotalCharacters();
  158.  
  159. if (
  160. typedCharacters === totalCharactersInRace &&
  161. totalCharactersInRace > 0
  162. ) {
  163. const currentTime = Date.now();
  164.  
  165. const persistData = localStorage.getItem("persist:nt");
  166. if (persistData) {
  167. const parsedPersistData = JSON.parse(persistData);
  168. const user = JSON.parse(parsedPersistData.user);
  169. sessionRaces = user.sessionRaces = (user.sessionRaces || 0) + 1;
  170. averageWPM = user.avgSpeed || 0;
  171. parsedPersistData.user = JSON.stringify(user);
  172. localStorage.setItem("persist:nt", JSON.stringify(parsedPersistData));
  173. }
  174.  
  175. const raceDuration =
  176. currentTime -
  177. (raceTimes.length > 0
  178. ? raceTimes[raceTimes.length - 1].timestamp
  179. : sessionStartTime);
  180. raceTimes.push({ timestamp: currentTime, duration: raceDuration });
  181. localStorage.setItem("raceTimes", JSON.stringify(raceTimes));
  182.  
  183. raceTimes = raceTimes.filter(
  184. (race) => race.timestamp >= Date.now() - 1000 * 60 * 60
  185. );
  186.  
  187. document.getElementById("sessionValue").textContent = sessionRaces;
  188. document.getElementById("averageWPM").textContent = averageWPM;
  189.  
  190. document.getElementById("hourlyRaces").textContent =
  191. calculateRacesLastHour();
  192.  
  193. const estimatedRaces = calculateEstimatedRacesPerHour();
  194. document.getElementById("estimatedRaces").textContent = estimatedRaces;
  195.  
  196. clearInterval(updateUI);
  197. console.log(
  198. "Race Complete. Session Races:",
  199. sessionRaces,
  200. "Average WPM:",
  201. averageWPM
  202. );
  203. }
  204. };
  205.  
  206. const interval = setInterval(() => {
  207. if (createPostRaceInfo()) {
  208. clearInterval(interval);
  209. }
  210. }, 500);
  211.  
  212. const updateUI = setInterval(() => {
  213. updateSessionInfo();
  214. }, 500);
  215. })();