mana-common
Version:
Common utils for mana
185 lines (143 loc) • 3.84 kB
JavaScript
let _Symbol$iterator;
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Disposable } from './disposable';
export var Event;
(function (Event) {
Event.None = () => Disposable.NONE;
function map(event, mapFunc) {
return (listener, context) => event(i => listener.call(context, mapFunc(i)), undefined);
}
Event.map = map;
})(Event || (Event = {}));
_Symbol$iterator = Symbol.iterator;
class CallbackList {
constructor(mono = false) {
this.mono = void 0;
this._callbacks = void 0;
this.mono = mono;
}
get callbacks() {
if (!this._callbacks) {
this._callbacks = [];
}
return this._callbacks;
}
get length() {
return this.callbacks.length;
}
add(callback, context = undefined) {
this.callbacks.push([callback, context]);
}
remove(callback, context = undefined) {
if (this.isEmpty()) return;
let foundCallbackWithDifferentContext = false;
for (let i = 0; i < this.length; i += 1) {
if (this.callbacks[i][0] === callback) {
if (this.callbacks[i][1] === context) {
// remove when callback & context match
this.callbacks.splice(i, 1);
return;
}
foundCallbackWithDifferentContext = true;
}
}
if (foundCallbackWithDifferentContext) {
throw new Error('You should remove it with the same context you add it');
}
}
[_Symbol$iterator]() {
if (this.isEmpty()) {
return [][Symbol.iterator]();
}
const callbacks = this.callbacks.slice(0);
return callbacks.map(callback => (...args) => callback[0].apply(callback[1], args))[Symbol.iterator]();
}
invoke(...args) {
const ret = [];
for (const callback of this) {
try {
ret.push(callback(...args));
} catch (e) {
console.error(e);
}
}
return ret;
}
isEmpty() {
return this.callbacks.length === 0;
}
dispose() {
this._callbacks = undefined;
}
}
export class Emitter {
constructor(options = {}) {
this.options = void 0;
this._event = void 0;
this._callbacks = void 0;
this.disposed = false;
this.options = options;
}
get callbacks() {
if (!this._callbacks) {
this._callbacks = new CallbackList();
}
return this._callbacks;
}
/**
* For the public to allow to subscribe
* to events from this Emitter
*/
get event() {
if (!this._event) {
this._event = (listener, thisArgs) => {
if (this.options.onFirstListenerAdd && this.callbacks.isEmpty()) {
this.options.onFirstListenerAdd(this);
}
this.callbacks.add(listener, thisArgs);
const result = {
dispose: () => {
result.dispose = Emitter.noop;
if (!this.disposed) {
this.callbacks.remove(listener, thisArgs);
result.dispose = Emitter.noop;
if (this.options.onLastListenerRemove && this.callbacks.isEmpty()) {
this.options.onLastListenerRemove(this);
}
}
}
};
return result;
};
}
return this._event;
}
fire(event) {
if (!this._callbacks) {
return;
}
this.callbacks.invoke(event);
}
/**
* Process each listener one by one.
* Return `false` to stop iterating over the listeners, `true` to continue.
*/
async sequence(processor) {
for (const listener of this.callbacks) {
// eslint-disable-next-line no-await-in-loop
const result = await processor(listener);
if (!result) {
break;
}
}
}
dispose() {
if (this._callbacks) {
this._callbacks.dispose();
this._callbacks = undefined;
}
this.disposed = true;
}
}
Emitter.noop = () => {};