UNPKG

13.1 kBJavaScriptView Raw
1'use strict';
2
3var domain;
4
5// This constructor is used to store event handlers. Instantiating this is
6// faster than explicitly calling `Object.create(null)` to get a "clean" empty
7// object (tested with v8 v4.9).
8function EventHandlers() {}
9EventHandlers.prototype = Object.create(null);
10
11function EventEmitter() {
12 EventEmitter.init.call(this);
13}
14export default EventEmitter;
15export {EventEmitter};
16
17// nodejs oddity
18// require('events') === require('events').EventEmitter
19EventEmitter.EventEmitter = EventEmitter
20
21EventEmitter.usingDomains = false;
22
23EventEmitter.prototype.domain = undefined;
24EventEmitter.prototype._events = undefined;
25EventEmitter.prototype._maxListeners = undefined;
26
27// By default EventEmitters will print a warning if more than 10 listeners are
28// added to it. This is a useful default which helps finding memory leaks.
29EventEmitter.defaultMaxListeners = 10;
30
31EventEmitter.init = function() {
32 this.domain = null;
33 if (EventEmitter.usingDomains) {
34 // if there is an active domain, then attach to it.
35 if (domain.active && !(this instanceof domain.Domain)) {
36 this.domain = domain.active;
37 }
38 }
39
40 if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
41 this._events = new EventHandlers();
42 this._eventsCount = 0;
43 }
44
45 this._maxListeners = this._maxListeners || undefined;
46};
47
48// Obviously not all Emitters should be limited to 10. This function allows
49// that to be increased. Set to zero for unlimited.
50EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
51 if (typeof n !== 'number' || n < 0 || isNaN(n))
52 throw new TypeError('"n" argument must be a positive number');
53 this._maxListeners = n;
54 return this;
55};
56
57function $getMaxListeners(that) {
58 if (that._maxListeners === undefined)
59 return EventEmitter.defaultMaxListeners;
60 return that._maxListeners;
61}
62
63EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
64 return $getMaxListeners(this);
65};
66
67// These standalone emit* functions are used to optimize calling of event
68// handlers for fast cases because emit() itself often has a variable number of
69// arguments and can be deoptimized because of that. These functions always have
70// the same number of arguments and thus do not get deoptimized, so the code
71// inside them can execute faster.
72function emitNone(handler, isFn, self) {
73 if (isFn)
74 handler.call(self);
75 else {
76 var len = handler.length;
77 var listeners = arrayClone(handler, len);
78 for (var i = 0; i < len; ++i)
79 listeners[i].call(self);
80 }
81}
82function emitOne(handler, isFn, self, arg1) {
83 if (isFn)
84 handler.call(self, arg1);
85 else {
86 var len = handler.length;
87 var listeners = arrayClone(handler, len);
88 for (var i = 0; i < len; ++i)
89 listeners[i].call(self, arg1);
90 }
91}
92function emitTwo(handler, isFn, self, arg1, arg2) {
93 if (isFn)
94 handler.call(self, arg1, arg2);
95 else {
96 var len = handler.length;
97 var listeners = arrayClone(handler, len);
98 for (var i = 0; i < len; ++i)
99 listeners[i].call(self, arg1, arg2);
100 }
101}
102function emitThree(handler, isFn, self, arg1, arg2, arg3) {
103 if (isFn)
104 handler.call(self, arg1, arg2, arg3);
105 else {
106 var len = handler.length;
107 var listeners = arrayClone(handler, len);
108 for (var i = 0; i < len; ++i)
109 listeners[i].call(self, arg1, arg2, arg3);
110 }
111}
112
113function emitMany(handler, isFn, self, args) {
114 if (isFn)
115 handler.apply(self, args);
116 else {
117 var len = handler.length;
118 var listeners = arrayClone(handler, len);
119 for (var i = 0; i < len; ++i)
120 listeners[i].apply(self, args);
121 }
122}
123
124EventEmitter.prototype.emit = function emit(type) {
125 var er, handler, len, args, i, events, domain;
126 var needDomainExit = false;
127 var doError = (type === 'error');
128
129 events = this._events;
130 if (events)
131 doError = (doError && events.error == null);
132 else if (!doError)
133 return false;
134
135 domain = this.domain;
136
137 // If there is no 'error' event listener then throw.
138 if (doError) {
139 er = arguments[1];
140 if (domain) {
141 if (!er)
142 er = new Error('Uncaught, unspecified "error" event');
143 er.domainEmitter = this;
144 er.domain = domain;
145 er.domainThrown = false;
146 domain.emit('error', er);
147 } else if (er instanceof Error) {
148 throw er; // Unhandled 'error' event
149 } else {
150 // At least give some kind of context to the user
151 var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
152 err.context = er;
153 throw err;
154 }
155 return false;
156 }
157
158 handler = events[type];
159
160 if (!handler)
161 return false;
162
163 var isFn = typeof handler === 'function';
164 len = arguments.length;
165 switch (len) {
166 // fast cases
167 case 1:
168 emitNone(handler, isFn, this);
169 break;
170 case 2:
171 emitOne(handler, isFn, this, arguments[1]);
172 break;
173 case 3:
174 emitTwo(handler, isFn, this, arguments[1], arguments[2]);
175 break;
176 case 4:
177 emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
178 break;
179 // slower
180 default:
181 args = new Array(len - 1);
182 for (i = 1; i < len; i++)
183 args[i - 1] = arguments[i];
184 emitMany(handler, isFn, this, args);
185 }
186
187 if (needDomainExit)
188 domain.exit();
189
190 return true;
191};
192
193function _addListener(target, type, listener, prepend) {
194 var m;
195 var events;
196 var existing;
197
198 if (typeof listener !== 'function')
199 throw new TypeError('"listener" argument must be a function');
200
201 events = target._events;
202 if (!events) {
203 events = target._events = new EventHandlers();
204 target._eventsCount = 0;
205 } else {
206 // To avoid recursion in the case that type === "newListener"! Before
207 // adding it to the listeners, first emit "newListener".
208 if (events.newListener) {
209 target.emit('newListener', type,
210 listener.listener ? listener.listener : listener);
211
212 // Re-assign `events` because a newListener handler could have caused the
213 // this._events to be assigned to a new object
214 events = target._events;
215 }
216 existing = events[type];
217 }
218
219 if (!existing) {
220 // Optimize the case of one listener. Don't need the extra array object.
221 existing = events[type] = listener;
222 ++target._eventsCount;
223 } else {
224 if (typeof existing === 'function') {
225 // Adding the second element, need to change to array.
226 existing = events[type] = prepend ? [listener, existing] :
227 [existing, listener];
228 } else {
229 // If we've already got an array, just append.
230 if (prepend) {
231 existing.unshift(listener);
232 } else {
233 existing.push(listener);
234 }
235 }
236
237 // Check for listener leak
238 if (!existing.warned) {
239 m = $getMaxListeners(target);
240 if (m && m > 0 && existing.length > m) {
241 existing.warned = true;
242 var w = new Error('Possible EventEmitter memory leak detected. ' +
243 existing.length + ' ' + type + ' listeners added. ' +
244 'Use emitter.setMaxListeners() to increase limit');
245 w.name = 'MaxListenersExceededWarning';
246 w.emitter = target;
247 w.type = type;
248 w.count = existing.length;
249 emitWarning(w);
250 }
251 }
252 }
253
254 return target;
255}
256function emitWarning(e) {
257 typeof console.warn === 'function' ? console.warn(e) : console.log(e);
258}
259EventEmitter.prototype.addListener = function addListener(type, listener) {
260 return _addListener(this, type, listener, false);
261};
262
263EventEmitter.prototype.on = EventEmitter.prototype.addListener;
264
265EventEmitter.prototype.prependListener =
266 function prependListener(type, listener) {
267 return _addListener(this, type, listener, true);
268 };
269
270function _onceWrap(target, type, listener) {
271 var fired = false;
272 function g() {
273 target.removeListener(type, g);
274 if (!fired) {
275 fired = true;
276 listener.apply(target, arguments);
277 }
278 }
279 g.listener = listener;
280 return g;
281}
282
283EventEmitter.prototype.once = function once(type, listener) {
284 if (typeof listener !== 'function')
285 throw new TypeError('"listener" argument must be a function');
286 this.on(type, _onceWrap(this, type, listener));
287 return this;
288};
289
290EventEmitter.prototype.prependOnceListener =
291 function prependOnceListener(type, listener) {
292 if (typeof listener !== 'function')
293 throw new TypeError('"listener" argument must be a function');
294 this.prependListener(type, _onceWrap(this, type, listener));
295 return this;
296 };
297
298// emits a 'removeListener' event iff the listener was removed
299EventEmitter.prototype.removeListener =
300 function removeListener(type, listener) {
301 var list, events, position, i, originalListener;
302
303 if (typeof listener !== 'function')
304 throw new TypeError('"listener" argument must be a function');
305
306 events = this._events;
307 if (!events)
308 return this;
309
310 list = events[type];
311 if (!list)
312 return this;
313
314 if (list === listener || (list.listener && list.listener === listener)) {
315 if (--this._eventsCount === 0)
316 this._events = new EventHandlers();
317 else {
318 delete events[type];
319 if (events.removeListener)
320 this.emit('removeListener', type, list.listener || listener);
321 }
322 } else if (typeof list !== 'function') {
323 position = -1;
324
325 for (i = list.length; i-- > 0;) {
326 if (list[i] === listener ||
327 (list[i].listener && list[i].listener === listener)) {
328 originalListener = list[i].listener;
329 position = i;
330 break;
331 }
332 }
333
334 if (position < 0)
335 return this;
336
337 if (list.length === 1) {
338 list[0] = undefined;
339 if (--this._eventsCount === 0) {
340 this._events = new EventHandlers();
341 return this;
342 } else {
343 delete events[type];
344 }
345 } else {
346 spliceOne(list, position);
347 }
348
349 if (events.removeListener)
350 this.emit('removeListener', type, originalListener || listener);
351 }
352
353 return this;
354 };
355
356EventEmitter.prototype.removeAllListeners =
357 function removeAllListeners(type) {
358 var listeners, events;
359
360 events = this._events;
361 if (!events)
362 return this;
363
364 // not listening for removeListener, no need to emit
365 if (!events.removeListener) {
366 if (arguments.length === 0) {
367 this._events = new EventHandlers();
368 this._eventsCount = 0;
369 } else if (events[type]) {
370 if (--this._eventsCount === 0)
371 this._events = new EventHandlers();
372 else
373 delete events[type];
374 }
375 return this;
376 }
377
378 // emit removeListener for all listeners on all events
379 if (arguments.length === 0) {
380 var keys = Object.keys(events);
381 for (var i = 0, key; i < keys.length; ++i) {
382 key = keys[i];
383 if (key === 'removeListener') continue;
384 this.removeAllListeners(key);
385 }
386 this.removeAllListeners('removeListener');
387 this._events = new EventHandlers();
388 this._eventsCount = 0;
389 return this;
390 }
391
392 listeners = events[type];
393
394 if (typeof listeners === 'function') {
395 this.removeListener(type, listeners);
396 } else if (listeners) {
397 // LIFO order
398 do {
399 this.removeListener(type, listeners[listeners.length - 1]);
400 } while (listeners[0]);
401 }
402
403 return this;
404 };
405
406EventEmitter.prototype.listeners = function listeners(type) {
407 var evlistener;
408 var ret;
409 var events = this._events;
410
411 if (!events)
412 ret = [];
413 else {
414 evlistener = events[type];
415 if (!evlistener)
416 ret = [];
417 else if (typeof evlistener === 'function')
418 ret = [evlistener.listener || evlistener];
419 else
420 ret = unwrapListeners(evlistener);
421 }
422
423 return ret;
424};
425
426EventEmitter.listenerCount = function(emitter, type) {
427 if (typeof emitter.listenerCount === 'function') {
428 return emitter.listenerCount(type);
429 } else {
430 return listenerCount.call(emitter, type);
431 }
432};
433
434EventEmitter.prototype.listenerCount = listenerCount;
435function listenerCount(type) {
436 var events = this._events;
437
438 if (events) {
439 var evlistener = events[type];
440
441 if (typeof evlistener === 'function') {
442 return 1;
443 } else if (evlistener) {
444 return evlistener.length;
445 }
446 }
447
448 return 0;
449}
450
451EventEmitter.prototype.eventNames = function eventNames() {
452 return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
453};
454
455// About 1.5x faster than the two-arg version of Array#splice().
456function spliceOne(list, index) {
457 for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
458 list[i] = list[k];
459 list.pop();
460}
461
462function arrayClone(arr, i) {
463 var copy = new Array(i);
464 while (i--)
465 copy[i] = arr[i];
466 return copy;
467}
468
469function unwrapListeners(arr) {
470 var ret = new Array(arr.length);
471 for (var i = 0; i < ret.length; ++i) {
472 ret[i] = arr[i].listener || arr[i];
473 }
474 return ret;
475}