3. Cleanup & Quantification Preparation
Method (${params.quant.replace('_', '-')}):
${cleanup.steps}
4. Final QC & Mass Spectrometry Prep
Final Resuspension (for ${params.instrument}):
${qc}
Note: Final peptide concentration must be verified via NanoDrop or similar method before injection.
Sample Type: ${params.sample}
Analysis Goal: ${params.goal}
Quant. Method: ${params.quant.replace('_', '-')}
Instrument: ${params.instrument}
`; outputContent.innerHTML = protocolHtml; } /** * Generates and downloads the PDF. */ async function downloadPDF() { if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { alert("Error: PDF libraries failed to load."); return; } // 1. Prepare PDF Clone pdfClone.classList.remove('hidden'); pdfClone.style.cssText = 'position: absolute; left: -9999px; top: 0; width: 800px; background: white; padding: 40px;'; pdfClone.innerHTML = document.getElementById('ppg-dashboard-output').innerHTML; // Clone content const { jsPDF } = window.jspdf; try { // 2. Render canvas from the clone const canvas = await html2canvas(pdfClone, { scale: 2, useCORS: true }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const imgProps = pdf.getImageProperties(imgData); const imgWidth = imgProps.width; const imgHeight = imgProps.height; // Scale image height to fit pdf width, handle multiple pages const margin = 40; const contentWidth = pdfWidth - (margin * 2); const contentHeight = (contentWidth * imgHeight) / imgWidth; let heightLeft = contentHeight; let position = 0; pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight); heightLeft -= (pdfHeight - margin * 2); while (heightLeft > 0) { position -= (pdfHeight - margin * 2); pdf.addPage(); pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight); heightLeft -= (pdfHeight - margin * 2); } const title = outputTitle.textContent.replace(/[^a-z0-9]/gi, '_'); pdf.save(`${title}_Protocol.pdf`); } catch (error) { console.error("PDF generation failed:", error); alert("An error occurred while generating the PDF."); } finally { pdfClone.classList.add('hidden'); pdfClone.innerHTML = ''; } } // --- EVENT LISTENERS & INITIALIZATION --- // Tab link clicks tabLinks.forEach((link, index) => { link.addEventListener('click', () => switchTab(index)); }); // Next button (Submit/Generate) if (nextButton) { nextButton.addEventListener('click', () => { renderDashboard(); switchTab(0); // Switch to Dashboard }); } // PDF download if (downloadButton) { downloadButton.addEventListener('click', downloadPDF); } // Initial render switchTab(0); // Ensure initial styles are correct (Dashboard first) renderDashboard(); // Generate initial protocol based on defaults });