clone

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

main
nobel / europium / europium.js
europium.js 2.7K
1(()=>{2 let g = window, d = document3 if (g.europium) return4 let send = (el, type, detail)=>el.dispatchEvent(new CustomEvent("neu:" + type, {detail, cancelable:1, bubbles:1, composed:1}))5 let markup = (link = g.neuCfg?.link ?? g.nneCfg?.link ?? "https://njump.me/")=>`<header neu-part="author"><img nrn-profile="{pubkey}" nrn-field="picture" neu-part="avatar" alt=""> <b nrn-profile="{pubkey}" nrn-field="name" neu-part="name">{npub}</b> <time nne-ago="{created_at}" neu-part="time">{ago}</time></header><div neu-part="content">{content}</div><a href="${link}{nevent}" neu-part="link">open</a>`6 let decode = (s = "")=>{7 if (/^[0-9a-f]{64}$/.test(s)) return {id:s}8 try {9 let {type, data} = neon.decode(s)10 return type == "note" ? {id:data} : type == "nevent" && data11 } catch {}12 }13 let init = (el)=>{14 let n = decode(el.getAttribute("neu-note").trim()), set = (k, v)=>el.hasAttribute(k) || el.setAttribute(k, v), t15 if (el.__neu || !n || el.closest("[neu-ignore]")) return16 el.__neu = 117 set("nxe-filter", JSON.stringify({ids:[n.id]}))18 set("nxe-swap", "innerHTML")19 if (n.relays?.length && !el.closest("[nhy-relay]")) el.setAttribute("nhy-relay", [...new Set([...n.relays, ...g.nhyCfg?.relays ?? []])].join(" "))20 if (!el.children.length) el.append(Object.assign(d.createElement("template"), {innerHTML:markup()}))21 el.setAttribute("neu-loading", "")22 let miss = ()=>{23 el.removeAttribute("neu-loading")24 if (send(el, "missing", {id:n.id})) el.setAttribute("neu-missing", "")25 }26 t = setTimeout(miss, +el.getAttribute("neu-timeout") || g.neuCfg?.timeout || 8e3)27 el.addEventListener("nxe:swapped", (evt)=>{28 if (evt.target != el) return29 clearTimeout(t)30 el.removeAttribute("neu-loading")31 el.removeAttribute("neu-missing")32 })33 el.dispatchEvent(new CustomEvent("nxe:process", {bubbles:1}))34 }35 let process = (n)=>{36 if (n.matches?.("[neu-note]")) init(n)37 n.querySelectorAll?.("[neu-note]").forEach(init)38 }39 // NIP-10: the parent is the "reply" marker, else "root", else the last unmarked e tag.40 d.addEventListener("nhy:event", (evt)=>{41 if (!evt.target.matches?.("[neu-note]")) return42 let e = evt.detail.event.tags.filter((t)=>t[0] == "e" && /^[0-9a-f]{64}$/.test(t[1]))43 let p = e.find((t)=>t[3] == "reply") ?? e.find((t)=>t[3] == "root") ?? e.findLast((t)=>!t[3])44 evt.detail.fields.reply = p && g.neon ? neon.encode("nevent", {id:p[1], relays:p[2] ? [p[2]] : []}) : ""45 })46 g.europium = {markup, process}47 let boot = ()=>{48 new MutationObserver((recs)=>recs.forEach((r)=>r.addedNodes.forEach(process))).observe(d.documentElement, {childList:1, subtree:1})49 process(d.body)50 }51 d.readyState == "loading" ? d.addEventListener("DOMContentLoaded", boot) : boot()52})()