function getAppBase() { const { pathname } = window.location; if (pathname === "/" || pathname === "/index.html") { return ""; } if (pathname.endsWith("/index.html")) { return pathname.slice(0, -"/index.html".length); } return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname; } const apiBase = getAppBase().replace(/\/bibliography\.html$/, ""); const bibliographyList = document.querySelector("#bibliography-list"); const bibliographySearch = document.querySelector("#bibliography-search"); const bibliographyStatus = document.querySelector("#bibliography-status"); const bibliographyDownload = document.querySelector("#bibliography-download"); let currentBibliographyItems = []; function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll('"', """) .replaceAll("<", "<") .replaceAll(">", ">"); } function normalizeAbstractForDisplay(value) { const raw = String(value || "").trim(); if (!raw) { return ""; } const temp = document.createElement("div"); temp.innerHTML = raw; return temp.textContent .replace(/^abstract\s*[:.\-]?\s*/i, "") .replace(/\s+/g, " ") .trim(); } function parseBibtexFields(draftBibtex) { const fields = {}; const text = String(draftBibtex || ""); const pattern = /([a-zA-Z_]+)\s*=\s*\{([^}]*)\}/g; let match = pattern.exec(text); while (match) { fields[match[1].toLowerCase()] = match[2].trim(); match = pattern.exec(text); } return fields; } function collectBibtexRecords(items) { const seen = new Set(); const records = []; for (const item of items || []) { const draftBibtex = String(item && item.draft_bibtex ? item.draft_bibtex : "").trim(); if (!draftBibtex || seen.has(draftBibtex)) { continue; } seen.add(draftBibtex); records.push(draftBibtex); } return records; } function downloadBibtexRecords(items, filenameStem) { const records = collectBibtexRecords(items); if (!records.length) { return false; } const blob = new Blob([`${records.join("\n\n")}\n`], { type: "application/x-bibtex;charset=utf-8" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = `${filenameStem}.bib`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.setTimeout(() => URL.revokeObjectURL(url), 0); return true; } function syncDownloadButton(items) { if (!bibliographyDownload) { return; } const recordCount = collectBibtexRecords(items).length; bibliographyDownload.disabled = !recordCount; bibliographyDownload.textContent = recordCount ? `Download BibTeX (${recordCount})` : "Download BibTeX"; } function buildCitationText(item) { const fields = parseBibtexFields(item.draft_bibtex || ""); if (item.normalized_text) { return escapeHtml(item.normalized_text); } const author = fields.author || ""; const year = fields.year || ""; const title = fields.title || ""; const venue = fields.journal || fields.booktitle || fields.publisher || ""; const volume = fields.volume || ""; const issue = fields.number || ""; const pages = fields.pages || ""; const parts = []; const lead = [author, year ? `(${year})` : ""].filter(Boolean).join(" "); if (lead) { parts.push(lead); } if (title) { parts.push(title); } const venueBits = [venue, volume ? `${volume}${issue ? `(${issue})` : ""}` : issue ? `(${issue})` : "", pages] .filter(Boolean) .join(", "); if (venueBits) { parts.push(venueBits); } return escapeHtml(parts.join(". ").trim() || item.raw_text || ""); } function renderSpeciesRefs(refs) { return refs .map( (ref) => `${escapeHtml(ref.common_name || ref.slug)}`, ) .join(", "); } function renderAbstractBlock(text) { const abstract = normalizeAbstractForDisplay(text); if (!abstract) { return ""; } return `
`; } function attachCitationAbstractToggles(root) { for (const toggle of root.querySelectorAll(".citation-abstract-toggle")) { const shell = toggle.parentElement; const display = shell && shell.querySelector(".citation-abstract-display"); if (!display) { continue; } toggle.addEventListener("click", () => { const hidden = display.classList.toggle("hidden"); toggle.setAttribute("aria-expanded", hidden ? "false" : "true"); toggle.textContent = hidden ? "Show Abstract" : "Hide Abstract"; }); } } function renderBibliography(items) { bibliographyList.innerHTML = ""; if (!items.length) { bibliographyList.innerHTML = `

No bibliography entries match the current search.

`; return; } for (const item of items) { const links = [ item.doi ? `DOI` : "", item.source_url ? `Source` : "", item.openalex_id ? `OpenAlex` : "", ] .filter(Boolean) .join(" · "); const article = document.createElement("article"); article.className = "public-citation-entry"; article.innerHTML = `

${buildCitationText(item)}

${renderAbstractBlock(item.abstract_text || "")}

Appears in ${item.species_count} species record${item.species_count === 1 ? "" : "s"} ${item.legacy_reference_numbers && item.legacy_reference_numbers.length ? ` • Imported references: ${item.legacy_reference_numbers.map((value) => escapeHtml(value)).join(", ")}` : ""}

Species: ${renderSpeciesRefs(item.species_refs || [])}

${links ? `` : ""} `; attachCitationAbstractToggles(article); bibliographyList.appendChild(article); } } async function loadBibliography(search = "") { bibliographyStatus.textContent = "Loading bibliography..."; const query = search ? `?search=${encodeURIComponent(search)}` : ""; const response = await fetch(`${apiBase}/api/bibliography${query}`); const data = await response.json(); if (!response.ok) { bibliographyList.innerHTML = `

${escapeHtml(data.error || "Unable to load bibliography.")}

`; bibliographyStatus.textContent = data.error || "Bibliography load failed"; return; } currentBibliographyItems = data.items || []; renderBibliography(currentBibliographyItems); syncDownloadButton(currentBibliographyItems); bibliographyStatus.textContent = `${data.count || 0} bibliography entr${data.count === 1 ? "y" : "ies"}`; } bibliographySearch.addEventListener("input", async (event) => { await loadBibliography(event.target.value); }); loadBibliography().catch((error) => { bibliographyList.innerHTML = `

Failed to load bibliography: ${escapeHtml(String(error))}

`; bibliographyStatus.textContent = "Bibliography load failed"; }); if (bibliographyDownload) { bibliographyDownload.addEventListener("click", () => { const downloaded = downloadBibtexRecords(currentBibliographyItems, "ecospecies-bibliography"); if (!downloaded) { bibliographyStatus.textContent = "No BibTeX records are available for download yet."; } }); }