const ajax = (function() { "use strict"; class AjaxError extends Error { constructor (message, response, ...rest) { super(message, ...rest); this.response = response; } } class NetworkError extends AjaxError {} class NotOkError extends AjaxError {} class UnexpectedMimeError extends AjaxError {} class InvalidJsonError extends AjaxError {} const MIME_JSON = "application/json"; /** * Wrap a function so that it receives `this` as first argument. */ function thisToArg(f) { return function(...rest) { return f(this, ...rest); } } 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.statusText, xhr); } } return xhr; } function getJson(url, options={}) { if (!options.headers) options.headers = {}; options.headers["Accept"] = MIME_JSON; 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 }; })();