alm
Version:
The best IDE for TypeScript
302 lines (249 loc) • 7.66 kB
text/typescript
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);
}
}