Bilibili-Evolved/client/ajax.js
2019-03-24 22:17:41 +08:00

148 lines
4.4 KiB
JavaScript

export class Ajax
{
static send(xhr, body, text = true)
{
return new Promise((resolve, reject) =>
{
xhr.addEventListener("load", () => resolve(text ? xhr.responseText : xhr.response));
xhr.addEventListener("error", () => reject(xhr.status));
xhr.send(body);
});
}
static getBlob(url)
{
const xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.open("GET", url);
return this.send(xhr, undefined, false);
}
static getBlobWithCredentials(url)
{
const xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.open("GET", url);
xhr.withCredentials = true;
return this.send(xhr, undefined, false);
}
static async getJson(url)
{
return JSON.parse(await this.getText(url));
}
static async getJsonWithCredentials(url)
{
return JSON.parse(await this.getTextWithCredentials(url));
}
static getText(url)
{
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
return this.send(xhr);
}
static getTextWithCredentials(url)
{
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.withCredentials = true;
return this.send(xhr);
}
static postText(url, body)
{
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
return this.send(xhr, body);
}
static postTextWithCredentials(url, body)
{
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
return this.send(xhr, body);
}
static getHandlers(name)
{
name = name.toLowerCase();
let handlers = Ajax[name];
if (handlers === undefined)
{
handlers = Ajax[name] = [];
}
return handlers;
}
static addEventListener(type, handler)
{
const handlers = Ajax.getHandlers(type);
handlers.push(handler);
}
static removeEventListener(type, handler)
{
const handlers = Ajax.getHandlers(type);
handlers.splice(handlers.indexOf(handler), 1);
}
}
// https://github.com/the1812/Bilibili-Evolved/issues/84
export function setupAjaxHook()
{
const original = {
open: XMLHttpRequest.prototype.open,
send: XMLHttpRequest.prototype.send,
};
const fireHandlers = (name, thisArg, ...args) => Ajax.getHandlers(name).forEach(it => it.call(thisArg, ...args));
const hook = (name, thisArgs, ...args) =>
{
fireHandlers("before" + name, thisArgs, ...args);
const returnValue = original[name].call(thisArgs, ...args);
fireHandlers("after" + name, thisArgs, ...args);
return returnValue;
};
const hookOnEvent = (name, thisArg) =>
{
if (thisArg[name])
{
const originalHandler = thisArg[name];
thisArg[name] = (...args) =>
{
fireHandlers("before" + name, thisArg, ...args);
originalHandler.apply(thisArg, args);
fireHandlers("after" + name, thisArg, ...args);
};
}
else
{
thisArg[name] = (...args) =>
{
fireHandlers("before" + name, thisArg, ...args);
fireHandlers("after" + name, thisArg, ...args);
};
}
};
XMLHttpRequest.prototype.open = function (...args) { return hook("open", this, ...args); };
XMLHttpRequest.prototype.send = function (...args)
{
hookOnEvent("onreadystatechange", this);
hookOnEvent("onload", this);
return hook("send", this, ...args);
};
}
export function downloadText(url, load, error) // The old method for compatibility
{
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
if (load !== undefined) // callback
{
xhr.addEventListener("load", () => load && load(xhr.responseText));
xhr.addEventListener("error", () => error && error(xhr.status));
xhr.send();
}
else
{
return new Promise((resolve, reject) =>
{
xhr.addEventListener("load", () => resolve(xhr.responseText));
xhr.addEventListener("error", () => reject(xhr.status));
xhr.send();
});
}
}