// SARS-CoV-2-Viz // Animated COVID case count visualization // Copyright 2022 Edward L. Platt async function runInTimeout(f) { let p = new Promise((resolve, reject) => { setTimeout(() => { resolve( f() ); }, 0); }); return p; } async function sleep(ms) { return new Promise((resolve, reject) => setTimeout(resolve, ms)); } function UI() { let frames = []; let currentFrame = 0; let msPerFrame = 500; let playing = false; let onTick = null; let onNormalize = () => null; let animate = function () { if (playing) { setTimeout(animate, msPerFrame); } if (onTick) { onTick(currentFrame); } currentFrame = (currentFrame + 7) % frames.length; }; let node = ( document.getElementById('controls-template').content .querySelector('div').cloneNode(true)); let controls = node.querySelector('.controls'); let normalizeAll = node.querySelector('input.all'); let normalizeToDate = node.querySelector('input.to-date'); let play = node.querySelector('button'); let display = node.querySelector('.display'); let mainDisplay = node.querySelector('.main-display'); let secondaryDisplays = {}; play.addEventListener( "click", () => { if (playing) { play.innerText = 'play'; playing = false; } else { play.innerText = 'pause'; playing = true; animate(); } }); let onNormalizeChange = () => { if (normalizeAll.checked) { onNormalize('all'); } else { onNormalize('toDate'); } }; normalizeAll.addEventListener('change', onNormalizeChange); normalizeToDate.addEventListener('change', onNormalizeChange); return { node: node, display: (s, id=null) => { if (id === null) { mainDisplay.textContent = s; } else { secondaryDisplays[id].textContent = s; } }, addDisplay: async (id) => { let secondary = document.createElement("div"); secondary.id = `display-${id}`; display.appendChild(secondary); secondaryDisplays[id] = secondary; }, showControls: (show=true) => { if (show) { controls.classList.remove("hidden"); } else { controls.classList.add("hidden"); } }, clearDisplays: () => { mainDisplay.innerText = ''; for (const [id, d] of Object.entries(secondaryDisplays)) { d.remove(); } secondaryDisplays = {}; }, onTick: (f) => { onTick = f; }, setFrames: (newFrames, current) => { frames = newFrames; currentFrame = current; }, onNormalize: (f) => { onNormalize = f; }, setMsPerFrame: (newMsPerFrame) => { msPerFrame = newMsPerFrame; } } }