Food Chain Diagram Generator
Generated Food Chain
Your food chain will appear here. Add an organism to start.
Manage Organism Database
Organism database is empty.
'; } } function renderOrganismDropdown() { if (!fcdgSelectOrganism) return; fcdgSelectOrganism.innerHTML = ''; let hasData = false; Object.keys(fcdgTypeLabels).forEach(type => { const label = fcdgTypeLabels[type]; const organisms = fcdgOrganismData[type]; if (organisms.length > 0) { hasData = true; const optgroup = document.createElement('optgroup'); optgroup.label = label; organisms.forEach(org => { const option = document.createElement('option'); option.value = `${type}|${org.id}|${org.name}`; option.textContent = org.name; optgroup.appendChild(option); }); fcdgSelectOrganism.appendChild(optgroup); } }); if (!hasData) { fcdgSelectOrganism.innerHTML = ''; } } function renderFoodChainDiagram() { if (!fcdgDiagramOutput || !fcdgEmptyMsg) return; fcdgDiagramOutput.innerHTML = ''; if (fcdgCurrentChain.length === 0) { fcdgDiagramOutput.appendChild(fcdgEmptyMsg); } else { fcdgCurrentChain.forEach((org, index) => { // Add arrow if (index > 0) { const arrow = document.createElement('span'); arrow.className = 'fcdg-arrow'; arrow.innerHTML = '→'; fcdgDiagramOutput.appendChild(arrow); } // Add node const node = document.createElement('div'); node.className = `fcdg-node fcdg-type-${org.type}`; node.innerHTML = `${org.name}
${fcdgTypeLabels[org.type]}
`;
fcdgDiagramOutput.appendChild(node);
});
}
}
// --- Event Handlers ---
function handleAddOrganismToDB(e) {
e.preventDefault();
const name = fcdgNewNameInput.value.trim();
const type = fcdgNewTypeSelect.value;
if (name && type) {
fcdgOrganismData[type].push({ id: fcdgNextOrganismId++, name: name });
renderOrganismDatabase();
renderOrganismDropdown();
fcdgNewNameInput.value = '';
}
}
function handleDeleteOrganismFromDB(e) {
if (e.target.matches('.fcdg-delete-btn')) {
const id = parseInt(e.target.dataset.id, 10);
const type = e.target.dataset.type;
if (id && type && fcdgOrganismData[type]) {
fcdgOrganismData[type] = fcdgOrganismData[type].filter(org => org.id !== id);
renderOrganismDatabase();
renderOrganismDropdown();
}
}
}
function addOrganismToChain() {
const selected = fcdgSelectOrganism.value;
if (!selected) return;
const [type, id, name] = selected.split('|');
if (type && id && name) {
fcdgCurrentChain.push({
id: parseInt(id, 10),
name: name,
type: type
});
renderFoodChainDiagram();
}
}
function removeLastFromChain() {
fcdgCurrentChain.pop();
renderFoodChainDiagram();
}
function clearChain() {
fcdgCurrentChain = [];
renderFoodChainDiagram();
}
/**
* Generates and downloads a PDF of the food chain diagram.
*/
async function downloadPDF() {
if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') {
console.error("jsPDF or html2canvas library not loaded.");
alert("Error: PDF generation libraries not loaded.");
return;
}
const { jsPDF } = window.jspdf;
fcdgPdfBtn.textContent = 'Generating...';
fcdgPdfBtn.disabled = true;
const contentToExport = document.getElementById('fcdg-pdf-content');
if (!contentToExport) {
console.error("PDF content area not found.");
fcdgPdfBtn.textContent = 'Download Diagram as PDF';
fcdgPdfBtn.disabled = false;
return;
}
// Add a class to hide the empty message in the PDF
if (fcdgEmptyMsg) fcdgEmptyMsg.classList.add('fcdg-pdf-hide');
try {
const canvas = await html2canvas(contentToExport, {
scale: 2,
useCORS: true,
logging: false,
backgroundColor: '#ffffff' // Ensure white background
});
const imgData = canvas.toDataURL('image/png');
// Use PDF dimensions that respect the canvas aspect ratio
const pdf = new jsPDF({
orientation: 'l', // Landscape
unit: 'px',
format: 'a4'
});
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
// Calculate ratio to fit
const ratio = Math.min(pdfWidth / canvasWidth, pdfHeight / canvasHeight);
const imgWidth = canvasWidth * ratio;
const imgHeight = canvasHeight * ratio;
// Center image
const x = (pdfWidth - imgWidth) / 2;
const y = (pdfHeight - imgHeight) / 2;
pdf.addImage(imgData, 'PNG', x, y, imgWidth, imgHeight);
pdf.save('food_chain_diagram.pdf');
} catch (error) {
console.error("Error during PDF generation:", error);
alert("An error occurred while generating the PDF.");
} finally {
if (fcdgEmptyMsg) fcdgEmptyMsg.classList.remove('fcdg-pdf-hide');
fcdgPdfBtn.textContent = 'Download Diagram as PDF';
fcdgPdfBtn.disabled = false;
}
}
// --- Tab Navigation Functions ---
window.fcdgOpenTab = function(evt, tabName) {
if (!fcdgTabContents.length || !fcdgTabLinks.length) return;
fcdgTabContents.forEach(tabcontent => {
tabcontent.style.display = "none";
tabcontent.classList.remove('fcdg-active');
});
fcdgTabLinks.forEach(tablink => {
tablink.classList.remove('fcdg-active');
});
const currentTabContent = document.getElementById(tabName);
if (currentTabContent) {
currentTabContent.style.display = "block";
currentTabContent.classList.add('fcdg-active');
}
if (evt && evt.currentTarget) {
evt.currentTarget.classList.add('fcdg-active');
}
fcdgCurrentTab = fcdgTabLinks.indexOf(evt.currentTarget);
updateNavButtons();
}
window.fcdgNavTab = function(n) {
const newIndex = fcdgCurrentTab + n;
if (newIndex >= 0 && newIndex < fcdgTabLinks.length) {
fcdgTabLinks[newIndex].click();
}
}
function updateNavButtons() {
if (fcdgPrevBtn) fcdgPrevBtn.disabled = (fcdgCurrentTab === 0);
if (fcdgNextBtn) fcdgNextBtn.disabled = (fcdgCurrentTab === fcdgTabLinks.length - 1);
}
})();
