From 2f416371bdefa60901d7db182496aba6661457bf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 29 Dec 2016 04:53:57 -0800 Subject: [PATCH 1/5] Convert EventEmitter to TS Pat of #335 --- src/EventEmitter.js | 64 ------------------------------------ src/EventEmitter.ts | 80 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 64 deletions(-) delete mode 100644 src/EventEmitter.js create mode 100644 src/EventEmitter.ts diff --git a/src/EventEmitter.js b/src/EventEmitter.js deleted file mode 100644 index 3661894857..0000000000 --- a/src/EventEmitter.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @license MIT - */ - -function EventEmitter() { - this._events = this._events || {}; -} - -EventEmitter.prototype.addListener = function(type, listener) { - this._events[type] = this._events[type] || []; - this._events[type].push(listener); -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.removeListener = function(type, listener) { - if (!this._events[type]) return; - - var obj = this._events[type] - , i = obj.length; - - while (i--) { - if (obj[i] === listener || obj[i].listener === listener) { - obj.splice(i, 1); - return; - } - } -}; - -EventEmitter.prototype.off = EventEmitter.prototype.removeListener; - -EventEmitter.prototype.removeAllListeners = function(type) { - if (this._events[type]) delete this._events[type]; -}; - -EventEmitter.prototype.once = function(type, listener) { - var self = this; - function on() { - var args = Array.prototype.slice.call(arguments); - this.removeListener(type, on); - return listener.apply(this, args); - } - on.listener = listener; - return this.on(type, on); -}; - -EventEmitter.prototype.emit = function(type) { - if (!this._events[type]) return; - - var args = Array.prototype.slice.call(arguments, 1) - , obj = this._events[type] - , l = obj.length - , i = 0; - - for (; i < l; i++) { - obj[i].apply(this, args); - } -}; - -EventEmitter.prototype.listeners = function(type) { - return this._events[type] = this._events[type] || []; -}; - -export { EventEmitter }; diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts new file mode 100644 index 0000000000..092e439ed6 --- /dev/null +++ b/src/EventEmitter.ts @@ -0,0 +1,80 @@ +/** + * @license MIT + */ + +interface ListenerType { + (): void; + listener?: () => void; +}; + +export class EventEmitter { + private _events: {[type: string]: ListenerType[]}; + + constructor() { + this._events = this._events || {}; + } + + // TODO: Merge addListener and on, no reason for an alias in a private component + public addListener(type, listener): void { + this._events[type] = this._events[type] || []; + this._events[type].push(listener); + } + + public on(type, listener): void { + this.addListener(type, listener); + } + + // TODO: Merge removeListener and off, no reason for an alias in a private component + public removeListener(type, listener): void { + if (!this._events[type]) { + return; + } + + let obj = this._events[type]; + let i = obj.length; + + while (i--) { + if (obj[i] === listener || obj[i].listener === listener) { + obj.splice(i, 1); + return; + } + } + } + + public off(type, listener): void { + this.removeListener(type, listener); + } + + public removeAllListeners(type): void { + if (this._events[type]) { + delete this._events[type]; + } + } + + public once(type, listener): any { + function on() { + let args = Array.prototype.slice.call(arguments); + this.removeListener(type, on); + return listener.apply(this, args); + } + (on).listener = listener; + return this.on(type, on); + } + + public emit(type): void { + if (!this._events[type]) { + return; + } + + let args = Array.prototype.slice.call(arguments, 1); + let obj = this._events[type]; + + for (let i = 0; i < obj.length; i++) { + obj[i].apply(this, args); + } + } + + public listeners(type): ListenerType[] { + return this._events[type] || []; + } +} From dba432d1b6e5649e97a0d75e98a66768e8357a46 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 29 Dec 2016 05:09:47 -0800 Subject: [PATCH 2/5] Add tests --- src/EventEmitter.test.ts | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/EventEmitter.test.ts diff --git a/src/EventEmitter.test.ts b/src/EventEmitter.test.ts new file mode 100644 index 0000000000..9a0005ba63 --- /dev/null +++ b/src/EventEmitter.test.ts @@ -0,0 +1,84 @@ +import { assert } from 'chai'; +import { EventEmitter } from './EventEmitter'; + +describe('EventEmitter', () => { + let eventEmitter: EventEmitter; + + beforeEach(() => { + eventEmitter = new EventEmitter(); + }); + + describe('once', () => { + it('should trigger the listener only once', () => { + let count = 0; + const listener = () => count++; + eventEmitter.once('test', listener); + eventEmitter.emit('test'); + assert.equal(count, 1); + eventEmitter.emit('test'); + assert.equal(count, 1); + }); + }); + + describe('emit', () => { + it('should emit events to listeners', () => { + let count1 = 0; + let count2 = 0; + const listener1 = () => count1++; + const listener2 = () => count2++; + eventEmitter.on('test', listener1); + eventEmitter.on('test', listener2); + eventEmitter.emit('test'); + assert.equal(count1, 1); + assert.equal(count2, 1); + eventEmitter.emit('test'); + assert.equal(count1, 2); + assert.equal(count2, 2); + }); + + it('should manage multiple listener types', () => { + let count1 = 0; + let count2 = 0; + const listener1 = () => count1++; + const listener2 = () => count2++; + eventEmitter.on('test', listener1); + eventEmitter.on('foo', listener2); + eventEmitter.emit('test'); + assert.equal(count1, 1); + assert.equal(count2, 0); + eventEmitter.emit('foo'); + assert.equal(count1, 1); + assert.equal(count2, 1); + }); + }); + + describe('listeners', () => { + it('should return listeners for the type requested', () => { + assert.equal(eventEmitter.listeners('test').length, 0); + const listener = () => {}; + eventEmitter.addListener('test', listener); + assert.deepEqual(eventEmitter.listeners('test'), [listener]); + }); + }); + + describe('removeListener', () => { + it('should remove the specific listener', () => { + const listener1 = () => {}; + const listener2 = () => {}; + eventEmitter.addListener('foo', listener1); + eventEmitter.addListener('foo', listener2); + assert.equal(eventEmitter.listeners('foo').length, 2); + eventEmitter.removeListener('foo', listener1); + assert.deepEqual(eventEmitter.listeners('foo'), [listener2]); + }); + }); + + describe('removeAllListeners', () => { + it('should clear all listeners', () => { + eventEmitter.addListener('foo', () => {}); + assert.equal(eventEmitter.listeners('foo').length, 1); + eventEmitter.removeAllListeners('foo'); + assert.equal(eventEmitter.listeners('foo').length, 0); + }); + }); +}); From 937cf43e091f8752515d83a99ad7bd91f1389c08 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 31 Dec 2016 07:07:45 -0800 Subject: [PATCH 3/5] Remove redundant assignment in constructor --- src/EventEmitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts index 092e439ed6..4849ed0bd5 100644 --- a/src/EventEmitter.ts +++ b/src/EventEmitter.ts @@ -11,7 +11,7 @@ export class EventEmitter { private _events: {[type: string]: ListenerType[]}; constructor() { - this._events = this._events || {}; + this._events = {}; } // TODO: Merge addListener and on, no reason for an alias in a private component From 0d9d3aca1a2d306f456ef95d2e33c7941a231c3a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 31 Dec 2016 07:10:45 -0800 Subject: [PATCH 4/5] Remove addListener/removeListener --- src/EventEmitter.test.ts | 12 ++++++------ src/EventEmitter.ts | 14 ++------------ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/EventEmitter.test.ts b/src/EventEmitter.test.ts index 9a0005ba63..7ae381c8ab 100644 --- a/src/EventEmitter.test.ts +++ b/src/EventEmitter.test.ts @@ -56,26 +56,26 @@ describe('EventEmitter', () => { it('should return listeners for the type requested', () => { assert.equal(eventEmitter.listeners('test').length, 0); const listener = () => {}; - eventEmitter.addListener('test', listener); + eventEmitter.on('test', listener); assert.deepEqual(eventEmitter.listeners('test'), [listener]); }); }); - describe('removeListener', () => { + describe('off', () => { it('should remove the specific listener', () => { const listener1 = () => {}; const listener2 = () => {}; - eventEmitter.addListener('foo', listener1); - eventEmitter.addListener('foo', listener2); + eventEmitter.on('foo', listener1); + eventEmitter.on('foo', listener2); assert.equal(eventEmitter.listeners('foo').length, 2); - eventEmitter.removeListener('foo', listener1); + eventEmitter.off('foo', listener1); assert.deepEqual(eventEmitter.listeners('foo'), [listener2]); }); }); describe('removeAllListeners', () => { it('should clear all listeners', () => { - eventEmitter.addListener('foo', () => {}); + eventEmitter.on('foo', () => {}); assert.equal(eventEmitter.listeners('foo').length, 1); eventEmitter.removeAllListeners('foo'); assert.equal(eventEmitter.listeners('foo').length, 0); diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts index 4849ed0bd5..80bd303515 100644 --- a/src/EventEmitter.ts +++ b/src/EventEmitter.ts @@ -14,18 +14,12 @@ export class EventEmitter { this._events = {}; } - // TODO: Merge addListener and on, no reason for an alias in a private component - public addListener(type, listener): void { + public on(type, listener): void { this._events[type] = this._events[type] || []; this._events[type].push(listener); } - public on(type, listener): void { - this.addListener(type, listener); - } - - // TODO: Merge removeListener and off, no reason for an alias in a private component - public removeListener(type, listener): void { + public off(type, listener): void { if (!this._events[type]) { return; } @@ -41,10 +35,6 @@ export class EventEmitter { } } - public off(type, listener): void { - this.removeListener(type, listener); - } - public removeAllListeners(type): void { if (this._events[type]) { delete this._events[type]; From 965ebde22f6ad8f7ea4866a5133cd78302918203 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 31 Dec 2016 07:11:47 -0800 Subject: [PATCH 5/5] Remove remaining removeListener ref --- src/EventEmitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts index 80bd303515..d05e3863a1 100644 --- a/src/EventEmitter.ts +++ b/src/EventEmitter.ts @@ -44,7 +44,7 @@ export class EventEmitter { public once(type, listener): any { function on() { let args = Array.prototype.slice.call(arguments); - this.removeListener(type, on); + this.off(type, on); return listener.apply(this, args); } (on).listener = listener;