quiz/public/shared.js

115 lines
2.9 KiB
JavaScript

"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
}