API Docs for: 1.4.0
Show:

File: src\time\Timer.ts

/**
* 
* @module Kiwi
* @submodule Time
*
*/

module Kiwi.Time {

	/**
	* The Timer class hooks into a game Clock and allows you run code at a specified point in game time.
	* Use the start() method to start a timer. Add TimerEvents to set-up code to be run on the timer interval.
	* Timer objects can run once or repeat at specified intervals to execute code on a schedule.
	*
	* @class Timer
	* @namespace Kiwi.Time
	* @constructor
	* @param name {string} The name of the timer.
	* @param clock {Kiwi.Time.Clock} The game clock instance this Timer is based on.
	* @param delay {Number} The number of clock units to wait between firing events.
	* @param [repeatCount=0] {Number} The number of times to repeat the timer before it is expired. If you don't want it to ever expire, set a value of -1.
	* @return {Kiwi.Time.Timer} This object.
	*
	*/
	export class Timer {

		constructor (name: string, clock: Clock, delay: number, repeatCount: number = 0) {

			this._clock = clock;

			this._startEvents = [];
			this._countEvents = [];
			this._stopEvents = [];

			this.name = name;
			this.delay = delay;
			this.repeatCount = repeatCount;

		}

		/**
		* The type of object that this is.
		* @method objType
		* @return {String} "Timer"
		* @public
		*/
		public objType() {
			return "Timer";
		}

		/**
		* The number of times the timer has repeated so far.
		* @property _currentCount
		* @type Number
		* @default 0 
		* @private
		*/
		private _currentCount: number = 0;

		/**
		* Get the number of times the timer has repeated.
		* @method getCurrentCount
		* @return {Number}
		* @public
		*/
		public currentCount(): number {

			return this._currentCount;

		}

		/**
		* A collection of the TimerEvents associated with TimerEvent.TIMER_START
		* @property _startEvents
		* @type Array
		* @private
		*/
		private _startEvents:TimerEvent[] = null;

		/**
		* A collection of the TimerEvents associated with TimerEvent.TIMER_COUNT
		* @property _countEvents
		* @private
		* @type Array
		*/
		private _countEvents:TimerEvent[] = null;

		/**
		* A collection of the TimerEvents associated with TimerEvent.TIMER_STOP
		* @property _stopEvents
		* @private
		* @type Array
		*/
		private _stopEvents:TimerEvent[] = null;

		/**
		* The clock which this timer bases its timing on.
		* @property _clock
		* @type Kiwi.Time.Clock
		* @private
		*/
		private _clock: Clock = null;

		/**
		* The time the last repeat occurred in clock units.
		* @property _timeLastCount
		* @type Number
		* @private
		* @deprecated Better time handling in 1.2.0 deprecates this data.
		*/
		private _timeLastCount: number = null;

		/**
		* Whether the timer is in a running state.
		* @property _isRunning
		* @type boolean
		* @default false
		* @private
		*/
		private _isRunning: boolean = false;

		/**
		* The Timers current state. True if the Timer is running, otherwise false.
		* @method running
		* @return {boolean} 
		* @public
		*/
		public isRunning(): boolean {

			return this._isRunning;

		}

		/**
		* Whether the timer is in a stopped state.
		* @property _isStopped
		* @type boolean
		* @default true 
		* @private
		*/
		private _isStopped: boolean = true;

		/**
		* Whether the timer is in a stopped state.
		* @method stopped
		* @return {boolean} 
		* @public
		*/
		public isStopped(): boolean {

			return this._isStopped;

		}

		/**
		* Whether the timer is in a paused state.
		* @property _isPaused
		* @type boolean
		* @default false
		* @private
		*/
		private _isPaused: boolean = false;

		/**
		* Whether the timer is in a paused state.
		* @method paused
		* @return {boolean} 
		* @public
		*/
		public isPaused(): boolean {

			return this._isPaused;

		}

		/**
		* The name of the timer.
		* @property name
		* @type String
		* @default null
		* @public 
		*/
		public name: string = null;

		/**
		* The delay, in game clock units, that the timer will wait before firing the event
		* @property _delay
		* @type Number
		* @default 0.016
		* @private
		*/
		private _delay: number = 0.016;

