API Docs for: 0.3.0
Show:

File: modules/Event/EventProvider.js

/**
 * @module Event
 * @namespace Event
 */

var TW = TW || {};
define([], function() {

	TW.Event = TW.Event || {};


	/**
	 * Abstract class representing an event provider.
	 * The class contains a list of variables with a certain state.
	 * When a variable change, all listeners are called.
	 *
	 * All inputs can be represented by a list of states (ex: mouse position, each key (pressed or released)).
	 *
	 * @class EventProvider
	 * @constructor
	 */
	function EventProvider() {
		/**
		 * List of all event variables name.
		 *
		 * @property {String[]} _states []
		 * @protected
		 */
		this._states = [];

		/**
		 * List of values for state variables.
		 * `this._states` and `this._values` share the same array index.
		 *
		 * @property {Array} _values
		 * @protected
		 */
		this._values = [];

		/**
		 * List of previous values for state variables.
		 * `this._states` and `this._oldValues` share the same array index.
		 *
		 * @property {Array}    _oldValues
		 * @protected
		 */
		this._oldValues = [];

		this._globalCallbacks = [];
		this._stateCallbacks = [];

		/* used for giving a unique id */
		this._nextId = 1;
	}

	/**
	 * return a const string representing the type of provider.
	 * All providers of the same type must return the same result.
	 *
	 * **Note:** All child class MUST implement this method.
	 *
	 * @method getType
	 * @return {String} name of Provider type.
	 */
	EventProvider.prototype.getType = function() {
		return null;
	};

	/**
	 * List all variables accessible by this provider
	 * Each variable can accept listeners.
	 *
	 * **Note:** return value is a reference. you should make a copy if you need to modify it.
	 * @method getStateList
	 * @return {String[]}   [] list of name variables.
	 */
	EventProvider.prototype.getStateList = function() {
		return this._states;
	};

	/**
	 *  Search the state of a state variable
	 *
	 * @method getState
	 * @param {String}  name
	 * @return {*}    value of corresponding variable
	 */
	EventProvider.prototype.getState = function(name) {
		var i, len;

		for (i = 0, len = this._states.length; i < len; ++i) {
			if (this._states[i] === name) {
				return this._values[i];
			}
		}
		throw new Error('EventProvider: Unknow state: ' + name);
	};

	/**
	 *  Search the previous state of a state variable.
	 *  The provider keep always one old state for each variable.
	 *  It's useful for compare the difference.
	 *
	 * @method getOldState
	 * @param {String}  name
	 * @return {*}    value of corresponding variable
	 */
	EventProvider.prototype.getOldState = function(name) {
		var i, len;

		for (i = 0, len = this._states.length; i < len; ++i) {
			if (this._states[i] === name) {
				return this._oldValues[i];
			}
		}
		throw new Error('EventProvider: Unknow state: ' + name);
	};

	/**
	 * add a listener.
	 *
	 * it can listen all events or only one event variable.
	 * The listener can choose to be called for all events associated to a variable,
	 * or only when the variable is in a certain state.
	 *
	 * @method addListener
	 * @param {String}   [event]    name of event variable. y default, all events are caught.
	 * @param {*}        [value]    value expected for call the callback. By default, any value call the callback.
	 * @param {Function} callback   callback function called with 3 parameters:
	 *      @param {String}         callback.event      event name
	 *      @param {*}              callback.value      new value
	 *      @param {EventProvider}  callback.provider   instance of provider
	 * @return {Number} listener id (used for remove it
	 * with {{#crossLink "Event.EventProvider/rmListener"}}rmListener{{/crossLink}})
	 *
	 * @example
	 *
	 *      //myCallback will be called for each events.
	 *      provider.addListener(myCallback);
	 *
	 *      //mySecondCallback will be called only when the "A" variable obtain the state KEY_PRESSED.
	 *      provider.addListener("A", provider.KEY_PRESSED, mySecondCallback);
	 */
	EventProvider.prototype.addListener = function(event, value, callback) {
		var i, len, id;

		if (callback === undefined) {
			callback = value;
			value = undefined;
		}
		if (callback === undefined) {
			callback = event;
			event = undefined;
		}

		id = this._nextId;
		this._nextId++;

		if (event === undefined) {
			this._globalCallbacks.push({
				                           id:       id,
				                           callback: callback
			                           });
			return id;
		} else {
			for (i = 0, len = this._states.length; i < len; ++i) {
				if (this._states[i] === event) {
					if (this._stateCallbacks[i] === undefined) {
						this._stateCallbacks[i] = [];
					}
					this._stateCallbacks[i].push({
						                             id:       id,
						                             filter:   value,
						                             callback: callback
					                             });
					return id;
				}
			}
			throw new Error('EventProvider: Unknow state: ' + event);
		}
	};

	/**
	 * Remove a listener.
	 *
	 * @method rmListener
	 * @param {Number} id id of the listener.
	 */
	EventProvider.prototype.rmListener = function(id) {
		var i, j, len, len2;

		for (i = 0, len = this._globalCallbacks.length; i < len; ++i) {
			if (this._globalCallbacks[i].id === id) {
				this._globalCallbacks.splice(i, 1);
				return;
			}
		}

		for (i = 0, len = this._stateCallbacks.length; i < len; ++i) {
			if (this._stateCallbacks[i] !== undefined) {
				for (j = 0, len2 = this._stateCallbacks[i].length; j < len2; ++j) {
					if (this._stateCallbacks[i][j].id === id) {
						this._stateCallbacks[i].splice(j, 1);
						return;
					}
				}
			}
		}
	};

	/**
	 * Apply a modification to an internal state variable
	 * and call listeners.
	 *
	 * @method _modifyState
	 * @param {String}  event       event name
	 * @param {*}       newValue   the new value.
	 * @protected
	 */
	EventProvider.prototype._modifyState = function(event, newValue) {
		var i, j, len, len2;

		for (i = 0, len = this._states.length; i < len; ++i) {
			if (this._states[i] === event) {
				this._oldValues[i] = this._values[i];
				this._values[i] = newValue;

				for (j = 0, len2 = this._globalCallbacks.length; j < len2; ++j) {
					this._globalCallbacks[j].callback(event, newValue, this);
				}
				if (this._stateCallbacks[i] !== undefined) {
					for (j = 0, len2 = this._stateCallbacks[i].length; j < len2; ++j) {
						if (this._stateCallbacks[i][j].filter === undefined ||
						    JSON.stringify(newValue) === JSON.stringify(this._stateCallbacks[i][j].filter)) {
							this._stateCallbacks[i][j].callback(event, newValue, this);
						}
					}
				}
				return;
			}
		}
		throw new Error('EventProvider: Unknow state: ' + event);
	};

	TW.Event.EventProvider = EventProvider;
	return EventProvider;
});