File:AnimatorTimeline.js

/**
 * @module Animation
 * @namespace springroll
 * @requires Core
 */
(function()
{
	/**
	 * Animator Timeline is a class designed to provide
	 * base animation functionality
	 * @class AnimatorTimeline
	 */
	var AnimatorTimeline = function()
	{
		/**
		 * The function to call when we're done
		 * @property {Function} onComplete
		 */
		this.onComplete = null;

		/**
		 * The function to call when stopped early.
		 * @property {Function} onCancelled
		 */
		this.onCancelled = null;

		/**
		 * An array of animations and pauses.
		 * @property {Array} eventList
		 */
		this.eventList = null;

		/**
		 * The index of the active animation in eventList.
		 * @property {int} listIndex
		 */
		this.listIndex = -1;

		/**
		 * The instance of the timeline to animate
		 * @property {springroll.AnimatorInstance} instance
		 */
		this.instance = null;

		/**
		 * If the current animation loops - determined by looking to see if it ends
		 * in "_stop" or "_loop"
		 * @property {Boolean} isLooping
		 */
		this.isLooping = false;

		/**
		 * If this timeline plays captions for the current sound.
		 * @property {Boolean} useCaptions
		 * @readOnly
		 */
		this.useCaptions = false;

		/**
		 * If the timeline is paused.
		 * @property {Boolean} _paused
		 * @private
		 */
		this._paused = false;

		/**
		 * The current animation duration in seconds.
		 * @property {Number} duration
		 */
		this.duration = 0;

		/**
		 * The animation speed for the current animation. Default is 1.
		 * @property {Number} speed
		 */
		this.speed = 1;

		/**
		 * Sound alias to sync to during the current animation.
		 * @property {String} soundAlias
		 */
		this.soundAlias = null;

		/**
		 * A sound instance object from springroll.Sound, used for tracking sound
		 * position for the current animation.
		 * @property {Object} soundInst
		 */
		this.soundInst = null;

		/**
		 * If the timeline will, but has yet to play a sound for the current animation.
		 * @property {Boolean} playSound
		 */
		this.playSound = false;

		/**
		 * The time (seconds) into the current animation that the sound starts.
		 * @property {Number} soundStart
		 */
		this.soundStart = 0;

		/**
		 * The time (seconds) into the animation that the sound ends
		 * @property {Number} soundEnd
		 */
		this.soundEnd = 0;

		/**
		 * If the timeline is complete. Looping timelines will never complete.
		 * @property {Boolean} complete
		 * @readOnly
		 */
		this.complete = false;

		this._position = 0;

		this.isTimer = false;
	};

	var p = extend(AnimatorTimeline);

	/**
	 * Reset the timeline so we can reuse
	 * @method reset
	 * @private
	 * @return {springroll.AnimatorTimeline} Instance of timeline
	 */
	p.reset = function()
	{
		if (this.instance)
		{
			this.instance.destroy();
			this.instance = null;
		}
		this.complete = false;
		this.soundEnd = 0;
		this.soundStart = 0;
		this.playSound = false;
		this.soundInst = null;
		this.soundAlias = null;
		this.speed = 1;
		this._position = 0;
		this.duration = 0;
		this._paused = false;
		this.useCaptions = false;
		this.isLooping = false;
		this.isTimer = false;
		this.listIndex = -1;
		this.eventList = null;
		this.onCancelled = null;
		this.onComplete = null;
		return this;
	};

	Object.defineProperty(p, "position",
	{
		get: function()
		{
			return this._position;
		},
		set: function(value)
		{
			this._position = value;
			if (!this.isTimer)
			{
				this.instance.setPosition(value);
			}
		}
	});

	/**
	 * Advances to the next item in the list of things to play.
	 * @method _nextItem
	 * @private
	 */
	p._nextItem = function()
	{
		var repeat = false;
		if (this.soundInst) this.soundInst._endCallback = null;
		//if on a looping animation, set up the animation to be replayed
		//(this will only happen on looping animations with audio)
		if (this.isLooping)
		{
			//if sound is playing, we need to stop it immediately
			//otherwise it can interfere with replaying the audio
			var sound = this.soundInst;
			if (sound)
			{
				sound.stop();
				this.soundInst = null;
			}
			//say that we are repeating, so that we start at the beginning of the loop
			//in case it started part way in
			repeat = true;
		}
		else
		{
			if (!this.isTimer)
				this.instance.endAnim();
			//reset variables
			this.soundEnd = this.soundStart = 0;
			this.isLooping = this.playSound = this.useCaptions = false;
			this.soundInst = this.soundAlias = null;

			//see if the animation list is complete
			if (++this.listIndex >= this.eventList.length)
			{
				this.complete = true;
				return;
			}
		}
		//take action based on the type of item in the list
		var listItem = this.eventList[this.listIndex];

		switch (typeof listItem)
		{
			case "object":
				{
					this.isTimer = false;
					var instance = this.instance;
					instance.beginAnim(listItem, repeat);
					this.duration = instance.duration;
					this.speed = listItem.speed;
					this.isLooping = instance.isLooping || listItem.loop;
					this._position = instance.position;

					if (listItem.alias)
					{
						this.soundAlias = listItem.alias;
						this.soundStart = listItem.audioStart;
						this.playSound = true;
						this.useCaptions = listItem.useCaptions;
					}
					break;
				}
			case "number":
				{
					this.isTimer = true;
					this.duration = listItem;
					this._position = 0;
					break;
				}
			case "function":
				{
					listItem();
					this._nextItem();
					break;
				}
		}
	};

	/**
	 * The position of the current animation, or the current pause timer, in milliseconds.
	 * @property {Number} time
	 */
	Object.defineProperty(p, "time",
	{
		get: function()
		{
			return this.position * 1000;
		},
		set: function(value)
		{
			this.position = value * 0.001;
		}
	});

	/**
	 * Sets and gets the animation's paused status.
	 * @property {Boolean} paused
	 */
	Object.defineProperty(p, "paused",
	{
		get: function()
		{
			return this._paused;
		},
		set: function(value)
		{
			if (value == this._paused)
				return;

			this._paused = !!value;
			var sound = this.soundInst;
			if (sound)
			{
				if (this.paused)
				{
					sound.pause();
				}
				else
				{
					sound.resume();
				}
			}
		}
	});

	//assign to the name space
	namespace('springroll').AnimatorTimeline = AnimatorTimeline;

}());