"use strict" /* global document, window */ const location = document.location const performance = window.performance import { Connection, ms_ns, node, q, s_ns, session_id, session_url, } from "./shared.js" let conn function setup_url() { const sid = session_id() || "" location.hash = sid } function prettynum(n) { let i = Math.abs(n) | 0 let s = [] while (true) { const m = i % 1000 i = (i / 1000) | 0 if (i === 0) { s.unshift(String(m)) break } else { s.unshift(String(m).padStart(3, "0")) } } return (n < 0 ? "-" : "") + s.join("'") } function assert(expected, got) { if (expected !== got) { throw Error("Assertion failed.") } } function test_prettynum() { assert("0", prettynum(0)) assert("1", prettynum(1)) assert("1'000", prettynum(1000)) assert("36'000", prettynum(36000)) assert("0", prettynum(-0)) assert("-1", prettynum(-1)) assert("-1'000", prettynum(-1000)) assert("-36'000", prettynum(-36000)) assert("-36'600", prettynum(-36600)) } test_prettynum() const player_list = q("#players") function _redraw_clients(me, clients) { if (!me) { return } // Remove all gone players. const client_ids = clients.map((c) => "" + c.id) for (const el of player_list.querySelectorAll(".player")) { if (!client_ids.includes(el.dataset.cid)) { el.remove() } } const player_tpl = q("template#player").content.firstElementChild for (const c of clients) { if (c.id === me.id) { continue } let player_el = q(`.player[data-cid='${c.id}']`) if (!player_el) { player_el = node(player_tpl.cloneNode(true), { data: { cid: c.id }, appendTo: player_list, }) } else if (q(".points", player_el).textContent !== prettynum(c.points)) { q(".points.fg", player_el).classList.add("big") q(".points.bg", player_el).classList.add("big") setTimeout(() => { q(".points.fg", player_el).classList.remove("big") q(".points.bg", player_el).classList.remove("big") }, 200) } q(".name", player_el).textContent = c.name q(".points.fg", player_el).textContent = prettynum(c.points) q(".points.bg", player_el).textContent = prettynum(c.points) q(".tokens", player_el).textContent = "🔴 ".repeat(c.tokens) } } function debounce(func, ms) { let timer return function (...args) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { timer = null func(...args) }, ms) } } const redraw_clients = debounce(_redraw_clients, 200) let highlighted = false function highlight(client_id, until_ns) { if (highlighted) { return } const timeout_ms = (until_ns - conn.servertime_now_ns()) / ms_ns if (timeout_ms <= 10) { console.warn("That highlight timeout was ridiculously low:", client_id, timeout_ms) return } for (const li of player_list.children) { if (li.dataset.cid === client_id) { li.classList.add("buzzing") highlighted = true setTimeout(() => { highlighted = false li.classList.remove("buzzing") }, timeout_ms) return } } } function setup_ws() { let clients = [], me const sid = session_id() conn = new Connection() conn.on("helo", () => { conn.send("name", "Monitor") }) conn.on("id", ({ value }) => { me = value redraw_clients(me, clients) }) conn.on("buzz", ({ value }) => { const { time: buzztime_ns, client: client_id } = value const duration_ns = 12 * s_ns const until_ns = buzztime_ns + duration_ns highlight(client_id, until_ns) }) conn.on("clients", ({ value }) => { clients = value.clients redraw_clients(me, clients) }) conn.on("client", ({ value: { client } }) => { const idx = clients.findIndex((c) => c.id === client.id) if (idx >= 0) { clients[idx] = client } else { clients.push(client) } redraw_clients(me, clients) }) conn.on("control", ({ value }) => { // ... }) conn.connect(session_url(sid)) } setup_url() setup_ws()