text: (function (exports) {
  	'use strict';
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	var AnimationUtils = {
  
  		// same as Array.prototype.slice, but also works on typed arrays
  		arraySlice: function ( array, from, to ) {
  
  			if ( AnimationUtils.isTypedArray( array ) ) {
  
  				// in ios9 array.subarray(from, undefined) will return empty array
  				// but array.subarray(from) or array.subarray(from, len) is correct
  				return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
  
  			}
  
  			return array.slice( from, to );
  
  		},
  
  		// converts an array to a specific type
  		convertArray: function ( array, type, forceClone ) {
  
  			if ( ! array || // let 'undefined' and 'null' pass
  				! forceClone && array.constructor === type ) return array;
  
  			if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
  
  				return new type( array ); // create typed array
  
  			}
  
  			return Array.prototype.slice.call( array ); // create Array
  
  		},
  
  		isTypedArray: function ( object ) {
  
  			return ArrayBuffer.isView( object ) &&
  				! ( object instanceof DataView );
  
  		},
  
  		// returns an array by which times and values can be sorted
  		getKeyframeOrder: function ( times ) {
  
  			function compareTime( i, j ) {
  
  				return times[ i ] - times[ j ];
  
  			}
  
  			var n = times.length;
  			var result = new Array( n );
  			for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
  
  			result.sort( compareTime );
  
  			return result;
  
  		},
  
  		// uses the array previously returned by 'getKeyframeOrder' to sort data
  		sortedArray: function ( values, stride, order ) {
  
  			var nValues = values.length;
  			var result = new values.constructor( nValues );
  
  			for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
  
  				var srcOffset = order[ i ] * stride;
  
  				for ( var j = 0; j !== stride; ++ j ) {
  
  					result[ dstOffset ++ ] = values[ srcOffset + j ];
  
  				}
  
  			}
  
  			return result;
  
  		},
  
  		// function for parsing AOS keyframe formats
  		flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
  
  			var i = 1, key = jsonKeys[ 0 ];
  
  			while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
  
  				key = jsonKeys[ i ++ ];
  
  			}
  
  			if ( key === undefined ) return; // no data
  
  			var value = key[ valuePropertyName ];
  			if ( value === undefined ) return; // no data
  
  			if ( Array.isArray( value ) ) {
  
  				do {
  
  					value = key[ valuePropertyName ];
  
  					if ( value !== undefined ) {
  
  						times.push( key.time );
  						values.push.apply( values, value ); // push all elements
  
  					}
  
  					key = jsonKeys[ i ++ ];
  
  				} while ( key !== undefined );
  
  			} else if ( value.toArray !== undefined ) {
  
  				// ...assume Math-ish
  
  				do {
  
  					value = key[ valuePropertyName ];
  
  					if ( value !== undefined ) {
  
  						times.push( key.time );
  						value.toArray( values, values.length );
  
  					}
  
  					key = jsonKeys[ i ++ ];
  
  				} while ( key !== undefined );
  
  			} else {
  
  				// otherwise push as-is
  
  				do {
  
  					value = key[ valuePropertyName ];
  
  					if ( value !== undefined ) {
  
  						times.push( key.time );
  						values.push( value );
  
  					}
  
  					key = jsonKeys[ i ++ ];
  
  				} while ( key !== undefined );
  
  			}
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	var InterpolateDiscrete = 2300;
  	var InterpolateLinear = 2301;
  	var InterpolateSmooth = 2302;
  	var ZeroCurvatureEnding = 2400;
  	var ZeroSlopeEnding = 2401;
  	var WrapAroundEnding = 2402;
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
  		this.parameterPositions = parameterPositions;
  		this._cachedIndex = 0;
  
  		this.resultBuffer = resultBuffer !== undefined ?
  			resultBuffer : new sampleValues.constructor( sampleSize );
  		this.sampleValues = sampleValues;
  		this.valueSize = sampleSize;
  
  	}
  
  	Object.assign( Interpolant.prototype, {
  
  		evaluate: function ( t ) {
  
  			var pp = this.parameterPositions,
  				i1 = this._cachedIndex,
  
  				t1 = pp[ i1 ],
  				t0 = pp[ i1 - 1 ];
  
  			validate_interval: {
  
  				seek: {
  
  					var right;
  
  					linear_scan: {
  
  						//- See http://jsperf.com/comparison-to-undefined/3
  						//- slower code:
  						//-
  						//- 				if ( t >= t1 || t1 === undefined ) {
  						forward_scan: if ( ! ( t < t1 ) ) {
  
  							for ( var giveUpAt = i1 + 2; ; ) {
  
  								if ( t1 === undefined ) {
  
  									if ( t < t0 ) break forward_scan;
  
  									// after end
  
  									i1 = pp.length;
  									this._cachedIndex = i1;
  									return this.afterEnd_( i1 - 1, t, t0 );
  
  								}
  
  								if ( i1 === giveUpAt ) break; // this loop
  
  								t0 = t1;
  								t1 = pp[ ++ i1 ];
  
  								if ( t < t1 ) {
  
  									// we have arrived at the sought interval
  									break seek;
  
  								}
  
  							}
  
  							// prepare binary search on the right side of the index
  							right = pp.length;
  							break linear_scan;
  
  						}
  
  						//- slower code:
  						//-					if ( t < t0 || t0 === undefined ) {
  						if ( ! ( t >= t0 ) ) {
  
  							// looping?
  
  							var t1global = pp[ 1 ];
  
  							if ( t < t1global ) {
  
  								i1 = 2; // + 1, using the scan for the details
  								t0 = t1global;
  
  							}
  
  							// linear reverse scan
  
  							for ( var giveUpAt = i1 - 2; ; ) {
  
  								if ( t0 === undefined ) {
  
  									// before start
  
  									this._cachedIndex = 0;
  									return this.beforeStart_( 0, t, t1 );
  
  								}
  
  								if ( i1 === giveUpAt ) break; // this loop
  
  								t1 = t0;
  								t0 = pp[ -- i1 - 1 ];
  
  								if ( t >= t0 ) {
  
  									// we have arrived at the sought interval
  									break seek;
  
  								}
  
  							}
  
  							// prepare binary search on the left side of the index
  							right = i1;
  							i1 = 0;
  							break linear_scan;
  
  						}
  
  						// the interval is valid
  
  						break validate_interval;
  
  					} // linear scan
  
  					// binary search
  
  					while ( i1 < right ) {
  
  						var mid = ( i1 + right ) >>> 1;
  
  						if ( t < pp[ mid ] ) {
  
  							right = mid;
  
  						} else {
  
  							i1 = mid + 1;
  
  						}
  
  					}
  
  					t1 = pp[ i1 ];
  					t0 = pp[ i1 - 1 ];
  
  					// check boundary cases, again
  
  					if ( t0 === undefined ) {
  
  						this._cachedIndex = 0;
  						return this.beforeStart_( 0, t, t1 );
  
  					}
  
  					if ( t1 === undefined ) {
  
  						i1 = pp.length;
  						this._cachedIndex = i1;
  						return this.afterEnd_( i1 - 1, t0, t );
  
  					}
  
  				} // seek
  
  				this._cachedIndex = i1;
  
  				this.intervalChanged_( i1, t0, t1 );
  
  			} // validate_interval
  
  			return this.interpolate_( i1, t0, t, t1 );
  
  		},
  
  		settings: null, // optional, subclass-specific settings structure
  		// Note: The indirection allows central control of many interpolants.
  
  		// --- Protected interface
  
  		DefaultSettings_: {},
  
  		getSettings_: function () {
  
  			return this.settings || this.DefaultSettings_;
  
  		},
  
  		copySampleValue_: function ( index ) {
  
  			// copies a sample value to the result buffer
  
  			var result = this.resultBuffer,
  				values = this.sampleValues,
  				stride = this.valueSize,
  				offset = index * stride;
  
  			for ( var i = 0; i !== stride; ++ i ) {
  
  				result[ i ] = values[ offset + i ];
  
  			}
  
  			return result;
  
  		},
  
  		// Template methods for derived classes:
  
  		interpolate_: function (  ) {
  
  			throw new Error( 'call to abstract method' );
  			// implementations shall return this.resultBuffer
  
  		},
  
  		intervalChanged_: function (  ) {
  
  			// empty
  
  		}
  
  	} );
  
  	//!\ DECLARE ALIAS AFTER assign prototype !
  	Object.assign( Interpolant.prototype, {
  
  		//( 0, t, t0 ), returns this.resultBuffer
  		beforeStart_: Interpolant.prototype.copySampleValue_,
  
  		//( N-1, tN-1, t ), returns this.resultBuffer
  		afterEnd_: Interpolant.prototype.copySampleValue_,
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
  		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
  		this._weightPrev = - 0;
  		this._offsetPrev = - 0;
  		this._weightNext = - 0;
  		this._offsetNext = - 0;
  
  	}
  
  	CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
  		constructor: CubicInterpolant,
  
  		DefaultSettings_: {
  
  			endingStart: ZeroCurvatureEnding,
  			endingEnd: ZeroCurvatureEnding
  
  		},
  
  		intervalChanged_: function ( i1, t0, t1 ) {
  
  			var pp = this.parameterPositions,
  				iPrev = i1 - 2,
  				iNext = i1 + 1,
  
  				tPrev = pp[ iPrev ],
  				tNext = pp[ iNext ];
  
  			if ( tPrev === undefined ) {
  
  				switch ( this.getSettings_().endingStart ) {
  
  					case ZeroSlopeEnding:
  
  						// f'(t0) = 0
  						iPrev = i1;
  						tPrev = 2 * t0 - t1;
  
  						break;
  
  					case WrapAroundEnding:
  
  						// use the other end of the curve
  						iPrev = pp.length - 2;
  						tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
  
  						break;
  
  					default: // ZeroCurvatureEnding
  
  						// f''(t0) = 0 a.k.a. Natural Spline
  						iPrev = i1;
  						tPrev = t1;
  
  				}
  
  			}
  
  			if ( tNext === undefined ) {
  
  				switch ( this.getSettings_().endingEnd ) {
  
  					case ZeroSlopeEnding:
  
  						// f'(tN) = 0
  						iNext = i1;
  						tNext = 2 * t1 - t0;
  
  						break;
  
  					case WrapAroundEnding:
  
  						// use the other end of the curve
  						iNext = 1;
  						tNext = t1 + pp[ 1 ] - pp[ 0 ];
  
  						break;
  
  					default: // ZeroCurvatureEnding
  
  						// f''(tN) = 0, a.k.a. Natural Spline
  						iNext = i1 - 1;
  						tNext = t0;
  
  				}
  
  			}
  
  			var halfDt = ( t1 - t0 ) * 0.5,
  				stride = this.valueSize;
  
  			this._weightPrev = halfDt / ( t0 - tPrev );
  			this._weightNext = halfDt / ( tNext - t1 );
  			this._offsetPrev = iPrev * stride;
  			this._offsetNext = iNext * stride;
  
  		},
  
  		interpolate_: function ( i1, t0, t, t1 ) {
  
  			var result = this.resultBuffer,
  				values = this.sampleValues,
  				stride = this.valueSize,
  
  				o1 = i1 * stride,		o0 = o1 - stride,
  				oP = this._offsetPrev, 	oN = this._offsetNext,
  				wP = this._weightPrev,	wN = this._weightNext,
  
  				p = ( t - t0 ) / ( t1 - t0 ),
  				pp = p * p,
  				ppp = pp * p;
  
  			// evaluate polynomials
  
  			var sP = - wP * ppp + 2 * wP * pp - wP * p;
  			var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1;
  			var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;
  			var sN = wN * ppp - wN * pp;
  
  			// combine data linearly
  
  			for ( var i = 0; i !== stride; ++ i ) {
  
  				result[ i ] =
  						sP * values[ oP + i ] +
  						s0 * values[ o0 + i ] +
  						s1 * values[ o1 + i ] +
  						sN * values[ oN + i ];
  
  			}
  
  			return result;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
  		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
  	}
  
  	LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
  		constructor: LinearInterpolant,
  
  		interpolate_: function ( i1, t0, t, t1 ) {
  
  			var result = this.resultBuffer,
  				values = this.sampleValues,
  				stride = this.valueSize,
  
  				offset1 = i1 * stride,
  				offset0 = offset1 - stride,
  
  				weight1 = ( t - t0 ) / ( t1 - t0 ),
  				weight0 = 1 - weight1;
  
  			for ( var i = 0; i !== stride; ++ i ) {
  
  				result[ i ] =
  						values[ offset0 + i ] * weight0 +
  						values[ offset1 + i ] * weight1;
  
  			}
  
  			return result;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
  		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
  	}
  
  	DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
  		constructor: DiscreteInterpolant,
  
  		interpolate_: function ( i1  ) {
  
  			return this.copySampleValue_( i1 - 1 );
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function KeyframeTrack( name, times, values, interpolation ) {
  
  		if ( name === undefined ) throw new Error( 'KeyframeTrack: track name is undefined' );
  		if ( times === undefined || times.length === 0 ) throw new Error( 'KeyframeTrack: no keyframes in track named ' + name );
  
  		this.name = name;
  
  		this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
  		this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
  
  		this.setInterpolation( interpolation || this.DefaultInterpolation );
  
  	}
  
  	// Static methods
  
  	Object.assign( KeyframeTrack, {
  
  		// Serialization (in static context, because of constructor invocation
  		// and automatic invocation of .toJSON):
  
  		toJSON: function ( track ) {
  
  			var trackType = track.constructor;
  
  			var json;
  
  			// derived classes can define a static toJSON method
  			if ( trackType.toJSON !== undefined ) {
  
  				json = trackType.toJSON( track );
  
  			} else {
  
  				// by default, we assume the data can be serialized as-is
  				json = {
  
  					'name': track.name,
  					'times': AnimationUtils.convertArray( track.times, Array ),
  					'values': AnimationUtils.convertArray( track.values, Array )
  
  				};
  
  				var interpolation = track.getInterpolation();
  
  				if ( interpolation !== track.DefaultInterpolation ) {
  
  					json.interpolation = interpolation;
  
  				}
  
  			}
  
  			json.type = track.ValueTypeName; // mandatory
  
  			return json;
  
  		}
  
  	} );
  
  	Object.assign( KeyframeTrack.prototype, {
  
  		constructor: KeyframeTrack,
  
  		TimeBufferType: Float32Array,
  
  		ValueBufferType: Float32Array,
  
  		DefaultInterpolation: InterpolateLinear,
  
  		InterpolantFactoryMethodDiscrete: function ( result ) {
  
  			return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
  
  		},
  
  		InterpolantFactoryMethodLinear: function ( result ) {
  
  			return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
  
  		},
  
  		InterpolantFactoryMethodSmooth: function ( result ) {
  
  			return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
  
  		},
  
  		setInterpolation: function ( interpolation ) {
  
  			var factoryMethod;
  
  			switch ( interpolation ) {
  
  				case InterpolateDiscrete:
  
  					factoryMethod = this.InterpolantFactoryMethodDiscrete;
  
  					break;
  
  				case InterpolateLinear:
  
  					factoryMethod = this.InterpolantFactoryMethodLinear;
  
  					break;
  
  				case InterpolateSmooth:
  
  					factoryMethod = this.InterpolantFactoryMethodSmooth;
  
  					break;
  
  			}
  
  			if ( factoryMethod === undefined ) {
  
  				var message = "unsupported interpolation for " +
  					this.ValueTypeName + " keyframe track named " + this.name;
  
  				if ( this.createInterpolant === undefined ) {
  
  					// fall back to default, unless the default itself is messed up
  					if ( interpolation !== this.DefaultInterpolation ) {
  
  						this.setInterpolation( this.DefaultInterpolation );
  
  					} else {
  
  						throw new Error( message ); // fatal, in this case
  
  					}
  
  				}
  
  				console.warn( 'KeyframeTrack:', message );
  				return this;
  
  			}
  
  			this.createInterpolant = factoryMethod;
  
  			return this;
  
  		},
  
  		getInterpolation: function () {
  
  			switch ( this.createInterpolant ) {
  
  				case this.InterpolantFactoryMethodDiscrete:
  
  					return InterpolateDiscrete;
  
  				case this.InterpolantFactoryMethodLinear:
  
  					return InterpolateLinear;
  
  				case this.InterpolantFactoryMethodSmooth:
  
  					return InterpolateSmooth;
  
  			}
  
  		},
  
  		getValueSize: function () {
  
  			return this.values.length / this.times.length;
  
  		},
  
  		// move all keyframes either forwards or backwards in time
  		shift: function ( timeOffset ) {
  
  			if ( timeOffset !== 0.0 ) {
  
  				var times = this.times;
  
  				for ( var i = 0, n = times.length; i !== n; ++ i ) {
  
  					times[ i ] += timeOffset;
  
  				}
  
  			}
  
  			return this;
  
  		},
  
  		// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
  		scale: function ( timeScale ) {
  
  			if ( timeScale !== 1.0 ) {
  
  				var times = this.times;
  
  				for ( var i = 0, n = times.length; i !== n; ++ i ) {
  
  					times[ i ] *= timeScale;
  
  				}
  
  			}
  
  			return this;
  
  		},
  
  		// removes keyframes before and after animation without changing any values within the range [startTime, endTime].
  		// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
  		trim: function ( startTime, endTime ) {
  
  			var times = this.times,
  				nKeys = times.length,
  				from = 0,
  				to = nKeys - 1;
  
  			while ( from !== nKeys && times[ from ] < startTime ) {
  
  				++ from;
  
  			}
  
  			while ( to !== - 1 && times[ to ] > endTime ) {
  
  				-- to;
  
  			}
  
  			++ to; // inclusive -> exclusive bound
  
  			if ( from !== 0 || to !== nKeys ) {
  
  				// empty tracks are forbidden, so keep at least one keyframe
  				if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
  
  				var stride = this.getValueSize();
  				this.times = AnimationUtils.arraySlice( times, from, to );
  				this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
  
  			}
  
  			return this;
  
  		},
  
  		// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
  		validate: function () {
  
  			var valid = true;
  
  			var valueSize = this.getValueSize();
  			if ( valueSize - Math.floor( valueSize ) !== 0 ) {
  
  				console.error( 'KeyframeTrack: Invalid value size in track.', this );
  				valid = false;
  
  			}
  
  			var times = this.times,
  				values = this.values,
  
  				nKeys = times.length;
  
  			if ( nKeys === 0 ) {
  
  				console.error( 'KeyframeTrack: Track is empty.', this );
  				valid = false;
  
  			}
  
  			var prevTime = null;
  
  			for ( var i = 0; i !== nKeys; i ++ ) {
  
  				var currTime = times[ i ];
  
  				if ( typeof currTime === 'number' && isNaN( currTime ) ) {
  
  					console.error( 'KeyframeTrack: Time is not a valid number.', this, i, currTime );
  					valid = false;
  					break;
  
  				}
  
  				if ( prevTime !== null && prevTime > currTime ) {
  
  					console.error( 'KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
  					valid = false;
  					break;
  
  				}
  
  				prevTime = currTime;
  
  			}
  
  			if ( values !== undefined ) {
  
  				if ( AnimationUtils.isTypedArray( values ) ) {
  
  					for ( var i = 0, n = values.length; i !== n; ++ i ) {
  
  						var value = values[ i ];
  
  						if ( isNaN( value ) ) {
  
  							console.error( 'KeyframeTrack: Value is not a valid number.', this, i, value );
  							valid = false;
  							break;
  
  						}
  
  					}
  
  				}
  
  			}
  
  			return valid;
  
  		},
  
  		// removes equivalent sequential keys as common in morph target sequences
  		// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
  		optimize: function () {
  
  			var times = this.times,
  				values = this.values,
  				stride = this.getValueSize(),
  
  				smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
  
  				writeIndex = 1,
  				lastIndex = times.length - 1;
  
  			for ( var i = 1; i < lastIndex; ++ i ) {
  
  				var keep = false;
  
  				var time = times[ i ];
  				var timeNext = times[ i + 1 ];
  
  				// remove adjacent keyframes scheduled at the same time
  
  				if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
  
  					if ( ! smoothInterpolation ) {
  
  						// remove unnecessary keyframes same as their neighbors
  
  						var offset = i * stride,
  							offsetP = offset - stride,
  							offsetN = offset + stride;
  
  						for ( var j = 0; j !== stride; ++ j ) {
  
  							var value = values[ offset + j ];
  
  							if ( value !== values[ offsetP + j ] ||
  								value !== values[ offsetN + j ] ) {
  
  								keep = true;
  								break;
  
  							}
  
  						}
  
  					} else {
  
  						keep = true;
  
  					}
  
  				}
  
  				// in-place compaction
  
  				if ( keep ) {
  
  					if ( i !== writeIndex ) {
  
  						times[ writeIndex ] = times[ i ];
  
  						var readOffset = i * stride,
  							writeOffset = writeIndex * stride;
  
  						for ( var j = 0; j !== stride; ++ j ) {
  
  							values[ writeOffset + j ] = values[ readOffset + j ];
  
  						}
  
  					}
  
  					++ writeIndex;
  
  				}
  
  			}
  
  			// flush last keyframe (compaction looks ahead)
  
  			if ( lastIndex > 0 ) {
  
  				times[ writeIndex ] = times[ lastIndex ];
  
  				for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
  
  					values[ writeOffset + j ] = values[ readOffset + j ];
  
  				}
  
  				++ writeIndex;
  
  			}
  
  			if ( writeIndex !== times.length ) {
  
  				this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
  				this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
  
  			}
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			var times = AnimationUtils.arraySlice( this.times, 0 );
  			var values = AnimationUtils.arraySlice( this.values, 0 );
  
  			var TypedKeyframeTrack = this.constructor;
  			var track = new TypedKeyframeTrack( this.name, times, values );
  
  			// Interpolant argument to constructor is not saved, so copy the factory method directly.
  			track.createInterpolant = this.createInterpolant;
  
  			return track;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function BooleanKeyframeTrack( name, times, values ) {
  
  		KeyframeTrack.call( this, name, times, values );
  
  	}
  
  	BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
  		constructor: BooleanKeyframeTrack,
  
  		ValueTypeName: 'bool',
  		ValueBufferType: Array,
  
  		DefaultInterpolation: InterpolateDiscrete,
  
  		InterpolantFactoryMethodLinear: undefined,
  		InterpolantFactoryMethodSmooth: undefined
  
  		// Note: Actually this track could have a optimized / compressed
  		// representation of a single value and a custom interpolant that
  		// computes "firstValue ^ isOdd( index )".
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function ColorKeyframeTrack( name, times, values, interpolation ) {
  
  		KeyframeTrack.call( this, name, times, values, interpolation );
  
  	}
  
  	ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
  		constructor: ColorKeyframeTrack,
  
  		ValueTypeName: 'color'
  
  		// ValueBufferType is inherited
  
  		// DefaultInterpolation is inherited
  
  		// Note: Very basic implementation and nothing special yet.
  		// However, this is the place for color space parameterization.
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function NumberKeyframeTrack( name, times, values, interpolation ) {
  
  		KeyframeTrack.call( this, name, times, values, interpolation );
  
  	}
  
  	NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
  		constructor: NumberKeyframeTrack,
  
  		ValueTypeName: 'number'
  
  		// ValueBufferType is inherited
  
  		// DefaultInterpolation is inherited
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	var _Math = {
  
  		DEG2RAD: Math.PI / 180,
  		RAD2DEG: 180 / Math.PI,
  
  		generateUUID: ( function () {
  
  			// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  
  			var lut = [];
  
  			for ( var i = 0; i < 256; i ++ ) {
  
  				lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
  
  			}
  
  			return function generateUUID() {
  
  				var d0 = Math.random() * 0xffffffff | 0;
  				var d1 = Math.random() * 0xffffffff | 0;
  				var d2 = Math.random() * 0xffffffff | 0;
  				var d3 = Math.random() * 0xffffffff | 0;
  				var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
  					lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
  					lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
  					lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
  
  				// .toUpperCase() here flattens concatenated strings to save heap memory space.
  				return uuid.toUpperCase();
  
  			};
  
  		} )(),
  
  		clamp: function ( value, min, max ) {
  
  			return Math.max( min, Math.min( max, value ) );
  
  		},
  
  		// compute euclidian modulo of m % n
  		// https://en.wikipedia.org/wiki/Modulo_operation
  
  		euclideanModulo: function ( n, m ) {
  
  			return ( ( n % m ) + m ) % m;
  
  		},
  
  		// Linear mapping from range <a1, a2> to range <b1, b2>
  
  		mapLinear: function ( x, a1, a2, b1, b2 ) {
  
  			return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
  
  		},
  
  		// https://en.wikipedia.org/wiki/Linear_interpolation
  
  		lerp: function ( x, y, t ) {
  
  			return ( 1 - t ) * x + t * y;
  
  		},
  
  		// http://en.wikipedia.org/wiki/Smoothstep
  
  		smoothstep: function ( x, min, max ) {
  
  			if ( x <= min ) return 0;
  			if ( x >= max ) return 1;
  
  			x = ( x - min ) / ( max - min );
  
  			return x * x * ( 3 - 2 * x );
  
  		},
  
  		smootherstep: function ( x, min, max ) {
  
  			if ( x <= min ) return 0;
  			if ( x >= max ) return 1;
  
  			x = ( x - min ) / ( max - min );
  
  			return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
  
  		},
  
  		// Random integer from <low, high> interval
  
  		randInt: function ( low, high ) {
  
  			return low + Math.floor( Math.random() * ( high - low + 1 ) );
  
  		},
  
  		// Random float from <low, high> interval
  
  		randFloat: function ( low, high ) {
  
  			return low + Math.random() * ( high - low );
  
  		},
  
  		// Random float from <-range/2, range/2> interval
  
  		randFloatSpread: function ( range ) {
  
  			return range * ( 0.5 - Math.random() );
  
  		},
  
  		degToRad: function ( degrees ) {
  
  			return degrees * _Math.DEG2RAD;
  
  		},
  
  		radToDeg: function ( radians ) {
  
  			return radians * _Math.RAD2DEG;
  
  		},
  
  		isPowerOfTwo: function ( value ) {
  
  			return ( value & ( value - 1 ) ) === 0 && value !== 0;
  
  		},
  
  		ceilPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
  
  		},
  
  		floorPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function Matrix4() {
  
  		this.elements = [
  
  			1, 0, 0, 0,
  			0, 1, 0, 0,
  			0, 0, 1, 0,
  			0, 0, 0, 1
  
  		];
  
  		if ( arguments.length > 0 ) {
  
  			console.error( 'Matrix4: the constructor no longer reads arguments. use .set() instead.' );
  
  		}
  
  	}
  
  	Object.assign( Matrix4.prototype, {
  
  		isMatrix4: true,
  
  		set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
  
  			var te = this.elements;
  
  			te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
  			te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
  			te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
  			te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
  
  			return this;
  
  		},
  
  		identity: function () {
  
  			this.set(
  
  				1, 0, 0, 0,
  				0, 1, 0, 0,
  				0, 0, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new Matrix4().fromArray( this.elements );
  
  		},
  
  		copy: function ( m ) {
  
  			var te = this.elements;
  			var me = m.elements;
  
  			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
  			te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
  			te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
  			te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
  
  			return this;
  
  		},
  
  		copyPosition: function ( m ) {
  
  			var te = this.elements, me = m.elements;
  
  			te[ 12 ] = me[ 12 ];
  			te[ 13 ] = me[ 13 ];
  			te[ 14 ] = me[ 14 ];
  
  			return this;
  
  		},
  
  		extractBasis: function ( xAxis, yAxis, zAxis ) {
  
  			xAxis.setFromMatrixColumn( this, 0 );
  			yAxis.setFromMatrixColumn( this, 1 );
  			zAxis.setFromMatrixColumn( this, 2 );
  
  			return this;
  
  		},
  
  		makeBasis: function ( xAxis, yAxis, zAxis ) {
  
  			this.set(
  				xAxis.x, yAxis.x, zAxis.x, 0,
  				xAxis.y, yAxis.y, zAxis.y, 0,
  				xAxis.z, yAxis.z, zAxis.z, 0,
  				0, 0, 0, 1
  			);
  
  			return this;
  
  		},
  
  		extractRotation: function () {
  
  			var v1 = new Vector3();
  
  			return function extractRotation( m ) {
  
  				// this method does not support reflection matrices
  
  				var te = this.elements;
  				var me = m.elements;
  
  				var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
  				var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
  				var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
  
  				te[ 0 ] = me[ 0 ] * scaleX;
  				te[ 1 ] = me[ 1 ] * scaleX;
  				te[ 2 ] = me[ 2 ] * scaleX;
  				te[ 3 ] = 0;
  
  				te[ 4 ] = me[ 4 ] * scaleY;
  				te[ 5 ] = me[ 5 ] * scaleY;
  				te[ 6 ] = me[ 6 ] * scaleY;
  				te[ 7 ] = 0;
  
  				te[ 8 ] = me[ 8 ] * scaleZ;
  				te[ 9 ] = me[ 9 ] * scaleZ;
  				te[ 10 ] = me[ 10 ] * scaleZ;
  				te[ 11 ] = 0;
  
  				te[ 12 ] = 0;
  				te[ 13 ] = 0;
  				te[ 14 ] = 0;
  				te[ 15 ] = 1;
  
  				return this;
  
  			};
  
  		}(),
  
  		makeRotationFromEuler: function ( euler ) {
  
  			if ( ! ( euler && euler.isEuler ) ) {
  
  				console.error( 'Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
  
  			}
  
  			var te = this.elements;
  
  			var x = euler.x, y = euler.y, z = euler.z;
  			var a = Math.cos( x ), b = Math.sin( x );
  			var c = Math.cos( y ), d = Math.sin( y );
  			var e = Math.cos( z ), f = Math.sin( z );
  
  			if ( euler.order === 'XYZ' ) {
  
  				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = - c * f;
  				te[ 8 ] = d;
  
  				te[ 1 ] = af + be * d;
  				te[ 5 ] = ae - bf * d;
  				te[ 9 ] = - b * c;
  
  				te[ 2 ] = bf - ae * d;
  				te[ 6 ] = be + af * d;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'YXZ' ) {
  
  				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
  				te[ 0 ] = ce + df * b;
  				te[ 4 ] = de * b - cf;
  				te[ 8 ] = a * d;
  
  				te[ 1 ] = a * f;
  				te[ 5 ] = a * e;
  				te[ 9 ] = - b;
  
  				te[ 2 ] = cf * b - de;
  				te[ 6 ] = df + ce * b;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'ZXY' ) {
  
  				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
  				te[ 0 ] = ce - df * b;
  				te[ 4 ] = - a * f;
  				te[ 8 ] = de + cf * b;
  
  				te[ 1 ] = cf + de * b;
  				te[ 5 ] = a * e;
  				te[ 9 ] = df - ce * b;
  
  				te[ 2 ] = - a * d;
  				te[ 6 ] = b;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'ZYX' ) {
  
  				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = be * d - af;
  				te[ 8 ] = ae * d + bf;
  
  				te[ 1 ] = c * f;
  				te[ 5 ] = bf * d + ae;
  				te[ 9 ] = af * d - be;
  
  				te[ 2 ] = - d;
  				te[ 6 ] = b * c;
  				te[ 10 ] = a * c;
  
  			} else if ( euler.order === 'YZX' ) {
  
  				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = bd - ac * f;
  				te[ 8 ] = bc * f + ad;
  
  				te[ 1 ] = f;
  				te[ 5 ] = a * e;
  				te[ 9 ] = - b * e;
  
  				te[ 2 ] = - d * e;
  				te[ 6 ] = ad * f + bc;
  				te[ 10 ] = ac - bd * f;
  
  			} else if ( euler.order === 'XZY' ) {
  
  				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
  				te[ 0 ] = c * e;
  				te[ 4 ] = - f;
  				te[ 8 ] = d * e;
  
  				te[ 1 ] = ac * f + bd;
  				te[ 5 ] = a * e;
  				te[ 9 ] = ad * f - bc;
  
  				te[ 2 ] = bc * f - ad;
  				te[ 6 ] = b * e;
  				te[ 10 ] = bd * f + ac;
  
  			}
  
  			// bottom row
  			te[ 3 ] = 0;
  			te[ 7 ] = 0;
  			te[ 11 ] = 0;
  
  			// last column
  			te[ 12 ] = 0;
  			te[ 13 ] = 0;
  			te[ 14 ] = 0;
  			te[ 15 ] = 1;
  
  			return this;
  
  		},
  
  		makeRotationFromQuaternion: function () {
  
  			var zero = new Vector3( 0, 0, 0 );
  			var one = new Vector3( 1, 1, 1 );
  
  			return function makeRotationFromQuaternion( q ) {
  
  				return this.compose( zero, q, one );
  
  			};
  
  		}(),
  
  		lookAt: function () {
  
  			var x = new Vector3();
  			var y = new Vector3();
  			var z = new Vector3();
  
  			return function lookAt( eye, target, up ) {
  
  				var te = this.elements;
  
  				z.subVectors( eye, target );
  
  				if ( z.lengthSq() === 0 ) {
  
  					// eye and target are in the same position
  
  					z.z = 1;
  
  				}
  
  				z.normalize();
  				x.crossVectors( up, z );
  
  				if ( x.lengthSq() === 0 ) {
  
  					// up and z are parallel
  
  					if ( Math.abs( up.z ) === 1 ) {
  
  						z.x += 0.0001;
  
  					} else {
  
  						z.z += 0.0001;
  
  					}
  
  					z.normalize();
  					x.crossVectors( up, z );
  
  				}
  
  				x.normalize();
  				y.crossVectors( z, x );
  
  				te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
  				te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
  				te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
  
  				return this;
  
  			};
  
  		}(),
  
  		multiply: function ( m, n ) {
  
  			if ( n !== undefined ) {
  
  				console.warn( 'Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
  				return this.multiplyMatrices( m, n );
  
  			}
  
  			return this.multiplyMatrices( this, m );
  
  		},
  
  		premultiply: function ( m ) {
  
  			return this.multiplyMatrices( m, this );
  
  		},
  
  		multiplyMatrices: function ( a, b ) {
  
  			var ae = a.elements;
  			var be = b.elements;
  			var te = this.elements;
  
  			var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
  			var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
  			var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
  			var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
  
  			var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
  			var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
  			var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
  			var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
  
  			te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
  			te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
  			te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
  			te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
  
  			te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
  			te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
  			te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
  			te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
  
  			te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
  			te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
  			te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
  			te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
  
  			te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
  			te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
  			te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
  			te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( s ) {
  
  			var te = this.elements;
  
  			te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
  			te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
  			te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
  			te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
  
  			return this;
  
  		},
  
  		applyToBufferAttribute: function () {
  
  			var v1 = new Vector3();
  
  			return function applyToBufferAttribute( attribute ) {
  
  				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
  					v1.x = attribute.getX( i );
  					v1.y = attribute.getY( i );
  					v1.z = attribute.getZ( i );
  
  					v1.applyMatrix4( this );
  
  					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
  				}
  
  				return attribute;
  
  			};
  
  		}(),
  
  		determinant: function () {
  
  			var te = this.elements;
  
  			var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
  			var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
  			var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
  			var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
  
  			//TODO: make this more efficient
  			//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
  
  			return (
  				n41 * (
  					+ n14 * n23 * n32
  					 - n13 * n24 * n32
  					 - n14 * n22 * n33
  					 + n12 * n24 * n33
  					 + n13 * n22 * n34
  					 - n12 * n23 * n34
  				) +
  				n42 * (
  					+ n11 * n23 * n34
  					 - n11 * n24 * n33
  					 + n14 * n21 * n33
  					 - n13 * n21 * n34
  					 + n13 * n24 * n31
  					 - n14 * n23 * n31
  				) +
  				n43 * (
  					+ n11 * n24 * n32
  					 - n11 * n22 * n34
  					 - n14 * n21 * n32
  					 + n12 * n21 * n34
  					 + n14 * n22 * n31
  					 - n12 * n24 * n31
  				) +
  				n44 * (
  					- n13 * n22 * n31
  					 - n11 * n23 * n32
  					 + n11 * n22 * n33
  					 + n13 * n21 * n32
  					 - n12 * n21 * n33
  					 + n12 * n23 * n31
  				)
  
  			);
  
  		},
  
  		transpose: function () {
  
  			var te = this.elements;
  			var tmp;
  
  			tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
  			tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
  			tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
  
  			tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
  			tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
  			tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
  
  			return this;
  
  		},
  
  		setPosition: function ( v ) {
  
  			var te = this.elements;
  
  			te[ 12 ] = v.x;
  			te[ 13 ] = v.y;
  			te[ 14 ] = v.z;
  
  			return this;
  
  		},
  
  		getInverse: function ( m, throwOnDegenerate ) {
  
  			// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
  			var te = this.elements,
  				me = m.elements,
  
  				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
  				n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
  				n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
  				n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
  
  				t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
  				t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
  				t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
  				t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
  
  			var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
  
  			if ( det === 0 ) {
  
  				var msg = "Matrix4: .getInverse() can't invert matrix, determinant is 0";
  
  				if ( throwOnDegenerate === true ) {
  
  					throw new Error( msg );
  
  				} else {
  
  					console.warn( msg );
  
  				}
  
  				return this.identity();
  
  			}
  
  			var detInv = 1 / det;
  
  			te[ 0 ] = t11 * detInv;
  			te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
  			te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
  			te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
  
  			te[ 4 ] = t12 * detInv;
  			te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
  			te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
  			te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
  
  			te[ 8 ] = t13 * detInv;
  			te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
  			te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
  			te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
  
  			te[ 12 ] = t14 * detInv;
  			te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
  			te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
  			te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
  
  			return this;
  
  		},
  
  		scale: function ( v ) {
  
  			var te = this.elements;
  			var x = v.x, y = v.y, z = v.z;
  
  			te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
  			te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
  			te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
  			te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
  
  			return this;
  
  		},
  
  		getMaxScaleOnAxis: function () {
  
  			var te = this.elements;
  
  			var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
  			var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
  			var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
  
  			return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
  
  		},
  
  		makeTranslation: function ( x, y, z ) {
  
  			this.set(
  
  				1, 0, 0, x,
  				0, 1, 0, y,
  				0, 0, 1, z,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationX: function ( theta ) {
  
  			var c = Math.cos( theta ), s = Math.sin( theta );
  
  			this.set(
  
  				1, 0, 0, 0,
  				0, c, - s, 0,
  				0, s, c, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationY: function ( theta ) {
  
  			var c = Math.cos( theta ), s = Math.sin( theta );
  
  			this.set(
  
  				 c, 0, s, 0,
  				 0, 1, 0, 0,
  				- s, 0, c, 0,
  				 0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationZ: function ( theta ) {
  
  			var c = Math.cos( theta ), s = Math.sin( theta );
  
  			this.set(
  
  				c, - s, 0, 0,
  				s, c, 0, 0,
  				0, 0, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeRotationAxis: function ( axis, angle ) {
  
  			// Based on http://www.gamedev.net/reference/articles/article1199.asp
  
  			var c = Math.cos( angle );
  			var s = Math.sin( angle );
  			var t = 1 - c;
  			var x = axis.x, y = axis.y, z = axis.z;
  			var tx = t * x, ty = t * y;
  
  			this.set(
  
  				tx * x + c, tx * y - s * z, tx * z + s * y, 0,
  				tx * y + s * z, ty * y + c, ty * z - s * x, 0,
  				tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
  				0, 0, 0, 1
  
  			);
  
  			 return this;
  
  		},
  
  		makeScale: function ( x, y, z ) {
  
  			this.set(
  
  				x, 0, 0, 0,
  				0, y, 0, 0,
  				0, 0, z, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		makeShear: function ( x, y, z ) {
  
  			this.set(
  
  				1, y, z, 0,
  				x, 1, z, 0,
  				x, y, 1, 0,
  				0, 0, 0, 1
  
  			);
  
  			return this;
  
  		},
  
  		compose: function ( position, quaternion, scale ) {
  
  			var te = this.elements;
  
  			var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
  			var x2 = x + x,	y2 = y + y, z2 = z + z;
  			var xx = x * x2, xy = x * y2, xz = x * z2;
  			var yy = y * y2, yz = y * z2, zz = z * z2;
  			var wx = w * x2, wy = w * y2, wz = w * z2;
  
  			var sx = scale.x, sy = scale.y, sz = scale.z;
  
  		        te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
  		        te[ 1 ] = ( xy + wz ) * sx;
  		        te[ 2 ] = ( xz - wy ) * sx;
  		        te[ 3 ] = 0;
  
  		        te[ 4 ] = ( xy - wz ) * sy;
  		        te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
  		        te[ 6 ] = ( yz + wx ) * sy;
  		        te[ 7 ] = 0;
  
  		        te[ 8 ] = ( xz + wy ) * sz;
  		        te[ 9 ] = ( yz - wx ) * sz;
  		        te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
  		        te[ 11 ] = 0;
  
  		        te[ 12 ] = position.x;
  		        te[ 13 ] = position.y;
  		        te[ 14 ] = position.z;
  		        te[ 15 ] = 1;
  
  		        return this;
  
  		},
  
  		decompose: function () {
  
  			var vector = new Vector3();
  			var matrix = new Matrix4();
  
  			return function decompose( position, quaternion, scale ) {
  
  				var te = this.elements;
  
  				var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
  				var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
  				var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
  
  				// if determine is negative, we need to invert one scale
  				var det = this.determinant();
  				if ( det < 0 ) sx = - sx;
  
  				position.x = te[ 12 ];
  				position.y = te[ 13 ];
  				position.z = te[ 14 ];
  
  				// scale the rotation part
  				matrix.copy( this );
  
  				var invSX = 1 / sx;
  				var invSY = 1 / sy;
  				var invSZ = 1 / sz;
  
  				matrix.elements[ 0 ] *= invSX;
  				matrix.elements[ 1 ] *= invSX;
  				matrix.elements[ 2 ] *= invSX;
  
  				matrix.elements[ 4 ] *= invSY;
  				matrix.elements[ 5 ] *= invSY;
  				matrix.elements[ 6 ] *= invSY;
  
  				matrix.elements[ 8 ] *= invSZ;
  				matrix.elements[ 9 ] *= invSZ;
  				matrix.elements[ 10 ] *= invSZ;
  
  				quaternion.setFromRotationMatrix( matrix );
  
  				scale.x = sx;
  				scale.y = sy;
  				scale.z = sz;
  
  				return this;
  
  			};
  
  		}(),
  
  		makePerspective: function ( left, right, top, bottom, near, far ) {
  
  			if ( far === undefined ) {
  
  				console.warn( 'Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
  
  			}
  
  			var te = this.elements;
  			var x = 2 * near / ( right - left );
  			var y = 2 * near / ( top - bottom );
  
  			var a = ( right + left ) / ( right - left );
  			var b = ( top + bottom ) / ( top - bottom );
  			var c = - ( far + near ) / ( far - near );
  			var d = - 2 * far * near / ( far - near );
  
  			te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
  			te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
  			te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
  			te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;
  
  			return this;
  
  		},
  
  		makeOrthographic: function ( left, right, top, bottom, near, far ) {
  
  			var te = this.elements;
  			var w = 1.0 / ( right - left );
  			var h = 1.0 / ( top - bottom );
  			var p = 1.0 / ( far - near );
  
  			var x = ( right + left ) * w;
  			var y = ( top + bottom ) * h;
  			var z = ( far + near ) * p;
  
  			te[ 0 ] = 2 * w;	te[ 4 ] = 0;	te[ 8 ] = 0;	te[ 12 ] = - x;
  			te[ 1 ] = 0;	te[ 5 ] = 2 * h;	te[ 9 ] = 0;	te[ 13 ] = - y;
  			te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = - 2 * p;	te[ 14 ] = - z;
  			te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = 0;	te[ 15 ] = 1;
  
  			return this;
  
  		},
  
  		equals: function ( matrix ) {
  
  			var te = this.elements;
  			var me = matrix.elements;
  
  			for ( var i = 0; i < 16; i ++ ) {
  
  				if ( te[ i ] !== me[ i ] ) return false;
  
  			}
  
  			return true;
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			for ( var i = 0; i < 16; i ++ ) {
  
  				this.elements[ i ] = array[ i + offset ];
  
  			}
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			var te = this.elements;
  
  			array[ offset ] = te[ 0 ];
  			array[ offset + 1 ] = te[ 1 ];
  			array[ offset + 2 ] = te[ 2 ];
  			array[ offset + 3 ] = te[ 3 ];
  
  			array[ offset + 4 ] = te[ 4 ];
  			array[ offset + 5 ] = te[ 5 ];
  			array[ offset + 6 ] = te[ 6 ];
  			array[ offset + 7 ] = te[ 7 ];
  
  			array[ offset + 8 ] = te[ 8 ];
  			array[ offset + 9 ] = te[ 9 ];
  			array[ offset + 10 ] = te[ 10 ];
  			array[ offset + 11 ] = te[ 11 ];
  
  			array[ offset + 12 ] = te[ 12 ];
  			array[ offset + 13 ] = te[ 13 ];
  			array[ offset + 14 ] = te[ 14 ];
  			array[ offset + 15 ] = te[ 15 ];
  
  			return array;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function Vector3( x, y, z ) {
  
  		this.x = x || 0;
  		this.y = y || 0;
  		this.z = z || 0;
  
  	}
  
  	Object.assign( Vector3.prototype, {
  
  		isVector3: true,
  
  		set: function ( x, y, z ) {
  
  			this.x = x;
  			this.y = y;
  			this.z = z;
  
  			return this;
  
  		},
  
  		setScalar: function ( scalar ) {
  
  			this.x = scalar;
  			this.y = scalar;
  			this.z = scalar;
  
  			return this;
  
  		},
  
  		setX: function ( x ) {
  
  			this.x = x;
  
  			return this;
  
  		},
  
  		setY: function ( y ) {
  
  			this.y = y;
  
  			return this;
  
  		},
  
  		setZ: function ( z ) {
  
  			this.z = z;
  
  			return this;
  
  		},
  
  		setComponent: function ( index, value ) {
  
  			switch ( index ) {
  
  				case 0: this.x = value; break;
  				case 1: this.y = value; break;
  				case 2: this.z = value; break;
  				default: throw new Error( 'index is out of range: ' + index );
  
  			}
  
  			return this;
  
  		},
  
  		getComponent: function ( index ) {
  
  			switch ( index ) {
  
  				case 0: return this.x;
  				case 1: return this.y;
  				case 2: return this.z;
  				default: throw new Error( 'index is out of range: ' + index );
  
  			}
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this.x, this.y, this.z );
  
  		},
  
  		copy: function ( v ) {
  
  			this.x = v.x;
  			this.y = v.y;
  			this.z = v.z;
  
  			return this;
  
  		},
  
  		add: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
  				return this.addVectors( v, w );
  
  			}
  
  			this.x += v.x;
  			this.y += v.y;
  			this.z += v.z;
  
  			return this;
  
  		},
  
  		addScalar: function ( s ) {
  
  			this.x += s;
  			this.y += s;
  			this.z += s;
  
  			return this;
  
  		},
  
  		addVectors: function ( a, b ) {
  
  			this.x = a.x + b.x;
  			this.y = a.y + b.y;
  			this.z = a.z + b.z;
  
  			return this;
  
  		},
  
  		addScaledVector: function ( v, s ) {
  
  			this.x += v.x * s;
  			this.y += v.y * s;
  			this.z += v.z * s;
  
  			return this;
  
  		},
  
  		sub: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
  				return this.subVectors( v, w );
  
  			}
  
  			this.x -= v.x;
  			this.y -= v.y;
  			this.z -= v.z;
  
  			return this;
  
  		},
  
  		subScalar: function ( s ) {
  
  			this.x -= s;
  			this.y -= s;
  			this.z -= s;
  
  			return this;
  
  		},
  
  		subVectors: function ( a, b ) {
  
  			this.x = a.x - b.x;
  			this.y = a.y - b.y;
  			this.z = a.z - b.z;
  
  			return this;
  
  		},
  
  		multiply: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
  				return this.multiplyVectors( v, w );
  
  			}
  
  			this.x *= v.x;
  			this.y *= v.y;
  			this.z *= v.z;
  
  			return this;
  
  		},
  
  		multiplyScalar: function ( scalar ) {
  
  			this.x *= scalar;
  			this.y *= scalar;
  			this.z *= scalar;
  
  			return this;
  
  		},
  
  		multiplyVectors: function ( a, b ) {
  
  			this.x = a.x * b.x;
  			this.y = a.y * b.y;
  			this.z = a.z * b.z;
  
  			return this;
  
  		},
  
  		applyEuler: function () {
  
  			var quaternion = new Quaternion();
  
  			return function applyEuler( euler ) {
  
  				if ( ! ( euler && euler.isEuler ) ) {
  
  					console.error( 'Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
  				}
  
  				return this.applyQuaternion( quaternion.setFromEuler( euler ) );
  
  			};
  
  		}(),
  
  		applyAxisAngle: function () {
  
  			var quaternion = new Quaternion();
  
  			return function applyAxisAngle( axis, angle ) {
  
  				return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
  
  			};
  
  		}(),
  
  		applyMatrix3: function ( m ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
  			this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
  			this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
  
  			return this;
  
  		},
  
  		applyMatrix4: function ( m ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
  
  			this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
  			this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
  			this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
  
  			return this;
  
  		},
  
  		applyQuaternion: function ( q ) {
  
  			var x = this.x, y = this.y, z = this.z;
  			var qx = q.x, qy = q.y, qz = q.z, qw = q.w;
  
  			// calculate quat * vector
  
  			var ix = qw * x + qy * z - qz * y;
  			var iy = qw * y + qz * x - qx * z;
  			var iz = qw * z + qx * y - qy * x;
  			var iw = - qx * x - qy * y - qz * z;
  
  			// calculate result * inverse quat
  
  			this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
  			this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
  			this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
  
  			return this;
  
  		},
  
  		project: function ( camera ) {
  
  			return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
  
  		},
  
  		unproject: function () {
  
  			var matrix = new Matrix4();
  
  			return function unproject( camera ) {
  
  				return this.applyMatrix4( matrix.getInverse( camera.projectionMatrix ) ).applyMatrix4( camera.matrixWorld );
  
  			};
  
  		}(),
  
  		transformDirection: function ( m ) {
  
  			// input: Matrix4 affine matrix
  			// vector interpreted as a direction
  
  			var x = this.x, y = this.y, z = this.z;
  			var e = m.elements;
  
  			this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
  			this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
  			this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
  
  			return this.normalize();
  
  		},
  
  		divide: function ( v ) {
  
  			this.x /= v.x;
  			this.y /= v.y;
  			this.z /= v.z;
  
  			return this;
  
  		},
  
  		divideScalar: function ( scalar ) {
  
  			return this.multiplyScalar( 1 / scalar );
  
  		},
  
  		min: function ( v ) {
  
  			this.x = Math.min( this.x, v.x );
  			this.y = Math.min( this.y, v.y );
  			this.z = Math.min( this.z, v.z );
  
  			return this;
  
  		},
  
  		max: function ( v ) {
  
  			this.x = Math.max( this.x, v.x );
  			this.y = Math.max( this.y, v.y );
  			this.z = Math.max( this.z, v.z );
  
  			return this;
  
  		},
  
  		clamp: function ( min, max ) {
  
  			// assumes min < max, componentwise
  
  			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
  			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
  			this.z = Math.max( min.z, Math.min( max.z, this.z ) );
  
  			return this;
  
  		},
  
  		clampScalar: function () {
  
  			var min = new Vector3();
  			var max = new Vector3();
  
  			return function clampScalar( minVal, maxVal ) {
  
  				min.set( minVal, minVal, minVal );
  				max.set( maxVal, maxVal, maxVal );
  
  				return this.clamp( min, max );
  
  			};
  
  		}(),
  
  		clampLength: function ( min, max ) {
  
  			var length = this.length();
  
  			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
  		},
  
  		floor: function () {
  
  			this.x = Math.floor( this.x );
  			this.y = Math.floor( this.y );
  			this.z = Math.floor( this.z );
  
  			return this;
  
  		},
  
  		ceil: function () {
  
  			this.x = Math.ceil( this.x );
  			this.y = Math.ceil( this.y );
  			this.z = Math.ceil( this.z );
  
  			return this;
  
  		},
  
  		round: function () {
  
  			this.x = Math.round( this.x );
  			this.y = Math.round( this.y );
  			this.z = Math.round( this.z );
  
  			return this;
  
  		},
  
  		roundToZero: function () {
  
  			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
  			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
  			this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
  
  			return this;
  
  		},
  
  		negate: function () {
  
  			this.x = - this.x;
  			this.y = - this.y;
  			this.z = - this.z;
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this.x * v.x + this.y * v.y + this.z * v.z;
  
  		},
  
  		// TODO lengthSquared?
  
  		lengthSq: function () {
  
  			return this.x * this.x + this.y * this.y + this.z * this.z;
  
  		},
  
  		length: function () {
  
  			return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
  
  		},
  
  		manhattanLength: function () {
  
  			return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
  
  		},
  
  		normalize: function () {
  
  			return this.divideScalar( this.length() || 1 );
  
  		},
  
  		setLength: function ( length ) {
  
  			return this.normalize().multiplyScalar( length );
  
  		},
  
  		lerp: function ( v, alpha ) {
  
  			this.x += ( v.x - this.x ) * alpha;
  			this.y += ( v.y - this.y ) * alpha;
  			this.z += ( v.z - this.z ) * alpha;
  
  			return this;
  
  		},
  
  		lerpVectors: function ( v1, v2, alpha ) {
  
  			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
  		},
  
  		cross: function ( v, w ) {
  
  			if ( w !== undefined ) {
  
  				console.warn( 'Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
  				return this.crossVectors( v, w );
  
  			}
  
  			return this.crossVectors( this, v );
  
  		},
  
  		crossVectors: function ( a, b ) {
  
  			var ax = a.x, ay = a.y, az = a.z;
  			var bx = b.x, by = b.y, bz = b.z;
  
  			this.x = ay * bz - az * by;
  			this.y = az * bx - ax * bz;
  			this.z = ax * by - ay * bx;
  
  			return this;
  
  		},
  
  		projectOnVector: function ( vector ) {
  
  			var scalar = vector.dot( this ) / vector.lengthSq();
  
  			return this.copy( vector ).multiplyScalar( scalar );
  
  		},
  
  		projectOnPlane: function () {
  
  			var v1 = new Vector3();
  
  			return function projectOnPlane( planeNormal ) {
  
  				v1.copy( this ).projectOnVector( planeNormal );
  
  				return this.sub( v1 );
  
  			};
  
  		}(),
  
  		reflect: function () {
  
  			// reflect incident vector off plane orthogonal to normal
  			// normal is assumed to have unit length
  
  			var v1 = new Vector3();
  
  			return function reflect( normal ) {
  
  				return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
  
  			};
  
  		}(),
  
  		angleTo: function ( v ) {
  
  			var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
  
  			// clamp, to handle numerical problems
  
  			return Math.acos( _Math.clamp( theta, - 1, 1 ) );
  
  		},
  
  		distanceTo: function ( v ) {
  
  			return Math.sqrt( this.distanceToSquared( v ) );
  
  		},
  
  		distanceToSquared: function ( v ) {
  
  			var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
  
  			return dx * dx + dy * dy + dz * dz;
  
  		},
  
  		manhattanDistanceTo: function ( v ) {
  
  			return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
  
  		},
  
  		setFromSpherical: function ( s ) {
  
  			return this.setFromSphericalCoords( s.radius, s.phi, s.theta );
  
  		},
  
  		setFromSphericalCoords: function ( radius, phi, theta ) {
  
  			var sinPhiRadius = Math.sin( phi ) * radius;
  
  			this.x = sinPhiRadius * Math.sin( theta );
  			this.y = Math.cos( phi ) * radius;
  			this.z = sinPhiRadius * Math.cos( theta );
  
  			return this;
  
  		},
  
  		setFromCylindrical: function ( c ) {
  
  			return this.setFromCylindricalCoords( c.radius, c.theta, c.y );
  
  		},
  
  		setFromCylindricalCoords: function ( radius, theta, y ) {
  
  			this.x = radius * Math.sin( theta );
  			this.y = y;
  			this.z = radius * Math.cos( theta );
  
  			return this;
  
  		},
  
  		setFromMatrixPosition: function ( m ) {
  
  			var e = m.elements;
  
  			this.x = e[ 12 ];
  			this.y = e[ 13 ];
  			this.z = e[ 14 ];
  
  			return this;
  
  		},
  
  		setFromMatrixScale: function ( m ) {
  
  			var sx = this.setFromMatrixColumn( m, 0 ).length();
  			var sy = this.setFromMatrixColumn( m, 1 ).length();
  			var sz = this.setFromMatrixColumn( m, 2 ).length();
  
  			this.x = sx;
  			this.y = sy;
  			this.z = sz;
  
  			return this;
  
  		},
  
  		setFromMatrixColumn: function ( m, index ) {
  
  			return this.fromArray( m.elements, index * 4 );
  
  		},
  
  		equals: function ( v ) {
  
  			return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this.x = array[ offset ];
  			this.y = array[ offset + 1 ];
  			this.z = array[ offset + 2 ];
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this.x;
  			array[ offset + 1 ] = this.y;
  			array[ offset + 2 ] = this.z;
  
  			return array;
  
  		},
  
  		fromBufferAttribute: function ( attribute, index, offset ) {
  
  			if ( offset !== undefined ) {
  
  				console.warn( 'Vector3: offset has been removed from .fromBufferAttribute().' );
  
  			}
  
  			this.x = attribute.getX( index );
  			this.y = attribute.getY( index );
  			this.z = attribute.getZ( index );
  
  			return this;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function Quaternion( x, y, z, w ) {
  
  		this._x = x || 0;
  		this._y = y || 0;
  		this._z = z || 0;
  		this._w = ( w !== undefined ) ? w : 1;
  
  	}
  
  	Object.assign( Quaternion, {
  
  		slerp: function ( qa, qb, qm, t ) {
  
  			return qm.copy( qa ).slerp( qb, t );
  
  		},
  
  		slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
  
  			// fuzz-free, array-based Quaternion SLERP operation
  
  			var x0 = src0[ srcOffset0 + 0 ],
  				y0 = src0[ srcOffset0 + 1 ],
  				z0 = src0[ srcOffset0 + 2 ],
  				w0 = src0[ srcOffset0 + 3 ],
  
  				x1 = src1[ srcOffset1 + 0 ],
  				y1 = src1[ srcOffset1 + 1 ],
  				z1 = src1[ srcOffset1 + 2 ],
  				w1 = src1[ srcOffset1 + 3 ];
  
  			if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
  
  				var s = 1 - t,
  
  					cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
  
  					dir = ( cos >= 0 ? 1 : - 1 ),
  					sqrSin = 1 - cos * cos;
  
  				// Skip the Slerp for tiny steps to avoid numeric problems:
  				if ( sqrSin > Number.EPSILON ) {
  
  					var sin = Math.sqrt( sqrSin ),
  						len = Math.atan2( sin, cos * dir );
  
  					s = Math.sin( s * len ) / sin;
  					t = Math.sin( t * len ) / sin;
  
  				}
  
  				var tDir = t * dir;
  
  				x0 = x0 * s + x1 * tDir;
  				y0 = y0 * s + y1 * tDir;
  				z0 = z0 * s + z1 * tDir;
  				w0 = w0 * s + w1 * tDir;
  
  				// Normalize in case we just did a lerp:
  				if ( s === 1 - t ) {
  
  					var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
  
  					x0 *= f;
  					y0 *= f;
  					z0 *= f;
  					w0 *= f;
  
  				}
  
  			}
  
  			dst[ dstOffset ] = x0;
  			dst[ dstOffset + 1 ] = y0;
  			dst[ dstOffset + 2 ] = z0;
  			dst[ dstOffset + 3 ] = w0;
  
  		}
  
  	} );
  
  	Object.defineProperties( Quaternion.prototype, {
  
  		x: {
  
  			get: function () {
  
  				return this._x;
  
  			},
  
  			set: function ( value ) {
  
  				this._x = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		y: {
  
  			get: function () {
  
  				return this._y;
  
  			},
  
  			set: function ( value ) {
  
  				this._y = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		z: {
  
  			get: function () {
  
  				return this._z;
  
  			},
  
  			set: function ( value ) {
  
  				this._z = value;
  				this.onChangeCallback();
  
  			}
  
  		},
  
  		w: {
  
  			get: function () {
  
  				return this._w;
  
  			},
  
  			set: function ( value ) {
  
  				this._w = value;
  				this.onChangeCallback();
  
  			}
  
  		}
  
  	} );
  
  	Object.assign( Quaternion.prototype, {
  
  		isQuaternion: true,
  
  		set: function ( x, y, z, w ) {
  
  			this._x = x;
  			this._y = y;
  			this._z = z;
  			this._w = w;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		clone: function () {
  
  			return new this.constructor( this._x, this._y, this._z, this._w );
  
  		},
  
  		copy: function ( quaternion ) {
  
  			this._x = quaternion.x;
  			this._y = quaternion.y;
  			this._z = quaternion.z;
  			this._w = quaternion.w;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromEuler: function ( euler, update ) {
  
  			if ( ! ( euler && euler.isEuler ) ) {
  
  				throw new Error( 'Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
  			}
  
  			var x = euler._x, y = euler._y, z = euler._z, order = euler.order;
  
  			// http://www.mathworks.com/matlabcentral/fileexchange/
  			// 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
  			//	content/SpinCalc.m
  
  			var cos = Math.cos;
  			var sin = Math.sin;
  
  			var c1 = cos( x / 2 );
  			var c2 = cos( y / 2 );
  			var c3 = cos( z / 2 );
  
  			var s1 = sin( x / 2 );
  			var s2 = sin( y / 2 );
  			var s3 = sin( z / 2 );
  
  			if ( order === 'XYZ' ) {
  
  				this._x = s1 * c2 * c3 + c1 * s2 * s3;
  				this._y = c1 * s2 * c3 - s1 * c2 * s3;
  				this._z = c1 * c2 * s3 + s1 * s2 * c3;
  				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
  			} else if ( order === 'YXZ' ) {
  
  				this._x = s1 * c2 * c3 + c1 * s2 * s3;
  				this._y = c1 * s2 * c3 - s1 * c2 * s3;
  				this._z = c1 * c2 * s3 - s1 * s2 * c3;
  				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
  			} else if ( order === 'ZXY' ) {
  
  				this._x = s1 * c2 * c3 - c1 * s2 * s3;
  				this._y = c1 * s2 * c3 + s1 * c2 * s3;
  				this._z = c1 * c2 * s3 + s1 * s2 * c3;
  				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
  			} else if ( order === 'ZYX' ) {
  
  				this._x = s1 * c2 * c3 - c1 * s2 * s3;
  				this._y = c1 * s2 * c3 + s1 * c2 * s3;
  				this._z = c1 * c2 * s3 - s1 * s2 * c3;
  				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
  			} else if ( order === 'YZX' ) {
  
  				this._x = s1 * c2 * c3 + c1 * s2 * s3;
  				this._y = c1 * s2 * c3 + s1 * c2 * s3;
  				this._z = c1 * c2 * s3 - s1 * s2 * c3;
  				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
  			} else if ( order === 'XZY' ) {
  
  				this._x = s1 * c2 * c3 - c1 * s2 * s3;
  				this._y = c1 * s2 * c3 - s1 * c2 * s3;
  				this._z = c1 * c2 * s3 + s1 * s2 * c3;
  				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
  			}
  
  			if ( update !== false ) this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromAxisAngle: function ( axis, angle ) {
  
  			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
  
  			// assumes axis is normalized
  
  			var halfAngle = angle / 2, s = Math.sin( halfAngle );
  
  			this._x = axis.x * s;
  			this._y = axis.y * s;
  			this._z = axis.z * s;
  			this._w = Math.cos( halfAngle );
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromRotationMatrix: function ( m ) {
  
  			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
  
  			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
  			var te = m.elements,
  
  				m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
  				m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
  				m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
  
  				trace = m11 + m22 + m33,
  				s;
  
  			if ( trace > 0 ) {
  
  				s = 0.5 / Math.sqrt( trace + 1.0 );
  
  				this._w = 0.25 / s;
  				this._x = ( m32 - m23 ) * s;
  				this._y = ( m13 - m31 ) * s;
  				this._z = ( m21 - m12 ) * s;
  
  			} else if ( m11 > m22 && m11 > m33 ) {
  
  				s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
  
  				this._w = ( m32 - m23 ) / s;
  				this._x = 0.25 * s;
  				this._y = ( m12 + m21 ) / s;
  				this._z = ( m13 + m31 ) / s;
  
  			} else if ( m22 > m33 ) {
  
  				s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
  
  				this._w = ( m13 - m31 ) / s;
  				this._x = ( m12 + m21 ) / s;
  				this._y = 0.25 * s;
  				this._z = ( m23 + m32 ) / s;
  
  			} else {
  
  				s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
  
  				this._w = ( m21 - m12 ) / s;
  				this._x = ( m13 + m31 ) / s;
  				this._y = ( m23 + m32 ) / s;
  				this._z = 0.25 * s;
  
  			}
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		setFromUnitVectors: function () {
  
  			// assumes direction vectors vFrom and vTo are normalized
  
  			var v1 = new Vector3();
  			var r;
  
  			var EPS = 0.000001;
  
  			return function setFromUnitVectors( vFrom, vTo ) {
  
  				if ( v1 === undefined ) v1 = new Vector3();
  
  				r = vFrom.dot( vTo ) + 1;
  
  				if ( r < EPS ) {
  
  					r = 0;
  
  					if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
  
  						v1.set( - vFrom.y, vFrom.x, 0 );
  
  					} else {
  
  						v1.set( 0, - vFrom.z, vFrom.y );
  
  					}
  
  				} else {
  
  					v1.crossVectors( vFrom, vTo );
  
  				}
  
  				this._x = v1.x;
  				this._y = v1.y;
  				this._z = v1.z;
  				this._w = r;
  
  				return this.normalize();
  
  			};
  
  		}(),
  
  		angleTo: function ( q ) {
  
  			return 2 * Math.acos( Math.abs( _Math.clamp( this.dot( q ), - 1, 1 ) ) );
  
  		},
  
  		rotateTowards: function ( q, step ) {
  
  			var angle = this.angleTo( q );
  
  			if ( angle === 0 ) return this;
  
  			var t = Math.min( 1, step / angle );
  
  			this.slerp( q, t );
  
  			return this;
  
  		},
  
  		inverse: function () {
  
  			// quaternion is assumed to have unit length
  
  			return this.conjugate();
  
  		},
  
  		conjugate: function () {
  
  			this._x *= - 1;
  			this._y *= - 1;
  			this._z *= - 1;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		dot: function ( v ) {
  
  			return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
  
  		},
  
  		lengthSq: function () {
  
  			return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
  
  		},
  
  		length: function () {
  
  			return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
  
  		},
  
  		normalize: function () {
  
  			var l = this.length();
  
  			if ( l === 0 ) {
  
  				this._x = 0;
  				this._y = 0;
  				this._z = 0;
  				this._w = 1;
  
  			} else {
  
  				l = 1 / l;
  
  				this._x = this._x * l;
  				this._y = this._y * l;
  				this._z = this._z * l;
  				this._w = this._w * l;
  
  			}
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		multiply: function ( q, p ) {
  
  			if ( p !== undefined ) {
  
  				console.warn( 'Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
  				return this.multiplyQuaternions( q, p );
  
  			}
  
  			return this.multiplyQuaternions( this, q );
  
  		},
  
  		premultiply: function ( q ) {
  
  			return this.multiplyQuaternions( q, this );
  
  		},
  
  		multiplyQuaternions: function ( a, b ) {
  
  			// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
  
  			var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
  			var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
  
  			this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
  			this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
  			this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
  			this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		slerp: function ( qb, t ) {
  
  			if ( t === 0 ) return this;
  			if ( t === 1 ) return this.copy( qb );
  
  			var x = this._x, y = this._y, z = this._z, w = this._w;
  
  			// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
  
  			var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
  
  			if ( cosHalfTheta < 0 ) {
  
  				this._w = - qb._w;
  				this._x = - qb._x;
  				this._y = - qb._y;
  				this._z = - qb._z;
  
  				cosHalfTheta = - cosHalfTheta;
  
  			} else {
  
  				this.copy( qb );
  
  			}
  
  			if ( cosHalfTheta >= 1.0 ) {
  
  				this._w = w;
  				this._x = x;
  				this._y = y;
  				this._z = z;
  
  				return this;
  
  			}
  
  			var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
  
  			if ( sqrSinHalfTheta <= Number.EPSILON ) {
  
  				var s = 1 - t;
  				this._w = s * w + t * this._w;
  				this._x = s * x + t * this._x;
  				this._y = s * y + t * this._y;
  				this._z = s * z + t * this._z;
  
  				return this.normalize();
  
  			}
  
  			var sinHalfTheta = Math.sqrt( sqrSinHalfTheta );
  			var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
  			var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
  				ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
  
  			this._w = ( w * ratioA + this._w * ratioB );
  			this._x = ( x * ratioA + this._x * ratioB );
  			this._y = ( y * ratioA + this._y * ratioB );
  			this._z = ( z * ratioA + this._z * ratioB );
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		equals: function ( quaternion ) {
  
  			return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
  
  		},
  
  		fromArray: function ( array, offset ) {
  
  			if ( offset === undefined ) offset = 0;
  
  			this._x = array[ offset ];
  			this._y = array[ offset + 1 ];
  			this._z = array[ offset + 2 ];
  			this._w = array[ offset + 3 ];
  
  			this.onChangeCallback();
  
  			return this;
  
  		},
  
  		toArray: function ( array, offset ) {
  
  			if ( array === undefined ) array = [];
  			if ( offset === undefined ) offset = 0;
  
  			array[ offset ] = this._x;
  			array[ offset + 1 ] = this._y;
  			array[ offset + 2 ] = this._z;
  			array[ offset + 3 ] = this._w;
  
  			return array;
  
  		},
  
  		onChange: function ( callback ) {
  
  			this.onChangeCallback = callback;
  
  			return this;
  
  		},
  
  		onChangeCallback: function () {}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
  		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
  	}
  
  	QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
  		constructor: QuaternionLinearInterpolant,
  
  		interpolate_: function ( i1, t0, t, t1 ) {
  
  			var result = this.resultBuffer,
  				values = this.sampleValues,
  				stride = this.valueSize,
  
  				offset = i1 * stride,
  
  				alpha = ( t - t0 ) / ( t1 - t0 );
  
  			for ( var end = offset + stride; offset !== end; offset += 4 ) {
  
  				Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
  
  			}
  
  			return result;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function QuaternionKeyframeTrack( name, times, values, interpolation ) {
  
  		KeyframeTrack.call( this, name, times, values, interpolation );
  
  	}
  
  	QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
  		constructor: QuaternionKeyframeTrack,
  
  		ValueTypeName: 'quaternion',
  
  		// ValueBufferType is inherited
  
  		DefaultInterpolation: InterpolateLinear,
  
  		InterpolantFactoryMethodLinear: function ( result ) {
  
  			return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
  
  		},
  
  		InterpolantFactoryMethodSmooth: undefined // not yet implemented
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function StringKeyframeTrack( name, times, values, interpolation ) {
  
  		KeyframeTrack.call( this, name, times, values, interpolation );
  
  	}
  
  	StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
  		constructor: StringKeyframeTrack,
  
  		ValueTypeName: 'string',
  		ValueBufferType: Array,
  
  		DefaultInterpolation: InterpolateDiscrete,
  
  		InterpolantFactoryMethodLinear: undefined,
  
  		InterpolantFactoryMethodSmooth: undefined
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function VectorKeyframeTrack( name, times, values, interpolation ) {
  
  		KeyframeTrack.call( this, name, times, values, interpolation );
  
  	}
  
  	VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
  		constructor: VectorKeyframeTrack,
  
  		ValueTypeName: 'vector'
  
  		// ValueBufferType is inherited
  
  		// DefaultInterpolation is inherited
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function AnimationClip( name, duration, tracks ) {
  
  		this.name = name;
  		this.tracks = tracks;
  		this.duration = ( duration !== undefined ) ? duration : - 1;
  
  		this.uuid = _Math.generateUUID();
  
  		// this means it should figure out its duration by scanning the tracks
  		if ( this.duration < 0 ) {
  
  			this.resetDuration();
  
  		}
  
  	}
  
  	function getTrackTypeForValueTypeName( typeName ) {
  
  		switch ( typeName.toLowerCase() ) {
  
  			case 'scalar':
  			case 'double':
  			case 'float':
  			case 'number':
  			case 'integer':
  
  				return NumberKeyframeTrack;
  
  			case 'vector':
  			case 'vector2':
  			case 'vector3':
  			case 'vector4':
  
  				return VectorKeyframeTrack;
  
  			case 'color':
  
  				return ColorKeyframeTrack;
  
  			case 'quaternion':
  
  				return QuaternionKeyframeTrack;
  
  			case 'bool':
  			case 'boolean':
  
  				return BooleanKeyframeTrack;
  
  			case 'string':
  
  				return StringKeyframeTrack;
  
  		}
  
  		throw new Error( 'KeyframeTrack: Unsupported typeName: ' + typeName );
  
  	}
  
  	function parseKeyframeTrack( json ) {
  
  		if ( json.type === undefined ) {
  
  			throw new Error( 'KeyframeTrack: track type undefined, can not parse' );
  
  		}
  
  		var trackType = getTrackTypeForValueTypeName( json.type );
  
  		if ( json.times === undefined ) {
  
  			var times = [], values = [];
  
  			AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
  
  			json.times = times;
  			json.values = values;
  
  		}
  
  		// derived classes can define a static parse method
  		if ( trackType.parse !== undefined ) {
  
  			return trackType.parse( json );
  
  		} else {
  
  			// by default, we assume a constructor compatible with the base
  			return new trackType( json.name, json.times, json.values, json.interpolation );
  
  		}
  
  	}
  
  	Object.assign( AnimationClip, {
  
  		parse: function ( json ) {
  
  			var tracks = [],
  				jsonTracks = json.tracks,
  				frameTime = 1.0 / ( json.fps || 1.0 );
  
  			for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) {
  
  				tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) );
  
  			}
  
  			return new AnimationClip( json.name, json.duration, tracks );
  
  		},
  
  		toJSON: function ( clip ) {
  
  			var tracks = [],
  				clipTracks = clip.tracks;
  
  			var json = {
  
  				'name': clip.name,
  				'duration': clip.duration,
  				'tracks': tracks,
  				'uuid': clip.uuid
  
  			};
  
  			for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) {
  
  				tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );
  
  			}
  
  			return json;
  
  		},
  
  		CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {
  
  			var numMorphTargets = morphTargetSequence.length;
  			var tracks = [];
  
  			for ( var i = 0; i < numMorphTargets; i ++ ) {
  
  				var times = [];
  				var values = [];
  
  				times.push(
  					( i + numMorphTargets - 1 ) % numMorphTargets,
  					i,
  					( i + 1 ) % numMorphTargets );
  
  				values.push( 0, 1, 0 );
  
  				var order = AnimationUtils.getKeyframeOrder( times );
  				times = AnimationUtils.sortedArray( times, 1, order );
  				values = AnimationUtils.sortedArray( values, 1, order );
  
  				// if there is a key at the first frame, duplicate it as the
  				// last frame as well for perfect loop.
  				if ( ! noLoop && times[ 0 ] === 0 ) {
  
  					times.push( numMorphTargets );
  					values.push( values[ 0 ] );
  
  				}
  
  				tracks.push(
  					new NumberKeyframeTrack(
  						'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',
  						times, values
  					).scale( 1.0 / fps ) );
  
  			}
  
  			return new AnimationClip( name, - 1, tracks );
  
  		},
  
  		findByName: function ( objectOrClipArray, name ) {
  
  			var clipArray = objectOrClipArray;
  
  			if ( ! Array.isArray( objectOrClipArray ) ) {
  
  				var o = objectOrClipArray;
  				clipArray = o.geometry && o.geometry.animations || o.animations;
  
  			}
  
  			for ( var i = 0; i < clipArray.length; i ++ ) {
  
  				if ( clipArray[ i ].name === name ) {
  
  					return clipArray[ i ];
  
  				}
  
  			}
  
  			return null;
  
  		},
  
  		CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {
  
  			var animationToMorphTargets = {};
  
  			// tested with https://regex101.com/ on trick sequences
  			// such flamingo_flyA_003, flamingo_run1_003, crdeath0059
  			var pattern = /^([\w-]*?)([\d]+)$/;
  
  			// sort morph target names into animation groups based
  			// patterns like Walk_001, Walk_002, Run_001, Run_002
  			for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {
  
  				var morphTarget = morphTargets[ i ];
  				var parts = morphTarget.name.match( pattern );
  
  				if ( parts && parts.length > 1 ) {
  
  					var name = parts[ 1 ];
  
  					var animationMorphTargets = animationToMorphTargets[ name ];
  					if ( ! animationMorphTargets ) {
  
  						animationToMorphTargets[ name ] = animationMorphTargets = [];
  
  					}
  
  					animationMorphTargets.push( morphTarget );
  
  				}
  
  			}
  
  			var clips = [];
  
  			for ( var name in animationToMorphTargets ) {
  
  				clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );
  
  			}
  
  			return clips;
  
  		},
  
  		// parse the animation.hierarchy format
  		parseAnimation: function ( animation, bones ) {
  
  			if ( ! animation ) {
  
  				console.error( 'AnimationClip: No animation in JSONLoader data.' );
  				return null;
  
  			}
  
  			var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {
  
  				// only return track if there are actually keys.
  				if ( animationKeys.length !== 0 ) {
  
  					var times = [];
  					var values = [];
  
  					AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );
  
  					// empty keys are filtered out, so check again
  					if ( times.length !== 0 ) {
  
  						destTracks.push( new trackType( trackName, times, values ) );
  
  					}
  
  				}
  
  			};
  
  			var tracks = [];
  
  			var clipName = animation.name || 'default';
  			// automatic length determination in AnimationClip.
  			var duration = animation.length || - 1;
  			var fps = animation.fps || 30;
  
  			var hierarchyTracks = animation.hierarchy || [];
  
  			for ( var h = 0; h < hierarchyTracks.length; h ++ ) {
  
  				var animationKeys = hierarchyTracks[ h ].keys;
  
  				// skip empty tracks
  				if ( ! animationKeys || animationKeys.length === 0 ) continue;
  
  				// process morph targets
  				if ( animationKeys[ 0 ].morphTargets ) {
  
  					// figure out all morph targets used in this track
  					var morphTargetNames = {};
  
  					for ( var k = 0; k < animationKeys.length; k ++ ) {
  
  						if ( animationKeys[ k ].morphTargets ) {
  
  							for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {
  
  								morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;
  
  							}
  
  						}
  
  					}
  
  					// create a track for each morph target with all zero
  					// morphTargetInfluences except for the keys in which
  					// the morphTarget is named.
  					for ( var morphTargetName in morphTargetNames ) {
  
  						var times = [];
  						var values = [];
  
  						for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {
  
  							var animationKey = animationKeys[ k ];
  
  							times.push( animationKey.time );
  							values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );
  
  						}
  
  						tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
  
  					}
  
  					duration = morphTargetNames.length * ( fps || 1.0 );
  
  				} else {
  
  					// ...assume skeletal animation
  
  					var boneName = '.bones[' + bones[ h ].name + ']';
  
  					addNonemptyTrack(
  						VectorKeyframeTrack, boneName + '.position',
  						animationKeys, 'pos', tracks );
  
  					addNonemptyTrack(
  						QuaternionKeyframeTrack, boneName + '.quaternion',
  						animationKeys, 'rot', tracks );
  
  					addNonemptyTrack(
  						VectorKeyframeTrack, boneName + '.scale',
  						animationKeys, 'scl', tracks );
  
  				}
  
  			}
  
  			if ( tracks.length === 0 ) {
  
  				return null;
  
  			}
  
  			var clip = new AnimationClip( clipName, duration, tracks );
  
  			return clip;
  
  		}
  
  	} );
  
  	Object.assign( AnimationClip.prototype, {
  
  		resetDuration: function () {
  
  			var tracks = this.tracks, duration = 0;
  
  			for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
  
  				var track = this.tracks[ i ];
  
  				duration = Math.max( duration, track.times[ track.times.length - 1 ] );
  
  			}
  
  			this.duration = duration;
  
  			return this;
  
  		},
  
  		trim: function () {
  
  			for ( var i = 0; i < this.tracks.length; i ++ ) {
  
  				this.tracks[ i ].trim( 0, this.duration );
  
  			}
  
  			return this;
  
  		},
  
  		validate: function () {
  
  			var valid = true;
  
  			for ( var i = 0; i < this.tracks.length; i ++ ) {
  
  				valid = valid && this.tracks[ i ].validate();
  
  			}
  
  			return valid;
  
  		},
  
  		optimize: function () {
  
  			for ( var i = 0; i < this.tracks.length; i ++ ) {
  
  				this.tracks[ i ].optimize();
  
  			}
  
  			return this;
  
  		},
  		clone: function () {
  
  			var tracks = [];
  
  			for ( var i = 0; i < this.tracks.length; i ++ ) {
  
  				tracks.push( this.tracks[ i ].clone() );
  
  			}
  
  			return new AnimationClip( this.name, this.duration, tracks );
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	var Cache = {
  
  		enabled: false,
  
  		files: {},
  
  		add: function ( key, file ) {
  
  			if ( this.enabled === false ) return;
  
  			// console.log( 'Cache', 'Adding key:', key );
  
  			this.files[ key ] = file;
  
  		},
  
  		get: function ( key ) {
  
  			if ( this.enabled === false ) return;
  
  			// console.log( 'Cache', 'Checking key:', key );
  
  			return this.files[ key ];
  
  		},
  
  		remove: function ( key ) {
  
  			delete this.files[ key ];
  
  		},
  
  		clear: function () {
  
  			this.files = {};
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function LoadingManager( onLoad, onProgress, onError ) {
  
  		var scope = this;
  
  		var isLoading = false;
  		var itemsLoaded = 0;
  		var itemsTotal = 0;
  		var urlModifier = undefined;
  
  		// Refer to #5689 for the reason why we don't set .onStart
  		// in the constructor
  
  		this.onStart = undefined;
  		this.onLoad = onLoad;
  		this.onProgress = onProgress;
  		this.onError = onError;
  
  		this.itemStart = function ( url ) {
  
  			itemsTotal ++;
  
  			if ( isLoading === false ) {
  
  				if ( scope.onStart !== undefined ) {
  
  					scope.onStart( url, itemsLoaded, itemsTotal );
  
  				}
  
  			}
  
  			isLoading = true;
  
  		};
  
  		this.itemEnd = function ( url ) {
  
  			itemsLoaded ++;
  
  			if ( scope.onProgress !== undefined ) {
  
  				scope.onProgress( url, itemsLoaded, itemsTotal );
  
  			}
  
  			if ( itemsLoaded === itemsTotal ) {
  
  				isLoading = false;
  
  				if ( scope.onLoad !== undefined ) {
  
  					scope.onLoad();
  
  				}
  
  			}
  
  		};
  
  		this.itemError = function ( url ) {
  
  			if ( scope.onError !== undefined ) {
  
  				scope.onError( url );
  
  			}
  
  		};
  
  		this.resolveURL = function ( url ) {
  
  			if ( urlModifier ) {
  
  				return urlModifier( url );
  
  			}
  
  			return url;
  
  		};
  
  		this.setURLModifier = function ( transform ) {
  
  			urlModifier = transform;
  			return this;
  
  		};
  
  	}
  
  	var DefaultLoadingManager = new LoadingManager();
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	var loading = {};
  
  	function FileLoader( manager ) {
  
  		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
  	}
  
  	Object.assign( FileLoader.prototype, {
  
  		load: function ( url, onLoad, onProgress, onError ) {
  
  			if ( url === undefined ) url = '';
  
  			if ( this.path !== undefined ) url = this.path + url;
  
  			url = this.manager.resolveURL( url );
  
  			var scope = this;
  
  			var cached = Cache.get( url );
  
  			if ( cached !== undefined ) {
  
  				scope.manager.itemStart( url );
  
  				setTimeout( function () {
  
  					if ( onLoad ) onLoad( cached );
  
  					scope.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;
  
  			}
  
  			// Check for data: URI
  			var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
  			var dataUriRegexResult = url.match( dataUriRegex );
  
  			// Safari can not handle Data URIs through XMLHttpRequest so process manually
  			if ( dataUriRegexResult ) {
  
  				var mimeType = dataUriRegexResult[ 1 ];
  				var isBase64 = !! dataUriRegexResult[ 2 ];
  				var data = dataUriRegexResult[ 3 ];
  
  				data = decodeURIComponent( data );
  
  				if ( isBase64 ) data = atob( data );
  
  				try {
  
  					var response;
  					var responseType = ( this.responseType || '' ).toLowerCase();
  
  					switch ( responseType ) {
  
  						case 'arraybuffer':
  						case 'blob':
  
  							var view = new Uint8Array( data.length );
  
  							for ( var i = 0; i < data.length; i ++ ) {
  
  								view[ i ] = data.charCodeAt( i );
  
  							}
  
  							if ( responseType === 'blob' ) {
  
  								response = new Blob( [ view.buffer ], { type: mimeType } );
  
  							} else {
  
  								response = view.buffer;
  
  							}
  
  							break;
  
  						case 'document':
  
  							var parser = new DOMParser();
  							response = parser.parseFromString( data, mimeType );
  
  							break;
  
  						case 'json':
  
  							response = JSON.parse( data );
  
  							break;
  
  						default: // 'text' or other
  
  							response = data;
  
  							break;
  
  					}
  
  					// Wait for next browser tick like standard XMLHttpRequest event dispatching does
  					setTimeout( function () {
  
  						if ( onLoad ) onLoad( response );
  
  						scope.manager.itemEnd( url );
  
  					}, 0 );
  
  				} catch ( error ) {
  
  					// Wait for next browser tick like standard XMLHttpRequest event dispatching does
  					setTimeout( function () {
  
  						if ( onError ) onError( error );
  
  						scope.manager.itemError( url );
  						scope.manager.itemEnd( url );
  
  					}, 0 );
  
  				}
  
  			} else {
  
  				// Initialise array for duplicate requests
  
  				loading[ url ] = [];
  
  				loading[ url ].push( {
  
  					onLoad: onLoad,
  					onProgress: onProgress,
  					onError: onError
  
  				} );
  
  				var request = new XMLHttpRequest();
  
  				request.open( 'GET', url, true );
  
  				request.addEventListener( 'load', function ( event ) {
  
  					var response = this.response;
  
  					Cache.add( url, response );
  
  					var callbacks = loading[ url ];
  
  					delete loading[ url ];
  
  					if ( this.status === 200 || this.status === 0 ) {
  
  						// Some browsers return HTTP Status 0 when using non-http protocol
  						// e.g. 'file://' or 'data://'. Handle as success.
  
  						if ( this.status === 0 ) console.warn( 'FileLoader: HTTP Status 0 received.' );
  
  						for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
  							var callback = callbacks[ i ];
  							if ( callback.onLoad ) callback.onLoad( response );
  
  						}
  
  						scope.manager.itemEnd( url );
  
  					} else {
  
  						for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
  							var callback = callbacks[ i ];
  							if ( callback.onError ) callback.onError( event );
  
  						}
  
  						scope.manager.itemError( url );
  						scope.manager.itemEnd( url );
  
  					}
  
  				}, false );
  
  				request.addEventListener( 'progress', function ( event ) {
  
  					var callbacks = loading[ url ];
  
  					for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
  						var callback = callbacks[ i ];
  						if ( callback.onProgress ) callback.onProgress( event );
  
  					}
  
  				}, false );
  
  				request.addEventListener( 'error', function ( event ) {
  
  					var callbacks = loading[ url ];
  
  					delete loading[ url ];
  
  					for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
  						var callback = callbacks[ i ];
  						if ( callback.onError ) callback.onError( event );
  
  					}
  
  					scope.manager.itemError( url );
  					scope.manager.itemEnd( url );
  
  				}, false );
  
  				request.addEventListener( 'abort', function ( event ) {
  
  					var callbacks = loading[ url ];
  
  					delete loading[ url ];
  
  					for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
  						var callback = callbacks[ i ];
  						if ( callback.onError ) callback.onError( event );
  
  					}
  
  					scope.manager.itemError( url );
  					scope.manager.itemEnd( url );
  
  				}, false );
  
  				if ( this.responseType !== undefined ) request.responseType = this.responseType;
  				if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;
  
  				if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' );
  
  				for ( var header in this.requestHeader ) {
  
  					request.setRequestHeader( header, this.requestHeader[ header ] );
  
  				}
  
  				request.send( null );
  
  			}
  
  			scope.manager.itemStart( url );
  
  			return request;
  
  		},
  
  		setPath: function ( value ) {
  
  			this.path = value;
  			return this;
  
  		},
  
  		setResponseType: function ( value ) {
  
  			this.responseType = value;
  			return this;
  
  		},
  
  		setWithCredentials: function ( value ) {
  
  			this.withCredentials = value;
  			return this;
  
  		},
  
  		setMimeType: function ( value ) {
  
  			this.mimeType = value;
  			return this;
  
  		},
  
  		setRequestHeader: function ( value ) {
  
  			this.requestHeader = value;
  			return this;
  
  		}
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	function AnimationLoader( manager ) {
  
  		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
  	}
  
  	Object.assign( AnimationLoader.prototype, {
  
  		load: function ( url, onLoad, onProgress, onError ) {
  
  			var scope = this;
  
  			var loader = new FileLoader( scope.manager );
  			loader.setPath( scope.path );
  			loader.load( url, function ( text ) {
  
  				onLoad( scope.parse( JSON.parse( text ) ) );
  
  			}, onProgress, onError );
  
  		},
  
  		parse: function ( json, onLoad ) {
  
  			var animations = [];
  
  			for ( var i = 0; i < json.length; i ++ ) {
  
  				var clip = AnimationClip.parse( json[ i ] );
  
  				animations.push( clip );
  
  			}
  
  			onLoad( animations );
  
  		},
  
  		setPath: function ( value ) {
  
  			this.path = value;
  			return this;
  
  		}
  
  	} );
  
  	exports.AnimationLoader = AnimationLoader;
  
  }((this.Three = this.Three || {})));
  