<!-- NEW3 — TEST 3 · THE RIVER'S JOURNEY · part 225 -->
no `interpolation` parameter because the
	 * interpolation is always discrete.
	 *
	 * @param {string} name - The keyframe track's name.
	 * @param {Array<number>} times - A list of keyframe times.
	 * @param {Array<string>} values - A list of keyframe values.
	 */
	constructor( name, times, values ) {

		super( name, times, values );

	}

}

/**
 * The value type name.
 *
 * @type {string}
 * @default 'string'
 */
StringKeyframeTrack.prototype.ValueTypeName = 'string';

/**
 * The value buffer type of this keyframe track.
 *
 * @type {TypedArray|Array}
 * @default Array.constructor
 */
StringKeyframeTrack.prototype.ValueBufferType = Array;

/**
 * The default interpolation type of this keyframe track.
 *
 * @type {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)}
 * @default InterpolateDiscrete
 */
StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete;
StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined;
StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined;

/**
 * A track for vector keyframe values.
 *
 * @augments KeyframeTrack
 */
class VectorKeyframeTrack extends KeyframeTrack {

	/**
	 * Constructs a new vector keyframe track.
	 *
	 * @param {string} name - The keyframe track's name.
	 * @param {Array<number>} times - A list of keyframe times.
	 * @param {Array<number>} values - A list of keyframe values.
	 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} [interpolation] - The interpolation type.
	 */
	constructor( name, times, values, interpolation ) {

		super( name, times, values, interpolation );

	}

}

/**
 * The value type name.
 *
 * @type {string}
 * @default 'vector'
 */
VectorKeyframeTrack.prototype.ValueTypeName = 'vector';

/**
 * A reusable set of keyframe tracks which represent an animation.
 */
class AnimationClip {

	/**
	 * Constructs a new animation clip.
	 *
	 * Note: Instead of instantiating an AnimationClip directly with the constructor, you can
	 * use the static interface of this class for creating clips. In most cases though, animation clips
	 * will automatically be created by loaders when importing animated 3D assets.
	 *
	 * @param {string} [name=''] - The clip's name.
	 * @param {number} [duration=-1] - The clip's duration in seconds. If a negative value is passed,
	 * the duration will be calculated from the passed keyframes.
	 * @param {Array<KeyframeTrack>} tracks - An array of keyframe tracks.
	 * @param {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)} [blendMode=NormalAnimationBlendMode] - Defines how the animation
	 * is blended/combined when two or more animations are simultaneously played.
	 */
	constructor( name = '', duration = -1, tracks = [], blendMode = NormalAnimationBlendMode ) {

		/**
		 * The clip's name.
		 *
		 * @type {string}
		 */
		this.name = name;

		/**
		 *  An array of keyframe tracks.
		 *
		 * @type {Array<KeyframeTrack>}
		 */
		this.tracks = tracks;

		/**
		 * The clip's duration in seconds.
		 *
		 * @type {number}
		 */
		this.duration = duration;

		/**
		 * Defines how the animation is blended/combined when two or more animations
		 * are simultaneously played.
		 *
		 * @type {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)}
		 */
		this.blendMode = blendMode;

		/**
		 * The UUID of the animation clip.
		 *
		 * @type {string}
		 * @readonly
		 */
		this.uuid = generateUUID();

		/**
		 * An object that can be used to store custom data about the animation clip.
		 * It should not hold references to functions as these will not be cloned.
		 *
		 * @type {Object}
		 */
		this.userData = {};

		// this means it should figure out its duration by scanning the tracks
		if ( this.duration < 0 ) {

			this.resetDuration();

		}

	}

	/**
	 * Factory method for creating an animation clip from the given JSON.
	 *
	 * @static
	 * @param {Object} json - The serialized animation clip.
	 * @return {AnimationClip} The new animation clip.
	 */
	static parse( json ) {

		const tracks = [],
			jsonTracks = json.tracks,
			frameTime = 1.0 / ( json.fps || 1.0 );

		for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) {

			tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) );

		}

		const clip = new this( json.name, json.duration, tracks, json.blendMode );
		clip.uuid = json.uuid;

		clip.userData = JSON.parse( json.userData || '{}' );

		return clip;

	}

	/**
	 * Serializes the given animation clip into JSON.
	 *
	 * @static
	 * @param {AnimationClip} clip - The animation clip to serialize.
	 * @return {Object} The JSON object.
	 */
	static toJSON( clip ) {

		const tracks = [],
			clipTracks = clip.tracks;

		const json = {

			'name': clip.name,
			'duration': clip.duration,
			'tracks': tracks,
			'uuid': clip.uuid,
			'blendMode': clip.blendMode,
			'userData': JSON.stringify( clip.userData ),

		};

		for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) {

			trac