Skip to content

Commit

Permalink
IE8 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop committed Jan 31, 2018
1 parent ae6fc1f commit 3229579
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 54 deletions.
93 changes: 62 additions & 31 deletions events.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,17 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var objectCreate = Object.create || objectCreatePolyfill
var objectKeys = Object.keys || objectKeysPolyfill
var bind = Function.prototype.bind || functionBindPolyfill

function EventEmitter() {
EventEmitter.init.call(this);
if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
this._events = objectCreate(null);
this._eventsCount = 0;
}

this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

Expand All @@ -34,28 +43,29 @@ EventEmitter.prototype._maxListeners = undefined;
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;

Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
// check whether the input is a positive number (whose value is zero or
// greater and not a NaN).
if (typeof arg !== 'number' || arg < 0 || arg !== arg)
throw new TypeError('"defaultMaxListeners" must be a positive number');
defaultMaxListeners = arg;
}
});

EventEmitter.init = function() {
if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}

this._maxListeners = this._maxListeners || undefined;
};
var hasDefineProperty;
try {
var o = {};
if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
hasDefineProperty = o.x === 0;
} catch (err) { hasDefineProperty = false }
if (hasDefineProperty) {
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
// check whether the input is a positive number (whose value is zero or
// greater and not a NaN).
if (typeof arg !== 'number' || arg < 0 || arg !== arg)
throw new TypeError('"defaultMaxListeners" must be a positive number');
defaultMaxListeners = arg;
}
});
} else {
EventEmitter.defaultMaxListeners = defaultMaxListeners;
}

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
Expand Down Expand Up @@ -200,7 +210,7 @@ function _addListener(target, type, listener, prepend) {

events = target._events;
if (!events) {
events = target._events = Object.create(null);
events = target._events = objectCreate(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
Expand Down Expand Up @@ -247,7 +257,9 @@ function _addListener(target, type, listener, prepend) {
w.emitter = target;
w.type = type;
w.count = existing.length;
console.warn('%s: %s', w.name, w.message);
if (typeof console === 'object' && console.warn) {
console.warn('%s: %s', w.name, w.message);
}
}
}
}
Expand Down Expand Up @@ -291,7 +303,7 @@ function onceWrapper() {

function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = onceWrapper.bind(state);
var wrapped = bind.call(onceWrapper, state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
Expand Down Expand Up @@ -330,7 +342,7 @@ EventEmitter.prototype.removeListener =

if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
this._events = objectCreate(null);
else {
delete events[type];
if (events.removeListener)
Expand Down Expand Up @@ -376,11 +388,11 @@ EventEmitter.prototype.removeAllListeners =
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._events = objectCreate(null);
this._eventsCount = 0;
} else if (events[type]) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
this._events = objectCreate(null);
else
delete events[type];
}
Expand All @@ -389,15 +401,15 @@ EventEmitter.prototype.removeAllListeners =

// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
var keys = objectKeys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._events = objectCreate(null);
this._eventsCount = 0;
return this;
}
Expand Down Expand Up @@ -486,3 +498,22 @@ function unwrapListeners(arr) {
}
return ret;
}

