UNPKG

alm

Version:

The best IDE for TypeScript

302 lines (249 loc) 7.66 kB
export class EmitterEvent { private _type:string; private _data:any; constructor(eventType:string=null, data:any=null) { this._type = eventType; this._data = data; } public getType():string { return this._type; } public getData():any { return this._data; } } export interface ListenerCallback { (value:any):void; } export interface BulkListenerCallback { (value:EmitterEvent[]):void; } export interface IEventEmitter extends IDisposable { addListener2(eventType:string, listener:ListenerCallback):IDisposable; addOneTimeDisposableListener(eventType:string, listener:ListenerCallback):IDisposable; addBulkListener2(listener:BulkListenerCallback):IDisposable; addEmitter2(eventEmitter:IEventEmitter):IDisposable; } export interface IListenersMap { [key:string]:ListenerCallback[]; } export class EventEmitter implements IEventEmitter { protected _listeners:IListenersMap; protected _bulkListeners:ListenerCallback[]; private _collectedEvents:EmitterEvent[]; private _deferredCnt:number; private _allowedEventTypes:{[eventType:string]:boolean;}; constructor(allowedEventTypes:string[] = null) { this._listeners = {}; this._bulkListeners = []; this._collectedEvents = []; this._deferredCnt = 0; if (allowedEventTypes) { this._allowedEventTypes = {}; for (var i = 0; i < allowedEventTypes.length; i++) { this._allowedEventTypes[allowedEventTypes[i]] = true; } } else { this._allowedEventTypes = null; } } public dispose(): void { this._listeners = {}; this._bulkListeners = []; this._collectedEvents = []; this._deferredCnt = 0; this._allowedEventTypes = null; } private addListener(eventType:string, listener:ListenerCallback):IDisposable { if (eventType === '*') { throw new Error('Use addBulkListener(listener) to register your listener!'); } if (this._allowedEventTypes && !this._allowedEventTypes.hasOwnProperty(eventType)) { throw new Error('This object will never emit this event type!'); } if (this._listeners.hasOwnProperty(eventType)) { this._listeners[eventType].push(listener); } else { this._listeners[eventType] = [listener]; } var bound = this; return { dispose: () => { if (!bound) { // Already called return; } bound._removeListener(eventType, listener); // Prevent leakers from holding on to the event emitter bound = null; listener = null; } }; } public addListener2(eventType:string, listener:ListenerCallback):IDisposable { return this.addListener(eventType, listener); } private addOneTimeListener(eventType:string, listener:ListenerCallback):IDisposable { var unbind = this.addListener(eventType, (value:any) => { unbind.dispose(); listener(value); }); return unbind; } public addOneTimeDisposableListener(eventType:string, listener:ListenerCallback):IDisposable { return this.addOneTimeListener(eventType, listener); } protected addBulkListener(listener:BulkListenerCallback):IDisposable { this._bulkListeners.push(listener); return { dispose: () => { this._removeBulkListener(listener); } }; } public addBulkListener2(listener:BulkListenerCallback):IDisposable { return this.addBulkListener(listener); } private addEmitter(eventEmitter:IEventEmitter):IDisposable { return eventEmitter.addBulkListener2((events:EmitterEvent[]):void => { var newEvents = events; if (this._deferredCnt === 0) { this._emitEvents(<EmitterEvent[]>newEvents); } else { // Collect for later this._collectedEvents.push.apply(this._collectedEvents, newEvents); } }); } public addEmitter2(eventEmitter:IEventEmitter):IDisposable { return this.addEmitter(eventEmitter); } private _removeListener(eventType:string, listener:ListenerCallback): void { if (this._listeners.hasOwnProperty(eventType)) { var listeners = this._listeners[eventType]; for (var i = 0, len = listeners.length; i < len; i++) { if (listeners[i] === listener) { listeners.splice(i, 1); break; } } } } private _removeBulkListener(listener:BulkListenerCallback): void { for (var i = 0, len = this._bulkListeners.length; i < len; i++) { if (this._bulkListeners[i] === listener) { this._bulkListeners.splice(i, 1); break; } } } protected _emitToSpecificTypeListeners(eventType:string, data:any): void { if (this._listeners.hasOwnProperty(eventType)) { var listeners = this._listeners[eventType].slice(0); for (var i = 0, len = listeners.length; i < len; i++) { safeInvoke1Arg(listeners[i], data); } } } protected _emitToBulkListeners(events:EmitterEvent[]): void { var bulkListeners = this._bulkListeners.slice(0); for (var i = 0, len = bulkListeners.length; i < len; i++) { safeInvoke1Arg(bulkListeners[i], events); } } protected _emitEvents(events:EmitterEvent[]): void { if (this._bulkListeners.length > 0) { this._emitToBulkListeners(events); } for (var i = 0, len = events.length; i < len; i++) { var e = events[i]; this._emitToSpecificTypeListeners(e.getType(), e.getData()); } } public emit(eventType:string, data:any={}):void { if (this._allowedEventTypes && !this._allowedEventTypes.hasOwnProperty(eventType)) { throw new Error('Cannot emit this event type because it wasn\'t white-listed!'); } // Early return if no listeners would get this if (!this._listeners.hasOwnProperty(eventType) && this._bulkListeners.length === 0) { return; } var emitterEvent = new EmitterEvent(eventType, data); if (this._deferredCnt === 0) { this._emitEvents([emitterEvent]); } else { // Collect for later this._collectedEvents.push(emitterEvent); } } public deferredEmit(callback:()=>any):any { this._deferredCnt = this._deferredCnt + 1; var result: any = safeInvokeNoArg(callback); this._deferredCnt = this._deferredCnt - 1; if (this._deferredCnt === 0) { this._emitCollected(); } return result; } private _emitCollected(): void { // Flush collected events var events = this._collectedEvents; this._collectedEvents = []; if (events.length > 0) { this._emitEvents(events); } } } class EmitQueueElement { public target: Function; public arg: any; constructor(target: Function, arg: any) { this.target = target; this.arg = arg; } } /** * Same as EventEmitter, but guarantees events are delivered in order to each listener */ export class OrderGuaranteeEventEmitter extends EventEmitter { private _emitQueue: EmitQueueElement[]; constructor(allowedEventTypes:string[] = null) { super(allowedEventTypes); this._emitQueue = []; } protected _emitToSpecificTypeListeners(eventType:string, data:any): void { if (this._listeners.hasOwnProperty(eventType)) { let listeners = this._listeners[eventType]; for (let i = 0, len = listeners.length; i < len; i++) { this._emitQueue.push(new EmitQueueElement(listeners[i], data)); } } } protected _emitToBulkListeners(events:EmitterEvent[]): void { let bulkListeners = this._bulkListeners; for (let i = 0, len = bulkListeners.length; i < len; i++) { this._emitQueue.push(new EmitQueueElement(bulkListeners[i], events)); } } protected _emitEvents(events:EmitterEvent[]): void { super._emitEvents(events); while (this._emitQueue.length > 0) { let queueElement = this._emitQueue.shift(); safeInvoke1Arg(queueElement.target, queueElement.arg); } } } function safeInvokeNoArg(func:Function): any { try { return func(); } catch(e) { console.error(e); } } function safeInvoke1Arg(func:Function, arg1:any): any { try { return func(arg1); } catch(e) { console.error(e); } }