wip/TweenGroup

 /**
* @author       Richard Davey 
* @copyright    2013 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* TweenGroup constructor
* Create a new TweenGroup.
*
* @class Phaser.TweenGroup
* @constructor
* @param {Phaser.Game} game - Current game instance.
* @param {number} delay - Delay before this tween will start, defaults to 0 (no delay).
* @param {number} repeat - How many time should the TweenGroup repeat itself. Infinity can be used for infinite repetition.
* @param {Phaser.Tween} yoyo - Whether this TweenGroup will reverse on completion.
* @param {boolean} autoStart - Whether this TweenGroup will start automatically or not.
*/
Phaser.TweenGroup = function (game, delay, repeat, yoyo, autoStart) {

	autoStart = autoStart || true;

	/**
	* @property {Phaser.Game} game - A reference to the currently running Game.
	*/
	this.game = game;

	/**
	* @property {number} _duration - Description.
	* @private
	* @default
	*/
	this._duration = 0;

	/**
	* @property {number} _repeat - Description.
	* @private
	* @default
	*/
	this._repeat = repeat || 0;

	/**
	* @property {boolean} _yoyo - Description.
	* @private
	* @default
	*/
	this._yoyo = yoyo || false;

	/**
	* @property {boolean} _reversed - Description.
	* @private
	* @default
	*/
	this._reversed = false;

	/**
	* @property {number} _delayTime - Description.
	* @private
	* @default
	*/
	this._delayTime = delay || 0;

	/**
	* @property {Description} _startTime - Description.
	* @private
	* @default null
	*/
	this._startTime = null;

	/**
	* @property {Description} _onStartCallback - Description.
	* @private
	* @default
	*/
	this._onStartCallback = null;

	/**
	* @property {boolean} _onStartCallbackFired - Description.
	* @private
	* @default
	*/
	this._onStartCallbackFired = false;

	/**
	* @property {Description} _onUpdateCallback - Description.
	* @private
	* @default null
	*/
	this._onUpdateCallback = null;

	/**
	* @property {Description} _onCompleteCallback - Description.
	* @private
	* @default null
	*/
	this._onCompleteCallback = null;

	/**
	* @property {boolean} _paused - Description.
	* @private
	* @default false
	*/
	this._paused = false;
	
	/**
	* @property {number} _pausedTime - Description.
	* @private
	* @default
	*/
	this._pausedTime = 0;

	/**
	* @property {boolean} pendingDelete - If this TweenGroup is ready to be deleted by the TweenManager.
	* @default
	*/
	this.pendingDelete = false;
	
	/**
	* @property {Phaser.Signal} onStart - Description.
	*/
	this.onStart = new Phaser.Signal();

	/**
	* @property {Phaser.Signal} onComplete - Description.
	*/
	this.onComplete = new Phaser.Signal();

	/**
	* @property {boolean} isRunning - Description.
	* @default
	*/
	this.isRunning = false;

	/**
	* @property {number} _time - Description.
	* @private
	*/
	this._time = 0;

	/**
	* @property {array} _tweens - Description.
	* @private
	*/
	this._tweens = [];

	/**
	* @property {array} _add - Description.
	* @private
	*/
	this._add = [];

	/**
	* @property {boolean} _dirty - Description.
	* @private
	*/
	this._dirty = false;

	if (autoStart) {
		this.start();
	}

};