		/**
		* The delay, in game clock units, that the timer will wait before firing the event
		*
		* This property must be greater than 0.
		* @property delay
		* @type Number
		* @default 0.016
		* @public
		*/
		public get delay(): number {
			return this._delay;
		}
		public set delay( value: number ) {
			if ( value > 0 ) {
				this._delay = value;
			} else {
				Kiwi.Log.error( "Attempted to set timer delay", value,
					"but value must be greater than 0", "#timer" );
			}
		}


		/**
		* The number of times the timer will repeat before stopping.
		* @property repeatCount
		* @type Number
		* @default 0
		* @public
		*/
		public repeatCount: number = 0;

		/**
		* Time elapsed on the current repetition
		* @property _elapsed
		* @type number
		* @private
		* @since 1.2.0
		*/
		private _elapsed: number = 0;

		/**
		* Clock time on last frame, used to calculate frame length and time elapsed
		* @property _lastElapsed
		* @type number
		* @private
		* @since 1.2.0
		*/
		private _lastElapsed: number;
		
		/**
		* Checks the list of TimerEvents added and processes them based on their type.
		* @method processEvents
		* @param type {Number} The type of events to dispatch
		* @private
		*/
		private processEvents(type: number) {

			if (type === TimerEvent.TIMER_START)
			{
				for (var i = 0; i < this._startEvents.length; i++)
				{
					this._startEvents[i].run();
				}
			}
			else if (type === TimerEvent.TIMER_COUNT)
			{
				for (var i = 0; i < this._countEvents.length; i++)
				{
					this._countEvents[i].run();
				}
			}
			else if (type === TimerEvent.TIMER_STOP)
			{
				for (var i = 0; i < this._stopEvents.length; i++)
				{
					this._stopEvents[i].run();
				}
			}

		}

		/**
		* Internal update loop called by the Clock that this Timer belongs to.
		* @method update
		* @public
		*/
		public update() {
			var frameLength = this._clock.elapsed() - this._lastElapsed;

			this._lastElapsed = this._clock.elapsed();

			if ( this._isRunning ) {
				this._elapsed += frameLength;
			}

			while ( this._elapsed >= this.delay ) {
				this._currentCount++;
				this.processEvents( TimerEvent.TIMER_COUNT );
				this._elapsed -= this.delay;

				if ( this.repeatCount !== -1 && this._currentCount >= this.repeatCount ) {
					this.stop();
				}
			}

			while ( this._elapsed < 0 ) {
				this._currentCount--;
				this._elapsed += this.delay;

				// Do not process events; they can happen when time flows forwards

				if ( this._currentCount < 0 ) {

					// Timer has regressed before its creation.
					// When time flows forward again, the Timer will probably
					// be restarted and repopulated.
					// There is a potential memory leak: if a Timer is created
					// for a single task, and has a TimerEvent that will
					// remove it upon completion, but the Timer is rewound to
					// before its creation, that removal will never fire.
					this.clear();
					this.stop();
				}
			}
		}

		/**
		* Start the Timer. This will reset the timer and start it. The timer can only be started if it is in a stopped state.
		* @method start
		* @return {Kiwi.Time.Timer} this object.
		* @public
		*/
		public start(): Timer {

			if (this._isStopped === true)
			{
				this._isRunning = true;
				this._isPaused = false;
				this._isStopped = false;

				this._currentCount = 0;

				this._elapsed = 0;
				this._lastElapsed = this._clock.elapsed() || 0;

				this.processEvents(TimerEvent.TIMER_START);
			}

			return this;
		}

		/**
		* Stop the Timer. Only possible when the timer is running or paused.
		* @method stop
		* @return {Kiwi.Time.Timer} this object.
		* @public
		*/
		public stop():Timer {

			if (this._isRunning === true || this._isPaused === true)
			{
				this._isRunning = false;
				this._isPaused = false;
				this._isStopped = true;

				this.processEvents(TimerEvent.TIMER_STOP);
			}

			return this;

		}

		/**
		* Pause the Timer. Only possible when the timer is running.
		* @method pause
		* @return {Kiwi.Time.Timer} this object.
		* @public
		*/
		public pause():Timer {

			if (this._isRunning === true)
			{
				this._isRunning = false;
				this._isPaused = true;
			}

			return this;
		}

