/* eslint-disable */ class XhrLoader { constructor(config) { if (config && config.xhrSetup) { this.xhrSetup = config.xhrSetup; } } destroy() { this.abort(); this.loader = null; } abort() { var loader = this.loader; if (loader && loader.readyState !== 4) { this.stats.aborted = true; loader.abort(); } window.clearTimeout(this.requestTimeout); this.requestTimeout = null; window.clearTimeout(this.retryTimeout); this.retryTimeout = null; } loadHead(context, config, callbacks) { this.context = context; this.config = config; this.callbacks = callbacks; this.stats = {trequest: performance.now(), retry: 0}; this.retryDelay = config.retryDelay; var xhr = new XMLHttpRequest; xhr.open('head', context.url); xhr.onload = function () { callbacks.onSuccess(xhr.getResponseHeader('content-length')); }; xhr.send(); } load(context, config, callbacks) { this.context = context; this.config = config; this.callbacks = callbacks; this.stats = {trequest: performance.now(), retry: 0}; this.retryDelay = config.retryDelay; this.loadInternal(); } loadInternal() { var xhr, context = this.context; if (typeof XDomainRequest !== 'undefined') { xhr = this.loader = new XDomainRequest(); } else { xhr = this.loader = new XMLHttpRequest(); } xhr.onloadend = this.loadend.bind(this); xhr.onprogress = this.loadprogress.bind(this); xhr.open('GET', context.url, true); if (context.rangeEnd) { xhr.setRequestHeader('Range','bytes=' + context.rangeStart + '-' + (context.rangeEnd-1)); } xhr.responseType = context.responseType; let stats = this.stats; stats.tfirst = 0; stats.loaded = 0; if (this.xhrSetup) { this.xhrSetup(xhr, context.url); } // setup timeout before we perform request this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout); xhr.send(); } loadend(event) { var xhr = event.currentTarget, status = xhr.status, stats = this.stats, context = this.context, config = this.config; // don't proceed if xhr has been aborted if (stats.aborted) { return; } // in any case clear the current xhrs timeout window.clearTimeout(this.requestTimeout); // http status between 200 to 299 are all successful if (status >= 200 && status < 300) { stats.tload = Math.max(stats.tfirst,performance.now()); let data,len; if (context.responseType === 'arraybuffer') { data = xhr.response; len = data.byteLength; } else { data = xhr.responseText; len = data.length; } stats.loaded = stats.total = len; let response = { url : xhr.responseURL, data : data }; this.callbacks.onSuccess(response, stats, context); } else { // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error if (stats.retry >= config.maxRetry || (status >= 400 && status < 499)) { // logger.error(`${status} while loading ${context.url}` ); this.callbacks.onError({ code : status, text : xhr.statusText}, context); } else { // retry // logger.warn(`${status} while loading ${context.url}, retrying in ${this.retryDelay}...`); // aborts and resets internal state this.destroy(); // schedule retry this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay); // set exponential backoff this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay); stats.retry++; } } } loadtimeout() { // logger.warn(`timeout while loading ${this.context.url}` ); this.callbacks.onTimeout(this.stats, this.context); } loadprogress(event) { var stats = this.stats; if (stats.tfirst === 0) { stats.tfirst = Math.max(performance.now(), stats.trequest); } stats.loaded = event.loaded; if (event.lengthComputable) { stats.total = event.total; } let onProgress = this.callbacks.onProgress; if (onProgress) { // last args is to provide on progress data onProgress(stats, this.context, null); } } } export default XhrLoader;