1(()=>{2 let doc = document, w = window3 if (w.oganesson) return4 let mine = new Set(), shows = new Set(), senders = new Set(), now = ()=>Date.now() / 1e3 | 05 let send = (el, type, detail)=>el.dispatchEvent(new CustomEvent("nog:" + type, {detail, cancelable:1, bubbles:1, composed:1}))6 let get = (el, a, d)=>el.getAttribute(a) ?? d7 let spec = (t)=>t == "presence" ? [20001, 180, 1e3] : t == "typing" ? [20002, 5, 3e3] : [w.nogCfg?.kind ?? 20078, 30, 1e3, 1]8 let room = (el)=>get(el.closest("[nog-room]") ?? el, "nog-room")9 let ttl = (el, t)=>+get(el, "nog-ttl", spec(t)[1])10 let hide = (el, pk)=>mine.has(pk) && !el.closest("[nog-self]")11 let drop = (el, pk)=>{12 let x = el.__nog[pk]13 if (x?.n && send(el, "expired", {pubkey:pk, elt:x.n})) x.n = x.n.remove(clearTimeout(x.t))14 }15 let publish = async(el, content)=>{16 let t = get(el, "nog-send"), [kind, , , custom] = spec(t), r = room(el), tags = [r && ["h", r], custom && ["t", t]].filter((x)=>x)17 content = content.trim ? content : JSON.stringify(content)18 let cfg = {elt:el, event:{kind, created_at:now(), tags, content}}19 if (!send(el, "send", {cfg})) return20 try {21 let e = await hydrogen.sign(cfg), pk = e?.pubkey22 if (!e) return23 mine.has(pk) || shows.forEach((s)=>hide(s, pk) && drop(s, pk), mine.add(pk))24 hydrogen.pub(e, cfg.relays)25 } catch(error) {26 send(el, "error", {cfg, error})27 }28 }29 let fire = (el, data)=>{30 el.__nd = [data]31 if (el.__nt) return32 publish(el, data)33 el.__nd = 034 el.__nt = setTimeout(()=>(el.__nt = 0, el.__nd && fire(el, el.__nd[0])), get(el, "nog-rate", spec(get(el, "nog-send"))[2]))35 }36 let stop = (el, s)=>publish(el, s, el.__nd = el.__ns = 0)37 let initSend = (el)=>{38 let t = get(el, "nog-send"), typing = t == "typing", on = get(el, "nog-on", /^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) ? "input" : "")39 let status = ()=>get(el, "nog-status", "online")40 senders.add(el)41 if (typing) el.addEventListener("blur", ()=>el.__ns && stop(el, "stopped"))42 if (on) on.split(/\s+/).forEach((type)=>el.addEventListener(type, (evt)=>{43 let r = el.getBoundingClientRect(), f = (v)=>+v.toFixed(3)44 if (!typing) fire(el, "clientX" in evt ? {x:f((evt.clientX - r.left) / r.width), y:f((evt.clientY - r.top) / r.height)} : status())45 else if (el.value) fire(el, "typing", el.__ns = 1)46 else if (el.__ns) stop(el, "stopped")47 }))48 else {49 let beat = el.__nb = ()=>el.isConnected ? !doc.hidden && fire(el, status()) : clearInterval(i)50 let i = setInterval(beat, ttl(el, t) * 333)51 beat()52 }53 }54 let leave = ()=>senders.forEach((s)=>s.isConnected ? s.__nb ? stop(s, "offline") : s.__ns && stop(s, "stopped") : senders.delete(s))55 let initShow = (el)=>{56 let t = get(el, "nog-show"), [kind, , , custom] = spec(t), life = ttl(el, t), r = room(el), m = el.__nog = {}, f = {kinds:[kind], since:now() - life, ...r && {"#h":[r]}, ...custom && {"#t":[t]}}57 let tpl = el.querySelector(":scope>template")?.innerHTML ?? "<span>{pubkey}</span>"58 shows.add(el)59 hydrogen.req({elt:el, filters:[f], onevent:(e, from, fields)=>{60 let pk = e.pubkey, x = m[pk] ??= {}, at = e.created_at, s = e.content.trim(), n = doc.createElement("template"), o61 if (hide(el, pk) || x.at > at || at > now() + 5 || at + life <= now()) return62 x.at = at63 try {64 o = JSON.parse(s)65 if (o && typeof o == "object"){66 s = o.status ?? (o.typing == 0 ? "stopped" : "")67 for (let k in o) fields[k] ??= hydrogen.esc(o[k])68 }69 } catch {}70 if (kind == 20002) s = /^(stopped|false|idle|offline)$/i.test(s) ? "stopped" : "typing"71 fields.status = hydrogen.esc(s ||= "online")72 if (/^(offline|stopped)$/.test(s)) return drop(el, pk)73 if (!x.n) senders.forEach((b)=>b.__nb && get(b, "nog-send") == t && room(b) == r && b.__nb())74 n.innerHTML = tpl.replace(/\{(#?\w+)\}/g, (a, k)=>fields[k] ?? a)75 if (!(n = n.content.firstElementChild)) return76 n.setAttribute("nog-pk", pk)77 x.n?.isConnected ? x.n.replaceWith(n) : el.append(n)78 clearTimeout(x.t)79 x.t = setTimeout(()=>drop(el, pk), (at + life) * 1e3 - Date.now())80 send(el, "shown", {event:e, fields, elt:x.n = n})81 }})82 }83 let init = (el)=>{84 if (el.__ni || el.closest("[nog-ignore]")) return85 el.__ni = 186 if (el.hasAttribute("nog-show")) initShow(el)87 if (el.hasAttribute("nog-send")) initSend(el)88 }89 let sel = "[nog-send],[nog-show]"90 let process = (n)=>{91 if (n.matches?.(sel)) init(n)92 n.querySelectorAll?.(sel).forEach(init)93 }94 w.oganesson = {send:fire, leave, mine}95 doc.addEventListener("nog:process", (evt)=>process(evt.target))96 doc.addEventListener("visibilitychange", ()=>doc.hidden ? leave() : senders.forEach((s)=>s.__nb?.()))97 let boot = ()=>{98 new MutationObserver((recs)=>recs.forEach((r)=>r.addedNodes.forEach(process))).observe(doc.documentElement, {childList:1, subtree:1})99 process(doc.body)100 }101 doc.readyState == "loading" ? doc.addEventListener("DOMContentLoaded", boot) : boot()102})()