const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars function getTrackedResponse(response, load_status) { let clen = 0; let compressed = false; response.headers.forEach(function (value, header) { const h = header.toLowerCase().trim(); // We can't accurately compute compressed stream length. if (h === 'content-encoding') { compressed = true; } else if (h === 'content-length') { const length = parseInt(value, 10); if (!Number.isNaN(length) && length > 0) { clen = length; } } }); if (!compressed && clen) { load_status.total = clen; } function onloadprogress(reader, controller) { return reader.read().then(function (result) { if (load_status.done) { return Promise.resolve(); } if (result.value) { controller.enqueue(result.value); load_status.loaded += result.value.length; } if (!result.done) { return onloadprogress(reader, controller); } load_status.done = true; return Promise.resolve(); }); } const reader = response.body.getReader(); return new Response(new ReadableStream({ start: function (controller) { onloadprogress(reader, controller).then(function () { controller.close(); }); }, }), { headers: response.headers }); } function loadFetch(file, tracker, fileSize, raw) { tracker[file] = { total: fileSize || 0, loaded: 0, done: false, }; return fetch(file).then(function (response) { if (!response.ok) { return Promise.reject(new Error(`Failed loading file '${file}'`)); } const tr = getTrackedResponse(response, tracker[file]); if (raw) { return Promise.resolve(tr); } return tr.arrayBuffer(); }); } function retry(func, attempts = 1) { function onerror(err) { if (attempts <= 1) { return Promise.reject(err); } return new Promise(function (resolve, reject) { setTimeout(function () { retry(func, attempts - 1).then(resolve).catch(reject); }, 1000); }); } return func().catch(onerror); } const DOWNLOAD_ATTEMPTS_MAX = 4; const loadingFiles = {}; const lastProgress = { loaded: 0, total: 0 }; let progressFunc = null; const animateProgress = function () { let loaded = 0; let total = 0; let totalIsValid = true; let progressIsFinal = true; Object.keys(loadingFiles).forEach(function (file) { const stat = loadingFiles[file]; if (!stat.done) { progressIsFinal = false; } if (!totalIsValid || stat.total === 0) { totalIsValid = false; total = 0; } else { total += stat.total; } loaded += stat.loaded; }); if (loaded !== lastProgress.loaded || total !== lastProgress.total) { lastProgress.loaded = loaded; lastProgress.total = total; if (typeof progressFunc === 'function') { progressFunc(loaded, total); } } if (!progressIsFinal) { requestAnimationFrame(animateProgress); } }; this.animateProgress = animateProgress; this.setProgressFunc = function (callback) { progressFunc = callback; }; this.loadPromise = function (file, fileSize, raw = false) { return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX); }; this.preloadedFiles = []; this.preload = function (pathOrBuffer, destPath, fileSize) { let buffer = null; if (typeof pathOrBuffer === 'string') { const me = this; return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) { me.preloadedFiles.push({ path: destPath || pathOrBuffer, buffer: buf, }); return Promise.resolve(); }); } else if (pathOrBuffer instanceof ArrayBuffer) { buffer = new Uint8Array(pathOrBuffer); } else if (ArrayBuffer.isView(pathOrBuffer)) { buffer = new Uint8Array(pathOrBuffer.buffer); } if (buffer) { this.preloadedFiles.push({ path: destPath, buffer: pathOrBuffer, }); return Promise.resolve(); } return Promise.reject(new Error('Invalid object for preloading')); }; };