您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
install XHook
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://updategreasyfork.deno.dev/scripts/15379/96218/installXHook.js
// installXHook - v0.0.1 ////////// Original copyright notice ////////// // XHook - v1.3.3 - https://github.com/jpillora/xhook // Jaime Pillora <[email protected]> - MIT Copyright 2015 function installXHook(window) { 'use strict'; var AFTER, BEFORE, COMMON_EVENTS, FIRE, FormData, NativeFormData, NativeXMLHttp, OFF, ON, READY_STATE, UPLOAD_EVENTS, XMLHTTP, document, msie, xhook; //for compression document = window.document; BEFORE = 'before'; AFTER = 'after'; READY_STATE = 'readyState'; ON = 'addEventListener'; OFF = 'removeEventListener'; FIRE = 'dispatchEvent'; XMLHTTP = 'XMLHttpRequest'; FormData = 'FormData'; //parse IE version UPLOAD_EVENTS = ['load', 'loadend', 'loadstart']; COMMON_EVENTS = ['progress', 'abort', 'error', 'timeout']; msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); if (isNaN(msie)) { msie = parseInt((/trident\/.*; rv:(\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); } //if required, add 'indexOf' method to Array if (!('indexOf' in Array.prototype)) { Array.prototype.indexOf = function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) { return i; } } return -1; }; } function slice(o, n) { return Array.prototype.slice.call(o, n); } function depricatedProp(p) { return p === 'returnValue' || p === 'totalSize' || p === 'position'; } function mergeObjects(src, dst) { var k; for (k in src) { if (depricatedProp(k)) { continue; } try { dst[k] = src[k]; } catch (_error) {} } return dst; } //proxy events from one emitter to another function proxyEvents(events, src, dst) { var event, i, len; function p(event) { return function(e) { var clone, k, val; clone = {}; //copies event, with dst emitter inplace of src for (k in e) { if (depricatedProp(k)) { continue; } val = e[k]; clone[k] = val === src ? dst : val; } //emits out the dst return dst[FIRE](event, clone); }; } //dont proxy manual events for (i = 0, len = events.length; i < len; i++) { event = events[i]; if (dst._has(event)) { src['on' + event] = p(event); } } } //create fake event function fakeEvent(type) { var msieEventObject; if (document.createEventObject != null) { msieEventObject = document.createEventObject(); msieEventObject.type = type; return msieEventObject; } else { // on some platforms like android 4.1.2 and safari on windows, it appears // that new Event is not allowed try { return new Event(type); } catch (_error) { return { type: type }; } } } //tiny event emitter function EventEmitter(nodeStyle) { var emitter, events; //private events = {}; function listeners(event) { return events[event] || []; } //public emitter = {}; emitter[ON] = function(event, callback, i) { events[event] = listeners(event); if (events[event].indexOf(callback) >= 0) { return; } if (i === void 0) { i = events[event].length; } events[event].splice(i, 0, callback); }; emitter[OFF] = function(event, callback) { var i; //remove all if (event === void 0) { events = {}; return; } //remove all of type event if (callback === void 0) { events[event] = []; } //remove particular handler i = listeners(event).indexOf(callback); if (i === -1) { return; } listeners(event).splice(i, 1); }; emitter[FIRE] = function() { var args, event, i, legacylistener, listener, _i, _len, _ref; args = slice(arguments); event = args.shift(); if (!nodeStyle) { args[0] = mergeObjects(args[0], fakeEvent(event)); } legacylistener = emitter['on' + event]; if (legacylistener) { legacylistener.apply(void 0, args); } _ref = listeners(event).concat(listeners('*')); for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { listener = _ref[i]; listener.apply(void 0, args); } }; emitter._has = function(event) { return !!(events[event] || emitter['on' + event]); }; //add extra aliases if (nodeStyle) { emitter.listeners = function(event) { return slice(listeners(event)); }; emitter.on = emitter[ON]; emitter.off = emitter[OFF]; emitter.fire = emitter[FIRE]; emitter.once = function(e, fn) { function fire() { emitter.off(e, fire); return fn.apply(null, arguments); } return emitter.on(e, fire); }; emitter.destroy = function() { events = {}; }; } return emitter; } //use event emitter to store hooks xhook = EventEmitter(true); xhook.EventEmitter = EventEmitter; xhook[BEFORE] = function(handler, i) { if (handler.length < 1 || handler.length > 2) { throw 'invalid hook'; } return xhook[ON](BEFORE, handler, i); }; xhook[AFTER] = function(handler, i) { if (handler.length < 2 || handler.length > 3) { throw 'invalid hook'; } return xhook[ON](AFTER, handler, i); }; xhook.enable = function() { window[XMLHTTP] = XHookHttpRequest; if (NativeFormData) { window[FormData] = XHookFormData; } return xhook; }; xhook.disable = function() { window[XMLHTTP] = xhook[XMLHTTP]; if (NativeFormData) { window[FormData] = NativeFormData; } return xhook; }; //helper function convertHeaders(h, dest) { var header, headers, k, name, v, value, _i, _len, _ref; if (dest == null) { dest = {}; } switch (typeof h) { case 'object': headers = []; for (k in h) { v = h[k]; name = k.toLowerCase(); headers.push('' + name + ':\t' + v); } return headers.join('\n'); case 'string': headers = h.split('\n'); for (_i = 0, _len = headers.length; _i < _len; _i++) { header = headers[_i]; if (/([^:]+):\s*(.+)/.test(header)) { name = RegExp.$1.toLowerCase(); value = RegExp.$2; if (dest[name] == null) { dest[name] = value; } } } return dest; } } xhook.headers = convertHeaders; //patch FormData // we can do this safely because all XHR // is hooked, so we can ensure the real FormData // object is used on send NativeFormData = window[FormData]; function XHookFormData(form) { var entries; this.fd = form ? new NativeFormData(form) : new NativeFormData(); this.form = form; entries = []; Object.defineProperty(this, 'entries', { get: function() { var fentries; //extract form entries fentries = !form ? [] : slice(form.querySelectorAll('input,select')).filter(function(e) { var _ref; return ((_ref = e.type) !== 'checkbox' && _ref !== 'radio') || e.checked; }).map(function(e) { return [e.name, e.type === 'file' ? e.files : e.value]; }); //combine with js entries return fentries.concat(entries); } }); this.append = (function(_this) { return function() { var args; args = slice(arguments); entries.push(args); return _this.fd.append.apply(_this.fd, args); }; })(this); } if (NativeFormData) { //expose native formdata as xhook.FormData incase its needed xhook[FormData] = NativeFormData; } //patch XHR NativeXMLHttp = window[XMLHTTP]; xhook[XMLHTTP] = NativeXMLHttp; function XHookHttpRequest() { var ABORTED, currentState, facade, hasError, request, response, status, transiting, xhr; ABORTED = -1; xhr = new xhook[XMLHTTP](); //========================== // Extra state request = {}; status = null; hasError = void 0; transiting = void 0; response = void 0; //========================== // Private API //read results from real xhr into response function readHead() { var key, name, val, _ref; // Accessing attributes on an aborted xhr object will // throw an 'c00c023f error' in IE9 and lower, don't touch it. response.status = status || xhr.status; if (!(status === ABORTED && msie < 10)) { response.statusText = xhr.statusText; } if (status !== ABORTED) { _ref = convertHeaders(xhr.getAllResponseHeaders()); for (key in _ref) { val = _ref[key]; if (!response.headers[key]) { name = key.toLowerCase(); response.headers[name] = val; } } } } function readBody() { //https://xhr.spec.whatwg.org/ if (!xhr.responseType || xhr.responseType === 'text') { response.text = xhr.responseText; response.data = xhr.responseText; } else if (xhr.responseType === 'document') { response.xml = xhr.responseXML; response.data = xhr.responseXML; } else { response.data = xhr.response; } //new in some browsers if ('responseURL' in xhr) { response.finalUrl = xhr.responseURL; } } //write response into facade xhr function writeHead() { facade.status = response.status; facade.statusText = response.statusText; } function writeBody() { if ('text' in response) { facade.responseText = response.text; } if ('xml' in response) { facade.responseXML = response.xml; } if ('data' in response) { facade.response = response.data; } if ('finalUrl' in response) { facade.responseURL = response.finalUrl; } } //ensure ready state 0 through 4 is handled function emitReadyState(n) { while (n > currentState && currentState < 4) { facade[READY_STATE] = ++currentState; // make fake events for libraries that actually check the type on // the event object if (currentState === 1) { facade[FIRE]('loadstart', {}); } if (currentState === 2) { writeHead(); } if (currentState === 4) { writeHead(); writeBody(); } facade[FIRE]('readystatechange', {}); //delay final events incase of error if (currentState === 4) { setTimeout(emitFinal, 0); } } } function emitFinal() { if (!hasError) { facade[FIRE]('load', {}); } facade[FIRE]('loadend', {}); if (hasError) { facade[READY_STATE] = 0; } } //control facade ready state currentState = 0; function setReadyState(n) { var hooks; //emit events until readyState reaches 4 if (n !== 4) { emitReadyState(n); return; } //before emitting 4, run all 'after' hooks in sequence hooks = xhook.listeners(AFTER); function process() { var hook; if (!hooks.length) { emitReadyState(4); return; } hook = hooks.shift(); if (hook.length === 2) { hook(request, response); process(); } else if (hook.length === 3 && request.async) { hook(request, response, process); } else { process(); } } process(); } //========================== // Facade XHR facade = request.xhr = EventEmitter(); //========================== // Handle the underlying ready state xhr.onreadystatechange = function(event) { //pull status and headers try { if (xhr[READY_STATE] === 2) { readHead(); } } catch (_error) {} //pull response data if (xhr[READY_STATE] === 4) { transiting = false; readHead(); readBody(); } setReadyState(xhr[READY_STATE]); }; //mark this xhr as errored function hasErrorHandler() { hasError = true; } facade[ON]('error', hasErrorHandler); facade[ON]('timeout', hasErrorHandler); facade[ON]('abort', hasErrorHandler); // progress means we're current downloading... facade[ON]('progress', function() { //progress events are followed by readystatechange for some reason... if (currentState < 3) { setReadyState(3); } else { facade[FIRE]('readystatechange', {}); //TODO fake an XHR event } }); // initialise 'withCredentials' on facade xhr in browsers with it // or if explicitly told to do so if ('withCredentials' in xhr || xhook.addWithCredentials) { facade.withCredentials = false; } facade.status = 0; facade.open = function(method, url, async, user, pass) { // Initailize empty XHR facade currentState = 0; hasError = false; transiting = false; request.headers = {}; request.headerNames = {}; request.status = 0; response = {}; response.headers = {}; request.method = method; request.url = url; request.async = async !== false; request.user = user; request.pass = pass; // openned facade xhr (not real xhr) setReadyState(1); }; facade.send = function(body) { var hooks, k, modk, _i, _len, _ref; //read xhr settings before hooking _ref = ['type', 'timeout', 'withCredentials']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { k = _ref[_i]; modk = k === 'type' ? 'responseType' : k; if (modk in facade) { request[k] = facade[modk]; } } request.body = body; function send() { var header, value, _i, _len, _ref; //proxy all events from real xhr to facade proxyEvents(COMMON_EVENTS, xhr, facade); if (facade.upload) { proxyEvents(COMMON_EVENTS.concat(UPLOAD_EVENTS), xhr.upload, facade.upload); } //prepare request all at once transiting = true; //perform open xhr.open(request.method, request.url, request.async, request.user, request.pass); //write xhr settings _ref = ['type', 'timeout', 'withCredentials']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { k = _ref[_i]; modk = k === 'type' ? 'responseType' : k; if (k in request) { xhr[modk] = request[k]; } } //insert headers _ref = request.headers; for (header in _ref) { value = _ref[header]; xhr.setRequestHeader(header, value); } //extract real formdata if (request.body instanceof XHookFormData) { request.body = request.body.fd; } //real send! xhr.send(request.body); } hooks = xhook.listeners(BEFORE); //process hooks sequentially function process() { var hook; if (!hooks.length) { return send(); } //go to next hook OR optionally provide response function done(userResponse) { //break chain - provide dummy response (readyState 4) if (typeof userResponse === 'object' && (typeof userResponse.status === 'number' || typeof response.status === 'number')) { if (!('data' in userResponse)) { userResponse.data = userResponse.response || userResponse.text; } mergeObjects(userResponse, response); setReadyState(4); return; } //continue processing until no hooks left process(); } //specifically provide headers (readyState 2) done.head = function(userResponse) { mergeObjects(userResponse, response); return setReadyState(2); }; //specifically provide partial text (responseText readyState 3) done.progress = function(userResponse) { mergeObjects(userResponse, response); return setReadyState(3); }; hook = hooks.shift(); //async or sync? if (hook.length === 1) { done(hook(request)); } else if (hook.length === 2 && request.async) { //async handlers must use an async xhr hook(request, done); } else { //skip async hook on sync requests done(); } } //kick off process(); }; facade.abort = function() { status = ABORTED; if (transiting) { xhr.abort(); //this will emit an 'abort' for us } else { facade[FIRE]('abort', {}); } }; facade.setRequestHeader = function(header, value) { var lName, name; //the first header set is used for all future case-alternatives of 'name' lName = header != null ? header.toLowerCase() : void 0; name = request.headerNames[lName] = request.headerNames[lName] || header; //append header to any previous values if (request.headers[name]) { value = request.headers[name] + ', ' + value; } request.headers[name] = value; }; facade.getResponseHeader = function(header) { var name; name = header != null ? header.toLowerCase() : void 0; return response.headers[name]; }; facade.getAllResponseHeaders = function() { return convertHeaders(response.headers); }; //proxy call only when supported if (xhr.overrideMimeType) { facade.overrideMimeType = function() { return xhr.overrideMimeType.apply(xhr, arguments); }; } //create emitter when supported if (xhr.upload) { facade.upload = request.upload = EventEmitter(); } this._facade = facade; } [ 'readyState', 'open', 'setRequestHeader', 'timeout', 'withCredentials', 'upload', 'send', 'abort', 'status', 'statusText', 'getResponseHeader', 'getAllResponseHeaders', 'overrideMimeType', 'responseType', 'response', 'responseText', 'responseXML', ON, OFF, FIRE, 'onreadystatechange', 'onloadstart', 'onprogress', 'onabort', 'onerror', 'onload', 'ontimeout', 'onloadend' ].forEach(function(k) { Object.defineProperty(XHookHttpRequest.prototype, k, { configurable: true, enumerable: true, get: function() { return this._facade[k]; }, set: function(v) { this._facade[k] = v; } }); }); return xhook; }