Time Control

Script allowing you to control time.

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

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