Biology Cell Viewer

Biology Cell Viewer

Cell Membrane Nucleus Mitochondrion Ribosomes Cytoplasm

Select an Organelle

Click on any labeled part of the diagram to see its description. You can edit these descriptions in the "Data Configuration" tab.

This report summarizes the organelles for the currently selected cell type, using any custom data you entered in the configuration tab.

All data is customizable. Changes made here will instantly update the "Cell Viewer" and "Generate Report" tabs.

`; return; } let tableHtml = `

Report for: ${cellTypeName}

${relevantOrganelles.map(id => { const item = bcv_organelleData[id]; // Use data from object, not UI (Spec Other 9) return ` ` }).join('')}
Organelle Description
${item.name} ${item.description}
`; reportOutput.innerHTML = tableHtml; } /** * PDF Download Function (Spec II.C) */ function bcv_downloadPdf() { // Check for jsPDF (Spec IV.B) if (typeof jspdf === "undefined" || typeof jspdf.plugin.autotable === "undefined") { alert("Error: PDF library is missing. Please refresh the page."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const cellType = bcv_currentState.selectedCellType; const cellTypeName = cellTypeSelect.options[cellTypeSelect.selectedIndex].text; // 1. Filter data (same as report tab) const relevantOrganelles = Object.keys(bcv_organelleData).filter(id => bcv_organelleData[id].types.includes(cellType) ); // 2. Format for autoTable (Spec II.C.2) const head = [['Organelle', 'Description']]; const body = relevantOrganelles.map(id => { const item = bcv_organelleData[id]; return [item.name, item.description]; // Clean data }); // 3. Build PDF doc.setFontSize(18); doc.text("Biology Cell Report", 14, 22); doc.setFontSize(12); doc.text(`Cell Type: ${cellTypeName}`, 14, 30); doc.autoTable({ startY: 35, head: head, body: body, theme: 'striped', headStyles: { fillColor: '#00796b' // Consistent color (Spec II.C.3) }, styles: { cellPadding: 3, fontSize: 10, }, columnStyles: { 0: { cellWidth: 40 }, 1: { cellWidth: 'auto' } } }); // 4. Save the PDF doc.save(`Biology-Report-${cellTypeName}.pdf`); } // --- Run Initialization --- bcv_init(); }); // --- Global Functions (required for inline 'onclick') (Spec IV.C) --- /** * Tab navigation function * @param {Event} evt - The click event * @param {string} tabName - The ID of the tab content to show */ function bcv_openTab(evt, tabName) { const container = document.getElementById("bcv-container"); if (!container) return; // Guard clause const tabcontent = container.getElementsByClassName("bcv-tab-content"); for (let i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; tabcontent[i].classList.remove("bcv-active"); } const tablinks = container.getElementsByClassName("bcv-tab-link"); for (let i = 0; i < tablinks.length; i++) { tablinks[i].classList.remove("bcv-active"); } const activeTab = document.getElementById(tabName); if (activeTab) { // Guard clause (Spec IV.B) activeTab.style.display = "block"; activeTab.classList.add("bcv-active"); } if (evt && evt.currentTarget) { evt.currentTarget.classList.add("bcv-active"); } // If opening report tab, refresh its content if (tabName === 'bcv-tab-2') { // Find the update function (it's inside DOMContentLoaded) // This is a common issue. We must ensure the function is accessible. // In our case, the report is *already* updated on every data change, // so this call is just for safety. if (typeof bcv_updateReportTab === "function") { bcv_updateReportTab(); } } } /** * Next/Previous button navigation (Spec II.B.4) * @param {number} direction - 1 for next, -1 for previous */ function bcv_navTab(direction) { const container = document.getElementById("bcv-container"); if (!container) return; // Guard clause const tabs = container.querySelectorAll(".bcv-tab-link"); let currentIndex = -1; for (let i = 0; i < tabs.length; i++) { if (tabs[i].classList.contains("bcv-active")) { currentIndex = i; break; } } if (currentIndex === -1) return; let newIndex = currentIndex + direction; if (newIndex >= tabs.length) { newIndex = 0; } if (newIndex < 0) { newIndex = tabs.length - 1; } // Simulate a click on the new tab button tabs[newIndex].click(); }