Event.js 4.4 KB

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