您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
it can hook everything
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/509211/1523440/Everything-Hook.js
if (window.performance) window.performance.now = Date.now; (function () { 'use strict'; // 延迟执行的函数 function speedHook() { // ==UserScript== // @name Everything-Hook // @namespace https://gitee.com/HGJing/everthing-hook/ // @updateURL https://gitee.com/HGJing/everthing-hook/raw/master/src/everything-hook.js // @version 0.5.9056 // @include * // @description it can hook everything // @author Cangshi // @match http://*/* // @grant none // @run-at document-start // ==/UserScript== (function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if (typeof define === 'function' && define.amd) define([], factory); else { var a = factory(); for (var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } })(typeof self !== 'undefined' ? self : this, function () { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ([ /* 0 */ /***/ (function (module, __unused_webpack_exports, __webpack_require__) { "use strict"; /** * --------------------------- * Time: 2017/9/20 18:33. * Author: Cangshi * View: http://palerock.cn * --------------------------- */ const eUtils = __webpack_require__(1); ~(function (global, factory) { "use strict"; if (true && typeof module.exports === "object") { var results = factory.bind(global)(global, eUtils, true) || []; var HookJS = {}; results.forEach(function (part) { HookJS[part.name] = part.module; }); module.exports = HookJS; } else { factory.bind(global)(global, eUtils); } }(typeof window !== "undefined" ? window : this, function (_global, utils, noGlobal) { /** * @namespace EHook * @author Cangshi * @constructor * @see {@link https://palerock.cn/projects/006HDUuOhBj} * @license Apache License 2.0 */ var EHook = function () { var _autoId = 1; var _hookedMap = {}; var _hookedContextMap = {}; this._getHookedMap = function () { return _hookedMap; }; this._getHookedContextMap = function () { return _hookedContextMap; }; this._getAutoStrId = function () { return '__auto__' + _autoId++; }; this._getAutoId = function () { return _autoId++; }; }; EHook.prototype = { /** * 获取一个对象的劫持id,若没有则创建一个 * @param context * @return {*} * @private */ _getHookedId: function (context) { var contextMap = this._getHookedContextMap(); var hookedId = null; Object.keys(contextMap).forEach(key => { if (context === contextMap[key]) { hookedId = key; } }); if (hookedId == null) { hookedId = this._getAutoStrId(); contextMap[hookedId] = context; } return hookedId; }, /** * 获取一个对象的劫持方法映射,若没有则创建一个 * @param context * @return {*} * @private */ _getHookedMethodMap: function (context) { var hookedId = this._getHookedId(context); var hookedMap = this._getHookedMap(); var thisTask = hookedMap[hookedId]; if (!utils.isExistObject(thisTask)) { thisTask = hookedMap[hookedId] = {}; } return thisTask; }, /** * 获取对应方法的hook原型任务对象,若没有则初始化一个。 * @param context * @param methodName * @private */ _getHookedMethodTask: function (context, methodName) { var thisMethodMap = this._getHookedMethodMap(context); var thisMethod = thisMethodMap[methodName]; if (!utils.isExistObject(thisMethod)) { thisMethod = thisMethodMap[methodName] = { original: undefined, replace: undefined, task: { before: [], current: undefined, after: [] } }; } return thisMethod; }, /** * 执行多个方法并注入一个方法和参数集合 * @param context * @param methods * @param args * @return result 最后一次执行方法的有效返回值 * @private */ _invokeMethods: function (context, methods, args) { if (!utils.isArray(methods)) { return; } var result = null; utils.ergodicArrayObject(context, methods, function (_method) { if (!utils.isFunction(_method.method)) { return; } var r = _method.method.apply(this, args); if (r != null) { result = r; } }); return result; }, /** * 生成和替换劫持方法 * @param parent * @param context * @param methodName {string} * @private */ _hook: function (parent, methodName, context) { if (context === undefined) { context = parent; } var method = parent[methodName]; var methodTask = this._getHookedMethodTask(parent, methodName); if (!methodTask.original) { methodTask.original = method; } if (utils.isExistObject(methodTask.replace) && utils.isFunction(methodTask.replace.method)) { parent[methodName] = methodTask.replace.method(methodTask.original); return; } var invokeMethods = this._invokeMethods; // 组装劫持函数 var builder = new utils.FunctionBuilder(function (v) { return { result: undefined }; }); if (methodTask.task.before.length > 0) { builder.push(function (v) { invokeMethods(context || v.this, methodTask.task.before, [methodTask.original, v.arguments]); }); } if (utils.isExistObject(methodTask.task.current) && utils.isFunction(methodTask.task.current.method)) { builder.push(function (v) { return { result: methodTask.task.current.method.call(context || v.this, parent, methodTask.original, v.arguments) } }); } else { builder.push(function (v) { return { result: methodTask.original.apply(context || v.this, v.arguments) } }); } if (methodTask.task.after.length > 0) { builder.push(function (v) { var args = []; args.push(methodTask.original); args.push(v.arguments); args.push(v.result); var r = invokeMethods(context || v.this, methodTask.task.after, args); return { result: (r != null ? r : v.result) }; }); } builder.push(function (v) { return { returnValue: v.result }; }); // var methodStr = '(function(){\n'; // methodStr = methodStr + 'var result = undefined;\n'; // if (methodTask.task.before.length > 0) { // methodStr = methodStr + 'invokeMethods(context, methodTask.task.before,[methodTask.original, arguments]);\n'; // } // if (utils.isExistObject(methodTask.task.current) && utils.isFunction(methodTask.task.current.method)) { // methodStr = methodStr + 'result = methodTask.task.current.method.call(context, parent, methodTask.original, arguments);\n'; // } else { // methodStr = methodStr + 'result = methodTask.original.apply(context, arguments);\n'; // } // if (methodTask.task.after.length > 0) { // methodStr = methodStr + 'var args = [];args.push(methodTask.original);args.push(arguments);args.push(result);\n'; // methodStr = methodStr + 'var r = invokeMethods(context, methodTask.task.after, args);result = (r!=null?r:result);\n'; // } // methodStr = methodStr + 'return result;\n})'; // 绑定劫持函数 var resultFunc = builder.result(); for (var proxyName in methodTask.original) { Object.defineProperty(resultFunc, proxyName, { get: function () { return methodTask.original[proxyName]; }, set: function (v) { methodTask.original[proxyName] = v; } }) } resultFunc.prototype = methodTask.original.prototype; parent[methodName] = resultFunc; }, /** * 劫持一个方法 * @inner * @memberOf EHook * @param parent{Object} 指定方法所在的对象 * @param methodName{String} 指定方法的名称 * @param config{Object} 劫持的配置对象 */ hook: function (parent, methodName, config) { var hookedFailure = -1; // 调用方法的上下文 var context = config.context !== undefined ? config.context : parent; if (parent[methodName] == null) { parent[methodName] = function () { } } if (!utils.isFunction(parent[methodName])) { return hookedFailure; } var methodTask = this._getHookedMethodTask(parent, methodName); var id = this._getAutoId(); if (utils.isFunction(config.replace)) { methodTask.replace = { id: id, method: config.replace }; hookedFailure = 0; } if (utils.isFunction(config.before)) { methodTask.task.before.push({ id: id, method: config.before }); hookedFailure = 0; } if (utils.isFunction(config.current)) { methodTask.task.current = { id: id, method: config.current }; hookedFailure = 0; } if (utils.isFunction(config.after)) { methodTask.task.after.push({ id: id, method: config.after }); hookedFailure = 0; } if (hookedFailure === 0) { this._hook(parent, methodName, context); return id; } else { return hookedFailure; } }, /** * 劫持替换一个方法 * @see 注意:该方法会覆盖指定劫持方法在之前所进行的一切劫持,也不能重复使用,并且不和hookAfter,hookCurrent,hookBefore共存,在同时使用的情况下,优先使用hookReplace而不是其他的方法 * @inner * @memberOf EHook * @param parent{Object} 指定方法所在的对象 * @param context{Object=} 回调方法的上下文 * @param methodName{String} 指定方法的名称 * @param replace {function} 回调方法,该方法的返回值便是替换的方法 回调参数及返回值:[ method:指定的原方法,类型:function return:规定被替换的方法内容,类型:function ] * @return {number} 该次劫持的id */ hookReplace: function (parent, methodName, replace, context) { return this.hook(parent, methodName, { replace: replace, context: context }); }, /** * 在指定方法前执行 * @inner * @memberOf EHook * @param parent{Object} 指定方法所在的对象 * @param methodName{String} 指定方法的名称 * @param before{function} 回调方法,该方法在指定方法运行前执行 回调参数:[ method:指定的原方法 args:原方法运行的参数(在此改变参数值会影响后续指定方法的参数值) ] * @param context{Object=} 回调方法的上下文 * @returns {number} 劫持id(用于解除劫持) */ hookBefore: function (parent, methodName, before, context) { return this.hook(parent, methodName, { before: before, context: context }); }, /** * 劫持方法的运行,在对制定方法进行该劫持的时候,指定方法不会主动执行,替换为执行参数中的current方法 * @see 注意:该方法只能对指定方法进行一次劫持,若再次使用该方法劫持就会覆盖之前的劫持[可以和hookBefore,hookAfter共存,且hookBefore和hookAfter可以对同个指定方法多次劫持] * @inner * @memberOf EHook * @param parent{Object} 指定方法所在的对象 * @param methodName{String} 指定方法的名称 * @param current{function} 回调方法,该方法在指定方法被调用时执行 回调参数及返回值:[ parent:指定方法所在的对象,类型:object method:指定的原方法,类型:function args:原方法的参数,类型:array return:规定被劫持方法的返回值,类型:* ] * @param context{Object=} 回调方法的上下文 * @returns {number} 劫持id(用于解除劫持) */ hookCurrent: function (parent, methodName, current, context) { return this.hook(parent, methodName, { current: current, context: context }); }, /** * 在指定方法后执行 * @inner * @memberOf EHook * @param parent{Object} 指定方法所在的对象 * @param methodName{String} 指定方法的名称 * @param after{function} 回调方法,该方法在指定方法运行后执行 回调参数及返回值:[ method:指定的原方法,类型:function args:原方法的参数,类型:array result:原方法的返回值,类型:* return:规定被劫持方法的返回值,类型:* ] * @param context{Object=} 回调方法的上下文 * @returns {number} 劫持id(用于解除劫持) */ hookAfter: function (parent, methodName, after, context) { return this.hook(parent, methodName, { after: after, context: context }); }, hookClass: function (parent, className, replace, innerName, excludeProperties) { var _this = this; var originFunc = parent[className]; if (!excludeProperties) { excludeProperties = []; } excludeProperties.push('prototype'); excludeProperties.push('caller'); excludeProperties.push('arguments'); innerName = innerName || '_innerHook'; var resFunc = function () { this[innerName] = new originFunc(); replace.apply(this, arguments); }; this.hookedToString(originFunc, resFunc); this.hookedToProperties(originFunc, resFunc, true, excludeProperties); var prototypeProperties = Object.getOwnPropertyNames(originFunc.prototype); var prototype = resFunc.prototype = { constructor: resFunc }; prototypeProperties.forEach(function (name) { if (name === 'constructor') { return; } var method = function () { if (originFunc.prototype[name] && utils.isFunction(originFunc.prototype[name])) { return originFunc.prototype[name].apply(this[innerName], arguments); } return undefined; }; _this.hookedToString(originFunc.prototype[name], method); prototype[name] = method; }); this.hookReplace(parent, className, function () { return resFunc; }, parent) }, hookedToProperties: function (originObject, resultObject, isDefined, excludeProperties) { var objectProperties = Object.getOwnPropertyNames(originObject); objectProperties.forEach(function (property) { if (utils.contains(excludeProperties, property)) { return; } if (!isDefined) { resultObject[property] = originObject[property]; } else { Object.defineProperty(resultObject, property, { configurable: false, enumerable: false, value: originObject[property], writable: false }); } }); }, hookedToString: function (originObject, resultObject) { Object.defineProperties(resultObject, { toString: { configurable: false, enumerable: false, value: originObject.toString.bind(originObject), writable: false }, toLocaleString: { configurable: false, enumerable: false, value: originObject.toLocaleString.bind(originObject), writable: false } }) }, /** * 劫持全局ajax * @inner * @memberOf EHook * @param methods {object} 劫持的方法 * @return {*|number} 劫持的id */ hookAjax: function (methods) { if (this.isHooked(_global, 'XMLHttpRequest')) { return; } var _this = this; var hookMethod = function (methodName) { if (utils.isFunction(methods[methodName])) { // 在执行方法之前hook原方法 _this.hookBefore(this.xhr, methodName, methods[methodName]); } // 返回方法调用内部的xhr return this.xhr[methodName].bind(this.xhr); }; var getProperty = function (attr) { return function () { return this.hasOwnProperty(attr + "_") ? this[attr + "_"] : this.xhr[attr]; }; }; var setProperty = function (attr) { return function (f) { var xhr = this.xhr; var that = this; if (attr.indexOf("on") !== 0) { this[attr + "_"] = f; return; } if (methods[attr]) { xhr[attr] = function () { f.apply(xhr, arguments); }; // on方法在set时劫持 _this.hookBefore(xhr, attr, methods[attr]); // console.log(1,attr); // xhr[attr] = function () { // methods[attr](that) || f.apply(xhr, arguments); // } } else { xhr[attr] = f; } }; }; return this.hookReplace(_global, 'XMLHttpRequest', function (XMLHttpRequest) { var resFunc = function () { this.xhr = new XMLHttpRequest(); for (var propertyName in this.xhr) { var property = this.xhr[propertyName]; if (utils.isFunction(property)) { // hook 原方法 this[propertyName] = hookMethod.bind(this)(propertyName); } else { Object.defineProperty(this, propertyName, { get: getProperty(propertyName), set: setProperty(propertyName) }); } } // 定义外部xhr可以在内部访问 this.xhr.xhr = this; }; _this.hookedToProperties(XMLHttpRequest, resFunc, true); _this.hookedToString(XMLHttpRequest, resFunc); return resFunc }); }, /** * 劫持全局ajax * @param methods {object} 劫持的方法 * @return {*|number} 劫持的id */ hookAjaxV2: function (methods) { this.hookClass(window, 'XMLHttpRequest', function () { }); utils.ergodicObject(this, methods, function (method) { }); }, /** * 解除劫持 * @inner * @memberOf EHook * @param context 上下文 * @param methodName 方法名 * @param isDeeply {boolean=} 是否深度解除[默认为false] * @param eqId {number=} 解除指定id的劫持[可选] */ unHook: function (context, methodName, isDeeply, eqId) { if (!context[methodName] || !utils.isFunction(context[methodName])) { return; } var methodTask = this._getHookedMethodTask(context, methodName); if (eqId) { if (this.unHookById(eqId)) { return; } } if (!methodTask.original) { delete this._getHookedMethodMap(context)[methodName]; return; } context[methodName] = methodTask.original; if (isDeeply) { delete this._getHookedMethodMap(context)[methodName]; } }, /** * 通过Id解除劫持 * @inner * @memberOf EHook * @param eqId * @returns {boolean} */ unHookById: function (eqId) { var hasEq = false; if (eqId) { var hookedMap = this._getHookedMap(); utils.ergodicObject(this, hookedMap, function (contextMap) { utils.ergodicObject(this, contextMap, function (methodTask) { methodTask.task.before = methodTask.task.before.filter(function (before) { hasEq = hasEq || before.id === eqId; return before.id !== eqId; }); methodTask.task.after = methodTask.task.after.filter(function (after) { hasEq = hasEq || after.id === eqId; return after.id !== eqId; }); if (methodTask.task.current && methodTask.task.current.id === eqId) { methodTask.task.current = undefined; hasEq = true; } if (methodTask.replace && methodTask.replace.id === eqId) { methodTask.replace = undefined; hasEq = true; } }) }); } return hasEq; }, /** * 移除所有劫持相关的方法 * @inner * @memberOf EHook * @param context 上下文 * @param methodName 方法名 */ removeHookMethod: function (context, methodName) { if (!context[methodName] || !utils.isFunction(context[methodName])) { return; } this._getHookedMethodMap(context)[methodName] = { original: undefined, replace: undefined, task: { before: [], current: undefined, after: [] } }; }, /** * 判断一个方法是否被劫持过 * @inner * @memberOf EHook * @param context * @param methodName */ isHooked: function (context, methodName) { var hookMap = this._getHookedMethodMap(context); return hookMap[methodName] !== undefined ? (hookMap[methodName].original !== undefined) : false; }, /** * 保护一个对象使之不会被篡改 * @inner * @memberOf EHook * @param parent * @param methodName */ protect: function (parent, methodName) { Object.defineProperty(parent, methodName, { configurable: false, writable: false }); }, preventError: function (parent, methodName, returnValue, context) { this.hookCurrent(parent, methodName, function (m, args) { var value = returnValue; try { value = m.apply(this, args); } catch (e) { console.log('Error Prevented from method ' + methodName, e); } return value; }, context) }, /** * 装载插件 * @inner * @memberOf EHook * @param option */ plugins: function (option) { if (utils.isFunction(option.mount)) { var result = option.mount.call(this, utils); if (typeof option.name === 'string') { _global[option.name] = result; } } } }; if (_global.eHook && (_global.eHook instanceof EHook)) { return; } var eHook = new EHook(); /** * @namespace AHook * @author Cangshi * @constructor * @see {@link https://palerock.cn/projects/006HDUuOhBj} * @license Apache License 2.0 */ var AHook = function () { this.isHooked = false; var autoId = 1; this._urlDispatcherList = []; this._getAutoId = function () { return autoId++; }; }; AHook.prototype = { /** * 执行配置列表中的指定方法组 * @param xhr * @param methodName * @param args * @private */ _invokeAimMethods: function (xhr, methodName, args) { var configs = utils.parseArrayByProperty(xhr.patcherList, 'config'); var methods = []; utils.ergodicArrayObject(xhr, configs, function (config) { if (utils.isFunction(config[methodName])) { methods.push(config[methodName]); } }); return utils.invokeMethods(xhr, methods, args); }, /** * 根据url获取配置列表 * @param url * @return {Array} * @private */ _urlPatcher: function (url) { var patcherList = []; utils.ergodicArrayObject(this, this._urlDispatcherList, function (patcherMap, i) { if (utils.UrlUtils.urlMatching(url, patcherMap.patcher)) { patcherList.push(patcherMap); } }); return patcherList; }, /** * 根据xhr对象分发回调请求 * @param xhr * @param fullUrl * @private */ _xhrDispatcher: function (xhr, fullUrl) { var url = utils.UrlUtils.getUrlWithoutParam(fullUrl); xhr.patcherList = this._urlPatcher(url); }, /** * 转换响应事件 * @param e * @param xhr * @private */ _parseEvent: function (e, xhr) { try { Object.defineProperties(e, { target: { get: function () { return xhr; } }, srcElement: { get: function () { return xhr; } } }); } catch (error) { console.warn('重定义返回事件失败,劫持响应可能失败'); } return e; }, /** * 解析open方法的参数 * @param args * @private */ _parseOpenArgs: function (args) { return { method: args[0], fullUrl: args[1], url: utils.UrlUtils.getUrlWithoutParam(args[1]), params: utils.UrlUtils.getParamFromUrl(args[1]), async: args[2] }; }, /** * 劫持ajax 请求参数 * @param argsObject * @param argsArray * @private */ _rebuildOpenArgs: function (argsObject, argsArray) { argsArray[0] = argsObject.method; argsArray[1] = utils.UrlUtils.margeUrlAndParams(argsObject.url, argsObject.params); argsArray[2] = argsObject.async; }, /** * 获取劫持方法的参数 [原方法,原方法参数,原方法返回值],剔除原方法参数 * @param args * @return {*|Array.<T>} * @private */ _getHookedArgs: function (args) { // 将参数中'原方法'剔除 return Array.prototype.slice.call(args, 0).splice(1); }, /** * 响应被触发时调用的方法 * @param outerXhr * @param funcArgs * @private */ _onResponse: function (outerXhr, funcArgs) { // 因为参数是被劫持的参数为[method(原方法),args(参数)],该方法直接获取参数并转换为数组 var args = this._getHookedArgs(funcArgs); args[0][0] = this._parseEvent(args[0][0], outerXhr.xhr); // 强制事件指向外部xhr // 执行所有的名为hookResponse的方法组 var results = this._invokeAimMethods(outerXhr, 'hookResponse', args); // 遍历结果数组并获取最后返回的有效的值作为响应值 var resultIndex = -1; utils.ergodicArrayObject(outerXhr, results, function (res, i) { if (res != null) { resultIndex = i; } }); if (resultIndex !== -1) { outerXhr.xhr.responseText_ = outerXhr.xhr.response_ = results[resultIndex]; } }, /** * 手动开始劫持 * @inner * @memberOf AHook */ startHook: function () { var _this = this; var normalMethods = { // 方法中的this指向内部xhr // 拦截响应 onreadystatechange: function () { if (this.readyState == 4 && this.status == 200 || this.status == 304) { _this._onResponse(this, arguments); } }, onload: function () { _this._onResponse(this, arguments); }, // 拦截请求 open: function () { var args = _this._getHookedArgs(arguments); var fullUrl = args[0][1]; _this._xhrDispatcher(this, fullUrl); var argsObject = _this._parseOpenArgs(args[0]); this.openArgs = argsObject; _this._invokeAimMethods(this, 'hookRequest', [argsObject]); _this._rebuildOpenArgs(argsObject, args[0]); }, send: function () { var args = _this._getHookedArgs(arguments); this.sendArgs = args; _this._invokeAimMethods(this, 'hookSend', args); } }; // 设置总的hookId this.___hookedId = _global.eHook.hookAjax(normalMethods); this.isHooked = true; }, /** * 注册ajaxUrl拦截 * @inner * @memberOf AHook * @param urlPatcher * @param configOrRequest * @param response * @return {number} */ register: function (urlPatcher, configOrRequest, response) { if (!urlPatcher) { return -1; } if (!utils.isExistObject(configOrRequest) && !utils.isFunction(response)) { return -1; } var config = {}; if (utils.isFunction(configOrRequest)) { config.hookRequest = configOrRequest; } if (utils.isFunction(response)) { config.hookResponse = response; } if (utils.isExistObject(configOrRequest)) { config = configOrRequest; } var id = this._getAutoId(); this._urlDispatcherList.push({ // 指定id便于后续取消 id: id, patcher: urlPatcher, config: config }); // 当注册一个register时,自动开始运行劫持 if (!this.isHooked) { this.startHook(); } return id; } // todo 注销 cancellation: function (registerId){}; }; _global['eHook'] = eHook; _global['aHook'] = new AHook(); return [{ name: 'eHook', module: eHook }, { name: 'aHook', module: _global['aHook'] }] })); /***/ }), /* 1 */ /***/ (function (module) { (function (global, factory) { "use strict"; if (true && typeof module.exports === "object") { module.exports = factory(global, true); } else { factory(global); } }(typeof window !== "undefined" ? window : this, function (_global, noGlobal) { var map = Array.prototype.map; var forEach = Array.prototype.forEach; var reduce = Array.prototype.reduce; var BaseUtils = { /** * 对象是否为数组 * @param arr */ isArray: function (arr) { return Array.isArray(arr) || Object.prototype.toString.call(arr) === "[object Array]"; }, /** * 判断是否为方法 * @param func * @return {boolean} */ isFunction: function (func) { if (!func) { return false; } return typeof func === 'function'; }, /** * 判断是否是一个有效的对象 * @param obj * @return {*|boolean} */ isExistObject: function (obj) { return obj && (typeof obj === 'object'); }, isString: function (str) { if (str === null) { return false; } return typeof str === 'string'; }, uniqueNum: 1000, /** * 根据当前时间戳生产一个随机id * @returns {string} */ buildUniqueId: function () { var prefix = new Date().getTime().toString(); var suffix = this.uniqueNum.toString(); this.uniqueNum++; return prefix + suffix; } }; // var serviceProvider = { _parseDepends: function (depends) { var dependsArr = []; if (!BaseUtils.isArray(depends)) { return; } forEach.call(depends, function (depend) { if (BaseUtils.isString(depend)) { dependsArr.push(serviceProvider[depend.toLowerCase()]); } }); return dependsArr; } }; var factory = function (name, depends, construction) { if (!BaseUtils.isFunction(construction)) { return; } serviceProvider[name.toLowerCase()] = construction.apply(this, serviceProvider._parseDepends(depends)); }; var depend = function (depends, construction) { if (!BaseUtils.isFunction(construction)) { return; } construction.apply(this, serviceProvider._parseDepends(depends)); }; factory('BaseUtils', [], function () { return BaseUtils; }); // logger factory('logger', [], function () { return console; }); // DateTimeUtils factory('DateTimeUtils', ['logger'], function (logger) { return { /** * 打印当前时间 */ printNowTime: function () { var date = new Date(); console.log(this.pattern(date, 'hh:mm:ss:S')); }, /** * 格式化日期 * @param date * @param fmt * @returns {*} */ pattern: function (date, fmt) { var o = { "M+": date.getMonth() + 1, //月份 "d+": date.getDate(), //日 "h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, //小时 "H+": date.getHours(), //小时 "m+": date.getMinutes(), //分 "s+": date.getSeconds(), //秒 "q+": Math.floor((date.getMonth() + 3) / 3), //季度 "S": date.getMilliseconds() //毫秒 }; var week = { "0": "/u65e5", "1": "/u4e00", "2": "/u4e8c", "3": "/u4e09", "4": "/u56db", "5": "/u4e94", "6": "/u516d" }; if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); } if (/(E+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[date.getDay() + ""]); } for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } return fmt; }, /** * 以当前时间获取id * @returns {number} */ getCurrentId: function () { var date = new Date(); return date.getTime(); }, /** * 获取指定时间距离现在相差多久 * @param date {number|Date} * @param isCeil{boolean=} 是否对结果向上取整,默认[false] * @param type {string=} 单位可取值['day','month','year']默认'day' * @returns {number} */ getNowBetweenADay: function (date, isCeil, type) { if (!type) { type = 'day' } if (typeof date === 'number') { date = new Date(date); } if (!(date instanceof Date)) { throw new TypeError('该参数类型必须是Date') } var time = date.getTime(); var now = new Date(); var nowTime = now.getTime(); if (nowTime - time < 0) { logger.warn('需要计算的时间必须在当前时间之前'); } var result = 0; switch (type) { default: case 'day': result = (nowTime - time) / (1000 * 60 * 60 * 24); break; case 'month': var yearDifference = now.getFullYear() - date.getFullYear(); if (yearDifference > 0) { result += yearDifference * 12; } result += now.getMonth() - date.getMonth(); break; case 'year': result += now.getFullYear() - date.getFullYear(); break; } if (!isCeil) { return Math.floor(result); } else { if (result === 0 && isCeil) { result = 1; } return Math.ceil(result); } } } }); // ArrayUtils factory('ArrayUtils', ['BaseUtils'], function (BaseUtils) { return { isArrayObject: function (arr) { return BaseUtils.isArray(arr); }, /** * 遍历数组 * @param context {Object} * @param arr {Array} * @param cb {Function} 回调函数 */ ergodicArrayObject: function (context, arr, cb) { if (!context) { context = _global; } if (!BaseUtils.isArray(arr) || !BaseUtils.isFunction(cb)) { return; } for (var i = 0; i < arr.length; i++) { var result = cb.call(context, arr[i], i); if (result && result === -1) { break; } } }, /** * 获取数组对象的一个属性发起动作 * @param context {Object} * @param arr {Array} * @param propertyName {String} * @param cb {Function} * @param checkProperty {boolean} 是否排除不拥有该属性的对象[default:true] */ getPropertyDo: function (context, arr, propertyName, cb, checkProperty) { if (checkProperty === null) { checkProperty = true; } this.ergodicArrayObject(context, arr, function (ele) { if (!checkProperty || ele.hasOwnProperty(propertyName)) { cb.call(context, ele[propertyName], ele); } }) }, /** * [私有方法]将多个键值对对象转换为map * @param arr {Array} * @returns {{}} */ parseKeyValue: function (arr) { var map = {}; if (!(BaseUtils.isArray(arr))) { return map; } this.ergodicArrayObject(this, arr, function (ele) { if (ele.key === null) { return; } if (!map.hasOwnProperty(ele.key)) { map[ele.key] = ele.value; } }); return map; }, /** * 获取数组的哈希码 * @param arr {Array} * @returns {number} */ getHashCode: function (arr) { var str = arr.toString(); var hash = 31; if (str.length === 0) return hash; for (var i = 0; i < str.length; i++) { var char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash; }, /** * 通过数组中每个对象的指定属性生成一个新数组 * @param arr {Array} * @param propertyName {String} */ parseArrayByProperty: function (arr, propertyName) { var result = []; if (!this.isArrayObject(arr)) { return result; } this.getPropertyDo(this, arr, propertyName, function (value) { result.push(value); }, true); return result; }, /** * 数组对象是否包含一个对象 * @param arr {Array} * @param obj * @param cb {function=} * @returns {boolean} */ isContainsObject: function (arr, obj, cb) { var isContainsObject = false; this.ergodicArrayObject(this, arr, function (value, i) { if (obj === value) { isContainsObject = true; if (BaseUtils.isFunction(cb)) { cb.call(_global, i); } return -1; } }); return isContainsObject; }, /** * 获取数组中的最大值 * @param arr 若数组中的对象还是数组,则按里面数组的每个对象进行多级比较 * @param cb * @returns {*} */ getMaxInArray: function (arr, cb) { var maxObject = null; var maxIndex = -1; while (maxObject === null && maxIndex < arr.length) { maxObject = arr[++maxIndex] } for (var i = maxIndex + 1; i < arr.length; i++) { // 若是比较对象都是数组,则对每个数组的第一个元素进行比较,若相同,则比较第二个元素 if (maxObject !== null && this.isArrayObject(maxObject) && this.isArrayObject(arr[i])) { var classLength = maxObject.length; var classLevel = 0; // console.log(maxObject[classLevel],arr[i][classLevel]); while (maxObject[classLevel] === arr[i][classLevel] && classLevel < classLength) { classLevel++ } if (maxObject[classLevel] !== null && maxObject[classLevel] < arr[i][classLevel]) { maxObject = arr[i]; maxIndex = i; } continue; } if (maxObject !== null && maxObject < arr[i]) { maxObject = arr[i]; maxIndex = i; } } if (BaseUtils.isFunction(cb)) { cb.call(this, maxObject, maxIndex); } return maxObject; }, /** * 获取数组中的总值 * @param arr{Array<number>} * @param cb {function=} */ getSumInArray: function (arr, cb) { if (!this.isArrayObject(arr)) { return; } var sum = 0; var count = 0; this.ergodicArrayObject(this, arr, function (value) { if (typeof value === 'number' && !Number.isNaN(value)) { sum += value; count += 1; } }); if (BaseUtils.isFunction(cb)) { cb.call(_global, sum, count); } return sum; }, /** * 获取数组中的平均值 * @param arr{Array<number>} */ getAverageInArray: function (arr) { var average = 0; this.getSumInArray(arr, function (sum, i) { i === 0 || (average = sum / i); }); return average; }, /** * 为数组排序 * @param arr * @param order * @param sortSetting {object=} */ sortingArrays: function (arr, order, sortSetting) { if (!this.isArrayObject(arr)) { return; } var DESC = 0; var ASC = 1; var thisArr = arr.slice(0); var _thisAction = null; // 解析配置 var isSetting = sortSetting && sortSetting.getComparedProperty && BaseUtils.isFunction(sortSetting.getComparedProperty); if (isSetting) { thisArr = sortSetting.getComparedProperty(arr); } switch (order) { default: case DESC: _thisAction = thisArr.push; break; case ASC: _thisAction = thisArr.unshift; break; } var resultArr = []; for (var j = 0; j < thisArr.length; j++) { this.getMaxInArray(thisArr, function (max, i) { delete thisArr[i]; _thisAction.call(resultArr, arr[i]); }); } if (sortSetting && sortSetting.createNewData) { return resultArr.slice(0); } return resultArr; }, /** * 将类数组转化为数组 * @param arrLike 类数组对象 */ toArray: function (arrLike) { if (!arrLike || arrLike.length === 0) { return []; } // 非伪类对象,直接返回最好 if (!arrLike.length) { return arrLike; } // 针对IE8以前 DOM的COM实现 try { return [].slice.call(arrLike); } catch (e) { var i = 0, j = arrLike.length, res = []; for (; i < j; i++) { res.push(arrLike[i]); } return res; } }, /** * 判断是否为类数组 * @param o * @returns {boolean} */ isArrayLick: function (o) { if (o && // o is not null, undefined, etc. typeof o === 'object' && // o is an object isFinite(o.length) && // o.length is a finite number o.length >= 0 && // o.length is non-negative o.length === Math.floor(o.length) && // o.length is an integer o.length < 4294967296) // o.length < 2^32 return true; // Then o is array-like else return false; // Otherwise it is not }, /** * 判断数组是否包含对象 * @param arr * @param obj */ contains: function (arr, obj) { var contains = false; this.ergodicArrayObject(this, arr, function (a) { if (a === obj) { contains = true; return -1; } }); return contains; } } }); // ObjectUtils factory('ObjectUtils', ['ArrayUtils', 'BaseUtils'], function (ArrayUtils, BaseUtils) { return { /** * 获取对象属性[支持链式表达式,如获取学生所在班级所在的学校(student.class.school):'class.school'] * @param obj * @param linkProperty {string|Array} 属性表达式,获取多个属性则用数组 * @param cb {function=} * @return 对象属性 */ readLinkProperty: function (obj, linkProperty, cb) { var callback = null; if (BaseUtils.isFunction(cb)) { callback = cb; } if (typeof linkProperty === 'string') { // 除去所有的空格 linkProperty = linkProperty.replace(/ /g, ''); // 不判断为空的值 if (linkProperty === '') { return null; } // 若是以','隔开的伪数组,则转化为数组再进行操作 if (linkProperty.indexOf(',') !== -1) { var propertyNameArr = linkProperty.split(','); return this.readLinkProperty(obj, propertyNameArr, callback); } if (linkProperty.indexOf('.') !== -1) { var names = linkProperty.split('.'); var iterationObj = obj; var result = null; ArrayUtils.ergodicArrayObject(this, names, function (name, i) { iterationObj = this.readLinkProperty(iterationObj, name); if (names[names.length - 1] === name && names.length - 1 === i) { result = iterationObj; if (callback) { callback.call(_global, result, linkProperty); } } // 终止对接下来的属性的遍历 if (typeof iterationObj === 'undefined') { return -1; } }); return result; } var normalResult = null; if (linkProperty.slice(linkProperty.length - 2) === '()') { var func = linkProperty.slice(0, linkProperty.length - 2); normalResult = obj[func](); } else { normalResult = obj[linkProperty]; } if (normalResult === null) { console.warn(obj, '的属性[\'' + linkProperty + '\']值未找到'); } if (callback) { callback.call(_global, normalResult, linkProperty); } return normalResult; } if (BaseUtils.isArray(linkProperty)) { var results = []; ArrayUtils.ergodicArrayObject(this, linkProperty, function (name) { var value = this.readLinkProperty(obj, name); results.push(value); if (callback && name !== '') { return callback.call(_global, value, name); } }); results.isMultipleResults = true; return results; } }, /** * 为对象属性赋值 * (同一个对象中不能够既有数字开头的属性名和普通属性名) * @param obj * @param linkProperty {string|Array} 属性表达式,多个属性则用数组 * @param value */ createLinkProperty: function (obj, linkProperty, value) { if (obj === null) { obj = {}; } if (typeof linkProperty === 'string') { // 除去所有的空格 linkProperty = linkProperty.replace(/ /g, ''); // 不判断为空的值 if (linkProperty === '') { throw new TypeError('对象属性名不能为空') } if (linkProperty.indexOf(',') !== -1) { var propertyNameArr = linkProperty.split(','); this.createLinkProperty(obj, propertyNameArr, value); return obj; } if (linkProperty.indexOf('.') !== -1) { var names = linkProperty.split('.'); if (!obj.hasOwnProperty(names[0])) { obj[names[0]] = {} } // 判断属性名是否以数字开头(若是代表是一个数组) if (!Number.isNaN(parseInt(names[0]))) { if (!ArrayUtils.isArrayObject(obj)) { obj = []; } } var propertyObj = obj[names[0]]; var newProperties = names.slice(1, names.length); var newLinkProperty = ''; ArrayUtils.ergodicArrayObject(this, newProperties, function (property, i) { if (i < newProperties.length - 1) { newLinkProperty = newLinkProperty + property + '.' } else { newLinkProperty = newLinkProperty + property; } }); obj[names[0]] = this.createLinkProperty(propertyObj, newLinkProperty, value); return obj; } // 判断属性名是否以数字开头(若是代表是一个数组) if (!Number.isNaN(parseInt(linkProperty))) { if (!ArrayUtils.isArrayObject(obj)) { obj = []; } } obj[linkProperty] = value; return obj; } else if (BaseUtils.isArray(linkProperty)) { ArrayUtils.ergodicArrayObject(this, linkProperty, function (link) { obj = this.createLinkProperty(obj, link, value); }); return obj; } }, /** * 遍历对象属性 * @param context {object} 上下文 * @param obj {object} 遍历对象 * @param cb {function} 回调函数 * @param isReadInnerObject {boolean=} 是否遍历内部对象的属性 */ ergodicObject: function (context, obj, cb, isReadInnerObject) { var keys = Object.keys(obj); ArrayUtils.ergodicArrayObject(this, keys, function (propertyName) { // 若内部对象需要遍历 var _propertyName = propertyName; if (isReadInnerObject && obj[propertyName] !== null && typeof obj[propertyName] === 'object') { this.ergodicObject(this, obj[propertyName], function (value, key) { return cb.call(context, value, _propertyName + '.' + key); }, true) } else { return cb.call(context, obj[propertyName], propertyName); } }) }, /** * 当指定属性为空或不存在时执行回到函数; * @param context {object} 上下文 * @param obj {object} 检测对象 * @param propertyNames{Array|string} 需要检测的属性名 * 可以检查多级属性如:'a.b.c.e', * 多个属性使用数组,支持纯字符串多个属性用','隔开 * @param cb {function} 回调函数[参数:为空或不存在的属性名,返回值为'-1'时,跳过之后的回调函数] */ whileEmptyObjectProperty: function (context, obj, propertyNames, cb) { // 解析单个属性名 if (typeof propertyNames === 'string') { // 除去所有的空格 propertyNames = propertyNames.replace(/ /g, ''); // 不判断为空的值 if (propertyNames === '') { return; } // 若是以','隔开的伪数组,则转化为数组再进行操作 if (propertyNames.indexOf(',') !== -1) { var propertyNameArr = propertyNames.split(','); return this.whileEmptyObjectProperty(context, obj, propertyNameArr, cb); } // 若指定级联属性 if (propertyNames.indexOf('.') !== -1) { var names = propertyNames.split('.'); var iterationObj = obj; var result = null; ArrayUtils.ergodicArrayObject(this, names, function (name) { if (iterationObj && iterationObj.hasOwnProperty(name)) { iterationObj = iterationObj[name]; } else { result = cb.call(_global, propertyNames); // 终止对接下来的属性的遍历 return -1; } }); return result; } // 正常流程 if (!obj.hasOwnProperty(propertyNames)) { return cb.call(context, propertyNames); } if (obj[propertyNames] === null || obj[propertyNames] === '') { return cb.call(context, propertyNames); } } else if (BaseUtils.isArray(propertyNames)) { // 解析数组 var _this = this; ArrayUtils.ergodicArrayObject(this, propertyNames, function (propertyName) { // 递归调用 return _this.whileEmptyObjectProperty(context, obj, propertyName, cb); }) } }, whileEmptyObjectPropertyV2: function (context, obj, propertyNames, cb) { this.readLinkProperty(obj, propertyNames, function (value, propertyName) { if (value === null || value === '' || parseInt(value) < 0) { return cb.call(context, propertyName); } }) }, /** * 克隆对象[只克隆属性,不克隆原型链] * @param obj {*} */ cloneObject: function (obj) { var newObj = {}; // 判断是否为基本数据类型,若是则直接返回 if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'undefined' || typeof obj === 'function' || typeof obj === 'boolean') { return obj; } // 判断是否是数组 if (BaseUtils.isArray(obj)) { newObj = []; // 遍历数组并递归调用该方法获取数组内部对象的克隆对象并push到新数组 ArrayUtils.ergodicArrayObject(this, obj, function (arrObjValue) { newObj.push(this.cloneObject(arrObjValue)); }) } else if (typeof obj === 'object') { // 当目标为一般对象时即 typeof 为 object if (obj === null) { // 当克隆对象为空时,返回空 return null; } // 遍历对象的属性并调用递归方法获得该属性对应的对象的克隆对象并将其重新赋值到该属性 this.ergodicObject(this, obj, function (value, key) { newObj[key] = this.cloneObject(value); }); } return newObj; }, /** * 获取对象的哈希码 * @param obj {Object} * @returns {number} */ getObjHashCode: function (obj) { var str = JSON.stringify(obj); var hash = 0, i, chr, len; console.log(str) console.log(hash) if (str.length === 0) return hash; for (i = 0, len = str.length; i < len; i++) { chr = str.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } console.log(str) console.log(hash) return hash; }, /** * 扩展对象属性 * @param obj 原对象 * @param extendedObj 被扩展的对象 * @param isCover {boolean=} 扩展的属性和原来属性冲突时是否覆盖 默认[false] * @param isClone {boolean=} 是否返回一个新的对象,默认[false]返回扩展后的原对象 */ expandObject: function (obj, extendedObj, isCover, isClone) { var resultObj = obj; if (isClone) { resultObj = this.cloneObject(obj); } this.ergodicObject(this, extendedObj, function (value, key) { if (isCover && this.readLinkProperty(resultObj, key) !== null) { return; } resultObj = this.createLinkProperty(resultObj, key, value); }, true); return resultObj; }, /** * 为数组排序,当数组中的元素为对象时,根据指定对象的属性名进行排序 * @param arr 数组 * @param propertyName 属性名(当有多个属性名时,为多级排序) * @param order 升降序 * @returns {*} */ sortingArrayByProperty: function (arr, propertyName, order) { var _this = this; var sortSetting = { // 是否创建新数据 createNewData: false, // 通过该方法获取数组中每个对象中用来比较的属性 getComparedProperty: function (arr) { var compareArr = []; ArrayUtils.ergodicArrayObject(_this, arr, function (obj, i) { if (typeof obj !== 'object') { compareArr.push(obj); } else { var compareValue = this.readLinkProperty(obj, propertyName); if (compareValue !== null) { compareArr.push(compareValue); } else { compareArr.push(obj); } } }); return compareArr.slice(0); } }; return ArrayUtils.sortingArrays(arr, order, sortSetting); }, /** * 转话为目标的实例 * @param constructor {function} 构造函数 * @param obj {object|Array}判断的对象 * @param defaultProperty {object=} */ toAimObject: function (obj, constructor, defaultProperty) { if (BaseUtils.isArray(obj)) { var originArr = []; ArrayUtils.ergodicArrayObject(this, obj, function (value) { originArr.push(this.toAimObject(value, constructor, defaultProperty)); }); return originArr; } else if (typeof obj === 'object') { if (defaultProperty) { this.ergodicObject(this, defaultProperty, function (value, key) { if (obj[key] === null) { obj[key] = value; } }); } if (obj instanceof constructor) { return obj; } var originObj = obj; while (originObj.__proto__ !== null && originObj.__proto__ !== Object.prototype) { originObj = originObj.__proto__; } originObj.__proto__ = constructor.prototype; return originObj; } }, /** * 将数组中结构类似对象指定属性融合为一个数组 * @param arr {Array} * @param propertyNames */ parseTheSameObjectPropertyInArray: function (arr, propertyNames) { var result = {}; var temp = {}; ArrayUtils.ergodicArrayObject(this, arr, function (obj) { // 获取想要得到的所有属性,以属性名为键值存储到temp中 this.readLinkProperty(obj, propertyNames, function (value, property) { if (!temp.hasOwnProperty(property) || !(BaseUtils.isArray(temp[property]))) { temp[property] = []; } temp[property].push(value); }); }); // 遍历temp获取每个键值中的值,并单独取出 this.ergodicObject(this, temp, function (value, key) { result = this.createLinkProperty(result, key, value); }); return this.cloneObject(result); }, /** * 将数组中结构类似对象指定属性融合为一个数组 * @param arr {Array} */ parseTheSameObjectAllPropertyInArray: function (arr) { if (!ArrayUtils.isArrayObject(arr) || arr.length < 1) { return; } // 获取一个对象的所有属性,包括内部对象的属性 var propertyNames = []; this.ergodicObject(this, arr[0], function (v, k) { propertyNames.push(k); }, true); return this.parseTheSameObjectPropertyInArray(arr, propertyNames); }, /** * 获取对象属性,若为数组则计算其中数字的平均值或其它 * @param obj * @param propertyNames{Array<string>|string} * @param type */ getCalculationInArrayByLinkPropertyNames: function (obj, propertyNames, type) { var resultObject = {}; var _this = this; switch (type) { default: case 'sum': this.readLinkProperty(obj, propertyNames, function (value, key) { if (ArrayUtils.isArrayObject(value)) { resultObject = _this.createLinkProperty(resultObject, key, ArrayUtils.getSumInArray(value)); } }); break; case 'average': this.readLinkProperty(obj, propertyNames, function (value, key) { if (ArrayUtils.isArrayObject(value)) { resultObject = _this.createLinkProperty(resultObject, key, ArrayUtils.getAverageInArray(value)); } }); break; } return resultObject; } } }); // ColorUtils factory('ColorUtils', [], function () { return { /** * 转换颜色rgb为16进制 * @param r * @param g * @param b * @return {string} */ rgbToHex: function (r, g, b) { var hex = ((r << 16) | (g << 8) | b).toString(16); return "#" + new Array(Math.abs(hex.length - 7)).join("0") + hex; }, /** * 转换颜色16进制为rgb * @param hex * @return {Array} */ hexToRgb: function (hex) { hex = hex.replace(/ /g, ''); var length = hex.length; var rgb = []; switch (length) { case 4: rgb.push(parseInt(hex[1] + hex[1], 16)); rgb.push(parseInt(hex[2] + hex[2], 16)); rgb.push(parseInt(hex[3] + hex[3], 16)); return rgb; case 7: for (var i = 1; i < 7; i += 2) { rgb.push(parseInt("0x" + hex.slice(i, i + 2))); } return rgb; default: break } }, /** * 根据两个颜色以及之间的百分比获取渐进色 * @param start * @param end * @param percentage * @return {*} */ gradientColorsPercentage: function (start, end, percentage) { var resultRgb = []; var startRgb = this.hexToRgb(start); if (end == null) { return start; } var endRgb = this.hexToRgb(end); var totalR = endRgb[0] - startRgb[0]; var totalG = endRgb[1] - startRgb[1]; var totalB = endRgb[2] - startRgb[2]; resultRgb.push(startRgb[0] + totalR * percentage); resultRgb.push(startRgb[1] + totalG * percentage); resultRgb.push(startRgb[2] + totalB * percentage); return this.rgbToHex(resultRgb[0], resultRgb[1], resultRgb[2]) } } }); factory('FunctionUtils', [], function () { return { /** * 获取方法的名字 * @param func * @returns {*} */ getFunctionName: function (func) { if (typeof func === 'function' || typeof func === 'object') { var name = ('' + func).match(/function\s*([\w\$]*)\s*\(/); } return func.name || name[1]; }, /** * 获取方法的参数名 * @param func * @returns {*} */ getFunctionParams: function (func) { if (typeof func === 'function' || typeof func === 'object') { var name = ('' + func).match(/function.*\(([^)]*)\)/); return name[1].replace(/( )|(\n)/g, '').split(','); } }, /** * 通过方法的arguments获取调用该方法的函数 * @param func_arguments * @returns {string} */ getCallerName: function (func_arguments) { var caller = func_arguments.callee.caller; var callerName = ''; if (caller) { callerName = this.getFunctionName(caller); } return callerName; }, FunctionBuilder: function (func) { var _this = this; var fs = []; fs.push(func); var properties = ['push', 'unshift', 'slice', 'map', 'forEach', 'keys', 'find', 'concat', 'fill', 'shift', 'values']; map.call(properties, function (property) { if (typeof Array.prototype[property] === 'function') { Object.defineProperty(_this, property, { get: function () { return function () { fs[property].apply(fs, arguments); return this; } } }); } }); this.result = function (context) { var rfs = []; map.call(fs, function (f, index) { if (typeof f === 'function') { rfs.push(f); } }); return function () { var declareVar = { arguments: arguments, this: this }; map.call(rfs, function (f) { var dv = f.apply(context || this, [declareVar]); if (dv) { map.call(Object.keys(dv), function (key) { declareVar[key] = dv[key]; }); } }); return declareVar.returnValue; } } }, invokeMethods: function (context, methods, args) { if (!this.isArray(methods)) { return; } var results = []; var _this = this; this.ergodicArrayObject(context, methods, function (method) { if (!_this.isFunction(method)) { return; } results.push( method.apply(context, args) ); }); return results; } } }); factory('UrlUtils', [], function () { return { urlMatching: function (url, matchUrl) { var pattern = new RegExp(matchUrl); return pattern.test(url); }, getUrlWithoutParam: function (url) { return url.split('?')[0]; }, getParamFromUrl: function (url) { var params = []; var parts = url.split('?'); if (parts.length < 2) { return params; } var paramsStr = parts[1].split('&'); for (var i = 0; i < paramsStr.length; i++) { var index = paramsStr[i].indexOf('='); var ps = new Array(2); if (index !== -1) { ps = [ paramsStr[i].substring(0, index), paramsStr[i].substring(index + 1), ]; } else { ps[0] = paramsStr[i]; } params.push({ key: ps[0], value: ps[1] }); } return params; }, margeUrlAndParams: function (url, params) { if (url.indexOf('?') !== -1 || !(params instanceof Array)) { return url; } var paramsStr = []; for (var i = 0; i < params.length; i++) { if (params[i].key !== null && params[i].value !== null) { paramsStr.push(params[i].key + '=' + params[i].value); } } return url + '?' + paramsStr.join('&'); } } }); factory('PointUtils', [], function () { var Point2D = function (x, y) { this.x = x || 0; this.y = y || 0; }; Point2D.prototype = { constructor: Point2D, /** * 获取指定距离和角度对应的平面点 * @param distance * @param deg */ getOtherPointFromDistanceAndDeg: function (distance, deg) { var radian = Math.PI / 180 * deg; var point = new this.constructor(); point.x = distance * Math.sin(radian) + this.x; point.y = this.y - distance * Math.cos(radian); return point; }, /** * 获取当前平面点与另一个平面点之间的距离 * @param p * @returns {number} */ getDistanceFromAnotherPoint: function (p) { return Math.sqrt((this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y)); }, /** * 获取当前平面点与另一个平面点之间的角度 * @param p * @returns {number} */ getDegFromAnotherPoint: function (p) { var usedPoint = new Point2D(p.x * 1000000 - this.x * 1000000, p.y * 1000000 - this.y * 1000000); var radian = Math.atan2(usedPoint.x * 1000000, usedPoint.y * 1000000); var deg = radian * 180 / Math.PI; return 180 - deg; }, /** * 判断该点是否位于一矩形内部 * @param x 矩形开始坐标x * @param y 矩形开始坐标y * @param width 矩形宽 * @param height 矩形长 * @returns {boolean} */ isInRect: function (x, y, width, height) { var px = this.x; var py = this.y; if (px < x || px > x + width) { return false; } return !(py < y || py > y + height); } }; return { Point2D: Point2D } }); factory('PropExpand', ['BaseUtils', 'ObjectUtils', 'ArrayUtils', 'UrlUtils'], function (BaseUtils, ObjectUtils, ArrayUtils, UrlUtils) { return { Object: { getProperty: function (_self, propertyLink) { return ObjectUtils.readLinkProperty(_self, propertyLink); }, setProperty: function (_self, propertyLink, value) { ObjectUtils.createLinkProperty(_self, propertyLink, value); }, mapConvert: function (_self, mapper) { }, keyMap: function (_self, cb) { }, keyValues: function (_self, cb) { }, keyFilter: function (_self, cb) { }, }, Array: { map: function () { }, forEach: function () { }, filter: function () { }, reduce: function () { }, keep: function () { }, remove: function () { } }, String: { join: function (_self, arr) { }, } } }); _global.everyUtils = function () { if (BaseUtils.isArray(arguments[0])) { depend.call(arguments[2] || this, arguments[0], arguments[1]); } else if (BaseUtils.isFunction(arguments[0])) { var args = arguments; depend.call(arguments[1] || this, ['FunctionUtils'], function (FunctionUtils) { var depends = FunctionUtils.getFunctionParams(args[0]); depend(depends, args[0]); }) } }; _global.eUtils = (function () { var utils = {}; if (_global.everyUtils) { _global.everyUtils([ 'ArrayUtils', 'ObjectUtils', 'BaseUtils', 'FunctionUtils', 'ColorUtils', 'PointUtils', 'UrlUtils' ], function ( ArrayUtils, ObjectUtils, BaseUtils, FunctionUtils, ColorUtils, PointUtils, UrlUtils) { utils = { ArrayUtils: ArrayUtils, ObjectUtils: ObjectUtils, BaseUtils: BaseUtils, ColorUtils: ColorUtils, UrlUtils: UrlUtils, urlUtils: UrlUtils, PointUtils: PointUtils, FunctionUtils: FunctionUtils }; }); } var proxy = {}; forEach.call(Object.keys(utils), function (utilName) { if (!utilName) { return; } Object.defineProperty(proxy, utilName, { get: function () { return utils[utilName]; } }); forEach.call(Object.keys(utils[utilName]), function (key) { if (!key) { return; } if (proxy[key]) { return; } Object.defineProperty(proxy, key, { get: function () { return utils[utilName][key]; } }) }) }); return proxy; })(); return _global.eUtils; })); /***/ }) /******/]); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if (__webpack_module_cache__[moduleId]) { /******/ return __webpack_module_cache__[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ // module exports must be returned from runtime so entry inlining is disabled /******/ // startup /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ })() ; }); // var path = require('path') // // alert(path.join(__dirname, 'everyHook.js')) // require(path.join(__dirname, '/js/everyHook.js')) // // document.addEventListener('readystatechange', function () { // // if (document.readyState === "interactive" || document.readyState === "complete") { // // window.isDOMLoaded = true; // // } // // }); ~function (global) { var workerURLs = []; var extraElements = []; var suppressEvents = {}; var helper = function (eHookContext, timerContext, util) { return { applyUI: function () { var style = '._th-container ._th-item{margin-bottom:3px;position:relative;width:0;height:0;cursor:pointer;opacity:.3;background-color:#ffffff;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:#ffffff;text-align:center;line-height:45px;right:-10px}._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:9999999999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}._th-container ._th-item:hover{opacity:.8;background-color:#87CEFA;color:aliceblue}._th-container ._th-item:active{opacity:.9;background-color:#87CEFA;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:#87CEFA;color:aliceblue}._th_cover-all-show-times{position:fixed;top:0;right:0;width:100%;height:100%;z-index:9999999;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:-99999999;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}'; var displayNum = (1 / timerContext._percentage).toFixed(2); // 在页面左边添加一个半圆便于修改 var html = '<div class="_th-container">\n' + ' <div class="_th-click-hover _item-input">\n' + ' x' + displayNum + '\n' + ' </div>\n' + ' <div class="_th-item _item-x2">></div>\n' + ' <div class="_th-item _item-x-2"><</div>\n' + ' <div class="_th-item _item-xx2">>></div>\n' + ' <div class="_th-item _item-xx-2"><<</div>\n' + ' <div class="_th-item _item-reset">O</div>\n' + '</div>\n' + '<div class="_th_cover-all-show-times _th_hidden">\n' + ' <div class="_th_times">x' + displayNum + '</div>\n' + '</div>' + ''; var stylenode = document.createElement('style'); stylenode.setAttribute("type", "text/css"); if (stylenode.styleSheet) {// IE stylenode.styleSheet.cssText = style; } else {// w3c var cssText = document.createTextNode(style); stylenode.appendChild(cssText); } var node = document.createElement('div'); node.innerHTML = html; var clickMapper = { '_item-input': function () { changeTime(); }, '_item-x2': function () { changeTime(2, 0, true); }, '_item-x-2': function () { changeTime(-2, 0, true); }, '_item-xx2': function () { changeTime(0, 2); }, '_item-xx-2': function () { changeTime(0, -2); }, '_item-reset': function () { changeTime(0, 0, false, true); } }; Object.keys(clickMapper).forEach(function (className) { var exec = clickMapper[className]; var targetEle = node.getElementsByClassName(className)[0]; if (targetEle) { targetEle.onclick = exec; } }); if (!global.isDOMLoaded) { // document.head.appendChild(stylenode); // document.body.appendChild(node); global.isDOMRendered = true; console.log('Time Hooker Works!'); // document.addEventListener('readystatechange', function () { // if ((document.readyState === "interactive" || document.readyState === "complete") && !global.isDOMRendered) { // } // }); } else { // document.head.appendChild(stylenode); // document.body.appendChild(node); global.isDOMRendered = true; console.log('Time Hooker Works!'); } }, applyGlobalAction: function (timer) { // 界面半圆按钮点击的方法 timer.changeTime = function (anum, cnum, isa, isr) { if (isr) { global.timer.change(1); return; } if (!global.timer) { return; } var result; if (!anum && !cnum) { return // var t = prompt("输入欲改变计时器变化倍率(当前:" + 1 / timerContext._percentage + ")"); // if (t == null) { // return; // } // if (isNaN(parseFloat(t))) { // alert("请输入正确的数字"); // timer.changeTime(); // return; // } // if (parseFloat(t) <= 0) { // alert("倍率不能小于等于0"); // timer.changeTime(); // return; // } // result = 1 / parseFloat(t); } else { if (isa && anum) { if (1 / timerContext._percentage <= 1 && anum < 0) { return; } result = 1 / (1 / timerContext._percentage + anum); } else { if (cnum <= 0) { cnum = 1 / -cnum } result = 1 / ((1 / timerContext._percentage) * cnum); } } timer.change(result); }; global.changeTime = timer.changeTime; }, applyHooking: function () { var _this = this; // 劫持循环计时器 eHookContext.hookReplace(window, 'setInterval', function (setInterval) { return _this.getHookedTimerFunction('interval', setInterval); }); // 劫持单次计时 eHookContext.hookReplace(window, 'setTimeout', function (setTimeout) { return _this.getHookedTimerFunction('timeout', setTimeout) }); // 劫持循环计时器的清除方法 eHookContext.hookBefore(window, 'clearInterval', function (method, args) { _this.redirectNewestId(args); }); // 劫持循环计时器的清除方法 eHookContext.hookBefore(window, 'clearTimeout', function (method, args) { _this.redirectNewestId(args); }); var newFunc = this.getHookedDateConstructor(); eHookContext.hookClass(window, 'Date', newFunc, '_innerDate', ['now']); Date.now = function () { // window.performance.now return new Date().getTime(); }; eHookContext.hookedToString(timerContext._Date.now, Date.now); var objToString = Object.prototype.toString; Object.prototype.toString = function toString() { 'use strict'; if (this instanceof timerContext._mDate) { return '[object Date]'; } else { return objToString.call(this); } }; eHookContext.hookedToString(objToString, Object.prototype.toString); eHookContext.hookedToString(timerContext._setInterval, setInterval); eHookContext.hookedToString(timerContext._setTimeout, setTimeout); eHookContext.hookedToString(timerContext._clearInterval, clearInterval); timerContext._mDate = window.Date; this.hookShadowRoot(); }, getHookedDateConstructor: function () { return function () { if (arguments.length === 1) { Object.defineProperty(this, '_innerDate', { configurable: false, enumerable: false, value: new timerContext._Date(arguments[0]), writable: false }); return; } else if (arguments.length > 1) { var definedValue; switch (arguments.length) { case 2: definedValue = new timerContext._Date( arguments[0], arguments[1] ); break; case 3: definedValue = new timerContext._Date( arguments[0], arguments[1], arguments[2], ); break; case 4: definedValue = new timerContext._Date( arguments[0], arguments[1], arguments[2], arguments[3], ); break; case 5: definedValue = new timerContext._Date( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); break; case 6: definedValue = new timerContext._Date( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); break; default: case 7: definedValue = new timerContext._Date( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); break; } Object.defineProperty(this, '_innerDate', { configurable: false, enumerable: false, value: definedValue, writable: false }); return; } var now = timerContext._Date.now(); var passTime = now - timerContext.__lastDatetime; var hookPassTime = passTime * (1 / timerContext._percentage); // console.log(__this.__lastDatetime + hookPassTime, now,__this.__lastDatetime + hookPassTime - now); Object.defineProperty(this, '_innerDate', { configurable: false, enumerable: false, value: new timerContext._Date(timerContext.__lastMDatetime + hookPassTime), writable: false }); }; }, getHookedTimerFunction: function (type, timer) { var property = '_' + type + 'Ids'; return function () { var uniqueId = timerContext.genUniqueId(); var callback = arguments[0]; if (typeof callback === 'string') { callback += ';timer.notifyExec(' + uniqueId + ')'; arguments[0] = callback; } if (typeof callback === 'function') { arguments[0] = function () { var returnValue = callback.apply(this, arguments); timerContext.notifyExec(uniqueId); return returnValue; } } // 储存原始时间间隔 var originMS = arguments[1]; // 获取变速时间间隔 arguments[1] *= timerContext._percentage; var resultId = timer.apply(window, arguments); // 保存每次使用计时器得到的id以及参数等 timerContext[property][resultId] = { args: arguments, originMS: originMS, originId: resultId, nowId: resultId, uniqueId: uniqueId, oldPercentage: timerContext._percentage, exceptNextFireTime: timerContext._Date.now() + originMS }; return resultId; }; }, redirectNewestId: function (args) { var id = args[0]; if (timerContext._intervalIds[id]) { args[0] = timerContext._intervalIds[id].nowId; // 清除该记录id delete timerContext._intervalIds[id]; } if (timerContext._timeoutIds[id]) { args[0] = timerContext._timeoutIds[id].nowId; // 清除该记录id delete timerContext._timeoutIds[id]; } }, registerShortcutKeys: function (timer) { // 快捷键注册 addEventListener('keydown', function (e) { switch (e.keyCode) { case 57: if (e.ctrlKey || e.altKey) { // custom timer.changeTime(); } break; // [=] case 190: case 187: { if (e.ctrlKey) { // console.log('+2'); timer.changeTime(2, 0, true); } else if (e.altKey) { // console.log('xx2'); timer.changeTime(0, 2); } break; } // [-] case 188: case 189: { if (e.ctrlKey) { // console.log('-2'); timer.changeTime(-2, 0, true); } else if (e.altKey) { // console.log('xx-2'); timer.changeTime(0, -2); } break; } // [0] case 48: { if (e.ctrlKey || e.altKey) { // console.log('reset'); timer.changeTime(0, 0, false, true); } break; } default: // console.log(e); } }); }, /** * 当计时器速率被改变时调用的回调方法 * @param percentage * @private */ percentageChangeHandler: function (percentage) { // 改变所有的循环计时 util.ergodicObject(timerContext, timerContext._intervalIds, function (idObj, id) { idObj.args[1] = Math.floor((idObj.originMS || 1) * percentage); // 结束原来的计时器 this._clearInterval.call(window, idObj.nowId); // 新开一个计时器 idObj.nowId = this._setInterval.apply(window, idObj.args); }); // 改变所有的延时计时 util.ergodicObject(timerContext, timerContext._timeoutIds, function (idObj, id) { var now = this._Date.now(); var exceptTime = idObj.exceptNextFireTime; var oldPercentage = idObj.oldPercentage; var time = exceptTime - now; if (time < 0) { time = 0; } var changedTime = Math.floor(percentage / oldPercentage * time); idObj.args[1] = changedTime; // 重定下次执行时间 idObj.exceptNextFireTime = now + changedTime; idObj.oldPercentage = percentage; // 结束原来的计时器 this._clearTimeout.call(window, idObj.nowId); // 新开一个计时器 idObj.nowId = this._setTimeout.apply(window, idObj.args); }); }, hookShadowRoot: function () { var origin = Element.prototype.attachShadow; eHookContext.hookAfter(Element.prototype, 'attachShadow', function (m, args, result) { extraElements.push(result); return result; }, false); eHookContext.hookedToString(origin, Element.prototype.attachShadow); }, hookDefine: function () { const _this = this; eHookContext.hookBefore(Object, 'defineProperty', function (m, args) { var option = args[2]; var ele = args[0]; var key = args[1]; var afterArgs = _this.hookDefineDetails(ele, key, option); afterArgs.forEach((arg, i) => { args[i] = arg; }) }); eHookContext.hookBefore(Object, 'defineProperties', function (m, args) { var option = args[1]; var ele = args[0]; if (ele && ele instanceof Element) { Object.keys(option).forEach(key => { var o = option[key]; var afterArgs = _this.hookDefineDetails(ele, key, o); args[0] = afterArgs[0]; delete option[key]; option[afterArgs[1]] = afterArgs[2] }) } }) }, hookDefineDetails: function (target, key, option) { if (option && target && target instanceof Element && typeof key === 'string' && key.indexOf('on') >= 0) { option.configurable = true; } if (target instanceof HTMLVideoElement && key === 'playbackRate') { option.configurable = true; console.warn('[Timer Hook]', '已阻止默认操作视频倍率'); key = 'playbackRate_hooked' } return [target, key, option]; }, suppressEvent: function (ele, eventName) { if (ele) { delete ele['on' + eventName]; delete ele['on' + eventName]; delete ele['on' + eventName]; ele['on' + eventName] = undefined; } if (!suppressEvents[eventName]) { eHookContext.hookBefore(EventTarget.prototype, 'addEventListener', function (m, args) { var eName = args[0]; if (eventName === eName) { console.warn(eventName, 'event suppressed.') args[0] += 'suppressed'; } }, false); suppressEvents[eventName] = true; } }, changePlaybackRate: function (ele, rate) { delete ele.playbackRate; delete ele.playbackRate; delete ele.playbackRate; ele.playbackRate = rate if (rate !== 1) { timerContext.defineProperty.call(Object, ele, 'playbackRate', { configurable: true, get: function () { return 1; }, set: function () { } }); } } } }; var normalUtil = { isInIframe: function () { let is = global.parent !== global; try { is = is && global.parent.document.body.tagName !== 'FRAMESET' } catch (e) { // ignore } return is; }, listenParentEvent: function (handler) { global.addEventListener('message', function (e) { var data = e.data; var type = data.type || ''; if (type === 'changePercentage') { handler(data.percentage || 0); } }) }, sentChangesToIframe: function (percentage) { var iframes = document.querySelectorAll('iframe') || []; var frames = document.querySelectorAll('frame'); console.error(iframes) console.error(frames) if (iframes.length) { for (var i = 0; i < iframes.length; i++) { iframes[i].contentWindow.postMessage( { type: 'changePercentage', percentage: percentage }, '*'); } } if (frames.length) { for (var j = 0; j < frames.length; j++) { frames[j].contentwindow.postMessage( { type: 'changePercentage', percentage: percentage }, '*'); } } } }; var querySelectorAll = function (ele, selector, includeExtra) { var elements = ele.querySelectorAll(selector); elements = Array.prototype.slice.call(elements || []); if (includeExtra) { extraElements.forEach(function (element) { elements = elements.concat(querySelectorAll(element, selector, false)); }) } return elements; }; var generate = function () { return function (util) { // disable worker workerURLs.forEach(function (url) { if (util.urlMatching(location.href, 'http.*://.*' + url + '.*')) { window['Worker'] = undefined; alert('Worker disabled'); } }); var eHookContext = this; var timerHooker = { // 用于储存计时器的id和参数 _intervalIds: {}, _timeoutIds: {}, _auoUniqueId: 1, // 计时器速率 __percentage: 1.0, // 劫持前的原始的方法 _setInterval: window['setInterval'], _clearInterval: window['clearInterval'], _clearTimeout: window['clearTimeout'], _setTimeout: window['setTimeout'], _Date: window['Date'], __lastDatetime: new Date().getTime(), __lastMDatetime: new Date().getTime(), videoSpeedInterval: 1000, defineProperty: Object.defineProperty, defineProperties: Object.defineProperties, genUniqueId: function () { return this._auoUniqueId++; }, notifyExec: function (uniqueId) { var _this = this; if (uniqueId) { // 清除 timeout 所储存的记录 var timeoutInfos = Object.values(this._timeoutIds).filter( function (info) { return info.uniqueId === uniqueId; } ); timeoutInfos.forEach(function (info) { _this._clearTimeout.call(window, info.nowId); delete _this._timeoutIds[info.originId] }) } // console.log(uniqueId, 'called') }, /** * 初始化方法 */ init: function () { var timerContext = this; var h = helper(eHookContext, timerContext, util); h.hookDefine(); h.applyHooking(); // 设定百分比属性被修改的回调 Object.defineProperty(timerContext, '_percentage', { get: function () { return timerContext.__percentage; }, set: function (percentage) { if (percentage === timerContext.__percentage) { return percentage; } h.percentageChangeHandler(percentage); timerContext.__percentage = percentage; return percentage; } }); if (!normalUtil.isInIframe()) { console.log('[TimeHooker]', 'loading outer window...'); h.applyUI(); h.applyGlobalAction(timerContext); h.registerShortcutKeys(timerContext); } else { console.log('[TimeHooker]', 'loading inner window...'); normalUtil.listenParentEvent((function (percentage) { console.log('[TimeHooker]', 'Inner Changed', percentage) this.change(percentage); }).bind(this)) } }, /** * 调用该方法改变计时器速率 * @param percentage */ change: function (percentage) { this.__lastMDatetime = this._mDate.now(); this.__lastDatetime = this._Date.now(); this._percentage = percentage; var oldNode = document.getElementsByClassName('_th-click-hover'); var oldNode1 = document.getElementsByClassName('_th_times'); var displayNum = (1 / this._percentage).toFixed(2); (oldNode[0] || {}).innerHTML = 'x' + displayNum; (oldNode1[0] || {}).innerHTML = 'x' + displayNum; var a = document.getElementsByClassName('_th_cover-all-show-times')[0] || {}; a.className = '_th_cover-all-show-times'; this._setTimeout.bind(window)(function () { a.className = '_th_cover-all-show-times _th_hidden'; }, 2000); // this.changeVideoSpeed(); normalUtil.sentChangesToIframe(percentage); }, changeVideoSpeed: function () { var timerContext = this; var h = helper(eHookContext, timerContext, util); var rate = 1 / this._percentage; rate > 16 && (rate = 16); rate < 0.065 && (rate = 0.065); var videos = querySelectorAll(document, 'video', true) || []; if (videos.length) { for (var i = 0; i < videos.length; i++) { h.changePlaybackRate(videos[i], rate); } } } }; // 默认初始化 timerHooker.init(); return timerHooker; } }; if (global.eHook) { global.eHook.plugins({ name: 'timer', /** * 插件装载 * @param util */ mount: generate() }); } }(window); window.performance.now = Date.now; } // 设置延迟时间(毫秒) var delayMilliseconds = 5000; // 5秒 // 使用setTimeout来延迟执行函数 setTimeout(speedHook, delayMilliseconds); })()