173 lines
5.6 KiB
TypeScript
173 lines
5.6 KiB
TypeScript
/*
|
|
* https://github.com/morethanwords/tweb
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
*/
|
|
|
|
// import { MOUNT_CLASS_TO } from "../config/debug";
|
|
import type {ArgumentTypes, SuperReturnType} from '../types';
|
|
import findAndSplice from './array/findAndSplice';
|
|
|
|
// class EventSystem {
|
|
// wm: WeakMap<any, Record<any, Set<any>>> = new WeakMap();
|
|
|
|
// add(target: any, event: any, listener: any) {
|
|
// let listeners = this.wm.get(target);
|
|
// if (listeners === undefined) {
|
|
// listeners = {};
|
|
// }
|
|
// let listenersForEvent = listeners[event];
|
|
// if (listenersForEvent === undefined) {
|
|
// listenersForEvent = new Set();
|
|
// }
|
|
// listenersForEvent.add(listener);
|
|
// listeners[event] = listenersForEvent;
|
|
// //target.addEventListener(event, listener);
|
|
// this.wm.set(target, listeners);
|
|
// };
|
|
|
|
// remove(target: any, event: any, listener: any) {
|
|
// let listeners = this.wm.get(target);
|
|
// if (!listeners) return;
|
|
// let listenersForEvent = listeners[event];
|
|
// if (!listenersForEvent) return;
|
|
// listenersForEvent.delete(listener);
|
|
// };
|
|
|
|
// /* fire(target, event) {
|
|
// let listeners = this.wm.get(target);
|
|
// if (!listeners) return;
|
|
// let listenersForEvent = listeners[event];
|
|
// if (!listenersForEvent) return;
|
|
// for (let handler of handlers) {
|
|
// setTimeout(handler, 0, event, target); // we use a setTimeout here because we want event triggering to be asynchronous.
|
|
// }
|
|
// }; */
|
|
// }
|
|
|
|
// console.log = () => {};
|
|
|
|
// const e = new EventSystem();
|
|
// MOUNT_CLASS_TO.e = e;
|
|
|
|
export type EventListenerListeners = Record<string, Function>;
|
|
// export type EventListenerListeners = Record<string, (...args: any[]) => any>;
|
|
// export type EventListenerListeners = {[name in string]: Function};
|
|
|
|
/**
|
|
* Better not to remove listeners during setting
|
|
* Should add listener callback only once
|
|
*/
|
|
|
|
type ListenerObject<T> = {callback: T, options: boolean | AddEventListenerOptions};
|
|
|
|
// type EventLitenerCallback<T> = (data: T) =>
|
|
// export default class EventListenerBase<Listeners extends {[name: string]: Function}> {
|
|
export default class EventListenerBase<Listeners extends EventListenerListeners> {
|
|
protected listeners: Partial<{
|
|
[k in keyof Listeners]: Array<ListenerObject<Listeners[k]>>
|
|
}>;
|
|
protected listenerResults: Partial<{
|
|
[k in keyof Listeners]: ArgumentTypes<Listeners[k]>
|
|
}>;
|
|
|
|
private reuseResults: boolean;
|
|
|
|
constructor(reuseResults?: boolean) {
|
|
this._constructor(reuseResults);
|
|
}
|
|
|
|
public _constructor(reuseResults?: boolean): any {
|
|
this.reuseResults = reuseResults;
|
|
this.listeners = {};
|
|
this.listenerResults = {};
|
|
}
|
|
|
|
public addEventListener<T extends keyof Listeners>(name: T, callback: Listeners[T], options?: boolean | AddEventListenerOptions) {
|
|
(this.listeners[name] ??= []).push({callback, options}); // ! add before because if you don't, you won't be able to delete it from callback
|
|
|
|
if(this.listenerResults.hasOwnProperty(name)) {
|
|
callback(...this.listenerResults[name]);
|
|
|
|
if((options as AddEventListenerOptions)?.once) {
|
|
this.listeners[name].pop();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// e.add(this, name, {callback, once});
|
|
}
|
|
|
|
public addMultipleEventsListeners(obj: {
|
|
[name in keyof Listeners]?: Listeners[name]
|
|
}) {
|
|
for(const i in obj) {
|
|
this.addEventListener(i, obj[i]);
|
|
}
|
|
}
|
|
|
|
public removeEventListener<T extends keyof Listeners>(name: T, callback: Listeners[T], options?: boolean | AddEventListenerOptions) {
|
|
if(this.listeners[name]) {
|
|
findAndSplice(this.listeners[name], (l) => l.callback === callback);
|
|
}
|
|
// e.remove(this, name, callback);
|
|
}
|
|
|
|
protected invokeListenerCallback<T extends keyof Listeners, L extends ListenerObject<any>>(name: T, listener: L, ...args: ArgumentTypes<L['callback']>) {
|
|
let result: any;
|
|
try {
|
|
result = listener.callback(...args);
|
|
} catch(err) {
|
|
console.error(err);
|
|
}
|
|
|
|
if((listener.options as AddEventListenerOptions)?.once) {
|
|
this.removeEventListener(name, listener.callback);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private _dispatchEvent<T extends keyof Listeners>(name: T, collectResults: boolean, ...args: ArgumentTypes<Listeners[T]>) {
|
|
if(this.reuseResults) {
|
|
this.listenerResults[name] = args;
|
|
}
|
|
|
|
const arr: Array<SuperReturnType<Listeners[typeof name]>> = collectResults && [];
|
|
|
|
const listeners = this.listeners[name];
|
|
if(listeners) {
|
|
// ! this one will guarantee execution even if delete another listener during setting
|
|
const left = listeners.slice();
|
|
left.forEach((listener) => {
|
|
const index = listeners.findIndex((l) => l.callback === listener.callback);
|
|
if(index === -1) {
|
|
return;
|
|
}
|
|
|
|
const result = this.invokeListenerCallback(name, listener, ...args);
|
|
if(arr) {
|
|
arr.push(result);
|
|
}
|
|
});
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
public dispatchResultableEvent<T extends keyof Listeners>(name: T, ...args: ArgumentTypes<Listeners[T]>) {
|
|
return this._dispatchEvent(name, true, ...args);
|
|
}
|
|
|
|
// * must be protected, but who cares
|
|
public dispatchEvent<L extends EventListenerListeners = Listeners, T extends keyof L = keyof L>(name: T, ...args: ArgumentTypes<L[T]>) {
|
|
// @ts-ignore
|
|
this._dispatchEvent(name, false, ...args);
|
|
}
|
|
|
|
public cleanup() {
|
|
this.listeners = {};
|
|
this.listenerResults = {};
|
|
}
|
|
}
|