You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
7.6 KiB

8 years ago
  1. 'use strict';
  2. var has = Object.prototype.hasOwnProperty;
  3. //
  4. // We store our EE objects in a plain object whose properties are event names.
  5. // If `Object.create(null)` is not supported we prefix the event names with a
  6. // `~` to make sure that the built-in object properties are not overridden or
  7. // used as an attack vector.
  8. // We also assume that `Object.create(null)` is available when the event name
  9. // is an ES6 Symbol.
  10. //
  11. var prefix = typeof Object.create !== 'function' ? '~' : false;
  12. /**
  13. * Representation of a single EventEmitter function.
  14. *
  15. * @param {Function} fn Event handler to be called.
  16. * @param {Mixed} context Context for function execution.
  17. * @param {Boolean} [once=false] Only emit once
  18. * @api private
  19. */
  20. function EE(fn, context, once) {
  21. this.fn = fn;
  22. this.context = context;
  23. this.once = once || false;
  24. }
  25. /**
  26. * Minimal EventEmitter interface that is molded against the Node.js
  27. * EventEmitter interface.
  28. *
  29. * @constructor
  30. * @api public
  31. */
  32. function EventEmitter() { /* Nothing to set */ }
  33. /**
  34. * Hold the assigned EventEmitters by name.
  35. *
  36. * @type {Object}
  37. * @private
  38. */
  39. EventEmitter.prototype._events = undefined;
  40. /**
  41. * Return an array listing the events for which the emitter has registered
  42. * listeners.
  43. *
  44. * @returns {Array}
  45. * @api public
  46. */
  47. EventEmitter.prototype.eventNames = function eventNames() {
  48. var events = this._events
  49. , names = []
  50. , name;
  51. if (!events) return names;
  52. for (name in events) {
  53. if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
  54. }
  55. if (Object.getOwnPropertySymbols) {
  56. return names.concat(Object.getOwnPropertySymbols(events));
  57. }
  58. return names;
  59. };
  60. /**
  61. * Return a list of assigned event listeners.
  62. *
  63. * @param {String} event The events that should be listed.
  64. * @param {Boolean} exists We only need to know if there are listeners.
  65. * @returns {Array|Boolean}
  66. * @api public
  67. */
  68. EventEmitter.prototype.listeners = function listeners(event, exists) {
  69. var evt = prefix ? prefix + event : event
  70. , available = this._events && this._events[evt];
  71. if (exists) return !!available;
  72. if (!available) return [];
  73. if (available.fn) return [available.fn];
  74. for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {
  75. ee[i] = available[i].fn;
  76. }
  77. return ee;
  78. };
  79. /**
  80. * Emit an event to all registered event listeners.
  81. *
  82. * @param {String} event The name of the event.
  83. * @returns {Boolean} Indication if we've emitted an event.
  84. * @api public
  85. */
  86. EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
  87. var evt = prefix ? prefix + event : event;
  88. if (!this._events || !this._events[evt]) return false;
  89. var listeners = this._events[evt]
  90. , len = arguments.length
  91. , args
  92. , i;
  93. if ('function' === typeof listeners.fn) {
  94. if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
  95. switch (len) {
  96. case 1: return listeners.fn.call(listeners.context), true;
  97. case 2: return listeners.fn.call(listeners.context, a1), true;
  98. case 3: return listeners.fn.call(listeners.context, a1, a2), true;
  99. case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
  100. case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
  101. case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
  102. }
  103. for (i = 1, args = new Array(len -1); i < len; i++) {
  104. args[i - 1] = arguments[i];
  105. }
  106. listeners.fn.apply(listeners.context, args);
  107. } else {
  108. var length = listeners.length
  109. , j;
  110. for (i = 0; i < length; i++) {
  111. if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
  112. switch (len) {
  113. case 1: listeners[i].fn.call(listeners[i].context); break;
  114. case 2: listeners[i].fn.call(listeners[i].context, a1); break;
  115. case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
  116. default:
  117. if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
  118. args[j - 1] = arguments[j];
  119. }
  120. listeners[i].fn.apply(listeners[i].context, args);
  121. }
  122. }
  123. }
  124. return true;
  125. };
  126. /**
  127. * Register a new EventListener for the given event.
  128. *
  129. * @param {String} event Name of the event.
  130. * @param {Function} fn Callback function.
  131. * @param {Mixed} [context=this] The context of the function.
  132. * @api public
  133. */
  134. EventEmitter.prototype.on = function on(event, fn, context) {
  135. var listener = new EE(fn, context || this)
  136. , evt = prefix ? prefix + event : event;
  137. if (!this._events) this._events = prefix ? {} : Object.create(null);
  138. if (!this._events[evt]) this._events[evt] = listener;
  139. else {
  140. if (!this._events[evt].fn) this._events[evt].push(listener);
  141. else this._events[evt] = [
  142. this._events[evt], listener
  143. ];
  144. }
  145. return this;
  146. };
  147. /**
  148. * Add an EventListener that's only called once.
  149. *
  150. * @param {String} event Name of the event.
  151. * @param {Function} fn Callback function.
  152. * @param {Mixed} [context=this] The context of the function.
  153. * @api public
  154. */
  155. EventEmitter.prototype.once = function once(event, fn, context) {
  156. var listener = new EE(fn, context || this, true)
  157. , evt = prefix ? prefix + event : event;
  158. if (!this._events) this._events = prefix ? {} : Object.create(null);
  159. if (!this._events[evt]) this._events[evt] = listener;
  160. else {
  161. if (!this._events[evt].fn) this._events[evt].push(listener);
  162. else this._events[evt] = [
  163. this._events[evt], listener
  164. ];
  165. }
  166. return this;
  167. };
  168. /**
  169. * Remove event listeners.
  170. *
  171. * @param {String} event The event we want to remove.
  172. * @param {Function} fn The listener that we need to find.
  173. * @param {Mixed} context Only remove listeners matching this context.
  174. * @param {Boolean} once Only remove once listeners.
  175. * @api public
  176. */
  177. EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
  178. var evt = prefix ? prefix + event : event;
  179. if (!this._events || !this._events[evt]) return this;
  180. var listeners = this._events[evt]
  181. , events = [];
  182. if (fn) {
  183. if (listeners.fn) {
  184. if (
  185. listeners.fn !== fn
  186. || (once && !listeners.once)
  187. || (context && listeners.context !== context)
  188. ) {
  189. events.push(listeners);
  190. }
  191. } else {
  192. for (var i = 0, length = listeners.length; i < length; i++) {
  193. if (
  194. listeners[i].fn !== fn
  195. || (once && !listeners[i].once)
  196. || (context && listeners[i].context !== context)
  197. ) {
  198. events.push(listeners[i]);
  199. }
  200. }
  201. }
  202. }
  203. //
  204. // Reset the array, or remove it completely if we have no more listeners.
  205. //
  206. if (events.length) {
  207. this._events[evt] = events.length === 1 ? events[0] : events;
  208. } else {
  209. delete this._events[evt];
  210. }
  211. return this;
  212. };
  213. /**
  214. * Remove all listeners or only the listeners for the specified event.
  215. *
  216. * @param {String} event The event want to remove all listeners for.
  217. * @api public
  218. */
  219. EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
  220. if (!this._events) return this;
  221. if (event) delete this._events[prefix ? prefix + event : event];
  222. else this._events = prefix ? {} : Object.create(null);
  223. return this;
  224. };
  225. //
  226. // Alias methods names because people roll like that.
  227. //
  228. EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
  229. EventEmitter.prototype.addListener = EventEmitter.prototype.on;
  230. //
  231. // This function doesn't apply anymore.
  232. //
  233. EventEmitter.prototype.setMaxListeners = function setMaxListeners() {
  234. return this;
  235. };
  236. //
  237. // Expose the prefix.
  238. //
  239. EventEmitter.prefixed = prefix;
  240. //
  241. // Expose the module.
  242. //
  243. if ('undefined' !== typeof module) {
  244. module.exports = EventEmitter;
  245. }