text: ( function () {
  
  	const _taskCache = new WeakMap();
  
  	class DRACOLoader extends THREE.Loader {
  
  		constructor( manager ) {
  
  			super( manager );
  			this.decoderPath = '';
  			this.decoderConfig = {};
  			this.decoderBinary = null;
  			this.decoderPending = null;
  			this.workerLimit = 4;
  			this.workerPool = [];
  			this.workerNextTaskID = 1;
  			this.workerSourceURL = '';
  			this.defaultAttributeIDs = {
  				position: 'POSITION',
  				normal: 'NORMAL',
  				color: 'COLOR',
  				uv: 'TEX_COORD'
  			};
  			this.defaultAttributeTypes = {
  				position: 'Float32Array',
  				normal: 'Float32Array',
  				color: 'Float32Array',
  				uv: 'Float32Array'
  			};
  
  		}
  
  		setDecoderPath( path ) {
  
  			this.decoderPath = path;
  			return this;
  
  		}
  
  		setDecoderConfig( config ) {
  
  			this.decoderConfig = config;
  			return this;
  
  		}
  
  		setWorkerLimit( workerLimit ) {
  
  			this.workerLimit = workerLimit;
  			return this;
  
  		}
  
  		load( url, onLoad, onProgress, onError ) {
  
  			const loader = new THREE.FileLoader( this.manager );
  			loader.setPath( this.path );
  			loader.setResponseType( 'arraybuffer' );
  			loader.setRequestHeader( this.requestHeader );
  			loader.setWithCredentials( this.withCredentials );
  			loader.load( url, buffer => {
  
  				const taskConfig = {
  					attributeIDs: this.defaultAttributeIDs,
  					attributeTypes: this.defaultAttributeTypes,
  					useUniqueIDs: false
  				};
  				this.decodeGeometry( buffer, taskConfig ).then( onLoad ).catch( onError );
  
  			}, onProgress, onError );
  
  		}
  		/** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
  
  
  		decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) {
  
  			const taskConfig = {
  				attributeIDs: attributeIDs || this.defaultAttributeIDs,
  				attributeTypes: attributeTypes || this.defaultAttributeTypes,
  				useUniqueIDs: !! attributeIDs
  			};
  			this.decodeGeometry( buffer, taskConfig ).then( callback );
  
  		}
  
  		decodeGeometry( buffer, taskConfig ) {
  
  			// TODO: For backward-compatibility, support 'attributeTypes' objects containing
  			// references (rather than names) to typed array constructors. These must be
  			// serialized before sending them to the worker.
  			for ( const attribute in taskConfig.attributeTypes ) {
  
  				const type = taskConfig.attributeTypes[ attribute ];
  
  				if ( type.BYTES_PER_ELEMENT !== undefined ) {
  
  					taskConfig.attributeTypes[ attribute ] = type.name;
  
  				}
  
  			} //
  
  
  			const taskKey = JSON.stringify( taskConfig ); // Check for an existing task using this buffer. A transferred buffer cannot be transferred
  			// again from this thread.
  
  			if ( _taskCache.has( buffer ) ) {
  
  				const cachedTask = _taskCache.get( buffer );
  
  				if ( cachedTask.key === taskKey ) {
  
  					return cachedTask.promise;
  
  				} else if ( buffer.byteLength === 0 ) {
  
  					// Technically, it would be possible to wait for the previous task to complete,
  					// transfer the buffer back, and decode again with the second configuration. That
  					// is complex, and I don't know of any reason to decode a Draco buffer twice in
  					// different ways, so this is left unimplemented.
  					throw new Error( 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + 'settings. Buffer has already been transferred.' );
  
  				}
  
  			} //
  
  
  			let worker;
  			const taskID = this.workerNextTaskID ++;
  			const taskCost = buffer.byteLength; // Obtain a worker and assign a task, and construct a geometry instance
  			// when the task completes.
  
  			const geometryPending = this._getWorker( taskID, taskCost ).then( _worker => {
  
  				worker = _worker;
  				return new Promise( ( resolve, reject ) => {
  
  					worker._callbacks[ taskID ] = {
  						resolve,
  						reject
  					};
  					worker.postMessage( {
  						type: 'decode',
  						id: taskID,
  						taskConfig,
  						buffer
  					}, [ buffer ] ); // this.debug();
  
  				} );
  
  			} ).then( message => this._createGeometry( message.geometry ) ); // Remove task from the task list.
  			// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
  
  
  			geometryPending.catch( () => true ).then( () => {
  
  				if ( worker && taskID ) {
  
  					this._releaseTask( worker, taskID ); // this.debug();
  
  				}
  
  			} ); // Cache the task result.
  
  			_taskCache.set( buffer, {
  				key: taskKey,
  				promise: geometryPending
  			} );
  
  			return geometryPending;
  
  		}
  
  		_createGeometry( geometryData ) {
  
  			const geometry = new THREE.BufferGeometry();
  
  			if ( geometryData.index ) {
  
  				geometry.setIndex( new THREE.BufferAttribute( geometryData.index.array, 1 ) );
  
  			}
  
  			for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
  
  				const attribute = geometryData.attributes[ i ];
  				const name = attribute.name;
  				const array = attribute.array;
  				const itemSize = attribute.itemSize;
  				geometry.setAttribute( name, new THREE.BufferAttribute( array, itemSize ) );
  
  			}
  
  			return geometry;
  
  		}
  
  		_loadLibrary( url, responseType ) {
  
  			const loader = new THREE.FileLoader( this.manager );
  			loader.setPath( this.decoderPath );
  			loader.setResponseType( responseType );
  			loader.setWithCredentials( this.withCredentials );
  			return new Promise( ( resolve, reject ) => {
  
  				loader.load( url, resolve, undefined, reject );
  
  			} );
  
  		}
  
  		preload() {
  
  			this._initDecoder();
  
  			return this;
  
  		}
  
  		_initDecoder() {
  
  			if ( this.decoderPending ) return this.decoderPending;
  			const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
  			const librariesPending = [];
  
  			if ( useJS ) {
  
  				librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
  
  			} else {
  
  				librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
  				librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
  
  			}
  
  			this.decoderPending = Promise.all( librariesPending ).then( libraries => {
  
  				const jsContent = libraries[ 0 ];
  
  				if ( ! useJS ) {
  
  					this.decoderConfig.wasmBinary = libraries[ 1 ];
  
  				}
  
  				const fn = DRACOWorker.toString();
  				const body = [ '/* draco decoder */', jsContent, '', '/* worker */', fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) ].join( '\n' );
  				this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
  
  			} );
  			return this.decoderPending;
  
  		}
  
  		_getWorker( taskID, taskCost ) {
  
  			return this._initDecoder().then( () => {
  
  				if ( this.workerPool.length < this.workerLimit ) {
  
  					const worker = new Worker( this.workerSourceURL );
  					worker._callbacks = {};
  					worker._taskCosts = {};
  					worker._taskLoad = 0;
  					worker.postMessage( {
  						type: 'init',
  						decoderConfig: this.decoderConfig
  					} );
  
  					worker.onmessage = function ( e ) {
  
  						const message = e.data;
  
  						switch ( message.type ) {
  
  							case 'decode':
  								worker._callbacks[ message.id ].resolve( message );
  
  								break;
  
  							case 'error':
  								worker._callbacks[ message.id ].reject( message );
  
  								break;
  
  							default:
  								console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
  
  						}
  
  					};
  
  					this.workerPool.push( worker );
  
  				} else {
  
  					this.workerPool.sort( function ( a, b ) {
  
  						return a._taskLoad > b._taskLoad ? - 1 : 1;
  
  					} );
  
  				}
  
  				const worker = this.workerPool[ this.workerPool.length - 1 ];
  				worker._taskCosts[ taskID ] = taskCost;
  				worker._taskLoad += taskCost;
  				return worker;
  
  			} );
  
  		}
  
  		_releaseTask( worker, taskID ) {
  
  			worker._taskLoad -= worker._taskCosts[ taskID ];
  			delete worker._callbacks[ taskID ];
  			delete worker._taskCosts[ taskID ];
  
  		}
  
  		debug() {
  
  			console.log( 'Task load: ', this.workerPool.map( worker => worker._taskLoad ) );
  
  		}
  
  		dispose() {
  
  			for ( let i = 0; i < this.workerPool.length; ++ i ) {
  
  				this.workerPool[ i ].terminate();
  
  			}
  
  			this.workerPool.length = 0;
  			return this;
  
  		}
  
  	}
  	/* WEB WORKER */
  
  
  	function DRACOWorker() {
  
  		let decoderConfig;
  		let decoderPending;
  
  		onmessage = function ( e ) {
  
  			const message = e.data;
  
  			switch ( message.type ) {
  
  				case 'init':
  					decoderConfig = message.decoderConfig;
  					decoderPending = new Promise( function ( resolve
  						/*, reject*/
  					) {
  
  						decoderConfig.onModuleLoaded = function ( draco ) {
  
  							// Module is Promise-like. Wrap before resolving to avoid loop.
  							resolve( {
  								draco: draco
  							} );
  
  						};
  
  						DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
  
  					} );
  					break;
  
  				case 'decode':
  					const buffer = message.buffer;
  					const taskConfig = message.taskConfig;
  					decoderPending.then( module => {
  
  						const draco = module.draco;
  						const decoder = new draco.Decoder();
  						const decoderBuffer = new draco.DecoderBuffer();
  						decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );
  
  						try {
  
  							const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );
  							const buffers = geometry.attributes.map( attr => attr.array.buffer );
  							if ( geometry.index ) buffers.push( geometry.index.array.buffer );
  							self.postMessage( {
  								type: 'decode',
  								id: message.id,
  								geometry
  							}, buffers );
  
  						} catch ( error ) {
  
  							console.error( error );
  							self.postMessage( {
  								type: 'error',
  								id: message.id,
  								error: error.message
  							} );
  
  						} finally {
  
  							draco.destroy( decoderBuffer );
  							draco.destroy( decoder );
  
  						}
  
  					} );
  					break;
  
  			}
  
  		};
  
  		function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {
  
  			const attributeIDs = taskConfig.attributeIDs;
  			const attributeTypes = taskConfig.attributeTypes;
  			let dracoGeometry;
  			let decodingStatus;
  			const geometryType = decoder.GetEncodedGeometryType( decoderBuffer );
  
  			if ( geometryType === draco.TRIANGULAR_MESH ) {
  
  				dracoGeometry = new draco.Mesh();
  				decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );
  
  			} else if ( geometryType === draco.POINT_CLOUD ) {
  
  				dracoGeometry = new draco.PointCloud();
  				decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );
  
  			} else {
  
  				throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
  
  			}
  
  			if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
  
  				throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
  
  			}
  
  			const geometry = {
  				index: null,
  				attributes: []
  			}; // Gather all vertex attributes.
  
  			for ( const attributeName in attributeIDs ) {
  
  				const attributeType = self[ attributeTypes[ attributeName ] ];
  				let attribute;
  				let attributeID; // A Draco file may be created with default vertex attributes, whose attribute IDs
  				// are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
  				// a Draco file may contain a custom set of attributes, identified by known unique
  				// IDs. glTF files always do the latter, and `.drc` files typically do the former.
  
  				if ( taskConfig.useUniqueIDs ) {
  
  					attributeID = attributeIDs[ attributeName ];
  					attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
  
  				} else {
  
  					attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
  					if ( attributeID === - 1 ) continue;
  					attribute = decoder.GetAttribute( dracoGeometry, attributeID );
  
  				}
  
  				geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
  
  			} // Add index.
  
  
  			if ( geometryType === draco.TRIANGULAR_MESH ) {
  
  				geometry.index = decodeIndex( draco, decoder, dracoGeometry );
  
  			}
  
  			draco.destroy( dracoGeometry );
  			return geometry;
  
  		}
  
  		function decodeIndex( draco, decoder, dracoGeometry ) {
  
  			const numFaces = dracoGeometry.num_faces();
  			const numIndices = numFaces * 3;
  			const byteLength = numIndices * 4;
  
  			const ptr = draco._malloc( byteLength );
  
  			decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
  			const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
  
  			draco._free( ptr );
  
  			return {
  				array: index,
  				itemSize: 1
  			};
  
  		}
  
  		function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
  
  			const numComponents = attribute.num_components();
  			const numPoints = dracoGeometry.num_points();
  			const numValues = numPoints * numComponents;
  			const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
  			const dataType = getDracoDataType( draco, attributeType );
  
  			const ptr = draco._malloc( byteLength );
  
  			decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
  			const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
  
  			draco._free( ptr );
  
  			return {
  				name: attributeName,
  				array: array,
  				itemSize: numComponents
  			};
  
  		}
  
  		function getDracoDataType( draco, attributeType ) {
  
  			switch ( attributeType ) {
  
  				case Float32Array:
  					return draco.DT_FLOAT32;
  
  				case Int8Array:
  					return draco.DT_INT8;
  
  				case Int16Array:
  					return draco.DT_INT16;
  
  				case Int32Array:
  					return draco.DT_INT32;
  
  				case Uint8Array:
  					return draco.DT_UINT8;
  
  				case Uint16Array:
  					return draco.DT_UINT16;
  
  				case Uint32Array:
  					return draco.DT_UINT32;
  
  			}
  
  		}
  
  	}
  
  	THREE.DRACOLoader = DRACOLoader;
  
  } )();
  