let tableKey = { name: "", passcode: "" } let table = null; let conn = null; function showTableModal(show) { const modal = document.getElementById("table_modal"); if (modal) { modal.style.display = show ? "block" : "none"; } } function fmtLeading(n) { return n < 10 ? "0" + n : String(n); } function formatDice(r) { const date = new Date(r.timestamp) const p = document.createElement("p"); const month = date.getMonth() + 1; const day = date.getDate(); const hours = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); p.innerHTML = `${date.getFullYear()}-${fmtLeading(month)}-${fmtLeading(day)} ${fmtLeading(hours)}:${fmtLeading(minutes)}:${fmtLeading(seconds)} ${r.player} rolled ${r.roll.length}d${r.faces} ${(r.note ? "(" + r.note + ")" : "")}
[${r.roll}] (total ${r.roll.reduce((a,c)=>a+c,0)})`; return p; } function logDice(dice, many) { const diceLog = document.getElementById("dice_log"); if (!many) { dice = [ dice ]; } else { if (diceLog) { diceLog.innerHTML = ""; } } if (diceLog) { for(const r of dice) { const p = formatDice(r); diceLog.append(p); p.scrollIntoView(); } } } function setAuxMsg(msg) { const auxDiv = document.getElementById("aux"); if (auxDiv) { auxDiv.innerText = msg; } } function makeUpToDate(table) { if (table) { if (table.diceRolls) { logDice(table.diceRolls, true); } else if (table.diceRoll) { logDice(table.diceRoll, false); } if (table.mapImg) { setMapImg(table.mapImg); } if (table.auxMsg) { setAuxMsg(table.auxMsg); } } } function setMapImg(url) { initializeMap(url); } function isTableKeyValid(name, passcode) { const r = /^[a-zA-Z0-9_]+$/; return r.test(name) && r.test(passcode); } function dial() { // get tableKey from UI const tblNameInput = document.getElementById("input_table_name"); const tblPassInput = document.getElementById("input_table_pass"); if (tblNameInput && tblPassInput && tblNameInput.value && tblPassInput.value) { if (isTableKeyValid(tblNameInput.value, tblPassInput.value)) { tableKey.name = tblNameInput.value; tableKey.passcode = tblPassInput.value; if (conn) { conn.close(1000); } conn = new WebSocket(`ws://${location.host}/subscribe`, `${tableKey.name}.${tableKey.passcode}`); conn.addEventListener("close", e => { if (e.code == 1006) { setErr("Table not found - check the name and passcode are correct"); } else if (e.code > 1001) { // TODO: add message to let user know they are reconnecting setErr("Websocket error: trying to reconnect"); setTimeout(dial, 1000) } else { tabletop = document.getElementById("tabletop"); if (tabletop) { tabletop.style.display = "none"; } table = null; } }); conn.addEventListener("open", e => { // TODO: add message to let user know they are at the table tabletop = document.getElementById("tabletop"); if (tabletop) { tabletop.style.display = "block"; } }); conn.addEventListener("error", e => { setErr(`${e.name}: ${e.message}`); conn.close(3000); }) conn.addEventListener("message", e => { const data = JSON.parse(e.data); if (table == null) { // first fetch comes from mongo, so the rolls array in each diceRoll is a byte array and needs to be decoded data.diceRolls.forEach(r=>{ r.roll = Uint8Array.from(atob(r.roll), c => c.charCodeAt(0)) }) table = data; makeUpToDate(table); } else { if (data.diceRoll) { data.diceRoll.roll = Uint8Array.from(atob(data.diceRoll.roll), c => c.charCodeAt(0)); } makeUpToDate(data); } console.log(data); }); } else { setErr("Table name and passcode can only be alphanumeric and underscores"); } }else { setErr("Table name and passcode required"); } } async function publish(msg) { msg.key = tableKey; const res = await fetch('/publish', { method: 'POST', body: JSON.stringify(msg) }); if (!res.ok) { setErr("Failed to publish message"); } }