events.js

  1. /**
  2. * Events API
  3. *
  4. * EventEmitter3 is a high performance EventEmitter. It has been micro-optimized for various
  5. * of code paths making this, one of, if not the fastest EventEmitter available for Node.js
  6. * and browsers. The module is API compatible with the EventEmitter that ships by default with Node.js.
  7. *
  8. * @see {@link https://github.com/primus/EventEmitter3}
  9. *
  10. * @module Events
  11. */
  12. 'use strict';
  13. var has = Object.prototype.hasOwnProperty,
  14. prefix = '~';
  15. /**
  16. * Constructor to create a storage for our `EE` objects.
  17. * An `Events` instance is a plain object whose properties are event names.
  18. *
  19. * @constructor
  20. * @private
  21. */
  22. function Events() {}
  23. //
  24. // We try to not inherit from `Object.prototype`. In some engines creating an
  25. // instance in this way is faster than calling `Object.create(null)` directly.
  26. // If `Object.create(null)` is not supported we prefix the event names with a
  27. // character to make sure that the built-in object properties are not
  28. // overridden or used as an attack vector.
  29. //
  30. if (Object.create) {
  31. Events.prototype = Object.create(null);
  32. //
  33. // This hack is needed because the `__proto__` property is still inherited in
  34. // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
  35. //
  36. if (!new Events().__proto__) prefix = false;
  37. }
  38. /**
  39. * Representation of a single event listener.
  40. *
  41. * @param {Function} fn The listener function.
  42. * @param {*} context The context to invoke the listener with.
  43. * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
  44. * @constructor
  45. * @private
  46. */
  47. function EE(fn, context, once) {
  48. this.fn = fn;
  49. this.context = context;
  50. this.once = once || false;
  51. }
  52. /**
  53. * Add a listener for a given event.
  54. *
  55. * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
  56. * @param {(String|Symbol)} event The event name.
  57. * @param {Function} fn The listener function.
  58. * @param {*} context The context to invoke the listener with.
  59. * @param {Boolean} once Specify if the listener is a one-time listener.
  60. * @returns {EventEmitter}
  61. * @private
  62. */
  63. function addListener(emitter, event, fn, context, once) {
  64. if (typeof fn !== 'function') {
  65. throw new TypeError('The listener must be a function');
  66. }
  67. var listener = new EE(fn, context || emitter, once),
  68. evt = prefix ? prefix + event : event;
  69. if (!emitter._events[evt])
  70. (emitter._events[evt] = listener), emitter._eventsCount++;
  71. else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
  72. else emitter._events[evt] = [emitter._events[evt], listener];
  73. return emitter;
  74. }
  75. /**
  76. * Clear event by name.
  77. *
  78. * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
  79. * @param {(String|Symbol)} evt The Event name.
  80. * @private
  81. */
  82. function clearEvent(emitter, evt) {
  83. if (--emitter._eventsCount === 0) emitter._events = new Events();
  84. else delete emitter._events[evt];
  85. }
  86. /**
  87. * Minimal `EventEmitter` interface that is molded against the Node.js
  88. * `EventEmitter` interface.
  89. *
  90. * @constructor
  91. * @public
  92. */
  93. function EventEmitter() {
  94. this._events = new Events();
  95. this._eventsCount = 0;
  96. }
  97. /**
  98. * Return an array listing the events for which the emitter has registered
  99. * listeners.
  100. *
  101. * @returns {Array}
  102. * @public
  103. */
  104. EventEmitter.prototype.eventNames = function eventNames() {
  105. var names = [],
  106. events,
  107. name;
  108. if (this._eventsCount === 0) return names;
  109. for (name in (events = this._events)) {
  110. if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
  111. }
  112. if (Object.getOwnPropertySymbols) {
  113. return names.concat(Object.getOwnPropertySymbols(events));
  114. }
  115. return names;
  116. };
  117. /**
  118. * Return the listeners registered for a given event.
  119. *
  120. * @param {(String|Symbol)} event The event name.
  121. * @returns {Array} The registered listeners.
  122. * @public
  123. */
  124. EventEmitter.prototype.listeners = function listeners(event) {
  125. var evt = prefix ? prefix + event : event,
  126. handlers = this._events[evt];
  127. if (!handlers) return [];
  128. if (handlers.fn) return [handlers.fn];
  129. for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
  130. ee[i] = handlers[i].fn;
  131. }
  132. return ee;
  133. };
  134. /**
  135. * Return the number of listeners listening to a given event.
  136. *
  137. * @param {(String|Symbol)} event The event name.
  138. * @returns {Number} The number of listeners.
  139. * @public
  140. */
  141. EventEmitter.prototype.listenerCount = function listenerCount(event) {
  142. var evt = prefix ? prefix + event : event,
  143. listeners = this._events[evt];
  144. if (!listeners) return 0;
  145. if (listeners.fn) return 1;
  146. return listeners.length;
  147. };
  148. /**
  149. * Calls each of the listeners registered for a given event.
  150. *
  151. * @param {(String|Symbol)} event The event name.
  152. * @returns {Boolean} `true` if the event had listeners, else `false`.
  153. * @public
  154. */
  155. EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
  156. var evt = prefix ? prefix + event : event;
  157. // If there is no 'error' event listener then throw.
  158. if (evt === 'error' && a1 && !this._events[evt]) {
  159. if (a1 instanceof Error) {
  160. throw a1;
  161. }
  162. throw new Error(a1);
  163. }
  164. if (!this._events[evt]) return false;
  165. var listeners = this._events[evt],
  166. len = arguments.length,
  167. args,
  168. i;
  169. if (listeners.fn) {
  170. if (listeners.once)
  171. this.removeListener(event, listeners.fn, undefined, true);
  172. switch (len) {
  173. case 1:
  174. return listeners.fn.call(listeners.context), true;
  175. case 2:
  176. return listeners.fn.call(listeners.context, a1), true;
  177. case 3:
  178. return listeners.fn.call(listeners.context, a1, a2), true;
  179. case 4:
  180. return listeners.fn.call(listeners.context, a1, a2, a3), true;
  181. case 5:
  182. return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
  183. case 6:
  184. return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
  185. }
  186. for (i = 1, args = new Array(len - 1); i < len; i++) {
  187. args[i - 1] = arguments[i];
  188. }
  189. listeners.fn.apply(listeners.context, args);
  190. } else {
  191. var length = listeners.length,
  192. j;
  193. for (i = 0; i < length; i++) {
  194. if (listeners[i].once)
  195. this.removeListener(event, listeners[i].fn, undefined, true);
  196. switch (len) {
  197. case 1:
  198. listeners[i].fn.call(listeners[i].context);
  199. break;
  200. case 2:
  201. listeners[i].fn.call(listeners[i].context, a1);
  202. break;
  203. case 3:
  204. listeners[i].fn.call(listeners[i].context, a1, a2);
  205. break;
  206. case 4:
  207. listeners[i].fn.call(listeners[i].context, a1, a2, a3);
  208. break;
  209. default:
  210. if (!args)
  211. for (j = 1, args = new Array(len - 1); j < len; j++) {
  212. args[j - 1] = arguments[j];
  213. }
  214. listeners[i].fn.apply(listeners[i].context, args);
  215. }
  216. }
  217. }
  218. return true;
  219. };
  220. /**
  221. * Add a listener for a given event.
  222. *
  223. * @param {(String|Symbol)} event The event name.
  224. * @param {Function} fn The listener function.
  225. * @param {*} [context=this] The context to invoke the listener with.
  226. * @returns {EventEmitter} `this`.
  227. * @public
  228. */
  229. EventEmitter.prototype.on = function on(event, fn, context) {
  230. return addListener(this, event, fn, context, false);
  231. };
  232. /**
  233. * Add a one-time listener for a given event.
  234. *
  235. * @param {(String|Symbol)} event The event name.
  236. * @param {Function} fn The listener function.
  237. * @param {*} [context=this] The context to invoke the listener with.
  238. * @returns {EventEmitter} `this`.
  239. * @public
  240. */
  241. EventEmitter.prototype.once = function once(event, fn, context) {
  242. return addListener(this, event, fn, context, true);
  243. };
  244. /**
  245. * Remove the listeners of a given event.
  246. *
  247. * @param {(String|Symbol)} event The event name.
  248. * @param {Function} fn Only remove the listeners that match this function.
  249. * @param {*} context Only remove the listeners that have this context.
  250. * @param {Boolean} once Only remove one-time listeners.
  251. * @returns {EventEmitter} `this`.
  252. * @public
  253. */
  254. EventEmitter.prototype.removeListener = function removeListener(
  255. event,
  256. fn,
  257. context,
  258. once
  259. ) {
  260. var evt = prefix ? prefix + event : event;
  261. if (!this._events[evt]) return this;
  262. if (!fn) {
  263. clearEvent(this, evt);
  264. return this;
  265. }
  266. var listeners = this._events[evt];
  267. if (listeners.fn) {
  268. if (
  269. listeners.fn === fn &&
  270. (!once || listeners.once) &&
  271. (!context || listeners.context === context)
  272. ) {
  273. clearEvent(this, evt);
  274. }
  275. } else {
  276. for (var i = 0, events = [], length = listeners.length; i < length; i++) {
  277. if (
  278. listeners[i].fn !== fn ||
  279. (once && !listeners[i].once) ||
  280. (context && listeners[i].context !== context)
  281. ) {
  282. events.push(listeners[i]);
  283. }
  284. }
  285. //
  286. // Reset the array, or remove it completely if we have no more listeners.
  287. //
  288. if (events.length)
  289. this._events[evt] = events.length === 1 ? events[0] : events;
  290. else clearEvent(this, evt);
  291. }
  292. return this;
  293. };
  294. /**
  295. * Remove all listeners, or those of the specified event.
  296. *
  297. * @param {(String|Symbol)} [event] The event name.
  298. * @returns {EventEmitter} `this`.
  299. * @public
  300. */
  301. EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
  302. var evt;
  303. if (event) {
  304. evt = prefix ? prefix + event : event;
  305. if (this._events[evt]) clearEvent(this, evt);
  306. } else {
  307. this._events = new Events();
  308. this._eventsCount = 0;
  309. }
  310. return this;
  311. };
  312. //
  313. // Alias methods names because people roll like that.
  314. //
  315. EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
  316. EventEmitter.prototype.addListener = EventEmitter.prototype.on;
  317. //
  318. // Expose the prefix.
  319. //
  320. EventEmitter.prefixed = prefix;
  321. //
  322. // Allow `EventEmitter` to be imported as module namespace.
  323. //
  324. EventEmitter.EventEmitter = EventEmitter;
  325. export { EventEmitter };