-
Notifications
You must be signed in to change notification settings - Fork 3
/
dispatcher.ts
84 lines (77 loc) · 2.59 KB
/
dispatcher.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { ArgumentTypes } from './types';
/**
* A dispatcher is an abstract object which holds a list of listeners
* to which data during certain events can be dispatched, by calling functions implemented by listeners.
*
*/
export abstract class Dispatcher<T> {
/**
* The list of listeners for this dispatcher.
*/
protected _listeners: Partial<T>[];
/**
* Locked listeners.
* Those listeners in this array can not be removed anymore.
*/
protected _lockedListeners: Partial<T>[];
/**
* Creates an instance of Dispatcher.
*/
constructor() {
this._listeners = [];
this._lockedListeners = [];
}
/**
* The current listeners for this dispatcher.
*/
get listeners(): readonly Partial<T>[] {
return this._listeners.slice();
}
/**
* Adds the given listener to this entity.
*
* @param listener
* @return Whether the listener has been added or not.
* It may not be added, if already present in the listener list.
*/
addListener(listener: Partial<T>, lock = false): boolean {
if (this._listeners.indexOf(listener) >= 0) return false;
this._listeners.push(listener);
if (lock) this._lockedListeners.push(listener);
return true;
}
/**
* Removes the given listener or the listener at the given index.
*
* @param listenerOrIndex
* @return Whether the listener has been removed or not.
* It may not have been removed, if it was not in the listener list.
*/
removeListener(listenerOrIndex: Partial<T> | number): boolean {
const idx = typeof listenerOrIndex === 'number' ? listenerOrIndex : this._listeners.indexOf(listenerOrIndex);
if (idx >= 0 && idx < this._listeners.length) {
const listener = this._listeners[idx];
const isLocked = this._lockedListeners.indexOf(listener) >= 0;
if (isLocked) throw new Error(`Listener at index ${idx} is locked.`);
this._listeners.splice(idx, 1);
return true;
}
return false;
}
/**
* Dispatches the given arguments by calling the given function name
* on each listener, if implemented.
* Note that the listener's scope will be used, when the listener's function gets called.
*
* @param name The function name to call.
* @param args The arguments to pass to the function.
*/
dispatch<K extends keyof T>(name: K, ...args: ArgumentTypes<T[K]>): void {
// TODO: optimize this; cache the listeners with the given function name
this._listeners.forEach(listener => {
const fn = listener[name];
if (typeof fn !== 'function') return;
fn.apply(listener, args);
});
}
}