jsboost
Version:
A tiny library that extends the capability of javascript
144 lines (120 loc) • 3.7 kB
JavaScript
/**
* Author: JCloudYu
* Create: 2019/10/16
**/
import {UTF8String} from "../utf8string.esm.js";
import {ObjectAssignProperties} from "../_helper.esm.js";
const CAMEL_CASE_PATTERN = /(\w)(\w*)(\W*)/g;
const CAMEL_REPLACER = (match, $1, $2, $3, index, input )=>{
return `${$1.toUpperCase()}${$2.toLowerCase()}${$3}`;
};
const READY_STATE = Object.assign(Object.create(null), {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
});
export function BrowserWebFetch(url, init={}) {
let resolve=DO_NOTHING, reject=DO_NOTHING;
const promise = new Promise((_res, _rej)=>{resolve=_res; reject=_rej;});
const {
mode='no-cors',
method='GET',
headers={},
timeout=0,
credentials='omit',
body=null,
} = init;
const request = new XMLHttpRequest();
promise.request = request;
promise.promise = promise;
request.addEventListener('readystatechange', (e)=>{
switch( request.readyState ) {
case READY_STATE.UNSENT:
break;
case READY_STATE.OPENED:
request.withCredentials = (mode === 'cors') && (credentials === 'include' || credentials === 'same-origin' );
request.timeout = timeout;
request.responseType = 'blob';
// NOTE: Set request headers
for (const header_name in headers) {
const normalize_header = header_name.replace(CAMEL_CASE_PATTERN, CAMEL_REPLACER);
const value = headers[header_name];
request.setRequestHeader( normalize_header, value );
}
break;
case READY_STATE.HEADERS_RECEIVED:
break;
case READY_STATE.LOADING:
break;
case READY_STATE.DONE:
break;
}
});
request.addEventListener('timeout', ON_ERROR.bind(request, reject));
request.addEventListener('abort', ON_ERROR.bind(request, reject));
request.addEventListener('error', ON_ERROR.bind(request, reject));
request.addEventListener('load', function(e) {
const response_blob = request.response;
const response = ObjectAssignProperties(Object.create(null), {
request,
ok: (request.status >= 200 && request.status <= 299),
status: request.status,
statusText: request.statusText,
url: request.responseURL,
headers: request.getAllResponseHeaders(),
text: async function(){
const response_bytes = await READ_ARRAY_BUFFER_FROM_BLOB(response_blob);
return UTF8String.Decode(response_bytes);
},
blob: function(){
return response_blob;
},
arrayBuffer: function() {
return READ_ARRAY_BUFFER_FROM_BLOB(response_blob);
},
json: async function(throw_if_error=true) {
const response_bytes = await READ_ARRAY_BUFFER_FROM_BLOB(response_blob);
try {
return JSON.parse(UTF8String.Decode(response_bytes));
}
catch(e) {
return throw_if_error ? Promise.reject(e) : undefined;
}
}
});
resolve(response);
});
request.addEventListener('progress', PROGRESS.bind(request, false));
request.upload.addEventListener('progress', PROGRESS.bind(request, true));
request.open( method, url, true );
request.send( body );
return promise;
}
function DO_NOTHING(){}
function PROGRESS(upload, e) {
const event = new Event( upload ? 'progress-up' : 'progress-down');
ObjectAssignProperties(event, {
lengthComputable:e.lengthComputable,
loaded:e.loaded,
total:e.total
});
this.dispatchEvent(event);
}
function ON_ERROR(reject, e) {
const response = ObjectAssignProperties(Object.create(null), {
request:this,
error: e,
ok: false
});
reject(response);
}
function READ_ARRAY_BUFFER_FROM_BLOB(blob) {
return new Promise((resolve, reject)=>{
const reader = new FileReader();
reader.onerror = reject;
reader.onload = ()=>resolve(reader.result);
reader.readAsArrayBuffer(blob);
});
}