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 }; })();