diff --git a/LICENSE.md b/LICENSE.md index 763d2e67..d0926f93 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -135,6 +135,19 @@ Copyright (c) 2011-2013 Ariya Hidayat and other Esprima contributors. The source code for Esprima is available at: https://github.com/ariya/esprima +## events ## + +Portions of the events source code are incorporated into the following files: + ++ `rhino/events.js` + +events is distributed under the MIT license, which is reproduced above. + +Copyright Joyent, Inc. and other Node contributors. All rights reserved. + +The source code for events is available at: +https://github.com/Gozala/events + ## github-flavored-markdown ## github-flavored-markdown is distributed under the BSD 3-clause license: @@ -240,18 +253,6 @@ above. The source code for node-browser-builtins is available at: https://github.com/alexgorbatchev/node-browser-builtins -## node-browserify ## - -Portions of the node-browserify source code are incorporated into the following -files: - -- `rhino/events.js` - -node-browserify is distributed under the MIT license, which is reproduced above. - -The source code for node-browserify is available at: -https://github.com/substack/node-browserify - ## Requizzle ## Requizzle is distributed under the MIT license, which is reproduced above. diff --git a/rhino/events.js b/rhino/events.js index 960b18d5..488025a5 100644 --- a/rhino/events.js +++ b/rhino/events.js @@ -1,62 +1,64 @@ /** - * Shim for Node.js `events` module. Adapted from `node-browserify`. - * @see https://github.com/substack/node-browserify + * Shim for Node.js `events` module. + * @see https://github.com/Gozala/events * @license MIT */ 'use strict'; +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; if (!process.EventEmitter) { - process.EventEmitter = function () {}; + process.EventEmitter = EventEmitter; } -var EventEmitter = exports.EventEmitter = process.EventEmitter; -var isArray = typeof Array.isArray === 'function' ? - Array.isArray : - function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; - } -; +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +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. +EventEmitter.defaultMaxListeners = 10; -// 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. -// // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. -var defaultMaxListeners = 10; EventEmitter.prototype.setMaxListeners = function(n) { - if (!this._events) { - this._events = {}; - } - this._events.maxListeners = n; + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; }; EventEmitter.prototype.emit = function(type) { - var args; + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { - if (!this._events || !this._events.error || - (isArray(this._events.error) && !this._events.error.length)) - { - if (arguments[1] instanceof Error) { - throw arguments[1]; // Unhandled 'error' event + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event } else { - throw new Error("Uncaught, unspecified 'error' event."); + throw TypeError('Uncaught, unspecified "error" event.'); } return false; } } - if (!this._events) { - return false; - } - var handler = this._events[type]; - if (!handler) { - return false; - } + handler = this._events[type]; - if (typeof handler == 'function') { + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: @@ -70,69 +72,73 @@ EventEmitter.prototype.emit = function(type) { break; // slower default: - args = Array.prototype.slice.call(arguments, 1); + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; handler.apply(this, args); } - return true; + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; - } else if (isArray(handler)) { - args = Array.prototype.slice.call(arguments, 1); - - var listeners = handler.slice(); - for (var i = 0, l = listeners.length; i < l; i++) { + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) listeners[i].apply(this, args); - } - return true; - - } else { - return false; } + + return true; }; -// EventEmitter is defined in src/node_events.cc -// EventEmitter.prototype.emit() is also defined there. EventEmitter.prototype.addListener = function(type, listener) { - if ('function' !== typeof listener) { - throw new Error('addListener only takes instances of Function'); - } + var m; - if (!this._events) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) this._events = {}; - } - // To avoid recursion in the case that type == "newListeners"! Before - // adding it to the listeners, first emit "newListeners". - this.emit('newListener', type, listener); + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); - if (!this._events[type]) { + if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; - } else if (isArray(this._events[type])) { + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; - // Check for listener leak - if (!this._events[type].warned) { - var m; - if (this._events.maxListeners !== undefined) { - m = this._events.maxListeners; - } else { - m = defaultMaxListeners; - } + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } - if (m && m > 0 && this._events[type].length > m) { - this._events[type].warned = true; - console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 console.trace(); } } - - // If we've already got an array, just append. - this._events[type].push(listener); - } else { - // Adding the second element, need to change to array. - this._events[type] = [this._events[type], listener]; } return this; @@ -141,60 +147,146 @@ EventEmitter.prototype.addListener = function(type, listener) { EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { - var self = this; - self.on(type, function g() { - self.removeListener(type, g); - listener.apply(this, arguments); - }); + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); return this; }; +// emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { - if ('function' !== typeof listener) { - throw new Error('removeListener only takes instances of Function'); - } + var list, position, length, i; - // does not use listeners(), so no side effect of creating _events[type] - if (!this._events || !this._events[type]) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) return this; - } - var list = this._events[type]; + list = this._events[type]; + length = list.length; + position = -1; - if (isArray(list)) { - var i = list.indexOf(listener); - if (i < 0) { - return this; - } - list.splice(i, 1); - if (list.length === 0) { - delete this._events[type]; - } - } else if (this._events[type] === listener) { + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { - // does not use listeners(), so no side effect of creating _events[type] - if (type && this._events && this._events[type]) { - this._events[type] = null; + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + return this; }; EventEmitter.prototype.listeners = function(type) { - if (!this._events) { - this._events = {}; - } - if (!this._events[type]) { - this._events[type] = []; - } - if (!isArray(this._events[type])) { - this._events[type] = [this._events[type]]; - } - return this._events[type]; + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; }; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} \ No newline at end of file