UNPKG

14.2 kBJavaScriptView Raw
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24var R = typeof Reflect === 'object' ? Reflect : null
25var ReflectApply = R && typeof R.apply === 'function'
26 ? R.apply
27 : function ReflectApply(target, receiver, args) {
28 return Function.prototype.apply.call(target, receiver, args);
29 }
30
31var ReflectOwnKeys
32if (R && typeof R.ownKeys === 'function') {
33 ReflectOwnKeys = R.ownKeys
34} else if (Object.getOwnPropertySymbols) {
35 ReflectOwnKeys = function ReflectOwnKeys(target) {
36 return Object.getOwnPropertyNames(target)
37 .concat(Object.getOwnPropertySymbols(target));
38 };
39} else {
40 ReflectOwnKeys = function ReflectOwnKeys(target) {
41 return Object.getOwnPropertyNames(target);
42 };
43}
44
45function ProcessEmitWarning(warning) {
46 if (console && console.warn) console.warn(warning);
47}
48
49var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
50 return value !== value;
51}
52
53function EventEmitter() {
54 EventEmitter.init.call(this);
55}
56module.exports = EventEmitter;
57module.exports.once = once;
58
59// Backwards-compat with node 0.10.x
60EventEmitter.EventEmitter = EventEmitter;
61
62EventEmitter.prototype._events = undefined;
63EventEmitter.prototype._eventsCount = 0;
64EventEmitter.prototype._maxListeners = undefined;
65
66// By default EventEmitters will print a warning if more than 10 listeners are
67// added to it. This is a useful default which helps finding memory leaks.
68var defaultMaxListeners = 10;
69
70function checkListener(listener) {
71 if (typeof listener !== 'function') {
72 throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
73 }
74}
75
76Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
77 enumerable: true,
78 get: function() {
79 return defaultMaxListeners;
80 },
81 set: function(arg) {
82 if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
83 throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
84 }
85 defaultMaxListeners = arg;
86 }
87});
88
89EventEmitter.init = function() {
90
91 if (this._events === undefined ||
92 this._events === Object.getPrototypeOf(this)._events) {
93 this._events = Object.create(null);
94 this._eventsCount = 0;
95 }
96
97 this._maxListeners = this._maxListeners || undefined;
98};
99
100// Obviously not all Emitters should be limited to 10. This function allows
101// that to be increased. Set to zero for unlimited.
102EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
103 if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
104 throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
105 }
106 this._maxListeners = n;
107 return this;
108};
109
110function _getMaxListeners(that) {
111 if (that._maxListeners === undefined)
112 return EventEmitter.defaultMaxListeners;
113 return that._maxListeners;
114}
115
116EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
117 return _getMaxListeners(this);
118};
119
120EventEmitter.prototype.emit = function emit(type) {
121 var args = [];
122 for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
123 var doError = (type === 'error');
124
125 var events = this._events;
126 if (events !== undefined)
127 doError = (doError && events.error === undefined);
128 else if (!doError)
129 return false;
130
131 // If there is no 'error' event listener then throw.
132 if (doError) {
133 var er;
134 if (args.length > 0)
135 er = args[0];
136 if (er instanceof Error) {
137 // Note: The comments on the `throw` lines are intentional, they show
138 // up in Node's output if this results in an unhandled exception.
139 throw er; // Unhandled 'error' event
140 }
141 // At least give some kind of context to the user
142 var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
143 err.context = er;
144 throw err; // Unhandled 'error' event
145 }
146
147 var handler = events[type];
148
149 if (handler === undefined)
150 return false;
151
152 if (typeof handler === 'function') {
153 ReflectApply(handler, this, args);
154 } else {
155 var len = handler.length;
156 var listeners = arrayClone(handler, len);
157 for (var i = 0; i < len; ++i)
158 ReflectApply(listeners[i], this, args);
159 }
160
161 return true;
162};
163
164function _addListener(target, type, listener, prepend) {
165 var m;
166 var events;
167 var existing;
168
169 checkListener(listener);
170
171 events = target._events;
172 if (events === undefined) {
173 events = target._events = Object.create(null);
174 target._eventsCount = 0;
175 } else {
176 // To avoid recursion in the case that type === "newListener"! Before
177 // adding it to the listeners, first emit "newListener".
178 if (events.newListener !== undefined) {
179 target.emit('newListener', type,
180 listener.listener ? listener.listener : listener);
181
182 // Re-assign `events` because a newListener handler could have caused the
183 // this._events to be assigned to a new object
184 events = target._events;
185 }
186 existing = events[type];
187 }
188
189 if (existing === undefined) {
190 // Optimize the case of one listener. Don't need the extra array object.
191 existing = events[type] = listener;
192 ++target._eventsCount;
193 } else {
194 if (typeof existing === 'function') {
195 // Adding the second element, need to change to array.
196 existing = events[type] =
197 prepend ? [listener, existing] : [existing, listener];
198 // If we've already got an array, just append.
199 } else if (prepend) {
200 existing.unshift(listener);
201 } else {
202 existing.push(listener);
203 }
204
205 // Check for listener leak
206 m = _getMaxListeners(target);
207 if (m > 0 && existing.length > m && !existing.warned) {
208 existing.warned = true;
209 // No error code for this since it is a Warning
210 // eslint-disable-next-line no-restricted-syntax
211 var w = new Error('Possible EventEmitter memory leak detected. ' +
212 existing.length + ' ' + String(type) + ' listeners ' +
213 'added. Use emitter.setMaxListeners() to ' +
214 'increase limit');
215 w.name = 'MaxListenersExceededWarning';
216 w.emitter = target;
217 w.type = type;
218 w.count = existing.length;
219 ProcessEmitWarning(w);
220 }
221 }
222
223 return target;
224}
225
226EventEmitter.prototype.addListener = function addListener(type, listener) {
227 return _addListener(this, type, listener, false);
228};
229
230EventEmitter.prototype.on = EventEmitter.prototype.addListener;
231
232EventEmitter.prototype.prependListener =
233 function prependListener(type, listener) {
234 return _addListener(this, type, listener, true);
235 };
236
237function onceWrapper() {
238 if (!this.fired) {
239 this.target.removeListener(this.type, this.wrapFn);
240 this.fired = true;
241 if (arguments.length === 0)
242 return this.listener.call(this.target);
243 return this.listener.apply(this.target, arguments);
244 }
245}
246
247function _onceWrap(target, type, listener) {
248 var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
249 var wrapped = onceWrapper.bind(state);
250 wrapped.listener = listener;
251 state.wrapFn = wrapped;
252 return wrapped;
253}
254
255EventEmitter.prototype.once = function once(type, listener) {
256 checkListener(listener);
257 this.on(type, _onceWrap(this, type, listener));
258 return this;
259};
260
261EventEmitter.prototype.prependOnceListener =
262 function prependOnceListener(type, listener) {
263 checkListener(listener);
264 this.prependListener(type, _onceWrap(this, type, listener));
265 return this;
266 };
267
268// Emits a 'removeListener' event if and only if the listener was removed.
269EventEmitter.prototype.removeListener =
270 function removeListener(type, listener) {
271 var list, events, position, i, originalListener;
272
273 checkListener(listener);
274
275 events = this._events;
276 if (events === undefined)
277 return this;
278
279 list = events[type];
280 if (list === undefined)
281 return this;
282
283 if (list === listener || list.listener === listener) {
284 if (--this._eventsCount === 0)
285 this._events = Object.create(null);
286 else {
287 delete events[type];
288 if (events.removeListener)
289 this.emit('removeListener', type, list.listener || listener);
290 }
291 } else if (typeof list !== 'function') {
292 position = -1;
293
294 for (i = list.length - 1; i >= 0; i--) {
295 if (list[i] === listener || list[i].listener === listener) {
296 originalListener = list[i].listener;
297 position = i;
298 break;
299 }
300 }
301
302 if (position < 0)
303 return this;
304
305 if (position === 0)
306 list.shift();
307 else {
308 spliceOne(list, position);
309 }
310
311 if (list.length === 1)
312 events[type] = list[0];
313
314 if (events.removeListener !== undefined)
315 this.emit('removeListener', type, originalListener || listener);
316 }
317
318 return this;
319 };
320
321EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
322
323EventEmitter.prototype.removeAllListeners =
324 function removeAllListeners(type) {
325 var listeners, events, i;
326
327 events = this._events;
328 if (events === undefined)
329 return this;
330
331 // not listening for removeListener, no need to emit
332 if (events.removeListener === undefined) {
333 if (arguments.length === 0) {
334 this._events = Object.create(null);
335 this._eventsCount = 0;
336 } else if (events[type] !== undefined) {
337 if (--this._eventsCount === 0)
338 this._events = Object.create(null);
339 else
340 delete events[type];
341 }
342 return this;
343 }
344
345 // emit removeListener for all listeners on all events
346 if (arguments.length === 0) {
347 var keys = Object.keys(events);
348 var key;
349 for (i = 0; i < keys.length; ++i) {
350 key = keys[i];
351 if (key === 'removeListener') continue;
352 this.removeAllListeners(key);
353 }
354 this.removeAllListeners('removeListener');
355 this._events = Object.create(null);
356 this._eventsCount = 0;
357 return this;
358 }
359
360 listeners = events[type];
361
362 if (typeof listeners === 'function') {
363 this.removeListener(type, listeners);
364 } else if (listeners !== undefined) {
365 // LIFO order
366 for (i = listeners.length - 1; i >= 0; i--) {
367 this.removeListener(type, listeners[i]);
368 }
369 }
370
371 return this;
372 };
373
374function _listeners(target, type, unwrap) {
375 var events = target._events;
376
377 if (events === undefined)
378 return [];
379
380 var evlistener = events[type];
381 if (evlistener === undefined)
382 return [];
383
384 if (typeof evlistener === 'function')
385 return unwrap ? [evlistener.listener || evlistener] : [evlistener];
386
387 return unwrap ?
388 unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
389}
390
391EventEmitter.prototype.listeners = function listeners(type) {
392 return _listeners(this, type, true);
393};
394
395EventEmitter.prototype.rawListeners = function rawListeners(type) {
396 return _listeners(this, type, false);
397};
398
399EventEmitter.listenerCount = function(emitter, type) {
400 if (typeof emitter.listenerCount === 'function') {
401 return emitter.listenerCount(type);
402 } else {
403 return listenerCount.call(emitter, type);
404 }
405};
406
407EventEmitter.prototype.listenerCount = listenerCount;
408function listenerCount(type) {
409 var events = this._events;
410
411 if (events !== undefined) {
412 var evlistener = events[type];
413
414 if (typeof evlistener === 'function') {
415 return 1;
416 } else if (evlistener !== undefined) {
417 return evlistener.length;
418 }
419 }
420
421 return 0;
422}
423
424EventEmitter.prototype.eventNames = function eventNames() {
425 return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
426};
427
428function arrayClone(arr, n) {
429 var copy = new Array(n);
430 for (var i = 0; i < n; ++i)
431 copy[i] = arr[i];
432 return copy;
433}
434
435function spliceOne(list, index) {
436 for (; index + 1 < list.length; index++)
437 list[index] = list[index + 1];
438 list.pop();
439}
440
441function unwrapListeners(arr) {
442 var ret = new Array(arr.length);
443 for (var i = 0; i < ret.length; ++i) {
444 ret[i] = arr[i].listener || arr[i];
445 }
446 return ret;
447}
448
449function once(emitter, name) {
450 return new Promise(function (resolve, reject) {
451 function eventListener() {
452 if (errorListener !== undefined) {
453 emitter.removeListener('error', errorListener);
454 }
455 resolve([].slice.call(arguments));
456 };
457 var errorListener;
458
459 // Adding an error listener is not optional because
460 // if an error is thrown on an event emitter we cannot
461 // guarantee that the actual event we are waiting will
462 // be fired. The result could be a silent way to create
463 // memory or file descriptor leaks, which is something
464 // we should avoid.
465 if (name !== 'error') {
466 errorListener = function errorListener(err) {
467 emitter.removeListener(name, eventListener);
468 reject(err);
469 };
470
471 emitter.once('error', errorListener);
472 }
473
474 emitter.once(name, eventListener);
475 });
476}
\No newline at end of file