TimerHooker

Control page timer speed | Speed up to skip page timing ads | Video fast forward (slow play) | Skip ads | Support almost all web pages.

当前为 2022-01-21 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name TimerHooker
  3. // @name:en TimerHooker
  4. // @namespace https://
  5. // @version 1.0.62
  6. // @description Control page timer speed | Speed up to skip page timing ads | Video fast forward (slow play) | Skip ads | Support almost all web pages.
  7. // @description:en it can hook the timer speed to change.
  8. // @include *
  9. // @author Tiger 27
  10. // @match http://*/*
  11. // @run-at document-start
  12. // @grant none
  13. // @license GPL-3.0-or-later
  14. // ==/UserScript==
  15. /**
  16. * ---------------------------
  17. * Time: 2022/1/20 16:11.
  18. * Author: Tiger 27
  19. * View: http://
  20. * ---------------------------
  21. */
  22.  
  23. /**
  24. * 1. hook Object.defineProperty | Object.defineProperties
  25. * 2. set configurable: true
  26. * 3. delete property
  27. * 4. can set property for onxx event method
  28. */
  29.  
  30. window.isDOMLoaded = false;
  31. window.isDOMRendered = false;
  32.  
  33. document.addEventListener('readystatechange', function () {
  34. if (document.readyState === "interactive" || document.readyState === "complete") {
  35. window.isDOMLoaded = true;
  36. }
  37. });
  38.  
  39. ~function (global) {
  40.  
  41. var workerURLs = [];
  42. var extraElements = [];
  43. var suppressEvents = {};
  44.  
  45. var helper = function (eHookContext, timerContext, util) {
  46. return {
  47. applyUI: function () {
  48. var style = '._th-container ._th-item{margin-bottom:3px;position:relative;width:0;height:0;cursor:pointer;opacity:.3;background-color:aquamarine;border-radius:100%;text-align:center;line-height:30px;-webkit-transition:all .35s;-o-transition:all .35s;transition:all .35s;right:30px}._th-container ._th-item,._th-container ._th-click-hover,._th_cover-all-show-times ._th_times{-webkit-box-shadow:-3px 4px 12px -5px black;box-shadow:-3px 4px 12px -5px black}._th-container:hover ._th-item._item-x2{margin-left:18px;width:40px;height:40px;line-height:40px}._th-container:hover ._th-item._item-x-2{margin-left:17px;width:38px;height:38px;line-height:38px}._th-container:hover ._th-item._item-xx2{width:36px;height:36px;margin-left:16px;line-height:36px}._th-container:hover ._th-item._item-xx-2{width:32px;height:32px;line-height:32px;margin-left:14px}._th-container:hover ._th-item._item-reset{width:30px;line-height:30px;height:30px;margin-left:10px}._th-click-hover{position:relative;-webkit-transition:all .5s;-o-transition:all .5s;transition:all .5s;height:45px;width:45px;cursor:pointer;opacity:.3;border-radius:100%;background-color:aquamarine;text-align:center;line-height:45px;right:0}._th-container:hover{left:-5px}._th-container{font-size:12px;-webkit-transition:all .5s;-o-transition:all .5s;transition:all .5s;left:-35px;top:20%;position:fixed;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:100000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}._th-container ._th-item:hover{opacity:.8;background-color:#5fb492;color:aliceblue}._th-container ._th-item:active{opacity:.9;background-color:#1b3a26;color:aliceblue}._th-container:hover ._th-click-hover{opacity:.8}._th-container:hover ._th-item{opacity:.6;right:0}._th-container ._th-click-hover:hover{opacity:.8;background-color:#5fb492;color:aliceblue}._th_cover-all-show-times{position:fixed;top:0;right:0;width:100%;height:100%;z-index:99999;opacity:1;font-weight:900;font-size:30px;color:#4f4f4f;background-color:rgba(0,0,0,0.1)}._th_cover-all-show-times._th_hidden{z-index:-99999;opacity:0;-webkit-transition:1s all;-o-transition:1s all;transition:1s all}._th_cover-all-show-times ._th_times{width:300px;height:300px;border-radius:50%;background-color:rgba(127,255,212,0.51);text-align:center;line-height:300px;position:absolute;top:50%;right:50%;margin-top:-150px;margin-right:-150px}';
  49.  
  50. var displayNum = (1 / timerContext._percentage).toFixed(2);
  51.  
  52. // Add a semicircle on the left side of the page for easy modification
  53. var html = '<div class="_th-container">\n' +
  54. ' <div class="_th-click-hover _item-input">\n' +
  55. ' x' + displayNum + '\n' +
  56. ' </div>\n' +
  57. ' <div class="_th-item _item-x2">&gt;</div>\n' +
  58. ' <div class="_th-item _item-x-2">&lt;</div>\n' +
  59. ' <div class="_th-item _item-xx2">&gt;&gt;</div>\n' +
  60. ' <div class="_th-item _item-xx-2">&lt;&lt;</div>\n' +
  61. ' <div class="_th-item _item-reset">O</div>\n' +
  62. '</div>\n' +
  63. '<div class="_th_cover-all-show-times _th_hidden">\n' +
  64. ' <div class="_th_times">x' + displayNum + '</div>\n' +
  65. '</div>' +
  66. '';
  67. var stylenode = document.createElement('style');
  68. stylenode.setAttribute("type", "text/css");
  69. if (stylenode.styleSheet) {// IE
  70. stylenode.styleSheet.cssText = style;
  71. } else {// w3c
  72. var cssText = document.createTextNode(style);
  73. stylenode.appendChild(cssText);
  74. }
  75. var node = document.createElement('div');
  76. node.innerHTML = html;
  77.  
  78. var clickMapper = {
  79. '_item-input': function () {
  80. changeTime();
  81. },
  82. '_item-x2': function () {
  83. changeTime(2, 0, true);
  84. },
  85. '_item-x-2': function () {
  86. changeTime(-2, 0, true);
  87. },
  88. '_item-xx2': function () {
  89. changeTime(0, 2);
  90. },
  91. '_item-xx-2': function () {
  92. changeTime(0, -2);
  93. },
  94. '_item-reset': function () {
  95. changeTime(0, 0, false, true);
  96. }
  97. };
  98.  
  99. Object.keys(clickMapper).forEach(function (className) {
  100. var exec = clickMapper[className];
  101. var targetEle = node.getElementsByClassName(className)[0];
  102. if (targetEle) {
  103. targetEle.onclick = exec;
  104. }
  105. });
  106.  
  107. if (!global.isDOMLoaded) {
  108. document.addEventListener('readystatechange', function () {
  109. if ((document.readyState === "interactive" || document.readyState === "complete") && !global.isDOMRendered) {
  110. document.head.appendChild(stylenode);
  111. document.body.appendChild(node);
  112. global.isDOMRendered = true;
  113. console.log('Time Hooker Works!');
  114. }
  115. });
  116. } else {
  117. document.head.appendChild(stylenode);
  118. document.body.appendChild(node);
  119. global.isDOMRendered = true;
  120. console.log('Time Hooker Works!');
  121. }
  122. },
  123. applyGlobalAction: function (timer) {
  124. // Method of clicking the semicircle button on the interface
  125. timer.changeTime = function (anum, cnum, isa, isr) {
  126. if (isr) {
  127. global.timer.change(1);
  128. return;
  129. }
  130. if (!global.timer) {
  131. return;
  132. }
  133. var result;
  134. if (!anum && !cnum) {
  135. var t = prompt("Enter the desired change rate of the timer change(current:" + 1 / timerContext._percentage + ")");
  136. if (t == null) {
  137. return;
  138. }
  139. if (isNaN(parseFloat(t))) {
  140. alert("Please enter the correct number");
  141. timer.changeTime();
  142. return;
  143. }
  144. if (parseFloat(t) <= 0) {
  145. alert("The magnification cannot be less than or equal to 0");
  146. timer.changeTime();
  147. return;
  148. }
  149. result = 1 / parseFloat(t);
  150. } else {
  151. if (isa && anum) {
  152. if (1 / timerContext._percentage <= 1 && anum < 0) {
  153. return;
  154. }
  155. result = 1 / (1 / timerContext._percentage + anum);
  156. } else {
  157. if (cnum <= 0) {
  158. cnum = 1 / -cnum
  159. }
  160. result = 1 / ((1 / timerContext._percentage) * cnum);
  161. }
  162. }
  163. timer.change(result);
  164. };
  165. global.changeTime = timer.changeTime;
  166. },
  167. applyHooking: function () {
  168. var _this = this;
  169. // Hijack the loop timer
  170. eHookContext.hookReplace(window, 'setInterval', function (setInterval) {
  171. return _this.getHookedTimerFunction('interval', setInterval);
  172. });
  173. // Hijack a single timer
  174. eHookContext.hookReplace(window, 'setTimeout', function (setTimeout) {
  175. return _this.getHookedTimerFunction('timeout', setTimeout)
  176. });
  177. // Hijack the clear method of the loop timer
  178. eHookContext.hookBefore(window, 'clearInterval', function (method, args) {
  179. _this.redirectNewestId(args);
  180. });
  181. // Hijack the clear method of the loop timer
  182. eHookContext.hookBefore(window, 'clearTimeout', function (method, args) {
  183. _this.redirectNewestId(args);
  184. });
  185. var newFunc = this.getHookedDateConstructor();
  186. eHookContext.hookClass(window, 'Date', newFunc, '_innerDate', ['now']);
  187. Date.now = function () {
  188. return new Date().getTime();
  189. };
  190. eHookContext.hookedToString(timerContext._Date.now, Date.now);
  191. var objToString = Object.prototype.toString;
  192.  
  193. Object.prototype.toString = function toString() {
  194. 'use strict';
  195. if (this instanceof timerContext._mDate) {
  196. return '[object Date]';
  197. } else {
  198. return objToString.call(this);
  199. }
  200. };
  201.  
  202. eHookContext.hookedToString(objToString, Object.prototype.toString);
  203. eHookContext.hookedToString(timerContext._setInterval, setInterval);
  204. eHookContext.hookedToString(timerContext._setTimeout, setTimeout);
  205. eHookContext.hookedToString(timerContext._clearInterval, clearInterval);
  206. timerContext._mDate = window.Date;
  207. this.hookShadowRoot();
  208. },
  209. getHookedDateConstructor: function () {
  210. return function () {
  211. if (arguments.length === 1) {
  212. Object.defineProperty(this, '_innerDate', {
  213. configurable: false,
  214. enumerable: false,
  215. value: new timerContext._Date(arguments[0]),
  216. writable: false
  217. });
  218. return;
  219. } else if (arguments.length > 1) {
  220. var definedValue;
  221. switch (arguments.length) {
  222. case 2:
  223. definedValue = new timerContext._Date(
  224. arguments[0],
  225. arguments[1]
  226. );
  227. break;
  228. case 3:
  229. definedValue = new timerContext._Date(
  230. arguments[0],
  231. arguments[1],
  232. arguments[2],
  233. );
  234. break;
  235. case 4:
  236. definedValue = new timerContext._Date(
  237. arguments[0],
  238. arguments[1],
  239. arguments[2],
  240. arguments[3],
  241. );
  242. break;
  243. case 5:
  244. definedValue = new timerContext._Date(
  245. arguments[0],
  246. arguments[1],
  247. arguments[2],
  248. arguments[3],
  249. arguments[4]
  250. );
  251. break;
  252. case 6:
  253. definedValue = new timerContext._Date(
  254. arguments[0],
  255. arguments[1],
  256. arguments[2],
  257. arguments[3],
  258. arguments[4],
  259. arguments[5]
  260. );
  261. break;
  262. default:
  263. case 7:
  264. definedValue = new timerContext._Date(
  265. arguments[0],
  266. arguments[1],
  267. arguments[2],
  268. arguments[3],
  269. arguments[4],
  270. arguments[5],
  271. arguments[6]
  272. );
  273. break;
  274. }
  275.  
  276. Object.defineProperty(this, '_innerDate', {
  277. configurable: false,
  278. enumerable: false,
  279. value: definedValue,
  280. writable: false
  281. });
  282. return;
  283. }
  284. var now = timerContext._Date.now();
  285. var passTime = now - timerContext.__lastDatetime;
  286. var hookPassTime = passTime * (1 / timerContext._percentage);
  287. // console.log(__this.__lastDatetime + hookPassTime, now,__this.__lastDatetime + hookPassTime - now);
  288. Object.defineProperty(this, '_innerDate', {
  289. configurable: false,
  290. enumerable: false,
  291. value: new timerContext._Date(timerContext.__lastMDatetime + hookPassTime),
  292. writable: false
  293. });
  294. };
  295. },
  296. getHookedTimerFunction: function (type, timer) {
  297. var property = '_' + type + 'Ids';
  298. return function () {
  299. var uniqueId = timerContext.genUniqueId();
  300. var callback = arguments[0];
  301. if (typeof callback === 'string') {
  302. callback += ';timer.notifyExec(' + uniqueId + ')';
  303. arguments[0] = callback;
  304. }
  305. if (typeof callback === 'function') {
  306. arguments[0] = function () {
  307. var returnValue = callback.apply(this, arguments);
  308. timerContext.notifyExec(uniqueId);
  309. return returnValue;
  310. }
  311. }
  312. // save the original time interval
  313. var originMS = arguments[1];
  314. // Get variable speed interval
  315. arguments[1] *= timerContext._percentage;
  316. var resultId = timer.apply(window, arguments);
  317. // Save the id and parameters obtained each time the timer is used
  318. timerContext[property][resultId] = {
  319. args: arguments,
  320. originMS: originMS,
  321. originId: resultId,
  322. nowId: resultId,
  323. uniqueId: uniqueId,
  324. oldPercentage: timerContext._percentage,
  325. exceptNextFireTime: timerContext._Date.now() + originMS
  326. };
  327. return resultId;
  328. };
  329. },
  330. redirectNewestId: function (args) {
  331. var id = args[0];
  332. if (timerContext._intervalIds[id]) {
  333. args[0] = timerContext._intervalIds[id].nowId;
  334. // clear this recordid
  335. delete timerContext._intervalIds[id];
  336. }
  337. if (timerContext._timeoutIds[id]) {
  338. args[0] = timerContext._timeoutIds[id].nowId;
  339. // clear this recordid
  340. delete timerContext._timeoutIds[id];
  341. }
  342. },
  343. registerShortcutKeys: function (timer) {
  344. // Shortcut key registration
  345. addEventListener('keydown', function (e) {
  346. switch (e.keyCode) {
  347. case 57:
  348. if (e.ctrlKey || e.altKey) {
  349. // custom
  350. timer.changeTime();
  351. }
  352. break;
  353. // [=]
  354. case 190:
  355. case 187: {
  356. if (e.ctrlKey) {
  357. // console.log('+2');
  358. timer.changeTime(2, 0, true);
  359. } else if (e.altKey) {
  360. // console.log('xx2');
  361. timer.changeTime(0, 2);
  362. }
  363. break;
  364. }
  365. // [-]
  366. case 188:
  367. case 189: {
  368. if (e.ctrlKey) {
  369. // console.log('-2');
  370. timer.changeTime(-2, 0, true);
  371. } else if (e.altKey) {
  372. // console.log('xx-2');
  373. timer.changeTime(0, -2);
  374. }
  375. break;
  376. }
  377. // [0]
  378. case 48: {
  379. if (e.ctrlKey || e.altKey) {
  380. // console.log('reset');
  381. timer.changeTime(0, 0, false, true);
  382. }
  383. break;
  384. }
  385. default:
  386. // console.log(e);
  387. }
  388. });
  389. },
  390. /**
  391. * Callback method called when the timer rate is changed
  392. * @param percentage
  393. * @private
  394. */
  395. percentageChangeHandler: function (percentage) {
  396. // Change all loop timings
  397. util.ergodicObject(timerContext, timerContext._intervalIds, function (idObj, id) {
  398. idObj.args[1] = Math.floor((idObj.originMS || 1) * percentage);
  399. // End the original timer
  400. this._clearInterval.call(window, idObj.nowId);
  401. // start a new timer
  402. idObj.nowId = this._setInterval.apply(window, idObj.args);
  403. });
  404. // Change all delay timings
  405. util.ergodicObject(timerContext, timerContext._timeoutIds, function (idObj, id) {
  406. var now = this._Date.now();
  407. var exceptTime = idObj.exceptNextFireTime;
  408. var oldPercentage = idObj.oldPercentage;
  409. var time = exceptTime - now;
  410. if (time < 0) {
  411. time = 0;
  412. }
  413. var changedTime = Math.floor(percentage / oldPercentage * time);
  414. idObj.args[1] = changedTime;
  415. // Reschedule the next execution time
  416. idObj.exceptNextFireTime = now + changedTime;
  417. idObj.oldPercentage = percentage;
  418. // end the original timer
  419. this._clearTimeout.call(window, idObj.nowId);
  420. // start a new timer
  421. idObj.nowId = this._setTimeout.apply(window, idObj.args);
  422. });
  423. },
  424. hookShadowRoot: function () {
  425. var origin = Element.prototype.attachShadow;
  426. eHookContext.hookAfter(Element.prototype, 'attachShadow',
  427. function (m, args, result) {
  428. extraElements.push(result);
  429. return result;
  430. }, false);
  431. eHookContext.hookedToString(origin, Element.prototype.attachShadow);
  432. },
  433. hookDefine: function () {
  434. const _this = this;
  435. eHookContext.hookBefore(Object, 'defineProperty', function (m, args) {
  436. var option = args[2];
  437. var ele = args[0];
  438. var key = args[1];
  439. var afterArgs = _this.hookDefineDetails(ele, key, option);
  440. afterArgs.forEach((arg, i) => {
  441. args[i] = arg;
  442. })
  443. });
  444. eHookContext.hookBefore(Object, 'defineProperties', function (m, args) {
  445. var option = args[1];
  446. var ele = args[0];
  447. if (ele && ele instanceof Element) {
  448. Object.keys(option).forEach(key => {
  449. var o = option[key];
  450. var afterArgs = _this.hookDefineDetails(ele, key, o);
  451. args[0] = afterArgs[0];
  452. delete option[key];
  453. option[afterArgs[1]] = afterArgs[2]
  454. })
  455. }
  456. })
  457. },
  458. hookDefineDetails: function (target, key, option) {
  459. if (option && target && target instanceof Element && typeof key === 'string' && key.indexOf('on') >= 0) {
  460. option.configurable = true;
  461. }
  462. if (target instanceof HTMLVideoElement && key === 'playbackRate') {
  463. option.configurable = true;
  464. console.warn('[Timer Hook]', 'Default action video magnification blocked');
  465. key = 'playbackRate_hooked'
  466. }
  467. return [target, key, option];
  468. },
  469. suppressEvent: function (ele, eventName) {
  470. if (ele) {
  471. delete ele['on' + eventName];
  472. delete ele['on' + eventName];
  473. delete ele['on' + eventName];
  474. ele['on' + eventName] = undefined;
  475. }
  476. if (!suppressEvents[eventName]) {
  477. eHookContext.hookBefore(EventTarget.prototype, 'addEventListener',
  478. function (m, args) {
  479. var eName = args[0];
  480. if (eventName === eName) {
  481. console.warn(eventName, 'event suppressed.')
  482. args[0] += 'suppressed';
  483. }
  484. }, false);
  485. suppressEvents[eventName] = true;
  486. }
  487. },
  488. changePlaybackRate: function (ele, rate) {
  489. delete ele.playbackRate;
  490. delete ele.playbackRate;
  491. delete ele.playbackRate;
  492. ele.playbackRate = rate
  493. if (rate !== 1) {
  494. timerContext.defineProperty.call(Object, ele, 'playbackRate', {
  495. configurable: true,
  496. get: function () {
  497. return 1;
  498. },
  499. set: function () {
  500. }
  501. });
  502. }
  503. }
  504. }
  505. };
  506.  
  507. var normalUtil = {
  508. isInIframe: function () {
  509. let is = global.parent !== global;
  510. try {
  511. is = is && global.parent.document.body.tagName !== 'FRAMESET'
  512. } catch (e) {
  513. // ignore
  514. }
  515. return is;
  516. },
  517. listenParentEvent: function (handler) {
  518. global.addEventListener('message', function (e) {
  519. var data = e.data;
  520. var type = data.type || '';
  521. if (type === 'changePercentage') {
  522. handler(data.percentage || 0);
  523. }
  524. })
  525. },
  526. sentChangesToIframe: function (percentage) {
  527. var iframes = document.querySelectorAll('iframe') || [];
  528. var frames = document.querySelectorAll('frame');
  529. if (iframes.length) {
  530. for (var i = 0; i < iframes.length; i++) {
  531. iframes[i].contentWindow.postMessage(
  532. {type: 'changePercentage', percentage: percentage}, '*');
  533. }
  534. }
  535. if (frames.length) {
  536. for (var j = 0; j < frames.length; j++) {
  537. frames[j].contentWindow.postMessage(
  538. {type: 'changePercentage', percentage: percentage}, '*');
  539. }
  540. }
  541. }
  542. };
  543.  
  544. var querySelectorAll = function (ele, selector, includeExtra) {
  545. var elements = ele.querySelectorAll(selector);
  546. elements = Array.prototype.slice.call(elements || []);
  547. if (includeExtra) {
  548. extraElements.forEach(function (element) {
  549. elements = elements.concat(querySelectorAll(element, selector, false));
  550. })
  551. }
  552. return elements;
  553. };
  554.  
  555. var generate = function () {
  556. return function (util) {
  557. // disable worker
  558. workerURLs.forEach(function (url) {
  559. if (util.urlMatching(location.href, 'http.*://.*' + url + '.*')) {
  560. window['Worker'] = undefined;
  561. console.log('Worker disabled');
  562. }
  563. });
  564. var eHookContext = this;
  565. var timerHooker = {
  566. // Used to store the id and parameters of the timer
  567. _intervalIds: {},
  568. _timeoutIds: {},
  569. _auoUniqueId: 1,
  570. // timer rate
  571. __percentage: 1.0,
  572. // Original method before hijacking
  573. _setInterval: window['setInterval'],
  574. _clearInterval: window['clearInterval'],
  575. _clearTimeout: window['clearTimeout'],
  576. _setTimeout: window['setTimeout'],
  577. _Date: window['Date'],
  578. __lastDatetime: new Date().getTime(),
  579. __lastMDatetime: new Date().getTime(),
  580. videoSpeedInterval: 1000,
  581. defineProperty: Object.defineProperty,
  582. defineProperties: Object.defineProperties,
  583. genUniqueId: function () {
  584. return this._auoUniqueId++;
  585. },
  586. notifyExec: function (uniqueId) {
  587. var _this = this;
  588. if (uniqueId) {
  589. // clear timeout stored records
  590. var timeoutInfos = Object.values(this._timeoutIds).filter(
  591. function (info) {
  592. return info.uniqueId === uniqueId;
  593. }
  594. );
  595. timeoutInfos.forEach(function (info) {
  596. _this._clearTimeout.call(window, info.nowId);
  597. delete _this._timeoutIds[info.originId]
  598. })
  599. }
  600. // console.log(uniqueId, 'called')
  601. },
  602. /**
  603. * initialization method
  604. */
  605. init: function () {
  606. var timerContext = this;
  607. var h = helper(eHookContext, timerContext, util);
  608.  
  609. h.hookDefine();
  610. h.applyHooking();
  611.  
  612. // Set the callback when the percentage property is modified
  613. Object.defineProperty(timerContext, '_percentage', {
  614. get: function () {
  615. return timerContext.__percentage;
  616. },
  617. set: function (percentage) {
  618. if (percentage === timerContext.__percentage) {
  619. return percentage;
  620. }
  621. h.percentageChangeHandler(percentage);
  622. timerContext.__percentage = percentage;
  623. return percentage;
  624. }
  625. });
  626.  
  627. if (!normalUtil.isInIframe()) {
  628. console.log('[TimeHooker]', 'loading outer window...');
  629. h.applyUI();
  630. h.applyGlobalAction(timerContext);
  631. h.registerShortcutKeys(timerContext);
  632. } else {
  633. console.log('[TimeHooker]', 'loading inner window...');
  634. normalUtil.listenParentEvent((function (percentage) {
  635. console.log('[TimeHooker]', 'Inner Changed', percentage)
  636. this.change(percentage);
  637. }).bind(this))
  638. }
  639. },
  640. /**
  641. * Call this method to change the timer rate
  642. * @param percentage
  643. */
  644. change: function (percentage) {
  645. this.__lastMDatetime = this._mDate.now();
  646. this.__lastDatetime = this._Date.now();
  647. this._percentage = percentage;
  648. var oldNode = document.getElementsByClassName('_th-click-hover');
  649. var oldNode1 = document.getElementsByClassName('_th_times');
  650. var displayNum = (1 / this._percentage).toFixed(2);
  651. (oldNode[0] || {}).innerHTML = 'x' + displayNum;
  652. (oldNode1[0] || {}).innerHTML = 'x' + displayNum;
  653. var a = document.getElementsByClassName('_th_cover-all-show-times')[0] || {};
  654. a.className = '_th_cover-all-show-times';
  655. this._setTimeout.bind(window)(function () {
  656. a.className = '_th_cover-all-show-times _th_hidden';
  657. }, 100);
  658. this.changeVideoSpeed();
  659. normalUtil.sentChangesToIframe(percentage);
  660. },
  661. changeVideoSpeed: function () {
  662. var timerContext = this;
  663. var h = helper(eHookContext, timerContext, util);
  664. var rate = 1 / this._percentage;
  665. rate > 16 && (rate = 16);
  666. rate < 0.065 && (rate = 0.065);
  667. var videos = querySelectorAll(document, 'video', true) || [];
  668. if (videos.length) {
  669. for (var i = 0; i < videos.length; i++) {
  670. h.changePlaybackRate(videos[i], rate);
  671. }
  672. }
  673. }
  674. };
  675. // default initialization
  676. timerHooker.init();
  677. return timerHooker;
  678. }
  679. };
  680.  
  681. if (global.eHook) {
  682. global.eHook.plugins({
  683. name: 'timer',
  684. /**
  685. * Plugin loading
  686. * @param util
  687. */
  688. mount: generate()
  689. });
  690. }
  691. }(window);