function objectCreatePolyfill(proto) {
var F = function() {};
F.prototype = proto;
return new F;
}
function objectKeysPolyfill(obj) {
var keys = [];
for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
return k;
}
function functionBindPolyfill(context) {
var fn = this;
return function () {
return fn.apply(context, arguments);
};
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
"node": ">=0.4.x"
},
"devDependencies": {
"isarray": "^2.0.2",
"mocha": "~1.21.4",
"object-keys": "^1.0.11",
"zuul": "~1.10.2"
},
"scripts": {
"test": "mocha --ui qunit -- tests/index.js && zuul -- tests/index.js"
},
"license": "MIT"
}
}
19 changes: 15 additions & 4 deletions tests/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var mustCallChecks = [];
function runCallChecks(exitCode) {
if (exitCode !== 0) return;

var failed = mustCallChecks.filter(function(context) {
var failed = filter(mustCallChecks, function(context) {
if ('minimum' in context) {
context.messageSegment = 'at least ' + context.minimum;
return context.actual < context.minimum;
Expand All @@ -38,13 +38,15 @@ function runCallChecks(exitCode) {
}
});

failed.forEach(function(context) {
for (var i = 0; i < failed.length; i++) {
var context = failed[i];
console.log('Mismatched %s function calls. Expected %s, actual %d.',
context.name,
context.messageSegment,
context.actual);
console.log(context.stack.split('\n').slice(2).join('\n'));
});
// IE8 has no .stack
if (context.stack) console.log(context.stack.split('\n').slice(2).join('\n'));
}

assert.strictEqual(failed.length, 0);
}
Expand Down Expand Up @@ -90,3 +92,12 @@ exports.mustNotCall = function(msg) {
assert.fail(msg || 'function should not have been called');
};
};

function filter(arr, fn) {
if (arr.filter) return arr.filter(fn);
var filtered = [];
for (var i = 0; i < arr.length; i++) {
if (fn(arr[i], i, arr)) filtered.push(arr[i]);
}
return filtered
}
4 changes: 1 addition & 3 deletions tests/legacy-compat.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,5 @@ for (var i=0 ; i<fns.length ; ++i) {
}

if (!Array.isArray) {
Array.isArray = function(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
Array.isArray = require('isarray');
}
5 changes: 3 additions & 2 deletions tests/listeners-side-effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ require('./common');
var assert = require('assert');

var EventEmitter = require('../').EventEmitter;
var objectKeys = require('object-keys');

var e = new EventEmitter();
var fl; // foo listeners

fl = e.listeners('foo');
assert.ok(Array.isArray(fl));
assert.strictEqual(fl.length, 0);
assert.ok(!(e._events instanceof Object));
assert.strictEqual(Object.keys(e._events).length, 0);
if (Object.create) assert.ok(!(e._events instanceof Object));
assert.strictEqual(objectKeys(e._events).length, 0);

e.on('foo', assert.fail);
fl = e.listeners('foo');
Expand Down
12 changes: 9 additions & 3 deletions tests/max-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter();

var hasDefineProperty = !!Object.defineProperty;
try { Object.defineProperty({}, 'x', { value: 0 }); } catch (err) { hasDefineProperty = false }

e.on('maxListeners', common.mustCall());

// Should not corrupt the 'maxListeners' queue.
Expand All @@ -33,9 +36,12 @@ var throwsObjs = [NaN, -1, 'and even this'];
var maxError = /^TypeError: "n" argument must be a positive number$/;
var defError = /^TypeError: "defaultMaxListeners" must be a positive number$/;

throwsObjs.forEach(function(obj) {
for (var i = 0; i < throwsObjs.length; i++) {
var obj = throwsObjs[i];
assert.throws(function() { e.setMaxListeners(obj); }, maxError);
assert.throws(function() { events.defaultMaxListeners = obj; }, defError);
});
if (hasDefineProperty) {
assert.throws(function() { events.defaultMaxListeners = obj; }, defError);
}
}

e.emit('maxListeners');
5 changes: 3 additions & 2 deletions tests/once.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ assert.throws(function() {
var restArgs = args.slice(1);
assert.ok(Array.isArray(params));
assert.strictEqual(params.length, restArgs.length);
params.forEach(function(param, index) {
for (var index = 0; index < params.length; index++) {
var param = params[index];
assert.strictEqual(param, restArgs[index]);
});
}
}));

EventEmitter.prototype.emit.apply(ee, args);
Expand Down
7 changes: 4 additions & 3 deletions tests/remove-all-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ function expect(expected) {
var sortedActual = actual.sort();
var sortedExpected = expected.sort();
assert.strictEqual(sortedActual.length, sortedExpected.length);
sortedActual.forEach(function(value, index) {
for (var index = 0; index < sortedActual.length; index++) {
var value = sortedActual[index];
assert.strictEqual(value, sortedExpected[index]);
});
}
});
function listener(name) {
actual.push(name);
Expand Down Expand Up @@ -109,7 +110,7 @@ function expect(expected) {
// Check for regression where removeAllListeners() throws when
// there exists a 'removeListener' listener, but there exists
// no listeners for the provided event type.
assert.doesNotThrow(ee.removeAllListeners.bind(ee, 'foo'));
assert.doesNotThrow(function () { ee.removeAllListeners(ee, 'foo') });
}

{
Expand Down
7 changes: 4 additions & 3 deletions tests/set-max-listeners-side-effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
require('./common');
var assert = require('assert');
var events = require('../');
var objectKeys = require('object-keys');

var e = new events.EventEmitter();

assert.ok(!(e._events instanceof Object));
assert.strictEqual(Object.keys(e._events).length, 0);
if (Object.create) assert.ok(!(e._events instanceof Object));
assert.strictEqual(objectKeys(e._events).length, 0);
e.setMaxListeners(5);
assert.strictEqual(Object.keys(e._events).length, 0);
assert.strictEqual(objectKeys(e._events).length, 0);
5 changes: 3 additions & 2 deletions tests/subclass.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var common = require('./common');
var assert = require('assert');
var EventEmitter = require('../').EventEmitter;
var util = require('util');
var objectKeys = require('object-keys');

var after_checks = [];
after(function() {
Expand Down Expand Up @@ -53,8 +54,8 @@ assert.throws(function() {
}, /blerg/);

after_checks.push(function() {
assert.ok(!(myee._events instanceof Object));
assert.strictEqual(Object.keys(myee._events).length, 0);
if (Object.create) assert.ok(!(myee._events instanceof Object));
assert.strictEqual(objectKeys(myee._events).length, 0);
});


Expand Down

0 comments on commit 3229579

Please sign in to comment.