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.
497 lines
14 KiB
497 lines
14 KiB
// Copyright Joyent, Inc. and other Node contributors. |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a |
|
// copy of this software and associated documentation files (the |
|
// "Software"), to deal in the Software without restriction, including |
|
// without limitation the rights to use, copy, modify, merge, publish, |
|
// distribute, sublicense, and/or sell copies of the Software, and to permit |
|
// persons to whom the Software is furnished to do so, subject to the |
|
// following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included |
|
// in all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
|
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
|
'use strict'; |
|
|
|
var R = typeof Reflect === 'object' ? Reflect : null |
|
var ReflectApply = R && typeof R.apply === 'function' |
|
? R.apply |
|
: function ReflectApply(target, receiver, args) { |
|
return Function.prototype.apply.call(target, receiver, args); |
|
} |
|
|
|
var ReflectOwnKeys |
|
if (R && typeof R.ownKeys === 'function') { |
|
ReflectOwnKeys = R.ownKeys |
|
} else if (Object.getOwnPropertySymbols) { |
|
ReflectOwnKeys = function ReflectOwnKeys(target) { |
|
return Object.getOwnPropertyNames(target) |
|
.concat(Object.getOwnPropertySymbols(target)); |
|
}; |
|
} else { |
|
ReflectOwnKeys = function ReflectOwnKeys(target) { |
|
return Object.getOwnPropertyNames(target); |
|
}; |
|
} |
|
|
|
function ProcessEmitWarning(warning) { |
|
if (console && console.warn) console.warn(warning); |
|
} |
|
|
|
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { |
|
return value !== value; |
|
} |
|
|
|
function EventEmitter() { |
|
EventEmitter.init.call(this); |
|
} |
|
module.exports = EventEmitter; |
|
module.exports.once = once; |
|
|
|
// Backwards-compat with node 0.10.x |
|
EventEmitter.EventEmitter = EventEmitter; |
|
|
|
EventEmitter.prototype._events = undefined; |
|
EventEmitter.prototype._eventsCount = 0; |
|
EventEmitter.prototype._maxListeners = undefined; |
|
|
|
// By default EventEmitters will print a warning if more than 10 listeners are |
|
// added to it. This is a useful default which helps finding memory leaks. |
|
var defaultMaxListeners = 10; |
|
|
|
function checkListener(listener) { |
|
if (typeof listener !== 'function') { |
|
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); |
|
} |
|
} |
|
|
|
Object.defineProperty(EventEmitter, 'defaultMaxListeners', { |
|
enumerable: true, |
|
get: function() { |
|
return defaultMaxListeners; |
|
}, |
|
set: function(arg) { |
|
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { |
|
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); |
|
} |
|
defaultMaxListeners = arg; |
|
} |
|
}); |
|
|
|
EventEmitter.init = function() { |
|
|
|
if (this._events === undefined || |
|
this._events === Object.getPrototypeOf(this)._events) { |
|
this._events = Object.create(null); |
|
this._eventsCount = 0; |
|
} |
|
|
|
this._maxListeners = this._maxListeners || undefined; |
|
}; |
|
|
|
// Obviously not all Emitters should be limited to 10. This function allows |
|
// that to be increased. Set to zero for unlimited. |
|
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { |
|
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { |
|
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); |
|
} |
|
this._maxListeners = n; |
|
return this; |
|
}; |
|
|
|
function _getMaxListeners(that) { |
|
if (that._maxListeners === undefined) |
|
return EventEmitter.defaultMaxListeners; |
|
return that._maxListeners; |
|
} |
|
|
|
EventEmitter.prototype.getMaxListeners = function getMaxListeners() { |
|
return _getMaxListeners(this); |
|
}; |
|
|
|
EventEmitter.prototype.emit = function emit(type) { |
|
var args = []; |
|
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); |
|
var doError = (type === 'error'); |
|
|
|
var events = this._events; |
|
if (events !== undefined) |
|
doError = (doError && events.error === undefined); |
|
else if (!doError) |
|
return false; |
|
|
|
// If there is no 'error' event listener then throw. |
|
if (doError) { |
|
var er; |
|
if (args.length > 0) |
|
er = args[0]; |
|
if (er instanceof Error) { |
|
// Note: The comments on the `throw` lines are intentional, they show |
|
// up in Node's output if this results in an unhandled exception. |
|
throw er; // Unhandled 'error' event |
|
} |
|
// At least give some kind of context to the user |
|
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); |
|
err.context = er; |
|
throw err; // Unhandled 'error' event |
|
} |
|
|
|
var handler = events[type]; |
|
|
|
if (handler === undefined) |
|
return false; |
|
|
|
if (typeof handler === 'function') { |
|
ReflectApply(handler, this, args); |
|
} else { |
|
var len = handler.length; |
|
var listeners = arrayClone(handler, len); |
|
for (var i = 0; i < len; ++i) |
|
ReflectApply(listeners[i], this, args); |
|
} |
|
|
|
return true; |
|
}; |
|
|
|
function _addListener(target, type, listener, prepend) { |
|
var m; |
|
var events; |
|
var existing; |
|
|
|
checkListener(listener); |
|
|
|
events = target._events; |
|
if (events === undefined) { |
|
events = target._events = Object.create(null); |
|
target._eventsCount = 0; |
|
} else { |
|
// To avoid recursion in the case that type === "newListener"! Before |
|
// adding it to the listeners, first emit "newListener". |
|
if (events.newListener !== undefined) { |
|
target.emit('newListener', type, |
|
listener.listener ? listener.listener : listener); |
|
|
|
// Re-assign `events` because a newListener handler could have caused the |
|
// this._events to be assigned to a new object |
|
events = target._events; |
|
} |
|
existing = events[type]; |
|
} |
|
|
|
if (existing === undefined) { |
|
// Optimize the case of one listener. Don't need the extra array object. |
|
existing = events[type] = listener; |
|
++target._eventsCount; |
|
} else { |
|
if (typeof existing === 'function') { |
|
// Adding the second element, need to change to array. |
|
existing = events[type] = |
|
prepend ? [listener, existing] : [existing, listener]; |
|
// If we've already got an array, just append. |
|
} else if (prepend) { |
|
existing.unshift(listener); |
|
} else { |
|
existing.push(listener); |
|
} |
|
|
|
// Check for listener leak |
|
m = _getMaxListeners(target); |
|
if (m > 0 && existing.length > m && !existing.warned) { |
|
existing.warned = true; |
|
// No error code for this since it is a Warning |
|
// eslint-disable-next-line no-restricted-syntax |
|
var w = new Error('Possible EventEmitter memory leak detected. ' + |
|
existing.length + ' ' + String(type) + ' listeners ' + |
|
'added. Use emitter.setMaxListeners() to ' + |
|
'increase limit'); |
|
w.name = 'MaxListenersExceededWarning'; |
|
w.emitter = target; |
|
w.type = type; |
|
w.count = existing.length; |
|
ProcessEmitWarning(w); |
|
} |
|
} |
|
|
|
return target; |
|
} |
|
|
|
EventEmitter.prototype.addListener = function addListener(type, listener) { |
|
return _addListener(this, type, listener, false); |
|
}; |
|
|
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener; |
|
|
|
EventEmitter.prototype.prependListener = |
|
function prependListener(type, listener) { |
|
return _addListener(this, type, listener, true); |
|
}; |
|
|
|
function onceWrapper() { |
|
if (!this.fired) { |
|
this.target.removeListener(this.type, this.wrapFn); |
|
this.fired = true; |
|
if (arguments.length === 0) |
|
return this.listener.call(this.target); |
|
return this.listener.apply(this.target, arguments); |
|
} |
|
} |
|
|
|
function _onceWrap(target, type, listener) { |
|
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; |
|
var wrapped = onceWrapper.bind(state); |
|
wrapped.listener = listener; |
|
state.wrapFn = wrapped; |
|
return wrapped; |
|
} |
|
|
|
EventEmitter.prototype.once = function once(type, listener) { |
|
checkListener(listener); |
|
this.on(type, _onceWrap(this, type, listener)); |
|
return this; |
|
}; |
|
|
|
EventEmitter.prototype.prependOnceListener = |
|
function prependOnceListener(type, listener) { |
|
checkListener(listener); |
|
this.prependListener(type, _onceWrap(this, type, listener)); |
|
return this; |
|
}; |
|
|
|
// Emits a 'removeListener' event if and only if the listener was removed. |
|
EventEmitter.prototype.removeListener = |
|
function removeListener(type, listener) { |
|
var list, events, position, i, originalListener; |
|
|
|
checkListener(listener); |
|
|
|
events = this._events; |
|
if (events === undefined) |
|
return this; |
|
|
|
list = events[type]; |
|
if (list === undefined) |
|
return this; |
|
|
|
if (list === listener || list.listener === listener) { |
|
if (--this._eventsCount === 0) |
|
this._events = Object.create(null); |
|
else { |
|
delete events[type]; |
|
if (events.removeListener) |
|
this.emit('removeListener', type, list.listener || listener); |
|
} |
|
} else if (typeof list !== 'function') { |
|
position = -1; |
|
|
|
for (i = list.length - 1; i >= 0; i--) { |
|
if (list[i] === listener || list[i].listener === listener) { |
|
originalListener = list[i].listener; |
|
position = i; |
|
break; |
|
} |
|
} |
|
|
|
if (position < 0) |
|
return this; |
|
|
|
if (position === 0) |
|
list.shift(); |
|
else { |
|
spliceOne(list, position); |
|
} |
|
|
|
if (list.length === 1) |
|
events[type] = list[0]; |
|
|
|
if (events.removeListener !== undefined) |
|
this.emit('removeListener', type, originalListener || listener); |
|
} |
|
|
|
return this; |
|
}; |
|
|
|
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; |
|
|
|
EventEmitter.prototype.removeAllListeners = |
|
function removeAllListeners(type) { |
|
var listeners, events, i; |
|
|
|
events = this._events; |
|
if (events === undefined) |
|
return this; |
|
|
|
// not listening for removeListener, no need to emit |
|
if (events.removeListener === undefined) { |
|
if (arguments.length === 0) { |
|
this._events = Object.create(null); |
|
this._eventsCount = 0; |
|
} else if (events[type] !== undefined) { |
|
if (--this._eventsCount === 0) |
|
this._events = Object.create(null); |
|
else |
|
delete events[type]; |
|
} |
|
return this; |
|
} |
|
|
|
// emit removeListener for all listeners on all events |
|
if (arguments.length === 0) { |
|
var keys = Object.keys(events); |
|
var key; |
|
for (i = 0; i < keys.length; ++i) { |
|
key = keys[i]; |
|
if (key === 'removeListener') continue; |
|
this.removeAllListeners(key); |
|
} |
|
this.removeAllListeners('removeListener'); |
|
this._events = Object.create(null); |
|
this._eventsCount = 0; |
|
return this; |
|
} |
|
|
|
listeners = events[type]; |
|
|
|
if (typeof listeners === 'function') { |
|
this.removeListener(type, listeners); |
|
} else if (listeners !== undefined) { |
|
// LIFO order |
|
for (i = listeners.length - 1; i >= 0; i--) { |
|
this.removeListener(type, listeners[i]); |
|
} |
|
} |
|
|
|
return this; |
|
}; |
|
|
|
function _listeners(target, type, unwrap) { |
|
var events = target._events; |
|
|
|
if (events === undefined) |
|
return []; |
|
|
|
var evlistener = events[type]; |
|
if (evlistener === undefined) |
|
return []; |
|
|
|
if (typeof evlistener === 'function') |
|
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; |
|
|
|
return unwrap ? |
|
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); |
|
} |
|
|
|
EventEmitter.prototype.listeners = function listeners(type) { |
|
return _listeners(this, type, true); |
|
}; |
|
|
|
EventEmitter.prototype.rawListeners = function rawListeners(type) { |
|
return _listeners(this, type, false); |
|
}; |
|
|
|
EventEmitter.listenerCount = function(emitter, type) { |
|
if (typeof emitter.listenerCount === 'function') { |
|
return emitter.listenerCount(type); |
|
} else { |
|
return listenerCount.call(emitter, type); |
|
} |
|
}; |
|
|
|
EventEmitter.prototype.listenerCount = listenerCount; |
|
function listenerCount(type) { |
|
var events = this._events; |
|
|
|
if (events !== undefined) { |
|
var evlistener = events[type]; |
|
|
|
if (typeof evlistener === 'function') { |
|
return 1; |
|
} else if (evlistener !== undefined) { |
|
return evlistener.length; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
EventEmitter.prototype.eventNames = function eventNames() { |
|
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; |
|
}; |
|
|
|
function arrayClone(arr, n) { |
|
var copy = new Array(n); |
|
for (var i = 0; i < n; ++i) |
|
copy[i] = arr[i]; |
|
return copy; |
|
} |
|
|
|
function spliceOne(list, index) { |
|
for (; index + 1 < list.length; index++) |
|
list[index] = list[index + 1]; |
|
list.pop(); |
|
} |
|
|
|
function unwrapListeners(arr) { |
|
var ret = new Array(arr.length); |
|
for (var i = 0; i < ret.length; ++i) { |
|
ret[i] = arr[i].listener || arr[i]; |
|
} |
|
return ret; |
|
} |
|
|
|
function once(emitter, name) { |
|
return new Promise(function (resolve, reject) { |
|
function errorListener(err) { |
|
emitter.removeListener(name, resolver); |
|
reject(err); |
|
} |
|
|
|
function resolver() { |
|
if (typeof emitter.removeListener === 'function') { |
|
emitter.removeListener('error', errorListener); |
|
} |
|
resolve([].slice.call(arguments)); |
|
}; |
|
|
|
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); |
|
if (name !== 'error') { |
|
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); |
|
} |
|
}); |
|
} |
|
|
|
function addErrorHandlerIfEventEmitter(emitter, handler, flags) { |
|
if (typeof emitter.on === 'function') { |
|
eventTargetAgnosticAddListener(emitter, 'error', handler, flags); |
|
} |
|
} |
|
|
|
function eventTargetAgnosticAddListener(emitter, name, listener, flags) { |
|
if (typeof emitter.on === 'function') { |
|
if (flags.once) { |
|
emitter.once(name, listener); |
|
} else { |
|
emitter.on(name, listener); |
|
} |
|
} else if (typeof emitter.addEventListener === 'function') { |
|
// EventTarget does not have `error` event semantics like Node |
|
// EventEmitters, we do not listen for `error` events here. |
|
emitter.addEventListener(name, function wrapListener(arg) { |
|
// IE does not have builtin `{ once: true }` support so we |
|
// have to do it manually. |
|
if (flags.once) { |
|
emitter.removeEventListener(name, wrapListener); |
|
} |
|
listener(arg); |
|
}); |
|
} else { |
|
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); |
|
} |
|
}
|
|
|