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.6
  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. storage: {
  75. save(saveTime = true, saveScale = true) {
  76. if (saveTime) {
  77. if (pristine) time.storage.reset(true, false);
  78. else time.storage.now = time.now;
  79. }
  80. if (saveScale) {
  81. if (scale === 1) time.storage.reset(false, true);
  82. else time.storage.scale = time.scale;
  83. }
  84. },
  85.  
  86. load(loadTime = true, loadScale = true) {
  87. if (time.storage.pristine) return time.sync();
  88. if (loadTime) {
  89. let baseTime = GM_getValue('baseTime', null);
  90. let contTime = GM_getValue('contTime', null);
  91. if (baseTime != null && contTime != null)
  92. time.jump((time.real - baseTime) + contTime);
  93. }
  94. if (loadScale) {
  95. time.scale = time.storage.scale;
  96. }
  97. },
  98.  
  99. reset(resetTime = true, resetScale = true) {
  100. if (resetTime) {
  101. GM_deleteValue('baseTime');
  102. GM_deleteValue('contTime');
  103. }
  104. if (resetScale) GM_deleteValue('scale');
  105. },
  106.  
  107. get now() {
  108. let baseTime = GM_getValue('baseTime', null);
  109. let contTime = GM_getValue('contTime', null);
  110. if (baseTime != null && contTime != null)
  111. return (time.real - baseTime) + contTime;
  112. return time.real;
  113. },
  114. set now(value) {
  115. GM_setValue('baseTime', time.real);
  116. GM_setValue('contTime', +value);
  117. },
  118. get pristine() {
  119. let baseTime = GM_getValue('baseTime', null);
  120. let contTime = GM_getValue('contTime', null);
  121. let scale = GM_getValue('scale', null);
  122. return (baseTime == null || contTime == null) && scale == null
  123. },
  124. set pristine(value) {
  125. if (!value) return;
  126. time.storage.reset();
  127. },
  128. get scale() {
  129. let scale = GM_getValue('scale', null);
  130. if (scale != null) return scale;
  131. return 1;
  132. },
  133. set scale(value) {
  134. if (value === time.storage.scale) return;
  135. GM_setValue('scale', +value);
  136. }
  137. },
  138.  
  139. get debug() { return debug; },
  140. set debug(value) { debug = !!value; },
  141.  
  142. get now() { return apply(date.now, DateConstructor, []); },
  143. set now(value) { time.jump(value); },
  144.  
  145. get pristine() { return pristine; },
  146. set pristine(value) { if (value) time.sync(); },
  147.  
  148. get real() { return apply(date.realTime, DateConstructor, []); },
  149.  
  150. get scale() { return scale; },
  151. set scale(value) {
  152. value = +value;
  153. if (value === scale) return;
  154. pristine = false; update(); scale = value;
  155. }
  156. };
  157.  
  158. freeze(time.storage);
  159. defineProperty(window, 'time', {
  160. value: freeze(time),
  161. writable: true,
  162. enumerable: false,
  163. configurable: true
  164. });
  165.  
  166. /** @type {(() => void)[]} */
  167. const updaters = [];
  168.  
  169. /**
  170. * @param {() => number} func
  171. * @param {any} self
  172. */
  173. function wrap_now(func, self, offset = 0) {
  174. let baseTime = 0;
  175. let contTime = baseTime;
  176.  
  177. /** @type {ProxyHandler<typeof func>} */
  178. const handler = {
  179. apply(target, self, args) {
  180. if (debug) log('apply(%o, %o, %o)', target, self, args);
  181. let time = apply(target, self, args);
  182. if (pristine || !isFinite(time)) return time;
  183. return ((time - baseTime) * scale) + contTime;
  184. }
  185. };
  186. setPrototypeOf(handler, null);
  187.  
  188. updaters[updaters.length] =
  189. function update() {
  190. contTime = timeJump == null ? handler.apply?.(func, self, []) : timeJump + offset;
  191. baseTime = apply(func, self, []);
  192. if (timeSync) contTime = baseTime;
  193. };
  194.  
  195. return new Proxy(func, handler);
  196. }
  197.  
  198. window.Performance.prototype.now = wrap_now(
  199. window.Performance.prototype.now,
  200. window.performance,
  201. window.performance.now() - window.Date.now()
  202. );
  203.  
  204. const DateConstructor = window.Date;
  205. /** @type {{ realTime: typeof Date.now, now: typeof Date.now, toString: typeof Date.prototype.toString, handler: ProxyHandler<DateConstructor> }} */
  206. const date = {
  207. realTime: window.Date.now,
  208. now: wrap_now(window.Date.now, window.Date),
  209. toString: DateConstructor.prototype.toString,
  210. handler: {
  211. apply(target, self, args) {
  212. if (debug) log('apply(%o, %o, %o)', target, self, args);
  213. if (!pristine) {
  214. args.length = 1;
  215. args[0] = apply(date.now, DateConstructor, []);
  216. } else return DateConstructor();
  217. return apply(date.toString, construct(DateConstructor, args), []);
  218. },
  219. construct(target, args, newTarget) {
  220. if (debug) log('construct(%o, %o, %o)', target, args, newTarget);
  221. if (!pristine && args.length < 1) {
  222. args[0] = apply(date.now, DateConstructor, []);
  223. }
  224. return construct(DateConstructor, args, newTarget);
  225. }
  226. }
  227. };
  228. setPrototypeOf(date, null);
  229. setPrototypeOf(date.handler, null);
  230. DateConstructor.now = date.now;
  231.  
  232. window.Date = new Proxy(DateConstructor, date.handler);
  233. window.Date.prototype.constructor = window.Date;
  234.  
  235. function noop() { }
  236.  
  237. /**
  238. * @param {(handler: TimerHandler, timeout?: number | undefined, ...args: any[]) => number} func
  239. */
  240. function wrap_timer(func) {
  241. /** @type {ProxyHandler<typeof func>} */
  242. const handler = {
  243. apply(target, self, args) {
  244. if (debug) log('apply(%o, %o, %o)', target, self, args);
  245. if (!pristine && args.length > 1) {
  246. args[1] = +args[1];
  247. if (args[1] && scale === 0)
  248. args[0] = noop;
  249. else if (args[1] && isFinite(args[1]))
  250. args[1] /= scale;
  251. }
  252. return apply(target, self, args);
  253. }
  254. };
  255. setPrototypeOf(handler, null);
  256. return new Proxy(func, handler);
  257. }
  258.  
  259. window.setTimeout = wrap_timer(window.setTimeout);
  260. window.setInterval = wrap_timer(window.setInterval);
  261.  
  262. time.load();
  263. })(
  264. /** @type {typeof window} */
  265. (typeof unsafeWindow === 'object' ? unsafeWindow : window)
  266. );