installXHook

install XHook

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @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;
}