UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

162 lines (151 loc) 6.2 kB
import {Cache} from './Cache.js'; import {Loader} from './Loader.js'; import {core} from "../core.js"; const loading = {}; class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) { url = ''; } if (this.path !== undefined) { url = this.path + url; } url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); core.scheduleTask(() => { if (onLoad) { onLoad(cached); } this.manager.itemEnd(url); }, 0); return cached; } if (loading[url] !== undefined) { loading[url].push({onLoad, onProgress, onError}); return; } loading[url] = []; loading[url].push({onLoad, onProgress, onError}); const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? 'include' : 'same-origin' }); const mimeType = this.mimeType; const responseType = this.responseType; fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. 'file://' or 'data://'. Handle as success. if (response.status === 0) { console.warn('FileLoader: HTTP Status 0 received.'); } if (typeof ReadableStream === 'undefined' || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); const contentLength = response.headers.get('Content-Length'); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({done, value}) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent('progress', {lengthComputable, loaded, total}); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) { callback.onProgress(event); } } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`); } }).then(response => { switch (responseType) { case 'arraybuffer': return response.arrayBuffer(); case 'blob': return response.blob(); case 'document': return response.text() .then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case 'json': return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"\s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) { callback.onLoad(data); } } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) { callback.onError(err); } } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } export {FileLoader};