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