/* widget: Timer */
/* =========================================================
Timer widget — digits, visual "kijkklok" disc, or both
==========================================================*/
function mountTimer(root, initState){
const DIAL = 3600; // the disc is a 60-minute dial, like a real kijkklok
let total = 300, left = 300, iv = null,
mode = "both", // dig | pie | both
snd = true;
if(initState){
total = initState.total || 300; left = total;
mode = initState.mode || "both";
snd = initState.snd !== false;
}
root.innerHTML = `
`;
const box = root.querySelector(".timer"),
svg = root.querySelector(".tpie"),
disp = root.querySelector(".tdisp"),
arc = root.querySelector(".parc"),
knob = root.querySelector(".knob"),
bstart = root.querySelector(".bstart"),
breset = root.querySelector(".breset"),
bsnd = root.querySelector(".bsnd"),
presets = root.querySelector(".presets"),
hint = root.querySelector(".thint");
const fmt = s => String(Math.floor(s/60)).padStart(2,"0")+":"+String(s%60).padStart(2,"0");
/* minute tick marks (every 5 min) */
const ticks = root.querySelector(".ticks");
for(let i=0;i<12;i++){
const a = i*30*Math.PI/180;
const l = document.createElementNS("http://www.w3.org/2000/svg","line");
l.setAttribute("x1", 75+62*Math.sin(a)); l.setAttribute("y1", 75-62*Math.cos(a));
l.setAttribute("x2", 75+69*Math.sin(a)); l.setAttribute("y2", 75-69*Math.cos(a));
l.setAttribute("stroke", "#b9c7d9"); l.setAttribute("stroke-width", i%3===0?2.5:1.5);
ticks.appendChild(l);
}
function drawPie(){
const a = Math.min(360, left/DIAL*360);
const rad = a*Math.PI/180;
const kx = 75 + 63*Math.sin(rad), ky = 75 - 63*Math.cos(rad);
knob.setAttribute("cx", kx.toFixed(2)); knob.setAttribute("cy", ky.toFixed(2));
if(a<=0){ arc.setAttribute("d",""); return; }
if(a>=359.99){ arc.setAttribute("d","M75,5 A70,70 0 1 1 74.99,5 Z"); return; }
const x = 75 + 70*Math.sin(rad), y = 75 - 70*Math.cos(rad);
arc.setAttribute("d",`M75,75 L75,5 A70,70 0 ${a>180?1:0} 1 ${x.toFixed(2)},${y.toFixed(2)} Z`);
}
function draw(){
disp.textContent = fmt(left);
disp.classList.toggle("warn", left<=10 && left>0 && iv!==null);
drawPie();
}
function labels(){
bstart.textContent = (iv ? "⏸ "+T("pause") : "▶ "+T("start"));
breset.textContent = "↺ "+T("reset");
presets.querySelectorAll("button").forEach(b=>b.textContent = b.dataset.m+" "+T("min"));
root.querySelectorAll(".tseg button").forEach(b=>{
const key = b.dataset.m==="dig" ? "tmDigits" : b.dataset.m==="pie" ? "tmVisual" : "tmBoth";
b.title = T(key);
b.classList.toggle("on", b.dataset.m===mode);
});
bsnd.textContent = snd ? "🔔" : "🔕";
bsnd.title = T("tmSound");
bsnd.classList.toggle("on", snd);
box.className = "timer m-"+mode + (box.classList.contains("flash")?" flash":"");
hint.textContent = mode==="dig" ? "" : T("tmHint");
}
[1,5,10,15,30].forEach(m=>{
const b = document.createElement("button");
b.dataset.m = m;
b.addEventListener("click", ()=>{ stop(); total=left=m*60; draw(); labels(); });
presets.appendChild(b);
});
root.querySelectorAll(".tseg button").forEach(b=>{
b.addEventListener("click", ()=>{ mode = b.dataset.m; labels(); });
});
bsnd.addEventListener("click", ()=>{ snd=!snd; labels(); });
/* drag on the disc to set the time */
function angleFromEvent(ev){
const r = svg.getBoundingClientRect();
const px = (ev.clientX - r.left)*150/r.width - 75;
const py = (ev.clientY - r.top)*150/r.height - 75;
return (Math.atan2(px, -py)*180/Math.PI + 360) % 360;
}
svg.addEventListener("pointerdown", e=>{
e.preventDefault();
svg.setPointerCapture(e.pointerId);
const setFrom = ev=>{
const mins = Math.min(60, Math.max(1, Math.round(angleFromEvent(ev)/6)));
total = left = mins*60;
draw();
};
setFrom(e);
const move = ev=>setFrom(ev);
const up = ()=>{ svg.removeEventListener("pointermove",move); svg.removeEventListener("pointerup",up); };
svg.addEventListener("pointermove",move);
svg.addEventListener("pointerup",up);
});
function stop(){ if(iv){ clearInterval(iv); iv=null; } }
bstart.addEventListener("click", ()=>{
if(iv){ stop(); }
else if(left>0){
iv = setInterval(()=>{
left--;
if(left<=0){
left=0; stop();
if(snd) sndWin();
box.classList.add("flash");
setTimeout(()=>box.classList.remove("flash"), 2200);
labels();
}
draw();
},1000);
}
labels(); draw();
});
breset.addEventListener("click", ()=>{ stop(); left=total; draw(); labels(); });
document.addEventListener("langchange", labels);
labels(); draw();
return { getState: ()=>({total, mode, snd}) };
}