Time Control

Script allowing you to control time.

当前为 2024-03-30 提交的版本,查看 最新版本

  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.2.1
  7. // @author lucaszheng
  8. // @license MIT
  9. //
  10. // @match *://*/*
  11. // @grant unsafeWindow
  12. // @inject-into page
  13. // @run-at document-start
  14. // ==/UserScript==
  15.  
  16. (window => {
  17. "use strict";
  18. let scale = 1, pristine = true;
  19. /** @type {null | number} */
  20. let timeJump = null;
  21.  
  22. let timeSync = false;
  23. let debug = false;
  24.  
  25. const {
  26. Reflect: {
  27. apply, construct,
  28. setPrototypeOf
  29. },
  30. Object: {
  31. defineProperty,
  32. freeze
  33. },
  34. Number: {
  35. isFinite
  36. },
  37. console: {
  38. trace: log
  39. }
  40. } = window;
  41.  
  42. function update() {
  43. for (let idx = 0; idx < updaters.length; idx++) {
  44. updaters[idx]();
  45. }
  46. }
  47.  
  48. const time = freeze({
  49. /**
  50. * @param {number} newTime
  51. */
  52. jump(newTime) {
  53. if (newTime == null) return;
  54. pristine = false;
  55. timeJump = +newTime;
  56. update();
  57. timeJump = null;
  58. },
  59.  
  60. sync(resetScale = true) {
  61. if (pristine) return;
  62. if (resetScale) scale = 1;
  63. timeSync = true;
  64. update();
  65. timeSync = false;
  66. pristine = scale === 1;
  67. },
  68.  
  69. get debug() { return debug; },
  70. set debug(value) { debug = !!value; },
  71.  
  72. get now() { return apply(date.now, DateConstructor, []); },
  73. set now(value) { time.jump(value); },
  74.  
  75. get pristine() { return pristine; },
  76. set pristine(value) { if (value) time.sync(); },
  77.  
  78. get real() { return apply(date.realTime, DateConstructor, []); },
  79.  
  80. get scale() { return scale; },
  81. set scale(value) { pristine = false; update(); scale = +value; }
  82. });
  83. defineProperty(window, 'time', {
  84. value: time,
  85. writable: true,
  86. enumerable: false,
  87. configurable: true
  88. });
  89.  
  90. /** @type {(() => void)[]} */
  91. const updaters = [];
  92.  
  93. /**
  94. * @param {() => number} func
  95. * @param {any} self
  96. */
  97. function wrap_now(func, self, offset = 0) {
  98. let baseTime = 0;
  99. let contTime = baseTime;
  100.  
  101. /** @type {ProxyHandler<typeof func>} */
  102. const handler = {
  103. apply(target, self, args) {
  104. if (debug) log('apply(%o, %o, %o)', target, self, args);
  105. let time = apply(target, self, args);
  106. if (pristine || !isFinite(time)) return time;
  107. return ((time - baseTime) * scale) + contTime;
  108. }
  109. };
  110. setPrototypeOf(handler, null);
  111.  
  112. updaters[updaters.length] =
  113. function update() {
  114. contTime = timeJump == null ? handler.apply?.(func, self, []) : timeJump + offset;
  115. baseTime = apply(func, self, []);
  116. if (timeSync) contTime = baseTime;
  117. };
  118.  
  119. return new Proxy(func, handler);
  120. }
  121.  
  122. window.Performance.prototype.now = wrap_now(
  123. window.Performance.prototype.now,
  124. window.performance,
  125. window.performance.now() - window.Date.now()
  126. );
  127.  
  128. const DateConstructor = window.Date;
  129. /** @type {{ realTime: typeof Date.now, now: typeof Date.now, toString: typeof Date.prototype.toString, handler: ProxyHandler<DateConstructor> }} */
  130. const date = {
  131. realTime: window.Date.now,
  132. now: wrap_now(window.Date.now, window.Date),
  133. toString: DateConstructor.prototype.toString,
  134. handler: {
  135. apply(target, self, args) {
  136. if (debug) log('apply(%o, %o, %o)', target, self, args);
  137. if (!pristine) {
  138. args.length = 1;
  139. args[0] = apply(date.now, DateConstructor, []);
  140. } else return DateConstructor();
  141. return apply(date.toString, construct(DateConstructor, args), []);
  142. },
  143. construct(target, args, newTarget) {
  144. if (debug) log('construct(%o, %o, %o)', target, args, newTarget);
  145. if (!pristine && args.length < 1) {
  146. args[0] = apply(date.now, DateConstructor, []);
  147. }
  148. return construct(DateConstructor, args, newTarget);
  149. }
  150. }
  151. };
  152. setPrototypeOf(date, null);
  153. setPrototypeOf(date.handler, null);
  154. DateConstructor.now = date.now;
  155.  
  156. window.Date = new Proxy(DateConstructor, date.handler);
  157. window.Date.prototype.constructor = window.Date;
  158.  
  159. function noop() { }
  160.  
  161. /**
  162. * @param {(handler: TimerHandler, timeout?: number | undefined, ...args: any[]) => number} func
  163. */
  164. function wrap_timer(func) {
  165. /** @type {ProxyHandler<typeof func>} */
  166. const handler = {
  167. apply(target, self, args) {
  168. if (debug) log('apply(%o, %o, %o)', target, self, args);
  169. if (!pristine && args.length > 1) {
  170. args[1] = +args[1];
  171. if (args[1] && scale === 0)
  172. args[0] = noop;
  173. else if (args[1] && isFinite(args[1]))
  174. args[1] /= scale;
  175. }
  176. return apply(target, self, args);
  177. }
  178. };
  179. setPrototypeOf(handler, null);
  180. return new Proxy(func, handler);
  181. }
  182.  
  183. window.setTimeout = wrap_timer(window.setTimeout);
  184. window.setInterval = wrap_timer(window.setInterval);
  185. })(
  186. /** @type {Window & typeof globalThis} */
  187. (typeof unsafeWindow === 'object' ? unsafeWindow : window)
  188. );