diff --git a/js/events.js b/js/events.js index 7a2098f98..6caac53ae 100644 --- a/js/events.js +++ b/js/events.js @@ -1,57 +1,291 @@ -var EventEmitter = function () { - this._events = {}; -}; +// 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. -EventEmitter.prototype.on = function (e, f) { - console.log('on', e, f) - if (e in this._events) { - if (this._events[e].indexOf(f) < 0) { - this._events[e].push(f); +var isArray = Array.isArray; +var domain; + +function EventEmitter() { + if (exports.usingDomains) { + // if there is an active domain, then attach to it. + domain = domain || require('domain'); + if (domain.active && !(this instanceof domain.Domain)) { + this.domain = domain.active; } - } else { - this._events[e] = [f]; } +} +exports.EventEmitter = EventEmitter; + +// 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._maxListeners = n; +}; + +// non-global reference, for speed. +var PROCESS; + +EventEmitter.prototype.emit = function() { + var type = arguments[0]; + // 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 (this.domain) { + var er = arguments[1]; + er.domain_emitter = this; + er.domain = this.domain; + er.domain_thrown = false; + this.domain.emit('error', er); + return false; + } + + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + } + + if (!this._events) return false; + var handler = this._events[type]; + if (!handler) return false; + + if (typeof handler == 'function') { + if (this.domain) { + PROCESS = PROCESS || process; + if (this !== PROCESS) { + this.domain.enter(); + } + } + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + var l = arguments.length; + var args = new Array(l - 1); + for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; + handler.apply(this, args); + } + if (this.domain && this !== PROCESS) { + this.domain.exit(); + } + return true; + + } else if (isArray(handler)) { + if (this.domain) { + PROCESS = PROCESS || process; + if (this !== PROCESS) { + this.domain.enter(); + } + } + var l = arguments.length; + var args = new Array(l - 1); + for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; + + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + if (this.domain && this !== PROCESS) { + this.domain.exit(); + } + return true; + + } else { + return false; + } +}; + +EventEmitter.prototype.addListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('addListener only takes instances of Function'); + } + + if (!this._events) this._events = {}; + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + if (this._events.newListener) { + this.emit('newListener', type, typeof listener.listener === 'function' ? + listener.listener : listener); + } + + 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])) { + + // 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 (isArray(this._events[type]) && !this._events[type].warned) { + var m; + if (this._maxListeners !== undefined) { + m = this._maxListeners; + } else { + m = 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); + console.trace(); + } + } + return this; }; -EventEmitter.prototype.off = function (e, f) { - if (f) { - function eq(x) { return function (y) { return x === y; } } - this._events[e] = this.listeners(e).filter(eq(f)); - } else { - delete this._events[e]; +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('.once only takes instances of Function'); } -}; -EventEmitter.prototype.removeListener = function (e, f) { - this.off(e, f); -}; + var self = this; + function g() { + self.removeListener(type, g); + listener.apply(this, arguments); + }; -EventEmitter.prototype.removeAllListeners = function (e) { - this.off(e); -}; + g.listener = listener; + self.on(type, g); -EventEmitter.prototype.emit = function (e) { - var args = Array.prototype.slice.call(arguments, 1), - fs = this.listeners(e); - console.log('emit', e, args) - - for (var i = 0; i < fs.length; i++) { - fs[i].apply(e, args); - } -}; - -EventEmitter.prototype.listeners = function (e) { - return this._events[e] || []; -}; - -EventEmitter.prototype.once = function (e, f) { - var that = this; - this.on(e, function g() { - f.apply(e, arguments); - that.off(e, g); - }); return this; }; -exports.EventEmitter = EventEmitter; \ No newline at end of file +// 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'); + } + + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events || !this._events[type]) return this; + + var list = this._events[type]; + + if (isArray(list)) { + var position = -1; + for (var i = 0, length = list.length; i < length; i++) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) + { + position = i; + break; + } + } + + if (position < 0) return this; + list.splice(position, 1); + if (list.length == 0) + delete this._events[type]; + + if (this._events.removeListener) { + this.emit('removeListener', type, listener); + } + } else if (list === listener || + (list.listener && list.listener === listener)) + { + delete this._events[type]; + + if (this._events.removeListener) { + this.emit('removeListener', type, listener); + } + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + if (!this._events) return this; + + // fast path + if (!this._events.removeListener) { + if (arguments.length === 0) { + this._events = {}; + } else if (type && this._events && this._events[type]) { + this._events[type] = null; + } + return this; + } + + // slow(ish) path, emit 'removeListener' events for all removals + if (arguments.length === 0) { + for (var key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + var listeners = this._events[type]; + if (isArray(listeners)) { + while (listeners.length) { + // LIFO order + this.removeListener(type, listeners[listeners.length - 1]); + } + } else if (listeners) { + this.removeListener(type, listeners); + } + this._events[type] = null; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + if (!this._events || !this._events[type]) return []; + if (!isArray(this._events[type])) { + return [this._events[type]]; + } + return this._events[type].slice(0); +}; \ No newline at end of file diff --git a/js/remote.js b/js/remote.js index 84a86d309..89a2cdb9d 100644 --- a/js/remote.js +++ b/js/remote.js @@ -16,11 +16,11 @@ // Node var util = require('util'); -var EventEmitter = require('events').EventEmitter; // npm var WebSocket = require('ws'); +var EventEmitter = require('./events').EventEmitter; var Amount = require('./amount.js').Amount; var UInt160 = require('./amount.js').UInt160; @@ -50,19 +50,6 @@ var Request = function (remote, command) { Request.prototype = new EventEmitter; -// Return this. node EventEmitter's on doesn't return this. -Request.prototype.on = function (e, c) { - EventEmitter.prototype.on.call(this, e, c); - - return this; -}; - -Request.prototype.once = function (e, c) { - EventEmitter.prototype.once.call(this, e, c); - - return this; -}; - // Send the request to a remote. Request.prototype.request = function (remote) { this.emit('request', remote); @@ -884,13 +871,6 @@ var Transaction = function (remote) { Transaction.prototype = new EventEmitter; -// Return this. node EventEmitter's on doesn't return this. -Transaction.prototype.on = function (e, c) { - EventEmitter.prototype.on.call(this, e, c); - - return this; -}; - Transaction.prototype.consts = { 'telLOCAL_ERROR' : -399, 'temMALFORMED' : -299,