1'use strict';
2
3var spliceOne;
4
5function EventEmitter() {
6    EventEmitter.init.call(this);
7}
8
9EventEmitter.EventEmitter = EventEmitter;
10
11EventEmitter.usingDomains = false;
12
13EventEmitter.prototype._events = undefined;
14EventEmitter.prototype._eventsCount = 0;
15EventEmitter.prototype._maxListeners = undefined;
16
17var defaultMaxListeners = 10;
18
19Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
20    enumerable: true,
21    get: function () {
22        return defaultMaxListeners;
23    },
24    set: function (arg) {
25        if (typeof arg !== 'number' || arg < 0 || Number.isNaN(arg)) {
26            throw new Error('defaultMaxListeners:a non-negative number');
27        }
28        defaultMaxListeners = arg;
29    }
30});
31
32EventEmitter.init = function () {
33
34    if (this._events === undefined ||
35        this._events === Object.getPrototypeOf(this)._events) {
36        this._events = Object.create(null);
37        this._eventsCount = 0;
38    }
39
40    this._maxListeners = this._maxListeners || undefined;
41};
42
43EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
44    if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) {
45        throw new Error('a non-negative number');
46    }
47    this._maxListeners = n;
48    var that = this;
49    return that;
50};
51
52function _getMaxListeners(that) {
53    if (that._maxListeners === undefined)
54        return EventEmitter.defaultMaxListeners;
55    return that._maxListeners;
56}
57
58EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
59    return _getMaxListeners(this);
60};
61
62function longestSeqContainedIn(a, b) {
63    for (var len = a.length; len >= 3; --len) {
64        for (var i = 0; i < a.length - len; ++i) {
65            for (var j = 0; j < b.length - len; ++j) {
66                var matches = true;
67                for (var k = 0; k < len; ++k) {
68                    if (a[i + k] !== b[j + k]) {
69                        matches = false;
70                        break;
71                    }
72                }
73                if (matches)
74                    return [len, i, j];
75            }
76        }
77    }
78
79    return [0, 0, 0];
80}
81
82function enhanceStackTrace(err, own) {
83    var sep = '\nEmitted \'error\' event at:\n';
84
85    var errStack = err.stack.split('\n').slice(1);
86    var ownStack = own.stack.split('\n').slice(1);
87
88    var seq = longestSeqContainedIn(ownStack, errStack);
89    if (seq.len > 0) {
90        ownStack.splice(seq.off + 1, seq.len - 1,
91            '    [... lines matching original stack trace ...]');
92    }
93    err.stack = err.stack + sep + ownStack.join('\n');
94}
95
96EventEmitter.prototype.emit = function emit(type) {
97    var doError = (type === 'error');
98
99    var events = this._events;
100    if (events !== undefined)
101        doError = (doError && events.error === undefined);
102    else if (!doError)
103        return false;
104
105    var args = [];
106    for (var i = 1, len = arguments.length; i < len; i++) {
107        args.push(arguments[i]);
108    }
109    if (doError) {
110        var er;
111        if (args.length > 0)
112            er = args[0];
113        if (er instanceof Error) {
114            throw er;
115        }
116        throw new Error('unhandled error');
117    }
118
119    var handler = events[type];
120
121    if (handler === undefined)
122        return false;
123
124        if (typeof handler === 'function') {
125        Function.prototype.apply.call(handler, this, args);
126    } else {
127        var len = handler.length;
128        var listeners = arrayClone(handler, len);
129        for (var i = 0; i < len; ++i)
130            Function.prototype.apply.call(listeners[i], this, args);
131    }
132
133    return true;
134};
135
136function _addListener(target, type, listener, prepend) {
137    var m;
138    var events;
139    var existing;
140
141    if (typeof listener !== 'function') {
142        throw new Error('addListener invalid arg type: Function');
143    }
144
145    events = target._events;
146    if (events === undefined) {
147        events = target._events = Object.create(null);
148        target._eventsCount = 0;
149    } else {
150        if (events.newListener !== undefined) {
151            target.emit('newListener', type,
152                listener.listener ? listener.listener : listener);
153            events = target._events;
154        }
155        existing = events[type];
156    }
157
158    if (existing === undefined) {
159        existing = events[type] = listener;
160        ++target._eventsCount;
161    } else {
162        if (typeof existing === 'function') {
163            existing = events[type] =
164                prepend ? [listener, existing] : [existing, listener];
165        } else if (prepend) {
166            existing.unshift(listener);
167        } else {
168            existing.push(listener);
169        }
170    }
171
172    return target;
173}
174
175EventEmitter.prototype.addListener = function addListener(type, listener) {
176    return _addListener(this, type, listener, false);
177};
178
179EventEmitter.prototype.on = EventEmitter.prototype.addListener;
180
181EventEmitter.prototype.prependListener =
182    function prependListener(type, listener) {
183        return _addListener(this, type, listener, true);
184    };
185
186function onceWrapper() {
187    if (!this.fired) {
188        this.target.removeListener(this.type, this.wrapFn);
189        this.fired = true;
190        Function.prototype.apply.call(this.listener, this.target, arguments);
191    }
192}
193
194function _onceWrap(target, type, listener) {
195    var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
196    var wrapped = onceWrapper.bind(state);
197    wrapped.listener = listener;
198    state.wrapFn = wrapped;
199    return wrapped;
200}
201
202EventEmitter.prototype.once = function once(type, listener) {
203    if (typeof listener !== 'function') {
204        throw new Error('listener invalid arg type');
205    }
206    this.on(type, _onceWrap(this, type, listener));
207    var that = this;
208    return that;
209};
210
211EventEmitter.prototype.prependOnceListener =
212    function prependOnceListener(type, listener) {
213        if (typeof listener !== 'function') {
214            throw new Error('prependOnceListener invalid arg type');
215        }
216        this.prependListener(type, _onceWrap(this, type, listener));
217        var that = this;
218        return that;
219    };
220
221EventEmitter.prototype.removeListener =
222    function removeListener(type, listener) {
223        var list, events, position, i, originalListener;
224        var that = this;
225
226        if (typeof listener !== 'function') {
227            throw new Error('removeListener invalid arg type');
228        }
229
230        events = this._events;
231        if (events === undefined)
232            return that;
233
234        list = events[type];
235        if (list === undefined)
236            return that;
237
238        if (list === listener || list.listener === listener) {
239            if (--this._eventsCount === 0)
240                this._events = Object.create(null);
241            else {
242                delete events[type];
243                if (events.removeListener)
244                    this.emit('removeListener', type, list.listener || listener);
245            }
246        } else if (typeof list !== 'function') {
247            position = -1;
248
249            for (i = list.length - 1; i >= 0; i--) {
250                if (list[i] === listener || list[i].listener === listener) {
251                    originalListener = list[i].listener;
252                    position = i;
253                    break;
254                }
255            }
256
257            if (position < 0)
258                return that;
259
260            if (position === 0)
261                list.shift();
262            else {
263                if (spliceOne === undefined)
264                    spliceOne = require('internal/util').spliceOne;
265                spliceOne(list, position);
266            }
267
268            if (list.length === 1)
269                events[type] = list[0];
270
271            if (events.removeListener !== undefined)
272                this.emit('removeListener', type, originalListener || listener);
273        }
274
275        return that;
276    };
277
278EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
279
280EventEmitter.prototype.removeAllListeners =
281    function removeAllListeners(type) {
282        var listeners, events, i;
283        var that = this;
284
285        events = this._events;
286        if (events === undefined)
287            return that;
288
289        if (events.removeListener === undefined) {
290            if (arguments.length === 0) {
291                this._events = Object.create(null);
292                this._eventsCount = 0;
293            } else if (events[type] !== undefined) {
294                if (--this._eventsCount === 0)
295                    this._events = Object.create(null);
296                else
297                    delete events[type];
298            }
299            return that;
300        }
301
302        if (arguments.length === 0) {
303            var keys = Object.keys(events);
304            var key;
305            for (i = 0; i < keys.length; ++i) {
306                key = keys[i];
307                if (key === 'removeListener') continue;
308                this.removeAllListeners(key);
309            }
310            this.removeAllListeners('removeListener');
311            this._events = Object.create(null);
312            this._eventsCount = 0;
313            return that;
314        }
315
316        listeners = events[type];
317
318        if (typeof listeners === 'function') {
319            this.removeListener(type, listeners);
320        } else if (listeners !== undefined) {
321            for (i = listeners.length - 1; i >= 0; i--) {
322                this.removeListener(type, listeners[i]);
323            }
324        }
325
326        return that;
327    };
328
329function _listeners(target, type, unwrap) {
330    var events = target._events;
331
332    if (events === undefined)
333        return [];
334
335    var evlistener = events[type];
336    if (evlistener === undefined)
337        return [];
338
339    if (typeof evlistener === 'function')
340        return unwrap ? [evlistener.listener || evlistener] : [evlistener];
341
342    return unwrap ?
343        unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
344}
345
346EventEmitter.prototype.listeners = function listeners(type) {
347    return _listeners(this, type, true);
348};
349
350EventEmitter.prototype.rawListeners = function rawListeners(type) {
351    return _listeners(this, type, false);
352};
353
354EventEmitter.listenerCount = function (emitter, type) {
355    if (typeof emitter.listenerCount === 'function') {
356        return emitter.listenerCount(type);
357    } else {
358        return listenerCount.call(emitter, type);
359    }
360};
361
362EventEmitter.prototype.listenerCount = listenerCount;
363function listenerCount(type) {
364    var events = this._events;
365
366    if (events !== undefined) {
367        var evlistener = events[type];
368
369        if (typeof evlistener === 'function') {
370            return 1;
371        } else if (evlistener !== undefined) {
372            return evlistener.length;
373        }
374    }
375
376    return 0;
377}
378
379EventEmitter.prototype.eventNames = function eventNames() {
380    return this._eventsCount > 0 ? Function.prototype.apply.call(this._events) : [];
381};
382
383function arrayClone(arr, n) {
384    var copy = new Array(n);
385    for (var i = 0; i < n; ++i)
386        copy[i] = arr[i];
387    return copy;
388}
389
390function unwrapListeners(arr) {
391    var ret = new Array(arr.length);
392    for (var i = 0; i < ret.length; ++i) {
393        ret[i] = arr[i].listener || arr[i];
394    }
395    return ret;
396}
397
398export  {EventEmitter};
399