(function () { 'use strict'; const scores = { left: parseInt(document.getElementById('score-left').textContent) || 0, right: parseInt(document.getElementById('score-right').textContent) || 0, }; let debounce = false; // ---- Auto-fit text ---- function fitText(el, maxWidth, maxHeight) { el.style.fontSize = '10px'; if (el.scrollWidth === 0) return; var lo = 8, hi = 600; while (lo < hi) { var mid = Math.ceil((lo + hi) / 2); el.style.fontSize = mid + 'px'; if (el.scrollWidth <= maxWidth && el.scrollHeight <= maxHeight) { lo = mid; } else { hi = mid - 1; } } el.style.fontSize = lo + 'px'; } function fitTextHeight(el, maxHeight) { el.style.fontSize = '10px'; if (el.scrollHeight === 0) return; var lo = 8, hi = 600; while (lo < hi) { var mid = Math.ceil((lo + hi) / 2); el.style.fontSize = mid + 'px'; if (el.scrollHeight <= maxHeight) { lo = mid; } else { hi = mid - 1; } } el.style.fontSize = lo + 'px'; } function fitAll() { var headerRow = document.querySelector('.header-row'); if (headerRow) { var span = headerRow.querySelector('span'); var hfs = parseFloat(headerRow.dataset.headerFontSize); if (hfs > 0) { span.style.fontSize = hfs + 'rem'; } else { var w = window.innerWidth * 0.9; fitText(span, w, 80); } } document.querySelectorAll('.side').forEach(function (side) { var rect = side.getBoundingClientRect(); var w = rect.width * 0.88; var h = rect.height; var name = side.querySelector('.team-name'); if (name) { var fs = parseFloat(name.dataset.nameFontSize); if (fs > 0) { name.style.fontSize = fs + 'rem'; } else { fitText(name, w, h * 0.14); } } var subtitle = side.querySelector('.team-subtitle'); if (subtitle) { var sfs = parseFloat(subtitle.dataset.subtitleFontSize); if (sfs > 0) { subtitle.style.fontSize = sfs + 'rem'; } else { subtitle.style.fontSize = ''; } } var score = side.querySelector('.score'); if (score) { var sfs = parseFloat(score.dataset.scoreFontSize); if (sfs > 0) { score.style.fontSize = sfs + 'rem'; } else { fitTextHeight(score, h * 0.55); } } var content = side.querySelector('.side-content'); if (content) { var cpt = parseFloat(document.querySelector('.scoreboard').dataset.contentPaddingTop); content.style.paddingTop = cpt > 0 ? cpt + 'rem' : ''; } }); } window.addEventListener('load', fitAll); window.addEventListener('resize', fitAll); // ---- Tap / Hold handlers ---- document.querySelectorAll('.side').forEach(function (side) { var sideName = side.dataset.side; var holdTimer = null; var holdFadeTimer = null; var holdFired = false; function onStart(e) { e.preventDefault(); if (debounce) return; holdFired = false; side.classList.remove('hold-cancel'); holdFadeTimer = setTimeout(function () { void side.offsetWidth; side.classList.add('holding'); }, 500); holdTimer = setTimeout(function () { holdFired = true; side.classList.remove('holding'); side.classList.remove('hold-cancel'); resetSide(sideName); }, 2000); } function onEnd(e) { if (holdFadeTimer) { clearTimeout(holdFadeTimer); holdFadeTimer = null; } if (holdTimer) { clearTimeout(holdTimer); holdTimer = null; } side.classList.remove('holding'); if (holdFired) { side.classList.remove('hold-cancel'); return; } side.classList.add('hold-cancel'); if (debounce) return; debounce = true; setTimeout(function () { debounce = false; }, 400); selectedSide = sideName; increment(sideName); } function onCancel() { if (holdFadeTimer) { clearTimeout(holdFadeTimer); holdFadeTimer = null; } if (holdTimer) { clearTimeout(holdTimer); holdTimer = null; } side.classList.remove('holding'); side.classList.remove('hold-cancel'); } side.addEventListener('pointerdown', onStart); side.addEventListener('pointerup', onEnd); side.addEventListener('pointercancel', onCancel); side.addEventListener('pointerleave', onCancel); }); var selectedSide = 'left'; var counting = false; var countdownTimer = null; var switchCount = 0; var originalWaveSide = 'left'; var confirming = false; var confirmTimer = null; function stopCountdown() { if (countdownTimer) { clearTimeout(countdownTimer); countdownTimer = null; } counting = false; document.querySelectorAll('.side').forEach(function (s) { s.classList.remove('counting'); var bar = s.querySelector('.hw-bar'); if (bar) { bar.classList.remove('active'); bar.style.width = '0'; } }); } function startCountdown(side) { stopCountdown(); counting = true; selectedSide = side; var el = document.querySelector('.side-' + side); if (el) el.classList.add('counting'); var bar = document.querySelector('.side-' + side + ' .hw-bar'); if (bar) { bar.style.width = '0'; void bar.offsetWidth; bar.classList.add('active'); } countdownTimer = setTimeout(function () { stopCountdown(); switchCount = 0; increment(side); }, 5000); } function handleWave() { if (confirming) { clearTimeout(confirmTimer); confirmTimer = null; confirming = false; document.getElementById('reset-confirm').classList.remove('show'); switchCount = 0; fetch('/reset', { method: 'POST' }).catch(function () {}); return; } if (counting) { switchCount++; if (switchCount >= 6) { stopCountdown(); switchCount = 0; confirming = true; document.getElementById('reset-confirm').classList.add('show'); confirmTimer = setTimeout(function () { confirming = false; document.getElementById('reset-confirm').classList.remove('show'); }, 10000); return; } var mode = (switchCount - 1) % 3; if (mode === 0) { // Show opposite side var next = selectedSide === 'left' ? 'right' : 'left'; startCountdown(next); } else if (mode === 1) { // Hidden — mistake cancel, timer keeps running silently var next = selectedSide === 'left' ? 'right' : 'left'; selectedSide = next; stopCountdown(); counting = true; countdownTimer = setTimeout(function () { stopCountdown(); switchCount = 0; increment(selectedSide); }, 5000); } else { // Show original side again startCountdown(originalWaveSide); } } else { switchCount = 0; originalWaveSide = selectedSide; startCountdown(selectedSide); } } document.addEventListener('keydown', function (e) { if (e.code !== 'Space') return; e.preventDefault(); handleWave(); }); var eventSource = new EventSource('/events'); eventSource.onmessage = function (e) { var data = JSON.parse(e.data); if (data.type === 'score') { var prevLeft = scores.left; var prevRight = scores.right; scores.left = data.scores.left; scores.right = data.scores.right; if (scores.left !== prevLeft) { selectedSide = 'left'; updateDisplay('left', scores.left); triggerSweep('left'); } if (scores.right !== prevRight) { selectedSide = 'right'; updateDisplay('right', scores.right); triggerSweep('right'); } debounce = false; } else if (data.type === 'config') { location.reload(); } }; // ---- Increment ---- function increment(side) { if (confirming) { clearTimeout(confirmTimer); confirmTimer = null; confirming = false; document.getElementById('reset-confirm').classList.remove('show'); switchCount = 0; } fetch('/increment/' + side, { method: 'POST' }).catch(function () {}); } // ---- Update display ---- function updateDisplay(side, value) { var el = document.getElementById('score-' + side); el.textContent = value; var sfs = parseFloat(el.dataset.scoreFontSize); if (sfs > 0) { el.style.fontSize = sfs + 'rem'; } else { var sideEl = document.querySelector('.side-' + side); var rect = sideEl.getBoundingClientRect(); fitTextHeight(el, rect.height * 0.55); } el.classList.remove('score-pop'); void el.offsetWidth; el.classList.add('score-pop'); } // ---- Trigger sweep animation ---- function triggerSweep(side) { var overlay = document.getElementById('sweep-' + side); if (!overlay) return; overlay.classList.remove('sweeping'); overlay.style.animation = 'none'; void overlay.offsetHeight; overlay.style.animation = ''; overlay.classList.add('sweeping'); } // ---- Reset single side ---- function resetSide(side) { fetch('/reset/' + side, { method: 'POST' }).catch(function () {}); } })();