js/listener.js
DarkWiiPlayer f612273632 Rename bind to bindContent in listener.js
This is partly to avoid confusion with the skooma `bind` function,
but also to make it clearer that only the inner content of an element is
being bound to the listener.

This function may also disappear entirely in the future if it turns out
it doesn't provide any benefits over the skooma bind function.
2021-11-25 10:08:52 +01:00

60 lines
1.5 KiB
JavaScript

/*
A somewhat refined Proxy generator that lets you selectively listen to changed
properties and react accordingly.
Example:
const l = listener()
l.listen("contract", contract => speaker.handle(contract))
l.contract = new Contract()
*/
export const listener = (target={}) => {
let callbacks = new Map()
function listen(prop, callback) {
if ("object" == typeof prop && "forEach" in prop) {
prop.forEach(prop => this.listen(prop, callback))
} else if (callback) {
let list = callbacks.get(prop)
if (!list) callbacks.set(prop, list=[])
list.push(callback)
} else {
callbacks.delete(prop)
}
}
let proxy = new Proxy(target, {
set: (target, prop, value) => {
if (callbacks.has("*")) callbacks.get("*").forEach(callback => callback(value, prop, target[prop]))
if (callbacks.has(prop)) callbacks.get(prop).forEach(callback => callback(value, prop, target[prop]))
return Reflect.set(target, prop, value)
},
get: (target, prop, value) => {
if (prop == "listen")
return listen
else if (prop == "__raw")
return target
else
return Reflect.get(target, prop)
}
})
return proxy
}
export const bindContent = (listener, prop="value", target=document.createTextNode(""), filter) => {
const run = data => {
data = filter
? filter(data)
: data
if ("innerText" in target)
if (typeof data == "string")
target.innerText = data
else
target.replaceChildren(data)
else
target.data = data
}
listener.listen(prop, run)
run(listener[prop])
return target
}
export default listener