events.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. module.metadata = {
  6. "stability": "deprecated"
  7. };
  8. const ERROR_TYPE = 'error',
  9. UNCAUGHT_ERROR = 'An error event was dispatched for which there was'
  10. + ' no listener.',
  11. BAD_LISTENER = 'The event listener must be a function.';
  12. /**
  13. * This object is used to create an `EventEmitter` that, useful for composing
  14. * objects that emit events. It implements an interface like `EventTarget` from
  15. * DOM Level 2, which is implemented by Node objects in implementations that
  16. * support the DOM Event Model.
  17. * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
  18. * @see http://nodejs.org/api.html#EventEmitter
  19. * @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/EventDispatcher.html
  20. */
  21. const eventEmitter = {
  22. /**
  23. * Registers an event `listener` that is called every time events of
  24. * specified `type` are emitted.
  25. * @param {String} type
  26. * The type of event.
  27. * @param {Function} listener
  28. * The listener function that processes the event.
  29. * @example
  30. * worker.on('message', function (data) {
  31. * console.log('data received: ' + data)
  32. * })
  33. */
  34. on: function on(type, listener) {
  35. if ('function' !== typeof listener)
  36. throw new Error(BAD_LISTENER);
  37. let listeners = this._listeners(type);
  38. if (0 > listeners.indexOf(listener))
  39. listeners.push(listener);
  40. // Use of `_public` is required by the legacy traits code that will go away
  41. // once bug-637633 is fixed.
  42. return this._public || this;
  43. },
  44. /**
  45. * Registers an event `listener` that is called once the next time an event
  46. * of the specified `type` is emitted.
  47. * @param {String} type
  48. * The type of the event.
  49. * @param {Function} listener
  50. * The listener function that processes the event.
  51. */
  52. once: function once(type, listener) {
  53. this.on(type, function selfRemovableListener() {
  54. this.removeListener(type, selfRemovableListener);
  55. listener.apply(this, arguments);
  56. });
  57. },
  58. /**
  59. * Unregister `listener` for the specified event type.
  60. * @param {String} type
  61. * The type of event.
  62. * @param {Function} listener
  63. * The listener function that processes the event.
  64. */
  65. removeListener: function removeListener(type, listener) {
  66. if ('function' !== typeof listener)
  67. throw new Error(BAD_LISTENER);
  68. let listeners = this._listeners(type),
  69. index = listeners.indexOf(listener);
  70. if (0 <= index)
  71. listeners.splice(index, 1);
  72. // Use of `_public` is required by the legacy traits code, that will go away
  73. // once bug-637633 is fixed.
  74. return this._public || this;
  75. },
  76. /**
  77. * Hash of listeners on this EventEmitter.
  78. */
  79. _events: null,
  80. /**
  81. * Returns an array of listeners for the specified event `type`. This array
  82. * can be manipulated, e.g. to remove listeners.
  83. * @param {String} type
  84. * The type of event.
  85. */
  86. _listeners: function listeners(type) {
  87. let events = this._events || (this._events = {});
  88. return (events.hasOwnProperty(type) && events[type]) || (events[type] = []);
  89. },
  90. /**
  91. * Execute each of the listeners in order with the supplied arguments.
  92. * Returns `true` if listener for this event was called, `false` if there are
  93. * no listeners for this event `type`.
  94. *
  95. * All the exceptions that are thrown by listeners during the emit
  96. * are caught and can be handled by listeners of 'error' event. Thrown
  97. * exceptions are passed as an argument to an 'error' event listener.
  98. * If no 'error' listener is registered exception will propagate to a
  99. * caller of this method.
  100. *
  101. * **It's recommended to have a default 'error' listener in all the complete
  102. * composition that in worst case may dump errors to the console.**
  103. *
  104. * @param {String} type
  105. * The type of event.
  106. * @params {Object|Number|String|Boolean}
  107. * Arguments that will be passed to listeners.
  108. * @returns {Boolean}
  109. */
  110. _emit: function _emit(type, event) {
  111. let args = Array.slice(arguments);
  112. // Use of `_public` is required by the legacy traits code that will go away
  113. // once bug-637633 is fixed.
  114. args.unshift(this._public || this);
  115. return this._emitOnObject.apply(this, args);
  116. },
  117. /**
  118. * A version of _emit that lets you specify the object on which listeners are
  119. * called. This is a hack that is sometimes necessary when such an object
  120. * (exports, for example) cannot be an EventEmitter for some reason, but other
  121. * object(s) managing events for the object are EventEmitters. Once bug
  122. * 577782 is fixed, this method shouldn't be necessary.
  123. *
  124. * @param {object} targetObj
  125. * The object on which listeners will be called.
  126. * @param {string} type
  127. * The event name.
  128. * @param {value} event
  129. * The first argument to pass to listeners.
  130. * @param {value} ...
  131. * More arguments to pass to listeners.
  132. * @returns {boolean}
  133. */
  134. _emitOnObject: function _emitOnObject(targetObj, type, event /* , ... */) {
  135. let listeners = this._listeners(type).slice(0);
  136. // If there is no 'error' event listener then throw.
  137. if (type === ERROR_TYPE && !listeners.length)
  138. console.exception(event);
  139. if (!listeners.length)
  140. return false;
  141. let params = Array.slice(arguments, 2);
  142. for each (let listener in listeners) {
  143. try {
  144. listener.apply(targetObj, params);
  145. } catch(e) {
  146. // Bug 726967: Ignore exceptions being throws while notifying the error
  147. // in order to avoid infinite loops.
  148. if (type !== ERROR_TYPE)
  149. this._emit(ERROR_TYPE, e);
  150. else
  151. console.exception("Exception in error event listener " + e);
  152. }
  153. }
  154. return true;
  155. },
  156. /**
  157. * Removes all the event listeners for the specified event `type`.
  158. * @param {String} type
  159. * The type of event.
  160. */
  161. _removeAllListeners: function _removeAllListeners(type) {
  162. if (typeof type == "undefined") {
  163. this._events = null;
  164. return this;
  165. }
  166. this._listeners(type).splice(0);
  167. return this;
  168. }
  169. };
  170. exports.EventEmitter = require("./traits").Trait.compose(eventEmitter);
  171. exports.EventEmitterTrait = require('./light-traits').Trait(eventEmitter);