Phaser.TweenGroup.prototype = {

	/**
	* Create and configure a tween that will be added to this group
	*
	* @method Phaser.TweenGroup#to
	* @param {object} object - Object to tween.
	* @param {object} properties - Properties you want to tween.
	* @param {number} duration - Duration of this tween.
	* @param {function} ease - Easing function.
	* @param {boolean} autoStart - Whether this tween will start automatically or not.
	* @param {number} delay - Delay before this tween will start, defaults to 0 (no delay).
	* @param {boolean} repeat - Should the tween automatically restart once complete? (ignores any chained tweens).
	* @param {Phaser.Tween} yoyo - Description.
	* @return {Phaser.TweenGroup} Itself.
	*/
	to: function ( object, properties, duration, ease, autoStart, delay, repeat, yoyo ) {

		var tween = new Phaser.Tween(object, this.game);
		tween.to(properties, duration, ease, autoStart, delay, repeat, yoyo);
		this.add(tween);

		return this;

	},

	/**
	* Starts the TweenGroup.
	*
	* @method Phaser.TweenGroup#start
	* @return {Phaser.TweenGroup} Itself.
	*/
	start: function () {

		this.game.tweens.add(this);

		this.onStart.dispatch();

		this.isRunning = true;

		this._onStartCallbackFired = false;

		this._startTime = this.game.time.now + this._delayTime;

		return this;

	},

	/**
	* Stops the TweenGroup if running and removes it from the TweenManager. If there are any onComplete callbacks or events they are not dispatched.
	*
	* @method Phaser.TweenGroup#stop
	* @return {Phaser.TweenGroup} Itself.
	*/
	stop: function () {

		this.game.tweens.remove(this);
		this.isRunning = false;

		return this;

	},

	/**
	* Sets a delay time before this tween will start.
	*
	* @method Phaser.TweenGroup#delay
	* @param {number} amount - The amount of the delay in ms.
	* @return {Phaser.TweenGroup} Itself.
	*/
	delay: function ( amount ) {

		this._delayTime = amount;
		return this;

	},

	/**
	* Sets the number of times this tween will repeat.
	*
	* @method Phaser.TweenGroup#repeat
	* @param {number} times - How many times to repeat.
	* @return {Phaser.TweenGroup} Itself.
	*/
	repeat: function ( times ) {

		this._repeat = times;
		return this;

	},

	/**
	* A tween that has yoyo set to true will run through from start to finish, then reverse from finish to start.
	* Used in combination with repeat you can create endless loops.
	*
	* @method Phaser.TweenGroup#yoyo
	* @param {boolean} yoyo - Set to true to yoyo this tween.
	* @return {Phaser.TweenGroup} Itself.
	*/
	yoyo: function( yoyo ) {

		this._yoyo = yoyo;
		return this;

	},

	/**
	* Sets a callback to be fired when the tween starts. Note: callback will be called in the context of the global scope.
	*
	* @method Phaser.TweenGroup#onStartCallback
	* @param {function} callback - The callback to invoke on start.
	* @return {Phaser.TweenGroup} Itself.
	*/
	onStartCallback: function ( callback ) {

		this._onStartCallback = callback;
		return this;

	},

	/**
	* Sets a callback to be fired each time this tween updates. Note: callback will be called in the context of the global scope.
	*
	* @method Phaser.TweenGroup#onUpdateCallback
	* @param {function} callback - The callback to invoke each time this tween is updated.
	* @return {Phaser.TweenGroup} Itself.
	*/
	onUpdateCallback: function ( callback ) {

		this._onUpdateCallback = callback;
		return this;

	},

	/**
	* Sets a callback to be fired when the tween completes. Note: callback will be called in the context of the global scope.
	*
	* @method Phaser.TweenGroup#onCompleteCallback
	* @param {function} callback - The callback to invoke on completion.
	* @return {Phaser.TweenGroup} Itself.
	*/
	onCompleteCallback: function ( callback ) {

		this._onCompleteCallback = callback;
		return this;

	},

	/**
	* Pauses the tween. 
	*
	* @method Phaser.TweenGroup#pause
	*/
	pause: function () {
		this._paused = true;
		this._pausedTime = this.game.time.now;
	},

	/**
	* Resumes a paused tween.
	*
	* @method Phaser.TweenGroup#resume
	*/
	resume: function () {
		this._paused = false;
		this._startTime += (this.game.time.now - this._pausedTime);
	},

	/**
	* Core tween update function called by the TweenManager. Does not need to be invoked directly.
	*
	* @method Phaser.TweenGroup#update
	* @param {number} time - A timestamp passed in by the TweenManager.
	* @return {boolean} false if the tween has completed and should be deleted from the manager, otherwise true (still active).
	*/
	update: function ( time ) {
		var i = 0,
			numTweens = this._tweens.length,
			tween = null;

		var rtime = 0;

		if ( this._onStartCallbackFired === false ) {

			if ( this._onStartCallback !== null ) {

				this._onStartCallback.call(this);

			}

			this.onStart.dispatch(this);

			this._onStartCallbackFired = true;

		}

		if ( this._dirty && numTweens > 0) {

			var max = 0;

			for (var j = numTweens - 1; j >= 0; j--) {
				tween = this._tweens[j];

				if ( max < ( tween._startTime + tween._duration ) ) {
					max = tween._startTime + tween._duration;
				}
			}

			this._duration = max;

			this._dirty = false;

		}

		while ( i < numTweens ) {

			tween = this._tweens[ i ];

			if ( ( time < tween._startTime ) || ( time > ( tween._startTime + tween._duration )) || tween._paused) {

				i++;
				continue;

			}

			rtime = time - tween._startTime;

			tween.update( rtime );

			i++;
		}

		if ( this._onUpdateCallback !== null ) {

			this._onUpdateCallback.call(this);

		}

		if ( ( ( time >= this._duration ) && !this._reversed ) || ( ( time <= 0) && this._reversed ) ) {

			this.onComplete.dispatch(this);

			if ( this._onCompleteCallback !== null ) {
				this._onCompleteCallback.call(this);
			}

			if ( this._repeat > 0 ) {

				if ( isFinite( this._repeat ) ) {
					this._repeat--;
				}

				if (this._yoyo) {
					this._reversed = !this._reversed;
				}

				this._startTime = this.game.time.now + this._delayTime;

			} else {
				return false;
			}

		}

		if (this._add.length > 0)
		{
			this._tweens = this._tweens.concat(this._add);
			this._add.length = 0;
		}

		return true;
	},

	/**
	* Add a new tween into the TweenGroup.
	*
	* @method Phaser.TweenGroup#add
	* @param {Phaser.Tween} tween - The tween object you want to add.
	* @returns {Phaser.Tween} The tween object you added to the group.
	*/
	add: function ( tween ) {

		if ( this.isRunning ) {
			this._add.push( tween );
		} else {
			this._tweens.push( tween );
		}

		tween._startTime = this._duration + tween._delayTime;

		if ( ( tween._startTime + tween._duration ) > this._duration ) {
			this._duration = tween._startTime + tween._duration;
		}

		this._dirty = true;

	},

	/**
	* Remove a tween from this TweenGroup.
	*
	* @method Phaser.TweenGroup#remove
	* @param {Phaser.Tween} tween - The tween object you want to remove.
	*/
	remove: function ( tween ) {

		var i = this._tweens.indexOf( tween );

		if ( i !== -1 ) {

			this._tweens[i].pendingDelete = true;

		}

		this._dirty = true;

	}
	
};