neo-neo-bblessed
Version:
A fork of neo-blessed (which is a fork of blessed) with bug fixes and maintenance.
213 lines (178 loc) • 5.63 kB
text/typescript
/**
* events.js - event emitter for blessed
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
* https://github.com/chjj/blessed
*/
const slice = Array.prototype.slice;
/**
* Type definitions
*/
type Listener = (...args: any[]) => any;
type Handler = Listener | Listener[];
interface ListenerWithOriginal extends Listener {
listener?: Listener;
}
interface Events {
[type: string]: Handler;
}
interface EventEmitterInterface {
_events?: Events;
_maxListeners?: number;
type?: string;
parent?: EventEmitterInterface;
}
/**
* EventEmitter
*/
function EventEmitter(this: EventEmitterInterface) {
if (!this._events) this._events = {};
}
EventEmitter.prototype.setMaxListeners = function(this: EventEmitterInterface, n: number): void {
this._maxListeners = n;
};
EventEmitter.prototype.addListener = function(this: EventEmitterInterface, type: string, listener: Listener): void {
if (!this._events![type]) {
this._events![type] = listener;
} else if (typeof this._events![type] === 'function') {
this._events![type] = [this._events![type] as Listener, listener];
} else {
(this._events![type] as Listener[]).push(listener);
}
this._emit('newListener', [type, listener]);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.removeListener = function(this: EventEmitterInterface, type: string, listener: Listener): void {
const handler = this._events![type];
if (!handler) return;
if (typeof handler === 'function' || (handler as Listener[]).length === 1) {
delete this._events![type];
this._emit('removeListener', [type, listener]);
return;
}
const handlers = handler as ListenerWithOriginal[];
for (let i = 0; i < handlers.length; i++) {
if (handlers[i] === listener || handlers[i].listener === listener) {
handlers.splice(i, 1);
this._emit('removeListener', [type, listener]);
return;
}
}
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = function(this: EventEmitterInterface, type?: string): void {
if (type) {
delete this._events![type];
} else {
this._events = {};
}
};
EventEmitter.prototype.once = function(this: EventEmitterInterface, type: string, listener: Listener): EventEmitterInterface {
const self = this;
function on(this: EventEmitterInterface, ...args: any[]): any {
self.removeListener(type, on);
return listener.apply(this, args);
}
(on as ListenerWithOriginal).listener = listener;
return this.on(type, on);
};
EventEmitter.prototype.listeners = function(this: EventEmitterInterface, type: string): Listener[] {
const handler = this._events![type];
return typeof handler === 'function' ? [handler] : (handler as Listener[]) || [];
};
EventEmitter.prototype._emit = function(this: EventEmitterInterface, type: string, args: any[]): any {
const handler = this._events![type];
let ret: any;
// if (type !== 'event') {
// this._emit('event', [type.replace(/^element /, '')].concat(args));
// }
if (!handler) {
if (type === 'error') {
throw new args[0];
}
return;
}
if (typeof handler === 'function') {
return handler.apply(this, args);
}
const handlers = handler as Listener[];
for (let i = 0; i < handlers.length; i++) {
if (handlers[i].apply(this, args) === false) {
ret = false;
}
}
return ret !== false;
};
EventEmitter.prototype.emit = function(this: EventEmitterInterface, type: string, ...eventArgs: any[]): boolean {
const args = slice.call(arguments, 1);
const params = slice.call(arguments);
let el: EventEmitterInterface | undefined = this;
this._emit('event', params);
if (this.type === 'screen') {
return this._emit(type, args);
}
if (this._emit(type, args) === false) {
return false;
}
const elementType = 'element ' + type;
args.unshift(this);
// `element` prefix
// params = [elementType].concat(args);
// no `element` prefix
// params.splice(1, 0, this);
do {
// el._emit('event', params);
if (!el._events![elementType]) continue;
if (el._emit(elementType, args) === false) {
return false;
}
} while (el = el.parent);
return true;
};
// For hooking into the main EventEmitter if we want to.
// Might be better to do things this way being that it
// will always be compatible with node, not to mention
// it gives us domain support as well.
// Node.prototype._emit = Node.prototype.emit;
// Node.prototype.emit = function(type) {
// var args, el;
//
// if (this.type === 'screen') {
// return this._emit.apply(this, arguments);
// }
//
// this._emit.apply(this, arguments);
// if (this._bubbleStopped) return false;
//
// args = slice.call(arguments, 1);
// el = this;
//
// args.unshift('element ' + type, this);
// this._bubbleStopped = false;
// //args.push(stopBubble);
//
// do {
// if (!el._events || !el._events[type]) continue;
// el._emit.apply(el, args);
// if (this._bubbleStopped) return false;
// } while (el = el.parent);
//
// return true;
// };
//
// Node.prototype._addListener = Node.prototype.addListener;
// Node.prototype.on =
// Node.prototype.addListener = function(type, listener) {
// function on() {
// if (listener.apply(this, arguments) === false) {
// this._bubbleStopped = true;
// }
// }
// on.listener = listener;
// return this._addListener(type, on);
// };
/**
* Expose
*/
const exports = EventEmitter as any;
exports.EventEmitter = EventEmitter;
module.exports = exports;