Source: Core/Event.js

  1. /*global define*/
  2. define([
  3. './defined',
  4. './defineProperties',
  5. './DeveloperError'
  6. ], function(
  7. defined,
  8. defineProperties,
  9. DeveloperError) {
  10. 'use strict';
  11. /**
  12. * A generic utility class for managing subscribers for a particular event.
  13. * This class is usually instantiated inside of a container class and
  14. * exposed as a property for others to subscribe to.
  15. *
  16. * @alias Event
  17. * @constructor
  18. *
  19. * @example
  20. * MyObject.prototype.myListener = function(arg1, arg2) {
  21. * this.myArg1Copy = arg1;
  22. * this.myArg2Copy = arg2;
  23. * }
  24. *
  25. * var myObjectInstance = new MyObject();
  26. * var evt = new Cesium.Event();
  27. * evt.addEventListener(MyObject.prototype.myListener, myObjectInstance);
  28. * evt.raiseEvent('1', '2');
  29. * evt.removeEventListener(MyObject.prototype.myListener);
  30. */
  31. function Event() {
  32. this._listeners = [];
  33. this._scopes = [];
  34. this._toRemove = [];
  35. this._insideRaiseEvent = false;
  36. }
  37. defineProperties(Event.prototype, {
  38. /**
  39. * The number of listeners currently subscribed to the event.
  40. * @memberof Event.prototype
  41. * @type {Number}
  42. * @readonly
  43. */
  44. numberOfListeners : {
  45. get : function() {
  46. return this._listeners.length - this._toRemove.length;
  47. }
  48. }
  49. });
  50. /**
  51. * Registers a callback function to be executed whenever the event is raised.
  52. * An optional scope can be provided to serve as the <code>this</code> pointer
  53. * in which the function will execute.
  54. *
  55. * @param {Function} listener The function to be executed when the event is raised.
  56. * @param {Object} [scope] An optional object scope to serve as the <code>this</code>
  57. * pointer in which the listener function will execute.
  58. * @returns {Event~RemoveCallback} A function that will remove this event listener when invoked.
  59. *
  60. * @see Event#raiseEvent
  61. * @see Event#removeEventListener
  62. */
  63. Event.prototype.addEventListener = function(listener, scope) {
  64. //>>includeStart('debug', pragmas.debug);
  65. if (typeof listener !== 'function') {
  66. throw new DeveloperError('listener is required and must be a function.');
  67. }
  68. //>>includeEnd('debug');
  69. this._listeners.push(listener);
  70. this._scopes.push(scope);
  71. var event = this;
  72. return function() {
  73. event.removeEventListener(listener, scope);
  74. };
  75. };
  76. /**
  77. * Unregisters a previously registered callback.
  78. *
  79. * @param {Function} listener The function to be unregistered.
  80. * @param {Object} [scope] The scope that was originally passed to addEventListener.
  81. * @returns {Boolean} <code>true</code> if the listener was removed; <code>false</code> if the listener and scope are not registered with the event.
  82. *
  83. * @see Event#addEventListener
  84. * @see Event#raiseEvent
  85. */
  86. Event.prototype.removeEventListener = function(listener, scope) {
  87. //>>includeStart('debug', pragmas.debug);
  88. if (typeof listener !== 'function') {
  89. throw new DeveloperError('listener is required and must be a function.');
  90. }
  91. //>>includeEnd('debug');
  92. var listeners = this._listeners;
  93. var scopes = this._scopes;
  94. var index = -1;
  95. for (var i = 0; i < listeners.length; i++) {
  96. if (listeners[i] === listener && scopes[i] === scope) {
  97. index = i;
  98. break;
  99. }
  100. }
  101. if (index !== -1) {
  102. if (this._insideRaiseEvent) {
  103. //In order to allow removing an event subscription from within
  104. //a callback, we don't actually remove the items here. Instead
  105. //remember the index they are at and undefined their value.
  106. this._toRemove.push(index);
  107. listeners[index] = undefined;
  108. scopes[index] = undefined;
  109. } else {
  110. listeners.splice(index, 1);
  111. scopes.splice(index, 1);
  112. }
  113. return true;
  114. }
  115. return false;
  116. };
  117. /**
  118. * Raises the event by calling each registered listener with all supplied arguments.
  119. *
  120. * @param {*} arguments This method takes any number of parameters and passes them through to the listener functions.
  121. *
  122. * @see Event#addEventListener
  123. * @see Event#removeEventListener
  124. */
  125. Event.prototype.raiseEvent = function() {
  126. this._insideRaiseEvent = true;
  127. var i;
  128. var listeners = this._listeners;
  129. var scopes = this._scopes;
  130. var length = listeners.length;
  131. for (i = 0; i < length; i++) {
  132. var listener = listeners[i];
  133. if (defined(listener)) {
  134. listeners[i].apply(scopes[i], arguments);
  135. }
  136. }
  137. //Actually remove items removed in removeEventListener.
  138. var toRemove = this._toRemove;
  139. length = toRemove.length;
  140. for (i = 0; i < length; i++) {
  141. var index = toRemove[i];
  142. listeners.splice(index, 1);
  143. scopes.splice(index, 1);
  144. }
  145. toRemove.length = 0;
  146. this._insideRaiseEvent = false;
  147. };
  148. /**
  149. * A function that removes a listener.
  150. * @callback Event~RemoveCallback
  151. */
  152. return Event;
  153. });