114 lines
3 KiB
JavaScript
114 lines
3 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) {
|
|
console.debug(url, queryParams);
|
|
if (!queryParams) return url;
|
|
|
|
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 = "&";
|
|
}
|
|
|
|
console.debug(url);
|
|
return url;
|
|
}
|
|
|
|
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, urlWithParams, "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
|
|
};
|
|
|
|
})();
|