/
pubsub.ts
139 lines (123 loc) · 2.88 KB
/
pubsub.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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
type Fn = () => void;
interface PubSubInterface {
on(name: string, fn: Fn): void;
off(name: string, fn: Fn): void;
trigger(name: string, data: any): void;
clear(): void;
}
/**
* Creates a new pubsub instance
*/
class PubSub implements PubSubInterface {
private listeners: { [key: string]: Fn[] } = {};
/**
*
* @param name - event name
* @param callback - callback will fired if its matches
* @returns {boolean}
*/
private hasWildcard(name: string, callback: (key: string) => void): boolean {
const index = name.indexOf('*');
if (index > -1) {
const nameWithoutAsterix = name.slice(0, index);
const keys = Object.keys(this.listeners);
for (const key of keys) {
if (key.startsWith(nameWithoutAsterix)) {
callback(key);
}
}
return true;
}
return false;
}
/**
* it registers a new event with provided values
*
* @example
* pubsub.on('test', 'hello world');
*
* @param name {string} - event name
* @param fn - {function} - callback function
* @returns {void}
*/
on(name: string, fn: Fn) {
this.listeners[name] = this.listeners[name] || [];
this.listeners[name].push(fn);
}
/**
* it removes an event from event listeners
*
* @example
* pubsub.off('test', predefinedFn);
*
* @example
* // with wildcard
* pubsub.off('test.*');
*
* @param name {string} - event name
* @param fn {function} callback function
* @returns {void}
*/
off(name: string, fn?: Fn) {
const completed = this.hasWildcard(name, (key) => delete this.listeners[key]);
if (completed) {
return true;
}
if (this.listeners[name]) {
for (let i = 0; i < this.listeners[name].length; i++) {
if (this.listeners[name][i] === fn) {
this.listeners[name].splice(i, 1);
break;
}
}
}
}
/**
* it trigger an event with spesified data
*
* @example
* * // without any data
* pubsub.trigger("test");
* @example
* // with string type data
* pubsub.trigger("test", "nothing");
* @example
* // with object type data
* pubsub.trigger("test", {'hello', 'world'});
*
* @example
* // with wildcard
* pubsub.trigger("test.*", "data");
*
* @param name event name
* @param data callback data
* @returns {void}
*/
trigger(name: string, data?: any) {
const triggerAll = (key: string) => {
this.listeners[key].forEach((fn: (data: any) => void) => {
fn(data);
});
};
const completed = this.hasWildcard(name, (key) => {
triggerAll(key);
});
if (completed) {
return true;
}
if (this.listeners[name]) {
triggerAll(name);
}
}
/**
* clears all listener
* @returns {void}
*/
clear() {
this.listeners = {};
}
_listeners() {
return this.listeners;
}
}
export default new PubSub();