Time Control

Script allowing you to control time.

当前为 2024-04-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Time Control
  3. // @description Script allowing you to control time.
  4. // @icon https://parsefiles.back4app.com/JPaQcFfEEQ1ePBxbf6wvzkPMEqKYHhPYv8boI1Rc/ce262758ff44d053136358dcd892979d_low_res_Time_Machine.png
  5. // @namespace mailto:lucaszheng2011@outlook.com
  6. // @version 1.2.5.2
  7. // @author lucaszheng
  8. // @license MIT
  9. //
  10. // @match *://*/*
  11. // @grant unsafeWindow
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_deleteValue
  15.  
  16. // @inject-into page
  17. // @run-at document-start
  18. // ==/UserScript==
  19. /*globals unsafeWindow,GM_setValue,GM_getValue,GM_deleteValue*/
  20.  
  21. (function (window) {
  22. 'use strict';
  23. let scale = 1, pristine = true;
  24. /** @type {null | number} */
  25. let timeJump = null;
  26.  
  27. let timeReset = false;
  28. let debug = false;
  29.  
  30. const {
  31. Reflect: {
  32. apply, construct,
  33. setPrototypeOf
  34. },
  35. Object: {
  36. defineProperty,
  37. freeze
  38. },
  39. Number: {
  40. isFinite
  41. },
  42. Symbol: {
  43. toPrimitive,
  44. toStringTag
  45. },
  46. console: {
  47. trace: log
  48. }
  49. } = window;
  50.  
  51. function update() {
  52. for (let idx = 0; idx < updaters.length; idx++) {
  53. updaters[idx]();
  54. }
  55. }
  56.  
  57. /**
  58. * @param {'string' | 'number' | 'default'} type
  59. */
  60. function timeToPrimitive(type) {
  61. switch (type) {
  62. case 'string': return this.toString();
  63. default: return this.now;
  64. }
  65. }
  66.  
  67. function timeToString() {
  68. return apply(date.toString, construct(DateConstructor, [this.now]), []);
  69. }
  70.  
  71. const time = {
  72. [toStringTag]: 'time',
  73. [toPrimitive]: timeToPrimitive,
  74. toString: timeToString,
  75. /**
  76. * @param {number} newTime
  77. */
  78. jump(newTime) {
  79. if (newTime == null) return;
  80. pristine = false;
  81. timeJump = +newTime;
  82. update();
  83. timeJump = null;
  84. },
  85.  
  86. reset(resetTime = true, resetScale = true, resetDebug = true) {
  87. if (resetDebug) debug = false;
  88. if (pristine) return;
  89.  
  90. if (resetScale) scale = 1;
  91.  
  92. if (!resetTime) return;
  93. timeReset = true;
  94. update();
  95. timeReset = false;
  96. pristine = scale === 1;
  97. },
  98.  
  99. storage: {
  100. [toStringTag]: 'storage',
  101. [toPrimitive]: timeToPrimitive,
  102. toString: timeToString,
  103.  
  104. /**
  105. * @param {number} newTime
  106. */
  107. jump(newTime) {
  108. GM_setValue('baseTime', time.real);
  109. GM_setValue('contTime', +newTime);
  110. },
  111.  
  112. save(saveTime = true, saveScale = true, saveDebug = true) {
  113. if (saveDebug) {
  114. if (debug === false) time.storage.reset(false, false, true);
  115. else time.storage.debug = debug;
  116. }
  117. if (saveTime) {
  118. if (pristine) time.storage.reset(true, false, false);
  119. else time.storage.now = time.now;
  120. }
  121. if (saveScale) {
  122. if (scale === 1) time.storage.reset(false, true, false);
  123. else time.storage.scale = scale;
  124. }
  125. },
  126.  
  127. load(loadTime = true, loadScale = true, loadDebug = true) {
  128. if (loadDebug) time.debug = time.storage.debug;
  129. if (time.storage.pristine) return time.reset(true, true, false);
  130.  
  131. if (loadTime) {
  132. let baseTime = GM_getValue('baseTime', null);
  133. let contTime = GM_getValue('contTime', null);
  134. if (baseTime != null && contTime != null)
  135. time.jump((time.real - baseTime) + contTime);
  136. }
  137. if (loadScale) time.scale = time.storage.scale;
  138. },
  139.  
  140. reset(resetTime = true, resetScale = true, resetDebug = true) {
  141. if (resetTime) {
  142. GM_deleteValue('baseTime');
  143. GM_deleteValue('contTime');
  144. }
  145. if (resetScale) GM_deleteValue('scale');
  146. if (resetDebug) GM_deleteValue('debug');
  147. },
  148.  
  149. get debug() { return GM_getValue('debug', false); },
  150. set debug(value) { GM_setValue('debug', !!value); },
  151.  
  152. get now() {
  153. let baseTime = GM_getValue('baseTime', null);
  154. let contTime = GM_getValue('contTime', null);
  155. if (baseTime != null && contTime != null)
  156. return (time.real - baseTime) + contTime;
  157. return time.real;
  158. },
  159. set now(value) { time.storage.jump(value); },
  160.  
  161. get pristine() {
  162. let baseTime = GM_getValue('baseTime', null);
  163. let contTime = GM_getValue('contTime', null);
  164. let scale = GM_getValue('scale', null);
  165. return (baseTime == null || contTime == null) && scale == null;
  166. },
  167. set pristine(value) {
  168. if (!value) return;
  169. time.storage.reset(true, true, false);
  170. },
  171.  
  172. get real() { return apply(date.realTime, DateConstructor, []); },
  173.  
  174. get scale() {
  175. let scale = GM_getValue('scale', null);
  176. if (scale != null) return scale;
  177. return 1;
  178. },
  179. set scale(value) {
  180. if (value === time.storage.scale) return;
  181. GM_setValue('scale', +value);
  182. }
  183. },
  184.  
  185. get debug() { return debug; },
  186. set debug(value) { debug = !!value; },
  187.  
  188. get now() { return apply(date.now, DateConstructor, []); },
  189. set now(value) { time.jump(value); },
  190.  
  191. get pristine() { return pristine; },
  192. set pristine(value) { if (value) time.reset(); },
  193.  
  194. get real() { return apply(date.realTime, DateConstructor, []); },
  195.  
  196. get scale() { return scale; },
  197. set scale(value) {
  198. value = +value;
  199. if (value === scale) return;
  200. pristine = false; update(); scale = value;
  201. }
  202. };
  203.  
  204. freeze(time.storage);
  205. defineProperty(window, 'time', {
  206. value: freeze(time),
  207. writable: true,
  208. enumerable: false,
  209. configurable: true
  210. });
  211.  
  212. /** @type {(() => void)[]} */
  213. const updaters = [];
  214.  
  215. /**
  216. * @param {() => number} func
  217. * @param {any} self
  218. */
  219. function wrap_now(func, self, offset = 0) {
  220. let baseTime = 0;
  221. let contTime = baseTime;
  222.  
  223. /** @type {ProxyHandler<typeof func>} */
  224. const handler = {
  225. apply(target, self, args) {
  226. if (debug) log('apply(%o, %o, %o)', target, self, args);
  227. let time = apply(target, self, args);
  228. if (pristine || !isFinite(time)) return time;
  229. return ((time - baseTime) * scale) + contTime;
  230. }
  231. };
  232. setPrototypeOf(handler, null);
  233.  
  234. updaters[updaters.length] =
  235. function update() {
  236. if (!handler.apply) return;
  237. contTime = timeJump == null ? handler.apply(func, self, []) : timeJump + offset;
  238. baseTime = apply(func, self, []);
  239. if (timeReset) contTime = baseTime;
  240. };
  241.  
  242. return new Proxy(func, handler);
  243. }
  244.  
  245. window.Performance.prototype.now = wrap_now(
  246. window.Performance.prototype.now,
  247. window.performance,
  248. window.performance.now() - window.Date.now()
  249. );
  250.  
  251. const DateConstructor = window.Date;
  252. /** @type {{ realTime: typeof Date.now, now: typeof Date.now, toString: typeof Date.prototype.toString, handler: ProxyHandler<DateConstructor> }} */
  253. const date = {
  254. realTime: window.Date.now,
  255. now: wrap_now(window.Date.now, window.Date),
  256. toString: DateConstructor.prototype.toString,
  257. handler: {
  258. apply(target, self, args) {
  259. if (debug) log('apply(%o, %o, %o)', target, self, args);
  260. if (pristine) return DateConstructor();
  261. return time.toString();
  262. },
  263. construct(target, args, newTarget) {
  264. if (debug) log('construct(%o, %o, %o)', target, args, newTarget);
  265. if (!pristine && args.length < 1) {
  266. args[0] = time.now;
  267. }
  268. return construct(DateConstructor, args, newTarget);
  269. }
  270. }
  271. };
  272. setPrototypeOf(date, null);
  273. setPrototypeOf(date.handler, null);
  274. DateConstructor.now = date.now;
  275.  
  276. window.Date = new Proxy(DateConstructor, date.handler);
  277. window.Date.prototype.constructor = window.Date;
  278.  
  279. function noop() { }
  280.  
  281. /**
  282. * @param {(handler: TimerHandler, timeout?: number | undefined, ...args: any[]) => number} func
  283. */
  284. function wrap_timer(func) {
  285. /** @type {ProxyHandler<typeof func>} */
  286. const handler = {
  287. apply(target, self, args) {
  288. if (debug) log('apply(%o, %o, %o)', target, self, args);
  289. if (!pristine && args.length > 1) {
  290. args[1] = +args[1];
  291. if (args[1] && scale === 0)
  292. args[0] = noop;
  293. else if (args[1] && isFinite(args[1]))
  294. args[1] /= scale;
  295. }
  296. return apply(target, self, args);
  297. }
  298. };
  299. setPrototypeOf(handler, null);
  300. return new Proxy(func, handler);
  301. }
  302.  
  303. window.setTimeout = wrap_timer(window.setTimeout);
  304. window.setInterval = wrap_timer(window.setInterval);
  305.  
  306. time.storage.load();
  307. })(/** @type {typeof window} */(unsafeWindow));