File:DelayedCall.js

/**
 * @module Core
 * @namespace springroll
 */
(function(undefined)
{

	var Application;

	/**
	 * A class for delaying a call through the Application, instead of relying on setInterval() or
	 * setTimeout().
	 *
	 * @class DelayedCall
	 * @constructor
	 * @param {function} callback The function to call when the delay has completed.
	 * @param {int} delay The time to delay the call, in milliseconds (or optionally frames).
	 * @param {Object|Boolean} [options=false] The options to use or repeat value
	 * @param {Boolean} [options.repeat=false] If the DelayedCall should automatically repeat itself when
	 *                              completed.
	 * @param {Boolean} [options.autoDestroy=true] If the DelayedCall should clean itself up when completed.
	 * @param {Boolean} [options.useFrames=false] If the DelayedCall should use frames instead of
	 *                                 milliseconds for the delay.
	 * @param {Boolean} [autoDestroy=true] If the DelayedCall should clean itself up when completed.
	 * @param {Boolean} [useFrames=false] If the DelayedCall should use frames instead of
	 *                                 milliseconds for the delay.
	 */
	var DelayedCall = function(callback, delay, options, autoDestroy, useFrames)
	{
		if (!Application)
		{
			Application = include('springroll.Application');
		}

		// @deprecate the options as repeat param
		if (typeof options === "boolean")
		{
			options = {
				repeat: !!options,
				autoDestroy: autoDestroy === undefined ? true : !!autoDestroy,
				useFrames: !!useFrames
			};
		}

		// Set the default options
		options = Object.merge(
		{
			repeat: false,
			autoDestroy: true,
			useFrames: false
		}, options ||
		{});


		/**
		 * The function to call when the delay is completed.
		 * @private
		 * @property {function} _callback
		 */
		this._callback = callback;

		/**
		 * The delay time, in milliseconds.
		 * @private
		 * @property {int} _delay
		 */
		this._delay = delay;

		/**
		 * The timer counting down from _delay, in milliseconds.
		 * @private
		 * @property {int} _timer
		 */
		this._timer = delay;

		/**
		 * If the DelayedCall should repeat itself automatically.
		 * @private
		 * @property {Boolean} _repeat
		 * @default false
		 */
		this._repeat = options.repeat;

		/**
		 * If the DelayedCall should destroy itself after completing
		 * @private
		 * @property {Boolean} _autoDestroy
		 * @default true
		 */
		this._autoDestroy = options.autoDestroy;

		/**
		 * If the DelayedCall should use frames instead of milliseconds for the delay.
		 * @private
		 * @property {Boolean} _useFrames
		 * @default false
		 */
		this._useFrames = options.useFrames;

		/**
		 * If the DelayedCall is currently paused (not stopped).
		 * @private
		 * @property {Boolean} _paused
		 */
		this._paused = false;

		//save a bound version of the update function
		this._update = this._update.bind(this);

		//start the delay
		Application.instance.on("update", this._update);
	};

	var p = extend(DelayedCall);

	/**
	 * The callback supplied to the Application for an update each frame.
	 * @private
	 * @method _update
	 * @param {int} elapsed The time elapsed since the previous frame.
	 */
	p._update = function(elapsed)
	{
		if (!this._callback)
		{
			this.destroy();
			return;
		}

		this._timer -= this._useFrames ? 1 : elapsed;
		if (this._timer <= 0)
		{
			this._callback(this);
			if (this._repeat)
				this._timer += this._delay;
			else if (this._autoDestroy)
				this.destroy();
			else
				Application.instance.off("update", this._update);
		}
	};

	/**
	 * Restarts the DelayedCall, whether it is running or not.
	 * @public
	 * @method restart
	 */
	p.restart = function()
	{
		if (!this._callback) return;
		var app = Application.instance;
		if (!app.has("update", this._update))
			app.on("update", this._update);
		this._timer = this._delay;
		this._paused = false;
	};

	/**
	 * Stops the DelayedCall, without destroying it.
	 * @public
	 * @method stop
	 */
	p.stop = function()
	{
		Application.instance.off("update", this._update);
		this._paused = false;
	};

	/**
	 * If the DelayedCall is paused or not.
	 * @public
	 * @property {Boolean} paused
	 */
	Object.defineProperty(p, "paused",
	{
		get: function()
		{
			return this._paused;
		},
		set: function(value)
		{
			if (!this._callback) return;
			var app = Application.instance;
			if (this._paused && !value)
			{
				this._paused = false;
				if (!app.has("update", this._update))
					app.on("update", this._update);
			}
			else if (value)
			{
				if (app.has("update", this._update))
				{
					this._paused = true;
					app.off("update", this._update);
				}
			}
		}
	});

	/**
	 * Stops and cleans up the DelayedCall. Do not use it after calling
	 * destroy().
	 * @public
	 * @method destroy
	 */
	p.destroy = function()
	{
		Application.instance.off("update", this._update);
		this._callback = null;
	};

	namespace('springroll').DelayedCall = DelayedCall;
}());