1 // Copyright Joyent, Inc. and other Node contributors.
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:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
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.
23 var util = require('util');
25 function EventEmitter() {
26 EventEmitter.init.call(this);
28 module.exports = EventEmitter;
30 // Backwards-compat with node 0.10.x
31 EventEmitter.EventEmitter = EventEmitter;
33 EventEmitter.usingDomains = false;
35 EventEmitter.prototype.domain = undefined;
36 EventEmitter.prototype._events = undefined;
37 EventEmitter.prototype._maxListeners = undefined;
39 // By default EventEmitters will print a warning if more than 10 listeners are
40 // added to it. This is a useful default which helps finding memory leaks.
41 EventEmitter.defaultMaxListeners = 10;
43 EventEmitter.init = function() {
45 if (EventEmitter.usingDomains) {
46 // if there is an active domain, then attach to it.
47 domain = domain || require('domain');
48 if (domain.active && !(this instanceof domain.Domain)) {
49 this.domain = domain.active;
53 if (!this._events || this._events === Object.getPrototypeOf(this)._events)
56 this._maxListeners = this._maxListeners || undefined;
59 // Obviously not all Emitters should be limited to 10. This function allows
60 // that to be increased. Set to zero for unlimited.
61 EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
62 if (!util.isNumber(n) || n < 0 || isNaN(n))
63 throw TypeError('n must be a positive number');
64 this._maxListeners = n;
68 EventEmitter.prototype.emit = function emit(type) {
69 var er, handler, len, args, i, listeners;
74 // If there is no 'error' event listener then throw.
75 if (type === 'error' && !this._events.error) {
79 er = new Error('Uncaught, unspecified "error" event.');
80 er.domainEmitter = this;
81 er.domain = this.domain;
82 er.domainThrown = false;
83 this.domain.emit('error', er);
84 } else if (er instanceof Error) {
85 throw er; // Unhandled 'error' event
87 throw Error('Uncaught, unspecified "error" event.');
92 handler = this._events[type];
94 if (util.isUndefined(handler))
97 if (this.domain && this !== process)
100 if (util.isFunction(handler)) {
101 switch (arguments.length) {
107 handler.call(this, arguments[1]);
110 handler.call(this, arguments[1], arguments[2]);
114 len = arguments.length;
115 args = new Array(len - 1);
116 for (i = 1; i < len; i++)
117 args[i - 1] = arguments[i];
118 handler.apply(this, args);
120 } else if (util.isObject(handler)) {
121 len = arguments.length;
122 args = new Array(len - 1);
123 for (i = 1; i < len; i++)
124 args[i - 1] = arguments[i];
126 listeners = handler.slice();
127 len = listeners.length;
128 for (i = 0; i < len; i++)
129 listeners[i].apply(this, args);
132 if (this.domain && this !== process)
138 EventEmitter.prototype.addListener = function addListener(type, listener) {
141 if (!util.isFunction(listener))
142 throw TypeError('listener must be a function');
147 // To avoid recursion in the case that type === "newListener"! Before
148 // adding it to the listeners, first emit "newListener".
149 if (this._events.newListener)
150 this.emit('newListener', type,
151 util.isFunction(listener.listener) ?
152 listener.listener : listener);
154 if (!this._events[type])
155 // Optimize the case of one listener. Don't need the extra array object.
156 this._events[type] = listener;
157 else if (util.isObject(this._events[type]))
158 // If we've already got an array, just append.
159 this._events[type].push(listener);
161 // Adding the second element, need to change to array.
162 this._events[type] = [this._events[type], listener];
164 // Check for listener leak
165 if (util.isObject(this._events[type]) && !this._events[type].warned) {
167 if (!util.isUndefined(this._maxListeners)) {
168 m = this._maxListeners;
170 m = EventEmitter.defaultMaxListeners;
173 if (m && m > 0 && this._events[type].length > m) {
174 this._events[type].warned = true;
175 console.error('(node) warning: possible EventEmitter memory ' +
176 'leak detected. %d %s listeners added. ' +
177 'Use emitter.setMaxListeners() to increase limit.',
178 this._events[type].length, type);
186 EventEmitter.prototype.on = EventEmitter.prototype.addListener;
188 EventEmitter.prototype.once = function once(type, listener) {
189 if (!util.isFunction(listener))
190 throw TypeError('listener must be a function');
195 this.removeListener(type, g);
199 listener.apply(this, arguments);
203 g.listener = listener;
209 // emits a 'removeListener' event iff the listener was removed
210 EventEmitter.prototype.removeListener =
211 function removeListener(type, listener) {
212 var list, position, length, i;
214 if (!util.isFunction(listener))
215 throw TypeError('listener must be a function');
217 if (!this._events || !this._events[type])
220 list = this._events[type];
221 length = list.length;
224 if (list === listener ||
225 (util.isFunction(list.listener) && list.listener === listener)) {
226 delete this._events[type];
227 if (this._events.removeListener)
228 this.emit('removeListener', type, listener);
230 } else if (util.isObject(list)) {
231 for (i = length; i-- > 0;) {
232 if (list[i] === listener ||
233 (list[i].listener && list[i].listener === listener)) {
242 if (list.length === 1) {
244 delete this._events[type];
246 list.splice(position, 1);
249 if (this._events.removeListener)
250 this.emit('removeListener', type, listener);
256 EventEmitter.prototype.removeAllListeners =
257 function removeAllListeners(type) {
263 // not listening for removeListener, no need to emit
264 if (!this._events.removeListener) {
265 if (arguments.length === 0)
267 else if (this._events[type])
268 delete this._events[type];
272 // emit removeListener for all listeners on all events
273 if (arguments.length === 0) {
274 for (key in this._events) {
275 if (key === 'removeListener') continue;
276 this.removeAllListeners(key);
278 this.removeAllListeners('removeListener');
283 listeners = this._events[type];
285 if (util.isFunction(listeners)) {
286 this.removeListener(type, listeners);
287 } else if (Array.isArray(listeners)) {
289 while (listeners.length)
290 this.removeListener(type, listeners[listeners.length - 1]);
292 delete this._events[type];
297 EventEmitter.prototype.listeners = function listeners(type) {
299 if (!this._events || !this._events[type])
301 else if (util.isFunction(this._events[type]))
302 ret = [this._events[type]];
304 ret = this._events[type].slice();
308 EventEmitter.listenerCount = function(emitter, type) {
310 if (!emitter._events || !emitter._events[type])
312 else if (util.isFunction(emitter._events[type]))
315 ret = emitter._events[type].length;