"use strict" /* global document, window */ const location = document.location const performance = window.performance import config from "./config.js" export function isString(x) { return typeof x === "string" } export function isEmpty(x) { return (Array.isArray(x) ? x : Object.keys(x)).length === 0 } export const q = (selector, root = document) => root.querySelector(selector) const _evlisteners = {} /** * @param {String} event [description] * @param {Function} cb [description] * @param {?{once: bool, target: Element = document}} options */ export function on(event, cb, options = {}) { const target = options.target || document if ((_evlisteners[target] ?? {})[event]) { console.warn(`Replacing previous event listener for '${event}' on target:`, target) off(event, target) } target.addEventListener(event, cb, { once: !!options.once }) if (!_evlisteners[target]) { _evlisteners[target] = {} } _evlisteners[target][event] = cb } export function off(event, target = document) { if (!_evlisteners[target] || !_evlisteners[target][event]) { return } target.removeEventListener(event, _evlisteners[target][event]) delete _evlisteners[target][event] if (isEmpty(_evlisteners[target])) { delete _evlisteners[target] } } export function node(type, { appendTo, cls, text, data, style, ...attrs } = {}) { let elem = isString(type) ? document.createElement(type) : type if (cls) { if (isString(cls)) { elem.className = cls } else { for (const [name, on] of Object.entries(cls)) { if (on) { elem.classList.add(name) } else { elem.classList.remove(name) } } } } if (text) { elem.textContent = text } Object.assign(elem.dataset, data ?? {}) Object.assign(elem.style, style ?? {}) for (const name in attrs) { elem.setAttribute(name, attrs[name]) } if (appendTo) { elem = appendTo.appendChild(elem) } return elem } /** * Some duration conversion constants. */ export const ms_ns = 1_000_000 // nanoseconds in a millisecond export const s_ms = 1_000 // milliseconds in a second export const s_ns = 1_000_000_000 // nanoseconds in a second export function session_url(sid) { return `${config.wsurl}/${sid}` } export function session_id_from_url(url) { const wsurl = new URL(config.wsurl) const match = RegExp(`${wsurl.pathname}/([^/]+)$`).exec(url) return !match ? null : match[1] } export function clear(container) { while (container.children.length > 0) { const child = container.children[0] child.remove() } } export function session_id() { const match = /^#?(.+)/.exec(location.hash) return match ? match[1] : null } /** * Guess the exact current server time. */ export function servertime_now_ns() { const now_ms = performance.now() const delta_ns = ms_ns * (now_ms - toffset_ms) return servertime + delta_ns }