DESIGNER
Blank 10 mm grid · tap to draw
Drag grid to pan

`} function saveHTML(){let blob=new Blob([printable()],{type:'text/html;charset=utf-8'}),u=URL.createObjectURL(blob),a=document.createElement('a');a.href=u;a.download=(active().name.replace(/[^a-z0-9-]+/gi,'_')||'job')+'_Flashing_Order.html';document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(u),3000)} function downloadBackup(){let blob=new Blob([JSON.stringify({jobs,jobId},null,2)],{type:'application/json'}),u=URL.createObjectURL(blob),a=document.createElement('a');a.href=u;a.download='FoldMate_Jobs_Backup.json';document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(u),3000)} function orderSheet(){if(!saved.length){modal('Job order','

Save a flashing first, then your order sheet will appear here.

');return} modal('Order · '+active().name,`

${saved.length} profiles in this job. Printable drawings contain the red COLOUR arrow, leg lengths and angles.

${saved.map((s,i)=>`
${i+1}. ${clean(s.name)}${svg(s)}

${clean(s.colour)} · ${clean(s.bmt)} BMT · ${runsText(s)} · Girth ${mm(total(s))} mm

`).join('')}

Email summary uses text only. For drawings, use Print / Save PDF then attach the PDF, or download the illustrated HTML order. This trial cannot automatically attach a PDF to an email.

`); $('#fm7-print').onclick=()=>{let w=window.open('','_blank');if(w){w.document.open();w.document.write(printable());w.document.close()}else{saveHTML()}}; $('#fm7-share').onclick=()=>{let subject='Flashing order - '+active().name;window.location.href='mailto:?subject='+encodeURIComponent(subject)+'&body='+encodeURIComponent(summary())}; $('#fm7-download').onclick=saveHTML; $('#fm7-native').onclick=async()=>{let filename=(active().name.replace(/[^a-z0-9-]+/gi,'_')||'job')+'_Flashing_Order.html',file=new File([printable()],filename,{type:'text/html'});try{if(navigator.canShare?.({files:[file]})&&navigator.share){await navigator.share({files:[file],title:'Flashing order - '+active().name})}else saveHTML()}catch(err){if(err.name!=='AbortError')saveHTML()}}; } function handle(act){if(act==='close'){close();return}if(act==='jobs'){jobMenu();return}if(act==='side'){hist.push(copy(draft));draft.coatingSide=draft.coatingSide==='left'?'right':'left';render();return}if(act==='next'){next();return}if(act==='bag'){briefcase();return}if(act==='undo'){if(hist.length){draft=hist.pop();render()}return}if(act==='draw'||act==='finish'){draw=!draw;render();return}if(act==='plus'){scale=Math.min(12,scale*1.25);render();return}if(act==='minus'){scale=Math.max(.2,scale/1.25);render();return}if(act==='fit'){fit();return}if(act==='reset'){modal('New blank flashing','

Clear the current drawing? Items already saved in your Briefcase will remain.

');$('#fm7-clear').onclick=()=>{draft.p=[];hist=[];editing=-1;draw=true;close();fit()};return}if(act==='colour'){modal('Colour',`

The red arrow shows the coated face.

${Object.entries(palette).map(([k,v])=>``).join('')}
`);$('#fm7-colour-flip').onclick=()=>{draft.coatingSide=draft.coatingSide==='left'?'right':'left';close();render()};$('.content').querySelectorAll('[data-col]').forEach(b=>b.onclick=()=>{draft.colour=b.dataset.col;close();render()});return}if(act==='fold'){modal('End folds',`

Return hems contribute to girth.

`);$('#fm7-hsave').onclick=()=>{hist.push(copy(draft));draft.hem1=Math.max(0,Number($('#fm7-h1').value)||0);draft.hem2=Math.max(0,Number($('#fm7-h2').value)||0);close();render()}}} root.addEventListener('click',e=>{let b=e.target.closest('[data-fm7]');if(b)handle(b.dataset.fm7)});$('.shade').onclick=close; canvas.addEventListener('pointerdown',e=>{const r=canvas.getBoundingClientRect();down={id:e.pointerId,x:e.clientX-r.left,y:e.clientY-r.top,panX:pan.x,panY:pan.y,moved:false};canvas.setPointerCapture(e.pointerId)}); canvas.addEventListener('pointermove',e=>{if(!down||down.id!==e.pointerId)return;const r=canvas.getBoundingClientRect(),dx=e.clientX-r.left-down.x,dy=e.clientY-r.top-down.y;if(Math.hypot(dx,dy)>9)down.moved=true;if(down.moved){pan.x=down.panX+dx;pan.y=down.panY+dy;render()}}); canvas.addEventListener('pointercancel',()=>{down=null}); canvas.addEventListener('pointerup',e=>{if(!down||down.id!==e.pointerId)return;const r=canvas.getBoundingClientRect(),v={x:e.clientX-r.left,y:e.clientY-r.top};let moved=down.moved;down=null;if(moved)return; const hit=hits.find(t=>Math.abs(v.x-t.x)=0)editLeg(seg);return} // In Draw mode tapping an existing measurement switches to Select rather than adding an unwanted fold. if(hit){draw=false;render();editLeg(hit.i);return} let p=snap(world(v));if(!p)return;hist.push(copy(draft));draft.p.push(p);autoFit();render()}); if(typeof ResizeObserver!=='undefined')new ResizeObserver(resize).observe(area);resize(); })();