// Copyright 2026 Logan Fick // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. (() => { "use strict"; const config = window.POLL_CONFIG; const tokenInfo = window.TOKEN_INFO; if (!config || !tokenInfo) return; const timerEl = document.getElementById("timer"); let remaining = config.timeRemaining; // --- Countdown timer --- const formatTime = (seconds) => { const m = Math.floor(seconds / 60); const s = seconds % 60; return `${m}:${String(s).padStart(2, "0")}`; }; const updateTimer = () => { if (remaining <= 0) { timerEl.textContent = "0:00"; return; } timerEl.textContent = formatTime(remaining); remaining--; }; updateTimer(); const timerInterval = setInterval(() => { updateTimer(); if (remaining < 0) { clearInterval(timerInterval); } }, 1000); // --- Update vote bars --- const updateBars = (counts) => { const total = counts.reduce((a, b) => a + b, 0); for (const bar of document.querySelectorAll(".option-bar")) { const idx = Number(bar.dataset.index); const fill = bar.querySelector(".bar-fill"); const countSpan = bar.querySelector(".vote-count"); const pct = total > 0 ? Math.round((counts[idx] / total) * 100) : 0; if (fill && counts[idx] !== undefined) { fill.style.width = `${pct}%`; } if (countSpan) { const countEl = countSpan.querySelector(".count"); const pctEl = countSpan.querySelector(".pct"); if (countEl) countEl.textContent = counts[idx]; if (pctEl) pctEl.textContent = pct; countSpan.classList.remove("d-none"); } } }; // Show vote counts immediately for non-hidden polls so the UI // doesn't look identical to a hidden poll before SSE delivers data. if (!config.hidden || tokenInfo.isModerator) { for (const el of document.querySelectorAll(".vote-count")) { el.classList.remove("d-none"); } } // --- SSE connection --- const evtSource = new EventSource(tokenInfo.eventsUrl); const parse = (e) => { try { return JSON.parse(e.data); } catch { return null; } }; evtSource.addEventListener("error", () => { if (evtSource.readyState === EventSource.CLOSED) { clearInterval(timerInterval); const status = document.getElementById("poll-status"); if (status) { status.innerHTML = '