117 lines
4.3 KiB
JavaScript
117 lines
4.3 KiB
JavaScript
|
#include csrf.js
|
||
|
|
||
|
const $x = (function() {
|
||
|
const send = function(method, url, options, body) {
|
||
|
if(options === undefined)
|
||
|
options = {};
|
||
|
else if(typeof options !== 'object')
|
||
|
throw 'options must be undefined or an object';
|
||
|
|
||
|
Object.freeze(options);
|
||
|
|
||
|
const xhr = new XMLHttpRequest;
|
||
|
const requestHeaders = new Map;
|
||
|
|
||
|
if('headers' in options && typeof options.headers === 'object')
|
||
|
for(const name in options.headers)
|
||
|
if(options.headers.hasOwnProperty(name))
|
||
|
requestHeaders.set(name.toLowerCase(), options.headers[name]);
|
||
|
|
||
|
if(options.csrf)
|
||
|
requestHeaders.set('x-csrf-token', MszCSRF.token);
|
||
|
|
||
|
if(typeof options.download === 'function') {
|
||
|
xhr.onloadstart = ev => options.download(ev);
|
||
|
xhr.onprogress = ev => options.download(ev);
|
||
|
xhr.onloadend = ev => options.download(ev);
|
||
|
}
|
||
|
|
||
|
if(typeof options.upload === 'function') {
|
||
|
xhr.upload.onloadstart = ev => options.upload(ev);
|
||
|
xhr.upload.onprogress = ev => options.upload(ev);
|
||
|
xhr.upload.onloadend = ev => options.upload(ev);
|
||
|
}
|
||
|
|
||
|
if(options.authed)
|
||
|
xhr.withCredentials = true;
|
||
|
|
||
|
if(typeof options.timeout === 'number')
|
||
|
xhr.timeout = options.timeout;
|
||
|
|
||
|
if(typeof options.type === 'string')
|
||
|
xhr.responseType = options.type;
|
||
|
|
||
|
if(typeof options.abort === 'function')
|
||
|
options.abort(() => xhr.abort());
|
||
|
|
||
|
if(typeof options.xhr === 'function')
|
||
|
options.xhr(() => xhr);
|
||
|
|
||
|
if(typeof body === 'object') {
|
||
|
if(body instanceof URLSearchParams) {
|
||
|
requestHeaders.set('content-type', 'application/x-www-form-urlencoded');
|
||
|
} else if(body instanceof FormData) {
|
||
|
// content-type is implicitly set
|
||
|
} else if(body instanceof Blob || body instanceof ArrayBuffer || body instanceof DataView) {
|
||
|
if(!requestHeaders.has('content-type'))
|
||
|
requestHeaders.set('content-type', 'application/octet-stream');
|
||
|
} else if(!requestHeaders.has('content-type')) {
|
||
|
const bodyParts = [];
|
||
|
for(const name in body)
|
||
|
if(body.hasOwnProperty(name))
|
||
|
bodyParts.push(encodeURIComponent(name) + '=' + encodeURIComponent(body[name]));
|
||
|
body = bodyParts.join('&');
|
||
|
requestHeaders.set('content-type', 'application/x-www-form-urlencoded');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return new Promise((resolve, reject) => {
|
||
|
xhr.onload = ev => {
|
||
|
const headers = (headersString => {
|
||
|
const headers = new Map;
|
||
|
|
||
|
const raw = headersString.trim().split(/[\r\n]+/);
|
||
|
for(const name in raw)
|
||
|
if(raw.hasOwnProperty(name)) {
|
||
|
const parts = raw[name].split(': ');
|
||
|
headers.set(parts.shift(), parts.join(': '));
|
||
|
}
|
||
|
|
||
|
return headers;
|
||
|
})(xhr.getAllResponseHeaders());
|
||
|
|
||
|
if(options.csrf && headers.has('x-csrf-token'))
|
||
|
MszCSRF.token = headers.get('x-csrf-token');
|
||
|
|
||
|
resolve({
|
||
|
status: xhr.status,
|
||
|
body: () => xhr.response,
|
||
|
text: () => xhr.responseText,
|
||
|
headers: () => headers,
|
||
|
xhr: xhr,
|
||
|
ev: ev,
|
||
|
});
|
||
|
};
|
||
|
|
||
|
xhr.onerror = ev => reject({
|
||
|
xhr: xhr,
|
||
|
ev: ev,
|
||
|
});
|
||
|
|
||
|
xhr.open(method, url);
|
||
|
for(const [name, value] of requestHeaders)
|
||
|
xhr.setRequestHeader(name, value);
|
||
|
xhr.send(body);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
return {
|
||
|
send: send,
|
||
|
get: (url, options, body) => send('GET', url, options, body),
|
||
|
post: (url, options, body) => send('POST', url, options, body),
|
||
|
delete: (url, options, body) => send('DELETE', url, options, body),
|
||
|
patch: (url, options, body) => send('PATCH', url, options, body),
|
||
|
put: (url, options, body) => send('PUT', url, options, body),
|
||
|
};
|
||
|
})();
|