330 lines
9.2 KiB
JavaScript
330 lines
9.2 KiB
JavaScript
(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 () {});
|
|
}
|
|
|
|
})();
|