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;
52 this._events = this._events || {};
53 this._maxListeners = this._maxListeners || undefined;
56 // Obviously not all Emitters should be limited to 10. This function allows
57 // that to be increased. Set to zero for unlimited.
58 EventEmitter.prototype.setMaxListeners = function(n) {
59 if (!util.isNumber(n) || n < 0 || isNaN(n))
60 throw TypeError('n must be a positive number');
61 this._maxListeners = n;
65 EventEmitter.prototype.emit = function(type) {
66 var er, handler, len, args, i, listeners;
71 // If there is no 'error' event listener then throw.
72 if (type === 'error' && !this._events.error) {
76 er = new Error('Uncaught, unspecified "error" event.');
77 er.domainEmitter = this;
78 er.domain = this.domain;
79 er.domainThrown = false;
80 this.domain.emit('error', er);
81 } else if (er instanceof Error) {
82 throw er; // Unhandled 'error' event
84 throw Error('Uncaught, unspecified "error" event.');
89 handler = this._events[type];
91 if (util.isUndefined(handler))
94 if (this.domain && this !== process)
97 if (util.isFunction(handler)) {
98 switch (arguments.length) {
104 handler.call(this, arguments[1]);
107 handler.call(this, arguments[1], arguments[2]);
111 len = arguments.length;
112 args = new Array(len - 1);
113 for (i = 1; i < len; i++)
114 args[i - 1] = arguments[i];
115 handler.apply(this, args);
117 } else if (util.isObject(handler)) {
118 len = arguments.length;
119 args = new Array(len - 1);
120 for (i = 1; i < len; i++)
121 args[i - 1] = arguments[i];
123 listeners = handler.slice();
124 len = listeners.length;
125 for (i = 0; i < len; i++)
126 listeners[i].apply(this, args);
129 if (this.domain && this !== process)
135 EventEmitter.prototype.addListener = function(type, listener) {
138 if (!util.isFunction(listener))
139 throw TypeError('listener must be a function');
144 // To avoid recursion in the case that type === "newListener"! Before
145 // adding it to the listeners, first emit "newListener".
146 if (this._events.newListener)
147 this.emit('newListener', type,
148 util.isFunction(listener.listener) ?
149 listener.listener : listener);
151 if (!this._events[type])
152 // Optimize the case of one listener. Don't need the extra array object.
153 this._events[type] = listener;
154 else if (util.isObject(this._events[type]))
155 // If we've already got an array, just append.
156 this._events[type].push(listener);
158 // Adding the second element, need to change to array.
159 this._events[type] = [this._events[type], listener];
161 // Check for listener leak
162 if (util.isObject(this._events[type]) && !this._events[type].warned) {
164 if (!util.isUndefined(this._maxListeners)) {
165 m = this._maxListeners;
167 m = EventEmitter.defaultMaxListeners;
170 if (m && m > 0 && this._events[type].length > m) {
171 this._events[type].warned = true;
172 console.error('(node) warning: possible EventEmitter memory ' +
173 'leak detected. %d listeners added. ' +
174 'Use emitter.setMaxListeners() to increase limit.',
175 this._events[type].length);
183 EventEmitter.prototype.on = EventEmitter.prototype.addListener;
185 EventEmitter.prototype.once = function(type, listener) {
186 if (!util.isFunction(listener))
187 throw TypeError('listener must be a function');
192 this.removeListener(type, g);
196 listener.apply(this, arguments);
200 g.listener = listener;
206 // emits a 'removeListener' event iff the listener was removed
207 EventEmitter.prototype.removeListener = function(type, listener) {
208 var list, position, length, i;
210 if (!util.isFunction(listener))
211 throw TypeError('listener must be a function');
213 if (!this._events || !this._events[type])
216 list = this._events[type];
217 length = list.length;
220 if (list === listener ||
221 (util.isFunction(list.listener) && list.listener === listener)) {
222 delete this._events[type];
223 if (this._events.removeListener)
224 this.emit('removeListener', type, listener);
226 } else if (util.isObject(list)) {
227 for (i = length; i-- > 0;) {
228 if (list[i] === listener ||
229 (list[i].listener && list[i].listener === listener)) {
238 if (list.length === 1) {
240 delete this._events[type];
242 list.splice(position, 1);
245 if (this._events.removeListener)
246 this.emit('removeListener', type, listener);
252 EventEmitter.prototype.removeAllListeners = function(type) {
258 // not listening for removeListener, no need to emit
259 if (!this._events.removeListener) {
260 if (arguments.length === 0)
262 else if (this._events[type])
263 delete this._events[type];
267 // emit removeListener for all listeners on all events
268 if (arguments.length === 0) {
269 for (key in this._events) {
270 if (key === 'removeListener') continue;
271 this.removeAllListeners(key);
273 this.removeAllListeners('removeListener');
278 listeners = this._events[type];
280 if (util.isFunction(listeners)) {
281 this.removeListener(type, listeners);
282 } else if (Array.isArray(listeners)) {
284 while (listeners.length)
285 this.removeListener(type, listeners[listeners.length - 1]);
287 delete this._events[type];
292 EventEmitter.prototype.listeners = function(type) {
294 if (!this._events || !this._events[type])
296 else if (util.isFunction(this._events[type]))
297 ret = [this._events[type]];
299 ret = this._events[type].slice();
303 EventEmitter.listenerCount = function(emitter, type) {
305 if (!emitter._events || !emitter._events[type])
307 else if (util.isFunction(emitter._events[type]))
310 ret = emitter._events[type].length;