-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathevents.js
131 lines (129 loc) · 3.02 KB
/
events.js
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
class EventBus {
constructor() {
this._listeners = {};
this.wildcards = new Set();
}
addRemote(remoteFn) {
// We listen for all own events and forward
// them to the remote callback function
return this.onAll((eventName, data) => {
remoteFn(eventName, data)
})
}
has(eventName) {
return this.getListeners(eventName).size;
}
getListeners(eventName) {
if(this._listeners[eventName]) {
return this._listeners[eventName];
}
return (this._listeners[eventName] = new Set());
}
// on(event, fn)
// on({event: fn, ...})
// on(document, {event: fn})
// on(document, event, fn)
on(...args) {
let context, eventName, fn;
if(args.length === 3) {
[context, eventName, fn] = args;
}
else if(args.length === 2) {
// context, map
if(typeof args[1] === 'object') {
[context, eventName] = args;
}
// name, fn
else {
[eventName, fn] = args;
context = this;
}
}
// event map
else if(args.length) {
eventName = args[0];
context = this;
}
else {
throw new Error(`on requires arguments`);
}
// Event map, possibly with context
if(typeof eventName === 'object') {
// eventName is map
return this.onMap(context, eventName)
}
if(!fn) {
throw new Error(`binding ${fn} listener to ${eventName}`)
}
// Wildcard
if(eventName === '*') {
return this.onAll(fn);
}
if(context === this) {
let listeners = this.getListeners(eventName);
listeners.add(fn);
return listeners.delete.bind(listeners, fn);
}
if(!context) {
throw new Error(`on is missing context`)
}
if(context.addEventListener) {
context.addEventListener(eventName, fn);
return context.removeEventListener.bind(context, eventName, fn);
}
console.info(context);
throw new Error(`on unable to bind to unknown context type`)
}
onAll(fn) {
this.wildcards.add(fn);
return this.wildcards.delete.bind(this.wildcards, fn);
}
onMap(...args) {
let context, eventMap;
if(args.length === 2) {
[context, eventMap] = args;
}
else if(args.length) {
[eventMap] = args[0];
}
else {
throw new Error(`onMap requires arguments`);
}
// Map is keyed by event name, with vals being handlers
var unbinders = [];
for(let [eventName, fn] of Object.entries(eventMap)) {
// Keep track of all unbind functions
unbinders.push(this.on(context, eventName, fn));
}
// Return the off function, which calls the stored offs
return () => {
if(unbinders) {
unbinders.forEach(unbinder => unbinder())
}
// Ensure we don't call off multiple times
unbinders = null;
}
}
off(eventName, fn) {
var listeners = this.getListeners(eventName);
if(fn) {
return listeners.delete(fn);
}
else {
return listeners.clear();
}
}
emit(eventName, data=null, originalEv=null) {
for(let fn of this.getListeners(eventName)) {
if(!fn) {
console.log('no listener', eventName);
}
fn(data, originalEv);
}
for(let fn of this.wildcards) {
fn(eventName, data, originalEv);
}
}
}
EventBus.prototype.trigger = EventBus.prototype.emit;
module.exports = EventBus;