		/**
		* Resume the Timer. Only possible if the timer has been paused.
		* @method resume
		* @return {Kiwi.Time.Timer} this object.
		* @public
		*/
		public resume(): Timer {

			if (this._isPaused === true)
			{
				this._isRunning = true;
				this._isPaused = false;
			}

			return this;

		}

		/**
		* Adds an existing TimerEvent object to this Timer.
		* @method addTimerEvent
		* @param {Kiwi.Time.TimerEvent} A TimerEvent object
		* @return {Kiwi.Time.TimerEvent} The TimerEvent object
		* @public
		*/
		public addTimerEvent(event:TimerEvent):TimerEvent {

			if (event.type === TimerEvent.TIMER_START)
			{
				this._startEvents.push(event);
			}
			else if (event.type === TimerEvent.TIMER_COUNT)
			{
				this._countEvents.push(event);
			}
			else if (event.type === TimerEvent.TIMER_STOP)
			{
				this._stopEvents.push(event);
			}

			return event;

		}

		/**
		* Creates a new TimerEvent and adds it to this Timer
		* @method createTimerEvent
		* @param type {Number} The type of TimerEvent to create (TIMER_START, TIMER_COUNT or TIMER_STOP).
		* @param callback {Function} The function to call when the TimerEvent fires.
		* @param context {Function} The context in which the given function will run (usually 'this')
		* @return {Kiwi.Time.TimerEvent} The newly created TimerEvent.
		* @public
		*/
		public createTimerEvent(type:number, callback, context):TimerEvent {

			if (type === TimerEvent.TIMER_START)
			{
				this._startEvents.push(new TimerEvent(type, callback, context));
				return this._startEvents[this._startEvents.length - 1];
			}
			else if (type === TimerEvent.TIMER_COUNT)
			{
				this._countEvents.push(new TimerEvent(type, callback, context));
				return this._countEvents[this._countEvents.length - 1];
			}
			else if (type === TimerEvent.TIMER_STOP)
			{
				this._stopEvents.push(new TimerEvent(type, callback, context));
				return this._stopEvents[this._stopEvents.length - 1];
			}

			return null;

		}

		/**
		* Removes a TimerEvent object from this Timer
		* @method removeTimerEvent
		* @param {Kiwi.Time.TimerEvent} The TimerEvent to remove
		* @return {boolean} True if the event was removed, otherwise false.
		* @public
		*/
		public removeTimerEvent(event:TimerEvent):boolean {

			var removed = [];

			if (event.type === TimerEvent.TIMER_START)
			{
				removed = this._startEvents.splice(this._startEvents.indexOf(event), 1);
			}
			else if (event.type === TimerEvent.TIMER_COUNT)
			{
				removed = this._countEvents.splice(this._countEvents.indexOf(event), 1);
			}
			else if (event.type === TimerEvent.TIMER_STOP)
			{
				removed = this._stopEvents.splice(this._stopEvents.indexOf(event), 1);
			}

			if (removed.length === 1)
			{
				return true;
			}
			else
			{
				return false;
			}

		}

		/**
		* Removes all TimerEvent objects from this Timer
		* @method clear
		* @param type {Number} The type of TimerEvents to remove. Set to zero to remove them all.
		* @return {boolean} True if the event was removed, otherwise false.
		* @public
		*/
		public clear(type:number = 0) {

			if (type === 0)
			{
				this._startEvents.length = 0;
				this._countEvents.length = 0;
				this._stopEvents.length = 0;
			}
			else if (type === TimerEvent.TIMER_START)
			{
				this._startEvents.length = 0;
			}
			else if (type === TimerEvent.TIMER_COUNT)
			{
				this._countEvents.length = 0;
			}
			else if (type === TimerEvent.TIMER_STOP)
			{
				this._stopEvents.length = 0;
			}

		}

		/**
		* Returns a string representation of this object.
		* @method toString
		* @return {string} a string representation of the instance.
		* @public
		*/
		public toString(): string {

			return "[{Timer (name=" + this.name + " delay=" + this.delay + " repeatCount=" + this.repeatCount + " running=" + this._isRunning + ")}]";

		}

	}

}