<!-- NEW2 — TEST 2 · THE DISTRICT OF 800 · part 227 -->
sh( 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 ) {

			tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );

		}

		return json;

	}

	/**
	 * Returns a new animation clip from the passed morph targets array of a
	 * geometry, taking a name and the number of frames per second.
	 *
	 * Note: The fps parameter is required, but the animation speed can be
	 * overridden via {@link AnimationAction#setDuration}.
	 *
	 * @static
	 * @param {string} name - The name of the animation clip.
	 * @param {Array<Object>} morphTargetSequence - A sequence of morph targets.
	 * @param {number} fps - The Frames-Per-Second value.
	 * @param {boolean} noLoop - Whether the clip should be no loop or not.
	 * @return {AnimationClip} The new animation clip.
	 */
	static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) {

		const numMorphTargets = morphTargetSequence.length;
		const tracks = [];

		for ( let i = 0; i < numMorphTargets; i ++ ) {

			let times = [];
			let values = [];

			times.push(
				( i + numMorphTargets - 1 ) % numMorphTargets,
				i,
				( i + 1 ) % numMorphTargets );

			values.push( 0, 1, 0 );

			const order = getKeyframeOrder( times );
			times = sortedArray( times, 1, order );
			values = 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 this( name, -1, tracks );

	}

	/**
	 * Searches for an animation clip by name, taking as its first parameter
	 * either an array of clips, or a mesh or geometry that contains an
	 * array named "animations" property.
	 *
	 * @static
	 * @param {(Array<AnimationClip>|Object3D)} objectOrClipArray - The array or object to search through.
	 * @param {string} name - The name to search for.
	 * @return {?AnimationClip} The found animation clip. Returns `null` if no clip has been found.
	 */
	static findByName( objectOrClipArray, name ) {

		let clipArray = objectOrClipArray;

		if ( ! Array.isArray( objectOrClipArray ) ) {

			const o = objectOrClipArray;
			clipArray = o.geometry && o.geometry.animations || o.animations;

		}

		for ( let i = 0; i < clipArray.length; i ++ ) {

			if ( clipArray[ i ].name === name ) {

				return clipArray[ i ];

			}

		}

		return null;

	}

	/**
	 * Returns an array of new AnimationClips created from the morph target
	 * sequences of a geometry, trying to sort morph target names into
	 * animation-group-based patterns like "Walk_001, Walk_002, Run_001, Run_002...".
	 *
	 * See {@link MD2Loader#parse} as an example for how the method should be used.
	 *
	 * @static
	 * @param {Array<Object>} morphTargets - A sequence of morph targets.
	 * @param {number} fps - The Frames-Per-Second value.
	 * @param {boolean} noLoop - Whether the clip should be no loop or not.
	 * @return {Array<AnimationClip>} An array of new animation clips.
	 */
	static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) {

		const animationToMorphTargets = {};

		// tested with https://regex101.com/ on trick sequences
		// such flamingo_flyA_003, flamingo_run1_003, crdeath0059
		const pattern = /^([\w-]*?)([\d]+)$/;

		// sort morph target names into animation groups based
		// patterns like Walk_001, Walk_002, Run_001, Run_002
		for ( let i = 0, il = morphTargets.length; i < il; i ++ ) {

			const morphTarget = morphTargets[ i ];
			const parts = morphTarget.name.match( pattern );

			if ( parts && parts.length > 1 ) {

				const name = parts[ 1 ];

				let animationMorphTargets = animationToMorphTargets[ name ];

				if ( ! animationMorphTargets ) {

					animationToMorphTargets[ name ] = animationMorphTargets = [];

				}

				animationMorphTargets.push( morphTarget );

			}

		}

		const clips = [];

		for ( const name in animationToMorphTargets ) {

			clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );

		}

		return clips;

	}

	/**
	