import {tools} from './registry.mjs'; import {svg,pdf,finish} from './engine.mjs'; const element=(tag,props={},text='')=>{const el=document.createElement(tag);Object.assign(el,props);if(text)el.textContent=text;return el;}; for(const root of document.querySelectorAll('[data-fpg-tool]')){ const tool=tools.find(t=>t.id===root.dataset.fpgTool);if(!tool)continue; const form=element('form',{className:'fpg-controls'}),preview=element('section',{className:'fpg-preview'}),status=element('p',{className:'fpg-status'});status.setAttribute('role','status');status.setAttribute('aria-live','polite');const uid='fpg-'+Math.random().toString(36).slice(2);let pages=[],imageData='',busy=false,dirty=false; const fields={};for(const field of tool.fields){const label=element('label',{},field.label),id=uid+'-'+field.key;label.htmlFor=id;let input;if(field.type==='textarea')input=element('textarea',{rows:6,maxLength:field.key==='text'&&tool.id==='checklist'?50000:5000});else if(field.type==='select'){input=element('select');for(const opt of field.options)input.append(element('option',{value:opt.value},opt.label));}else input=element('input',{type:field.type});input.id=id;input.name=field.key;input.required=field.type!=='file';if(field.min!==undefined)input.min=field.min;if(field.max!==undefined)input.max=field.max;input.step=field.step||'any';if(field.type==='file'){input.accept='image/png,image/jpeg';}else input.value=field.key==='paper'?(root.dataset.paper||field.default):field.default;label.append(input);form.append(label);fields[field.key]=input;} const generate=element('button',{type:'submit',className:'fpg-primary'},'Generate printable');form.append(generate);const controls=element('div',{className:'fpg-actions'}),scope=element('select');scope.setAttribute('aria-label','PDF pages');scope.append(element('option',{value:'student'},'Student pages'),element('option',{value:'all'},'All pages + answers'),element('option',{value:'answers'},'Answer key / call sheet')); const download=element('button',{type:'button',disabled:true},'Download PDF'),print=element('button',{type:'button',disabled:true},'Print'),pager=element('select');pager.setAttribute('aria-label','Preview page');controls.append(scope,download,print);preview.append(controls);const pageArea=element('div',{className:'fpg-page'});preview.append(pager,pageArea,status);root.replaceChildren(form,preview);root.classList.add('fpg-workspace'); const selected=()=>pages.filter(p=>scope.value==='all'||(scope.value==='answers'?p.answer:!p.answer));const show=()=>{const pool=selected();pager.replaceChildren(...pool.map((_,i)=>element('option',{value:i},`Page ${i+1} of ${pool.length}`)));pageArea.innerHTML=pool[0]?svg(pool[0]):'';download.disabled=print.disabled=dirty||!pool.length;};pager.addEventListener('change',()=>{const p=selected()[Number(pager.value)];pageArea.innerHTML=p?svg(p):'';});scope.addEventListener('change',show); form.addEventListener('input',()=>{dirty=true;download.disabled=print.disabled=true;status.textContent='Settings changed. Generate again to update the preview and download.';}); form.addEventListener('change',e=>{if(e.target.name==='grade'){fields.max.value=({k:10,1:20,2:100,3:12,4:100,5:1000,6:1000})[fields.grade.value];fields.operation.value=Number(fields.grade.value)>=3?'*':'+';}if(tool.id==='maze'&&e.target.name==='difficulty')fields.grid.value=({easy:10,medium:20,hard:35})[fields.difficulty.value];if(e.target.name==='ruling'&&fields.ruling.value!=='custom'){fields.unit.value='mm';fields.spacing.value=({wide:8.7,college:7.1,narrow:6.4})[fields.ruling.value];}}); const run=async e=>{e?.preventDefault();if(busy||!form.reportValidity())return;busy=true;generate.disabled=download.disabled=print.disabled=true;status.textContent='Creating your printable…';try{const o=Object.fromEntries(new FormData(form));if(fields.image?.files[0]){const file=fields.image.files[0];if(!['image/png','image/jpeg'].includes(file.type)||file.size>2*1024*1024)throw Error('Choose a PNG or JPEG no larger than 2 MB.');imageData=await new Promise((resolve,reject)=>{const reader=new FileReader();reader.onload=()=>resolve(reader.result);reader.onerror=()=>reject(Error('Image could not be read.'));reader.readAsDataURL(file);});const bitmap=await createImageBitmap(file);if(bitmap.width>4000||bitmap.height>4000){bitmap.close();throw Error('Choose an image no larger than 4000 pixels per side.');}bitmap.close();o.image=imageData;}else delete o.image;await new Promise(resolve=>requestAnimationFrame(resolve));const module=await import(`./generators/${tool.module}.mjs`);pages=finish(module.generate(tool.id,o));dirty=false;show();status.textContent=`Ready: ${pages.length} page${pages.length===1?'':'s'}. Print at 100% / Actual size. Your input stays in this browser.`;}catch(err){pages=[];pageArea.replaceChildren();status.textContent=err.message||'Could not generate. Please check your settings.';}finally{busy=false;generate.disabled=false;}}; form.addEventListener('submit',run); download.addEventListener('click',async()=>{download.disabled=true;try{const bytes=await pdf(selected()),url=URL.createObjectURL(new Blob([bytes],{type:'application/pdf'}));const a=element('a',{href:url,download:tool.id+'.pdf'});document.body.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),60000);status.textContent='PDF created. Check your downloads.';}catch(err){status.textContent=err.message;}finally{download.disabled=!selected().length;}}); print.addEventListener('click',()=>{const pool=selected();if(!pool.length)return;const win=window.open('','_blank');if(!win){status.textContent='Allow pop-ups for this site to open the print view.';return;}win.opener=null;const p=pool[0];win.document.write(`