feathermost/ajax.js

108 lines
2.9 KiB
JavaScript

const ajax = (function() { "use strict";
class AjaxError extends Error {
constructor (message, xhr, ...rest) {
super(message, ...rest);
this.xhr = xhr;
}
}
class NetworkError extends AjaxError {}
class NotOkError extends AjaxError {}
class UnexpectedMimeError extends AjaxError {}
class InvalidJsonError extends AjaxError {}
const MIME_JSON = "application/json";
function xhrInitForPromise(resolve, reject, url, method, headers) {
const t_resolve = thisToArg(resolve),
t_reject = thisToArg(reject);
let xhr = new XMLHttpRequest();
xhr.addEventListener("load", t_resolve);
xhr.addEventListener("error", t_resolve);
xhr.addEventListener("abort", t_reject);
xhr.addEventListener("timeout", t_reject);
xhr.open(method, url);
for (let header of Object.getOwnPropertyNames(headers)) {
xhr.setRequestHeader(header, headers[header]);
}
return xhr;
}
function xhrParseJsonResponse(xhr) {
xhr.responseJson = null;
xhr.ok = false;
if (xhr.responseText) {
const contentType = xhr.getResponseHeader("Content-Type");
if (contentType != MIME_JSON) {
throw new UnexpectedMimeError(`Server did not reply with JSON but with ${contentType}`, xhr);
}
try {
xhr.responseJson = JSON.parse(xhr.responseText);
} catch(e) {
throw new InvalidJsonError("Server replied with JSON that we couldn't parse", xhr);
}
}
xhr.ok = 200 <= xhr.status && xhr.status < 300;
if (!xhr.ok) {
console.error(xhr);
if (xhr.status === 0) {
throw new NetworkError("Failed to connect to server. Developer console may have more information", xhr);
} else {
throw new NotOkError(`${xhr.status} ${xhr.statusText}`, xhr);
}
}
return xhr;
}
function withParams(url, queryParams) {
let sep = "?";
for (let paramName of Object.getOwnPropertyNames(queryParams)) {
const paramValue = queryParams[paramName];
if (!paramValue) continue;
url += `${sep}${encodeURIComponent(paramName)}`;
if (paramValue !== true) {
url += `=${encodeURIComponent(paramValue)}`;
}
sep = "&";
}
}
function getJson(url, options={}) {
if (!options.queryParams) options.queryParams = {};
if (!options.headers) options.headers = {};
options.headers["Accept"] = MIME_JSON;
const urlWithParams = withParams(url, options.queryParams);
return new Promise((resolve, reject) => {
let xhr = xhrInitForPromise(resolve, reject, url, "GET", options.headers);
xhr.send();
}).then(xhrParseJsonResponse);
}
function postJson(url, data=undefined, options={}) {
if (!options.headers) options.headers = {};
options.headers["Content-Type"] = MIME_JSON;
options.headers["Accept"] = MIME_JSON;
return new Promise((resolve, reject) => {
let xhr = xhrInitForPromise(resolve, reject, url, "POST", options.headers);
if (data === undefined) {
xhr.send();
} else {
xhr.send(JSON.stringify(data));
}
}).then(xhrParseJsonResponse);
}
return {
NetworkError, NotOkError, UnexpectedMimeError, InvalidJsonError,
getJson, postJson
};
})();