Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/flo-bit/shapecraft. flo-bit.dev/shapecraft
Something went wrong. Try again.
5.6 kB · 288 lines
JavaScript
at commit da69385e
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289import { Cache } from './Cache.js';import { Loader } from './Loader.js';
const loading = {};
class HttpError extends Error {
constructor( message, response ) {
super( message ); this.response = response;
}
}
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 );
setTimeout( () => {
if ( onLoad ) onLoad( cached );
this.manager.itemEnd( url );
}, 0 );
return cached;
}
// Check if request is duplicate
if ( loading[ url ] !== undefined ) {
loading[ url ].push( {
onLoad: onLoad, onProgress: onProgress, onError: onError
} );
return;
}
// Initialise array for duplicate requests loading[ url ] = [];
loading[ url ].push( { onLoad: onLoad, onProgress: onProgress, onError: onError, } );
// create request const req = new Request( url, { headers: new Headers( this.requestHeader ), credentials: this.withCredentials ? 'include' : 'same-origin', // An abort controller could be added within a future PR } );
// record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType;
// start the fetch 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( 'THREE.FileLoader: HTTP Status 0 received.' );
}
// Workaround: Checking if response.body === undefined for Alipay browser #23548
if ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) {
return response;
}
const callbacks = loading[ url ]; const reader = response.body.getReader();
// Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get( 'X-File-Size' ) || response.headers.get( 'Content-Length' ); const total = contentLength ? parseInt( contentLength ) : 0; const lengthComputable = total !== 0; let loaded = 0;
// periodically read data into the new stream tracking while download progress 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();
}
}, ( e ) => {
controller.error( e );
} );
}
}
} );
return new Response( stream );
} else {
throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response );
}
} ) .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 };