/*global define*/
define([
'./defined',
'./defineProperties',
'./DeveloperError'
], function(
defined,
defineProperties,
DeveloperError) {
'use strict';
/**
* A generic utility class for managing subscribers for a particular event.
* This class is usually instantiated inside of a container class and
* exposed as a property for others to subscribe to.
*
* @alias Event
* @constructor
*
* @example
* MyObject.prototype.myListener = function(arg1, arg2) {
* this.myArg1Copy = arg1;
* this.myArg2Copy = arg2;
* }
*
* var myObjectInstance = new MyObject();
* var evt = new Cesium.Event();
* evt.addEventListener(MyObject.prototype.myListener, myObjectInstance);
* evt.raiseEvent('1', '2');
* evt.removeEventListener(MyObject.prototype.myListener);
*/
function Event() {
this._listeners = [];
this._scopes = [];
this._toRemove = [];
this._insideRaiseEvent = false;
}
defineProperties(Event.prototype, {
/**
* The number of listeners currently subscribed to the event.
* @memberof Event.prototype
* @type {Number}
* @readonly
*/
numberOfListeners : {
get : function() {
return this._listeners.length - this._toRemove.length;
}
}
});
/**
* Registers a callback function to be executed whenever the event is raised.
* An optional scope can be provided to serve as the <code>this</code> pointer
* in which the function will execute.
*
* @param {Function} listener The function to be executed when the event is raised.
* @param {Object} [scope] An optional object scope to serve as the <code>this</code>
* pointer in which the listener function will execute.
* @returns {Event~RemoveCallback} A function that will remove this event listener when invoked.
*
* @see Event#raiseEvent
* @see Event#removeEventListener
*/
Event.prototype.addEventListener = function(listener, scope) {
//>>includeStart('debug', pragmas.debug);
if (typeof listener !== 'function') {
throw new DeveloperError('listener is required and must be a function.');
}
//>>includeEnd('debug');
this._listeners.push(listener);
this._scopes.push(scope);
var event = this;
return function() {
event.removeEventListener(listener, scope);
};
};
/**
* Unregisters a previously registered callback.
*
* @param {Function} listener The function to be unregistered.
* @param {Object} [scope] The scope that was originally passed to addEventListener.
* @returns {Boolean} <code>true</code> if the listener was removed; <code>false</code> if the listener and scope are not registered with the event.
*
* @see Event#addEventListener
* @see Event#raiseEvent
*/
Event.prototype.removeEventListener = function(listener, scope) {
//>>includeStart('debug', pragmas.debug);
if (typeof listener !== 'function') {
throw new DeveloperError('listener is required and must be a function.');
}
//>>includeEnd('debug');
var listeners = this._listeners;
var scopes = this._scopes;
var index = -1;
for (var i = 0; i < listeners.length; i++) {
if (listeners[i] === listener && scopes[i] === scope) {
index = i;
break;
}
}
if (index !== -1) {
if (this._insideRaiseEvent) {
//In order to allow removing an event subscription from within
//a callback, we don't actually remove the items here. Instead
//remember the index they are at and undefined their value.
this._toRemove.push(index);
listeners[index] = undefined;
scopes[index] = undefined;
} else {
listeners.splice(index, 1);
scopes.splice(index, 1);
}
return true;
}
return false;
};
/**
* Raises the event by calling each registered listener with all supplied arguments.
*
* @param {*} arguments This method takes any number of parameters and passes them through to the listener functions.
*
* @see Event#addEventListener
* @see Event#removeEventListener
*/
Event.prototype.raiseEvent = function() {
this._insideRaiseEvent = true;
var i;
var listeners = this._listeners;
var scopes = this._scopes;
var length = listeners.length;
for (i = 0; i < length; i++) {
var listener = listeners[i];
if (defined(listener)) {
listeners[i].apply(scopes[i], arguments);
}
}
//Actually remove items removed in removeEventListener.
var toRemove = this._toRemove;
length = toRemove.length;
for (i = 0; i < length; i++) {
var index = toRemove[i];
listeners.splice(index, 1);
scopes.splice(index, 1);
}
toRemove.length = 0;
this._insideRaiseEvent = false;
};
/**
* A function that removes a listener.
* @callback Event~RemoveCallback
*/
return Event;
});