Bird Call Identifier Bird Call Identifier Identifier (Dashboard) Settings (Data Configuration) 📤 Click or drag audio file to this area Start Identification Identification Results Download Results (PDF) Geographic Region Filter: North America (USA & Canada) Europe Global (Any) Identification Sensitivity: High Confidence (75%) Save Settings ← Previous Next → "; } else { bci_lastResults.forEach((bird) => { html += ` ${bird.name} ${bird.confidence.toFixed(1)}% Confidence `; }); } if (bci_resultsOutput) bci_resultsOutput.innerHTML = html; } // --- Settings Functions --- function bci_updateSliderValue() { if (!bci_sensitivitySlider || !bci_sliderValue) return; const value = bci_sensitivitySlider.value; bci_settings.sensitivity = parseInt(value, 10); let label = "Medium Confidence"; if (value >= 85) { label = "Very High Confidence"; } else if (value >= 70) { label = "High Confidence"; } else if (value < 60) { label = "Low Confidence"; } bci_sliderValue.textContent = `${label} (${value}%)`; } function bci_saveSettings() { if (!bci_regionSelect || !bci_saveStatus) return; bci_settings.region = bci_regionSelect.value; bci_saveStatus.textContent = "Settings saved!"; setTimeout(() => { if (bci_saveStatus) bci_saveStatus.textContent = ""; }, 2000); } // --- PDF Download Function --- function bci_downloadPDF() { if (typeof jspdf === "undefined" || !jspdf.jsPDF) { alert("Error: jsPDF library not found."); return; } if (bci_lastResults.length === 0) { alert("No results to download."); return; } try { const { jsPDF } = jspdf; const doc = new jsPDF(); // Check if autoTable method exists on the instance. This is the robust way. if (typeof doc.autoTable !== "function") { console.error("jsPDF-AutoTable plugin is not loaded or not a function."); alert("Error: PDF generation functionality is not available. The AutoTable plugin failed to load."); return; } const tableColumn = ["Species Name", "Confidence Score"]; const tableRows = []; bci_lastResults.forEach((bird) => { tableRows.push([bird.name, `${bird.confidence.toFixed(1)}%`]); }); doc.setFontSize(20); doc.setTextColor("#1a253c"); doc.text("Bird Call Identification Report", 105, 20, { align: "center" }); doc.setFontSize(12); doc.setTextColor("#333"); doc.text(`Audio File: ${bci_currentFileName}`, 14, 35); doc.text( `Region Filter: ${ bci_regionSelect.options[bci_regionSelect.selectedIndex].text }`, 14, 42 ); doc.text( `Sensitivity Threshold: ${bci_settings.sensitivity}%`, 14, 49 ); doc.autoTable({ startY: 60, head: [tableColumn], body: tableRows, theme: "grid", headStyles: { fillColor: "#0073e6", textColor: 255, fontStyle: "bold", }, styles: { cellPadding: 3, fontSize: 12 }, }); doc.setFontSize(10); doc.setTextColor("#888"); doc.text( `Report generated on: ${new Date().toLocaleString()}`, 105, doc.internal.pageSize.height - 10, { align: "center" } ); doc.save("Bird_Call_Report.pdf"); } catch (error) { console.error("Error generating PDF:", error); alert("An error occurred while generating the PDF."); } } // --- TAB NAVIGATION FUNCTIONS --- // These are attached to the window object to be accessible by inline onclick attributes in the HTML. /** * Opens a specific tab and hides others. * @param {number} tabIndex - The index of the tab to show. * @param {HTMLElement} [elmnt] - The button element that was clicked. */ window.bci_openTab = function (tabIndex, elmnt) { if (!bci_tabContents || !bci_tabButtons) return; bci_tabContents.forEach((tab) => (tab.style.display = "none")); bci_tabButtons.forEach((btn) => btn.classList.remove("active")); const tabToShow = document.getElementById(`bci-tab-content-${tabIndex}`); if (tabToShow) tabToShow.style.display = "block"; if (elmnt) { elmnt.classList.add("active"); } else if (bci_tabButtons[tabIndex]) { bci_tabButtons[tabIndex].classList.add("active"); } bci_currentTab = tabIndex; bci_updateNavButtons(); }; /** * Navigates to the next or previous tab. * @param {number} direction - (1 for next, -1 for previous). */ window.bci_navigateTab = function (direction) { let newIndex = bci_currentTab + direction; if (newIndex >= 0 && newIndex < bci_tabButtons.length) { bci_openTab(newIndex, bci_tabButtons[newIndex]); } }; /** * Updates the disabled state of Next/Previous buttons. */ function bci_updateNavButtons() { if (!bci_prevBtn || !bci_nextBtn) return; bci_prevBtn.disabled = bci_currentTab === 0; bci_nextBtn.disabled = bci_currentTab === bci_tabButtons.length - 1; } // --- INITIALIZATION --- // Use window.onload to ensure all external scripts like jsPDF are fully loaded. window.onload = bci_initializeTool; })();