Experiment Log Generator

Experiment Log Generator

Experiment Log (Editable)

Define your experiment details and add trials using the "Entry Configuration" tab.

Experiment Details

Add Trial / Step

Current Trials

No trials added yet.

"; return; } trials.forEach(t => { const itemEl = document.createElement("div"); itemEl.className = "elg-config-list-item"; itemEl.dataset.id = t.id; itemEl.innerHTML = ` Trial ${trials.indexOf(t) + 1}: Input ${escapeHTML(t.input)} -> Result ${escapeHTML(t.outcome.substring(0, 30))}... `; configTrialList.appendChild(itemEl); }); } // --- Dashboard Management --- function renderDashboard(isInitial = true) { // Get config values const project = configProject.value; const date = configDate.value; const procedure = configProcedureSummary.value; // --- Build Dashboard HTML --- dashboardOutput.innerHTML = `
# Input / Variable Outcome / Result Observation / Notes Action
`; // Populate Table Body const tbody = dashboardOutput.querySelector('#elg-dash-tbody'); if (trials.length === 0) { tbody.innerHTML = ` No trials logged yet. `; } else { trials.forEach((t, index) => { const tr = document.createElement('tr'); tr.dataset.id = t.id; tr.innerHTML = ` ${index + 1} `; tbody.appendChild(tr); }); } setupDashboardListeners(); if (!isInitial) showTab('elg-tab-dashboard'); } /** * Attaches listeners to the dashboard table elements */ function setupDashboardListeners() { const output = dashboardOutput; if (!output) return; // Event delegation for removal output.addEventListener('click', (e) => { if (e.target.dataset.action === 'remove-dash-item') { const tr = e.target.closest('tr'); trials.splice(trials.findIndex(t => t.id === tr.dataset.id), 1); // Rerender entirely to renumber and update state/config renderDashboard(false); updateConfigListDisplay(); } }); // Event delegation for input/change updates output.addEventListener('input', handleDashboardUpdate); output.addEventListener('change', handleDashboardUpdate); } /** * Handles updates made directly to the dashboard inputs/textareas */ function handleDashboardUpdate(e) { const target = e.target; const value = target.value; // Update Config header fields if (target.id === 'elg-dash-project') configProject.value = value; else if (target.id === 'elg-dash-date') configDate.value = value; else if (target.id === 'elg-dash-procedure-summary') configProcedureSummary.value = value; // Update Trial State const tr = target.closest('tr'); if (tr) { const tId = tr.dataset.id; const trial = trials.find(t => t.id === tId); if (trial) { const field = target.dataset.field; if (field) { trial[field] = value; // Rerender config list immediately updateConfigListDisplay(); } } } } /** * Generates a PDF report from the dashboard data */ function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') { alert("Error: PDF libraries could not be loaded. Please try again."); return; } const projectTitle = configProject.value || "Experiment Log"; const date = configDate.value || "[Date]"; if (!projectTitle.trim()) { alert("Please generate the log first."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF("l", "pt", "a4"); // Landscape for wide table const margin = 40; let yPos = margin; const lineHeight = 16; const usableWidth = doc.internal.pageSize.getWidth() - margin * 2; // Function to add a section function addSection(title, text) { const titleHeight = 20; const textLines = doc.splitTextToSize(text, usableWidth); const textHeight = textLines.length * lineHeight; if (yPos + titleHeight + textHeight > doc.internal.pageSize.getHeight() - margin) { doc.addPage(); yPos = margin; } doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.text(title, margin, yPos); yPos += lineHeight * 1.5; doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.text(textLines, margin, yPos); yPos += textHeight + lineHeight * 1.5; } // --- Document Title --- doc.setFontSize(20); doc.setFont(undefined, 'bold'); doc.text(`Experiment Log: ${projectTitle}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += lineHeight * 1.5; // --- Project Header --- doc.setFontSize(11); doc.setFont(undefined, 'normal'); doc.text(`Run Date: ${date}`, margin, yPos); yPos += lineHeight * 2; // --- 1. Procedure Summary --- addSection("1. Procedure Overview", configProcedureSummary.value); // --- 2. Trial Log --- if (yPos > doc.internal.pageSize.getHeight() - 100) { doc.addPage(); yPos = margin; } doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.text("2. Trial Log", margin, yPos); yPos += 20; const tableHead = [["#", "Input / Variable", "Outcome / Result", "Observation / Notes"]]; const tableBody = trials.map((t, index) => [ index + 1, t.input, t.outcome, t.observation ]); doc.autoTable({ startY: yPos, head: tableHead, body: tableBody, theme: 'striped', headStyles: { fillColor: [0, 115, 230], textColor: [255, 255, 255], fontSize: 9 }, styles: { fontSize: 8, cellPadding: 4, overflow: 'linebreak' }, columnStyles: { 0: { cellWidth: 20, halign: 'center' }, 1: { cellWidth: 150 }, 2: { cellWidth: 150 }, 3: { cellWidth: 'auto' } }, margin: { left: margin, right: margin } }); doc.save(`${projectTitle.replace(/ /g,"_")}_Log_${date}.pdf`); } /** * Helper to escape HTML */ function escapeHTML(str) { if (!str) return ""; return str .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // --- 4. INITIALIZATION & EVENT LISTENERS --- // Tab Listeners tabButtons.forEach((btn) => { btn.addEventListener("click", () => showTab(btn.dataset.target)); }); navButtons.forEach((btn) => { btn.addEventListener("click", () => showTab(btn.dataset.target)); }); // Config Tab Listeners if (addTrialForm) { addTrialForm.addEventListener("submit", handleAddTrial); } if (configTrialList) { configTrialList.addEventListener("click", handleRemoveConfigItem); } if (generateBtn) { generateBtn.addEventListener("click", () => renderDashboard(false)); } // PDF Button if (pdfBtn) { pdfBtn.addEventListener("click", downloadPDF); } // Set default date configDate.valueAsDate = new Date(); // Initial config list display updateConfigListDisplay(); // Initial State: Generate dashboard with samples renderDashboard(); showTab("elg-tab-dashboard"); });
Scroll to Top