I❤️Pagetree
พื้นที่ตัดโจทย์
💡 ลากเมาส์/นิ้วบนรูปเพื่อตีกรอบโจทย์ทีละข้อ — แต่ละกรอบจะกลายเป็น 1 หน้าโดยอัตโนมัติ
`; } function downloadHTML(){ const blob = new Blob([buildExportHTML()], { type: 'text/html;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'pagetree-problems.html'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 2000); } function printPDF(){ const iframe = document.createElement('iframe'); iframe.style.position = 'fixed'; iframe.style.right = '0'; iframe.style.bottom = '0'; iframe.style.width = '0'; iframe.style.height = '0'; iframe.style.border = '0'; document.body.appendChild(iframe); iframe.onload = () => { setTimeout(() => { try { iframe.contentWindow.focus(); iframe.contentWindow.print(); } catch (err) { alert('เบราว์เซอร์ไม่อนุญาตให้พิมพ์อัตโนมัติ กรุณาใช้ "ดาวน์โหลดไฟล์ HTML" แล้วเปิดไฟล์เพื่อพิมพ์เป็น PDF'); } setTimeout(() => iframe.remove(), 1500); }, 350); }; iframe.srcdoc = buildExportHTML(); } /* ========================================================= EVENTS ========================================================= */ els.addBtn.onclick = () => els.fileInput.click(); els.fileInput.onchange = e => { addFiles(e.target.files); e.target.value = ''; }; els.removeSrc.onclick = () => { const src = activeSource(); if (!src) return; if (!confirm('ลบรูปนี้และโจทย์ที่ตัดจากรูปนี้ทั้งหมด?')) return; state.problems = state.problems.filter(p => p.sourceId !== src.id); state.sources = state.sources.filter(s => s.id !== src.id); state.activeId = state.sources.length ? state.sources[state.sources.length - 1].id : null; render(); }; els.clearBtn.onclick = () => { if (!state.sources.length && !state.problems.length) return; if (!confirm('ล้างรูปและโจทย์ทั้งหมด?')) return; state.sources = []; state.problems = []; state.activeId = null; render(); }; els.setBtn.onclick = e => { e.stopPropagation(); if (!state.problems.length){ alert('ยังไม่มีโจทย์ให้บันทึก — ลากกรอบบนรูปก่อนนะ'); return; } els.setMenu.classList.toggle('open'); }; els.setMenu.onclick = e => { const btn = e.target.closest('button[data-act]'); if (!btn) return; els.setMenu.classList.remove('open'); if (btn.dataset.act === 'pdf') printPDF(); if (btn.dataset.act === 'html') downloadHTML(); }; document.addEventListener('click', e => { if (!els.setMenu.contains(e.target) && e.target !== els.setBtn){ els.setMenu.classList.remove('open'); } }); /* Paste (Ctrl+V) */ document.addEventListener('paste', e => { const items = e.clipboardData && e.clipboardData.items; if (!items) return; const files = []; for (const it of items){ if (it.type && it.type.startsWith('image/')){ const f = it.getAsFile(); if (f) files.push(f); } } if (files.length){ e.preventDefault(); addFiles(files); } }); /* Drag & drop onto stage */ ['dragenter','dragover'].forEach(ev => { els.stage.addEventListener(ev, e => { e.preventDefault(); els.stage.style.outline = '2px dashed #e11d48'; els.stage.style.outlineOffset = '-10px'; }); }); ['dragleave','drop'].forEach(ev => { els.stage.addEventListener(ev, e => { e.preventDefault(); els.stage.style.outline = 'none'; }); }); els.stage.addEventListener('drop', e => { if (e.dataTransfer && e.dataTransfer.files.length) addFiles(e.dataTransfer.files); }); /* Init */ render();