`;
const sel=li.querySelector('.b-type'); if(sel) sel.value=type;
attachBlockHandlers(li);
return li;
}
function attachBlockHandlers(li){
const row = li.querySelector('.row');
const edit = li.querySelector('.edit');
const timeSpan = li.querySelector('.time');
const typeSpan = li.querySelector('.type');
const titleSpan = li.querySelector('.title');
const minsInp = li.querySelector('.b-mins');
const typeSel = li.querySelector('.b-type');
const titleInp = li.querySelector('.b-title');
const insTa = li.querySelector('.b-ins');
const btnEdit = li.querySelector('.b-edit');
const btnUp = li.querySelector('.b-up');
const btnDown = li.querySelector('.b-down');
const btnDup = li.querySelector('.b-dup');
const btnDel = li.querySelector('.b-del');
on(row,'click', ()=>{ if(edit) edit.style.display='grid'; });
on(btnEdit,'click', ()=>{ if(edit) edit.style.display='none'; update(); });
[minsInp,typeSel,titleInp,insTa].forEach(el=> on(el,'input', update));
on(btnUp,'click', ()=>{ const prev = li.previousElementSibling; if(prev && els.agenda){ els.agenda.insertBefore(li, prev); renumber(); save(); } });
on(btnDown,'click', ()=>{ const next = li.nextElementSibling; if(next && els.agenda){ els.agenda.insertBefore(next, li); renumber(); save(); } });
on(btnDup,'click', ()=>{ const clone = mkBlock(minsInp.value, typeSel.value, titleInp.value, insTa.value); if(els.agenda) els.agenda.insertBefore(clone, li.nextElementSibling); renumber(); save(); });
on(btnDel,'click', ()=>{ li.remove(); renumber(); save(); });
function update(){ if(timeSpan&&minsInp) timeSpan.textContent = two(minsInp.value)+'m'; if(typeSpan&&typeSel) typeSpan.textContent = typeSel.value; if(titleSpan&&titleInp) titleSpan.textContent = esc(titleInp.value); renumber(); save(); }
}
function renumber(){
const items = $$('#agenda .block');
const target = Math.max(15, Number(els.duration && els.duration.value || 60));
let total = 0; items.forEach(b=>{ const minsEl=b.querySelector('.b-mins'); const mins = Number(minsEl && minsEl.value || 0); total += Math.max(1, mins); });
if(els.totalMins) els.totalMins.textContent = total;
if(els.targetMins) els.targetMins.textContent = target;
const status = (total===target? 'ok':'warn');
if(els.totalHelp){ els.totalHelp.classList.remove('ok','warn'); els.totalHelp.classList.add(status); }
// Live hints
let hint = '';
const diff = total - target;
if(diff>0) hint = `You’re +${diff} minutes over. Trim a block or press Rebalance.`;
else if(diff addBlock());
// Simple allocation engine
const templates = {
base(dur, inter, energy){
const blocks=[]; const include = new Set($$('.type').filter(c=>c.checked).map(c=>c.value));
const push=(m,t,ti,ins)=>{ if(m>0) blocks.push([m,t,ti,ins]); };
const short = dur=90;
const intro = short? 3:5; const wrap = 5; let remain = dur-intro-wrap; let breakAdded=false;
push(intro,'Intro','Welcome & objectives','Set outcomes, agenda, norms.');
if(include.has('icebreaker') && !short) push(6,'Icebreaker','Quick pair warm‑up','Prompt: “What will make this session useful?” 2 min each, then share.');
while(remain>0){
if(include.has('minilecture')){ const m = Math.min( remain>30? 8 : 6, remain); push(m,'Mini‑lecture','Show & tell','Explain with one concrete example.'); remain-=m; }
if(remain include.has(k)) || 'discussion';
const map = {pairshare:['Pair‑share','Prompt + 2 min each + harvest'],hands:['Hands‑on','Follow the steps and produce a draft'],case:['Case','Read, decide, justify'],roleplay:['Role‑play','Set roles, 3‑minute rounds, swap, debrief'],discussion:['Discussion','Discuss prompt and capture 3 insights']};
const pair = map[pick]||['Practice','Do the activity'];
push(p, pair[0], pair[1], 'Facilitator roams, timebox, and debrief.'); remain-=p;
if(!breakAdded && $('#breaks') && $('#breakLen') && $('#breaks').checked && long && remain>30){ const b = Math.min(Number($('#breakLen').value||5), remain); push(b,'Break','Break','Hydrate & move.'); remain-=b; breakAdded=true; }
}
let sum = blocks.reduce((a,b)=>a+b[0],0);
const diff = dur - sum - wrap;
if(diff!==0 && blocks.length){ blocks[blocks.length-1][0] = Math.max(1, blocks[blocks.length-1][0] + diff); }
push(wrap,'Wrap','Commitments & next steps','Capture actions, owners, dates. Quick feedback.');
return blocks;
}
};
function generate(){
const dur = Math.max(15, Number(els.duration && els.duration.value || 60));
const inter = els.inter && els.inter.value || 'medium';
const energy = els.energy && els.energy.value || 'balanced';
const blocks = templates.base(dur, inter, energy);
if(els.agenda) els.agenda.innerHTML='';
blocks.forEach(([m,t,ti,ins])=> addBlock(m,t,ti,ins));
if(els.targetMins) els.targetMins.textContent = dur; renumber(); save();
}
// Robust reset
function clearForm(){
const form = $('#wab-form');
let usedNative = false;
try{ if(form && typeof form.reset === 'function') { form.reset(); usedNative = true; } }catch(e){}
if(!usedNative && form){
form.querySelectorAll('input').forEach(inp=>{ if(inp.type==='checkbox' || inp.type==='radio') inp.checked = inp.defaultChecked; else inp.value = (inp.defaultValue || ''); });
form.querySelectorAll('select').forEach(sel=>{ const def = Array.from(sel.options).find(o=>o.defaultSelected); sel.selectedIndex = def? def.index : 0; });
form.querySelectorAll('textarea').forEach(ta=> ta.value = (ta.defaultValue || ''));
}
// Explicit defaults
if(els.duration) els.duration.value=60;
if(els.size) els.size.value=12;
if(els.goal) els.goal.value='';
if(els.inter) els.inter.value='medium';
if(els.energy) els.energy.value='balanced';
if($('#breaks')) $('#breaks').checked=true;
if($('#breakEvery')) $('#breakEvery').value=60;
if($('#breakLen')) $('#breakLen').value=5;
$$('.type').forEach(c=> c.checked=true);
if(els.agenda) els.agenda.innerHTML='';
if(els.targetMins) els.targetMins.textContent='60';
if(els.totalMins) els.totalMins.textContent='0';
}
on(els.generate,'click', generate);
on(els.reset,'click', ()=>{ clearForm(); save(); generate(); });
// Rebalance
function rebalance(){
const target = Math.max(15, Number(els.duration && els.duration.value || 60));
const items = $$('#agenda .block');
let sum = 0; const mins = items.map(li=> Number(li.querySelector('.b-mins') && li.querySelector('.b-mins').value || 0));
mins.forEach(m=> sum += Math.max(1,m));
if(sum===0) return;
const scale = target/sum; const newM = mins.map(m=> Math.max(1, Math.round(m*scale)));
const adj = target - newM.reduce((a,b)=>a+b,0);
if(newM.length) newM[newM.length-1] = Math.max(1, newM[newM.length-1] + adj);
items.forEach((li,i)=>{ const inp=li.querySelector('.b-mins'); const time=li.querySelector('.time'); if(inp) inp.value = newM[i]; if(time) time.textContent = two(newM[i])+'m'; });
renumber(); save();
}
on(els.rebalance,'click', rebalance);
// Export helpers
function toBlocks(){ return $$('#agenda .block').map(li=>({ mins:Number(li.querySelector('.b-mins') && li.querySelector('.b-mins').value || 0), type:li.querySelector('.b-type') && li.querySelector('.b-type').value || '', title:li.querySelector('.b-title') && li.querySelector('.b-title').value || '', ins:li.querySelector('.b-ins') && li.querySelector('.b-ins').value || '' })); }
function toText(){ const head = `Title: ${els.title && els.title.value || '-'}\nDuration: ${els.duration && els.duration.value || 60} mins\nOutcome: ${els.goal && els.goal.value || '-'}\n`; const body = toBlocks().map((b,i)=>`${i+1}. [${b.mins}m] ${b.type} — ${b.title}\n ${b.ins}`).join('\n'); return head+'\n'+body; }
function toMarkdown(){ return '# Workshop Agenda\n\n'+toText(); }
function writeClipboard(txt){ if(navigator.clipboard&&navigator.clipboard.writeText){ navigator.clipboard.writeText(txt).then(()=>flash(els.copy)).catch(()=>fallback(txt)); } else fallback(txt); }
function fallback(txt){ try{ const ta=document.createElement('textarea'); ta.value=txt; ta.setAttribute('readonly',''); ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta);}catch(e){} flash(els.copy); }
function flash(btn){ if(!btn) return; const t=btn.textContent; btn.textContent='Copied!'; setTimeout(()=>btn.textContent=t,1200); }
on(els.copy,'click', ()=> writeClipboard(toText()));
on(els.download,'click', ()=>{ const md = toMarkdown(); const blob=new Blob([md],{type:'text/markdown'}); const url=URL.createObjectURL(blob); const a=document.createElement('a'); a.href=url; a.download='agenda.md'; document.body.appendChild(a); a.click(); setTimeout(()=>{ URL.revokeObjectURL(url); a.remove(); }, 400); });
// Share state
function collectState(){ return { title:els.title&&els.title.value, duration:els.duration&&els.duration.value, size:els.size&&els.size.value, goal:els.goal&&els.goal.value, inter:els.inter&&els.inter.value, energy:els.energy&&els.energy.value, breaks: $('#breaks') && $('#breaks').checked, breakEvery: $('#breakEvery') && $('#breakEvery').value, breakLen: $('#breakLen') && $('#breakLen').value, types: $$('.type').map(c=>({v:c.value, on:c.checked})), blocks: toBlocks() }; }
function applyState(st){ if(!st) return; if(els.title) els.title.value=st.title||''; if(els.duration) els.duration.value=st.duration||60; if(els.size) els.size.value=st.size||12; if(els.goal) els.goal.value=st.goal||''; if(els.inter) els.inter.value=st.inter||'medium'; if(els.energy) els.energy.value=st.energy||'balanced'; if($('#breaks')) $('#breaks').checked=!!st.breaks; if($('#breakEvery')) $('#breakEvery').value=st.breakEvery||60; if($('#breakLen')) $('#breakLen').value=st.breakLen||5; $$('.type').forEach(c=>{ const f=(st.types||[]).find(x=>x.v===c.value); if(f) c.checked=!!f.on; }); if(els.agenda) els.agenda.innerHTML=''; (st.blocks||[]).forEach(b=> addBlock(b.mins,b.type,b.title,b.ins)); if(els.targetMins) els.targetMins.textContent = els.duration && els.duration.value || 60; renumber(); }
function save(){ store.set('wab_state_v1', collectState()); }
;['input','change'].forEach(ev=>{ ['#title','#duration','#size','#goal','#interactivity','#energy','#breaks','#breakEvery','#breakLen'].forEach(sel=>{ const node=$(sel); on(node, ev, ()=>{ renumber(); save(); }); }); $$('.type').forEach(cb=> on(cb, ev, ()=> save())); });
// Guided tour (friendlier copy)
const tour = { steps:[
{sel:'#fld-duration',t:'Set total minutes. The agenda will fit this exactly.'},
{sel:'#fld-goal',t:'Write the one thing people must leave with.'},
{sel:'#fld-inter',t:'Pick how much time should be hands-on.'},
{sel:'#fld-breaks',t:'Add a short break about once per hour.'},
{sel:'#generate',t:'Click generate. Edit blocks, then Rebalance to fit.'}
], i:0 };
function highlight(sel){ const el=$(sel); if(!el) return; el.classList.add('wab-focus'); el.scrollIntoView({behavior:'smooth',block:'center'}); }
function unhighlight(sel){ const el=$(sel); if(!el) return; el.classList.remove('wab-focus'); }
function startTour(){ tour.i=0; if(els.tour) els.tour.style.display='flex'; if(els.tourText) els.tourText.textContent=tour.steps[0].t; highlight(tour.steps[0].sel); }
function nextTour(){ unhighlight(tour.steps[tour.i].sel); tour.i++; if(tour.i>=tour.steps.length){ endTour(); return;} if(els.tourText) els.tourText.textContent=tour.steps[tour.i].t; highlight(tour.steps[tour.i].sel); }
function endTour(){ unhighlight(tour.steps[Math.min(tour.i,tour.steps.length-1)].sel); if(els.tour) els.tour.style.display='none'; }
on(els.runTour,'click', startTour);
on(els.tourNext,'click', nextTour);
on(els.tourSkip,'click', endTour);
// Load from URL or storage
(function init(){
const params = new URLSearchParams(location.search); const fromUrl=params.get('wab'); if(fromUrl){ try{ applyState(JSON.parse(decodeURIComponent(fromUrl))); }catch(e){} } else { applyState(store.get('wab_state_v1', null)); }
if(!(els.agenda && els.agenda.children.length)) generate();
})();
// Self-tests (console)
(function tests(){
try{
// Generate for different durations
if(els.duration){ els.duration.value=30; generate(); }
let total30 = $$('#agenda .b-mins').reduce((a,i)=>a+Number(i.value||0),0); console.assert(total30===30,'Agenda sums to 30');
if(els.duration){ els.duration.value=90; generate(); }
let total90 = $$('#agenda .b-mins').reduce((a,i)=>a+Number(i.value||0),0); console.assert(total90===90,'Agenda sums to 90');
// Rebalance works
if(els.duration){ els.duration.value=60; generate(); }
const first = $('#agenda .b-mins'); if(first){ first.value = Number(first.value)+5; }
renumber(); rebalance();
const total60 = $$('#agenda .b-mins').reduce((a,i)=>a+Number(i.value||0),0); console.assert(total60===60,'Rebalance to 60');
// Manual reset path (clear only)
clearForm();
console.assert(Number(els.duration.value)===60,'Reset sets duration');
console.assert(($$('#agenda .block').length)===0,'Reset clears agenda');
// Reset button regenerates a fresh agenda automatically
if(els.reset && typeof els.reset.click === 'function'){
els.reset.click();
const blocksAfterReset = $$('#agenda .block').length;
console.assert(blocksAfterReset>0,'Reset regenerates agenda');
const totalAfterReset = $$('#agenda .b-mins').reduce((a,i)=>a+Number(i.value||0),0);
const expected = Number(els.duration && els.duration.value || 60);
console.assert(totalAfterReset===expected,'Reset agenda sums to duration');
}
}catch(e){ console.warn('Self-tests failed', e); }
})();
})();