clone

tiny dependency-free nostr libraries for plain html, each under 5kb and named for an element. public domain

main
nobel / xenon / xenon.js
xenon.js 3.1K
1(()=>{2 let doc = document3 if (doc.__nxe_mo) return4 let live = new Set()5 doc.__nxe_mo = new MutationObserver((recs)=>recs.forEach((r)=>{6 r.addedNodes.forEach(process)7 if (r.removedNodes.length) live.forEach((c)=>c.elt.isConnected || (c.close(), live.delete(c)))8 }))9 let send = (el, type, detail)=>el.dispatchEvent(new CustomEvent("nxe:" + type, {detail, cancelable:1, bubbles:1, composed:1}))10 let attr = (el, name, d)=>el.getAttribute(name) || d11 let swap = (cfg, html, event)=>{12 let s = cfg.swap, t = cfg.target, at = event.created_at13 if (s instanceof Function) s(cfg, html, event)14 else if (/^(newest|oldest)$/.test(s)){15 let tp = doc.createElement("template")16 tp.innerHTML = html17 tp.content.firstElementChild?.setAttribute("nxe-at", at)18 t.insertBefore(tp.content, [...t.children].find((c)=>{19 let x = c.getAttribute("nxe-at")20 return x != null && (s == "newest" ? x <= at : x > at)21 }))22 }23 else if (/(before|after)(begin|end)/.test(s)) t.insertAdjacentHTML(s, html)24 else if (s in t) t[s] = html25 }26 let subscribe = (el)=>{27 let tpl = doc.querySelector(attr(el, "nxe-template")) ?? el.querySelector(":scope>template")28 let cfg = {29 elt:el,30 filters:[].concat(JSON.parse(attr(el, "nxe-filter"))),31 target:doc.querySelector(attr(el, "nxe-target")) ?? el,32 swap:attr(el, "nxe-swap", "newest"),33 template:tpl?.innerHTML ?? "<p>{content}</p>",34 onevent:(event, from, fields)=>{35 swap(cfg, cfg.template.replace(/\{(#?\w+)\}/g, (m, k)=>fields[k] ?? m), event)36 send(el, "swapped", {cfg, event})37 },38 oneose:(from)=>send(el, "eose", {cfg, from})39 }40 if (hydrogen.req(cfg)) live.add(cfg)41 }42 let publish = async(el, form, evt)=>{43 evt.preventDefault()44 let fd = form ? new FormData(el, evt.submitter) : new FormData()45 let tags = JSON.parse(attr(el, "nxe-tags", "[]"))46 for (let [k, v] of fd) if (k[0] == "#" && v) tags.push([k.slice(1), v])47 let cfg = {elt:el, event:{kind:+attr(el, "nxe-kind", 1), created_at:Date.now() / 1e3 | 0, tags, content:attr(el, "nxe-content", fd.get("content") ?? "")}}48 try {49 if (!await hydrogen.sign(cfg) || !send(el, "publish", {cfg})) return50 let rs = cfg.results = await hydrogen.pub(cfg.event, cfg.relays)51 if (form && (!rs.length || rs.some((r)=>r.ok))) el.reset()52 send(el, "published", {cfg})53 } catch(error) {54 send(el, "error", {cfg, error})55 }56 }57 let init = (el)=>{58 if (el.__nxe || el.closest("[nxe-ignore]") || !send(el, "init", {})) return59 el.__nxe = 160 if (el.hasAttribute("nxe-filter")) subscribe(el)61 if (el.hasAttribute("nxe-publish")){62 let form = el.tagName == "FORM"63 el.addEventListener(attr(el, "nxe-trigger", form ? "submit" : "click"), (evt)=>publish(el, form, evt))64 }65 }66 let sel = "[nxe-filter],[nxe-publish]"67 let process = (n)=>{68 if (n.matches?.(sel)) init(n)69 n.querySelectorAll?.(sel).forEach(init)70 }71 window.xenon = {process}72 doc.addEventListener("nxe:process", (evt)=>process(evt.target))73 let boot = ()=>{74 doc.__nxe_mo.observe(doc.documentElement, {childList:1, subtree:1})75 process(doc.body)76 }77 doc.readyState == "loading" ? doc.addEventListener("DOMContentLoaded", boot) : boot()78})()