-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
on-delegate-it.js
160 lines (137 loc) · 3.49 KB
/
on-delegate-it.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// import delegate from 'delegate-it'
import * as delegate from 'delegated-events'
import tuple from 'immutable-tuple'
import { isElement } from './util'
const cache = new WeakMap()
export default function on(target, evt, fn = () => { }) {
let seq = evt.split(/\s*\>\s*/)
// on(target, 'mousedown > mousemove > mouseup', fn => fn => fn)
if (seq.length > 1) return onSequence(target, seq, fn)
let resolve, p, off
p = new Promise(ok => { resolve = ok })
// no-fn awaits for event
if (!fn) fn = (() => off())
// on(target, 'mousedown touchdown', fn)
let evts = evt.split(/\s+/)
if (evts.length > 1) {
let offs = evts.map(evt => on(target, evt, fn))
Promise.race(offs).then(e => {
fn(e)
resolve(e)
p = new Promise(ok => { resolve = ok })
off.then = p.then.bind(p)
})
off = () => offs.forEach(off => off())
off.then = p.then.bind(p)
return off
}
// on(target, 'click', fn)
let key = tuple(target, evt)
let listeners = cache.get(key)
if (!listeners) cache.set(key, listeners = [])
let cb = e => {
fn(e)
resolve(e)
p = new Promise(ok => { resolve = ok })
off.then = p.then.bind(p)
}
listeners.push(cb)
if (typeof target === 'string') {
delegate.on(evt, target, cb)
off = () => {
delegate.off(evt, target, cb)
let idx = listeners.indexOf(cb)
if (idx >= 0) listeners.splice(idx, 1)
}
}
else if (target.addEventListener) {
target.addEventListener(evt, cb)
off = () => {
target.removeEventListener(evt, cb)
let idx = listeners.indexOf(cb)
if (idx >= 0) listeners.splice(idx, 1)
}
}
// non-dom targets
// not intended to be used directly, just as common event bus
else {
let gatedCb = (e) => {
if (e.detail.target !== target) return
cb(e)
}
document.addEventListener(evt, gatedCb)
off = () => {
document.removeEventListener(evt, gatedCb)
}
}
off.then = p.then.bind(p)
return off
}
// TODO: that can be a separate module
export function onSequence(target, seq, fn) {
let currSeq = [], currFn, enabled = true
let resolve, p
p = new Promise(ok => { resolve = ok })
; (async () => {
while (enabled) {
// reset cycle
if (!currSeq.length) {
currSeq = [...seq]
currFn = fn
}
let evt = currSeq.shift()
let off = on(target, evt)
let e = await off
off()
currFn = currFn(e)
resolve(e)
p = new Promise(ok => { resolve = ok })
offSeq.then = p.then.bind(p)
}
})()
function offSeq() {
enabled = false
off()
}
offSeq.then = p.then.bind(p)
return offSeq
}
export function fire(target, evt, detail = {}) {
if (typeof target === 'string') {
target = document.querySelectorAll(target)
}
// redirect target
if (!target.dispatchEvent) {
detail.target = target
target = document
}
if (evt instanceof Event) {
target.dispatchEvent(evt)
}
else {
evt.split(/\s+/).forEach(evt => {
if (target.dispatchEvent) {
target.dispatchEvent(new CustomEvent(evt, {
bubbles: true,
cancelable: true,
detail
}))
}
})
}
return this
}
export function fire(target, evt, o) {
if (evt instanceof Event) {
target.dispatchEvent(evt)
}
else {
evt = evt.split(/\s+/)
evt.forEach(evt => target.dispatchEvent(new CustomEvent(evt, {
bubbles: true,
cancelable: true,
detail: o
})))
}
return this
}