import { Countdown, integer } from './timer.mjs'; const timer = new Countdown(); const minutes = document.querySelector('#minutes'); const seconds = document.querySelector('#seconds'); const counter = document.querySelector('.counter'); const startStop = document.querySelector('#start-stop'); const reset = document.querySelector('#reset'); const status = document.querySelector('#status'); const fields = [minutes, seconds]; let ticker; function resize(field) { field.style.width = `${Math.max(field === seconds ? 2 : 1, field.value.length)}ch`; field.dataset.valid = field.value; } function render(updateFields = true) { if (updateFields) { const display = timer.display(); minutes.value = display.minutes; seconds.value = display.seconds; } fields.forEach(field => { field.readOnly = timer.state === 'running'; resize(field); }); counter.classList.toggle('finished', timer.finished); startStop.textContent = timer.state === 'running' ? 'Stop' : 'Start'; const message = timer.finished ? 'Countdown finished' : timer.state === 'running' ? 'Running' : timer.state === 'paused' ? 'Paused' : 'Idle'; if (status.textContent !== message) status.textContent = message; if (timer.state !== 'running') { clearInterval(ticker); ticker = undefined; } } function edited() { timer.edit(minutes.value, seconds.value); render(false); } for (const field of fields) { resize(field); field.addEventListener('input', () => { if (field.readOnly || !/^\d*$/.test(field.value)) { field.value = field.dataset.valid; return; } edited(); }); field.addEventListener('keydown', event => { if (field.readOnly || !['ArrowUp', 'ArrowDown'].includes(event.key)) return; event.preventDefault(); const step = event.shiftKey ? 10n : 1n; const value = integer(field.value) + (event.key === 'ArrowUp' ? step : -step); field.value = String(value < 0n ? 0n : value); edited(); }); field.addEventListener('blur', () => { if (field.readOnly) return; field.value = String(integer(field.value)); if (field === seconds) field.value = field.value.padStart(2, '0'); resize(field); }); } startStop.addEventListener('click', () => { timer.toggle(); render(); if (timer.state === 'running' && ticker === undefined) { // This is only a display refresh; elapsed time comes from the deadline. ticker = setInterval(() => { timer.update(); render(); }, 50); } }); reset.addEventListener('click', () => { timer.reset(); render(); }); document.addEventListener('visibilitychange', () => { if (timer.state === 'running') { timer.update(); render(); } }); render();