(function () { "use strict"; const dataService = window.TarotDataService || {}; const state = { initialized: false, catalog: null, selectedSourceId: "", selectedWorkId: "", selectedSectionId: "", currentPassage: null, lexiconEntry: null, lexiconRequestId: 0, lexiconOccurrenceResults: null, lexiconOccurrenceLoading: false, lexiconOccurrenceError: "", lexiconOccurrenceVisible: false, lexiconOccurrenceRequestId: 0, globalSearchQuery: "", localSearchQuery: "", activeSearchScope: "global", searchQuery: "", searchResults: null, searchLoading: false, searchError: "", searchRequestId: 0, highlightedVerseId: "", displayPreferencesBySource: {} }; let sourceListEl; let sourceCountEl; let globalSearchFormEl; let globalSearchInputEl; let localSearchFormEl; let localSearchInputEl; let workSelectEl; let sectionSelectEl; let detailNameEl; let detailSubEl; let detailBodyEl; let lexiconPopupEl; let lexiconPopupTitleEl; let lexiconPopupSubtitleEl; let lexiconPopupBodyEl; let lexiconPopupCloseEl; let lexiconReturnFocusEl = null; function normalizeId(value) { return String(value || "") .trim() .toLowerCase(); } function getElements() { sourceListEl = document.getElementById("alpha-text-source-list"); sourceCountEl = document.getElementById("alpha-text-source-count"); globalSearchFormEl = document.getElementById("alpha-text-global-search-form"); globalSearchInputEl = document.getElementById("alpha-text-global-search-input"); localSearchFormEl = document.getElementById("alpha-text-local-search-form"); localSearchInputEl = document.getElementById("alpha-text-local-search-input"); workSelectEl = document.getElementById("alpha-text-work-select"); sectionSelectEl = document.getElementById("alpha-text-section-select"); detailNameEl = document.getElementById("alpha-text-detail-name"); detailSubEl = document.getElementById("alpha-text-detail-sub"); detailBodyEl = document.getElementById("alpha-text-detail-body"); ensureLexiconPopup(); } function ensureLexiconPopup() { if (lexiconPopupEl instanceof HTMLElement) { return; } const popup = document.createElement("div"); popup.className = "alpha-text-lexicon-popup"; popup.hidden = true; popup.setAttribute("aria-hidden", "true"); const backdrop = document.createElement("div"); backdrop.className = "alpha-text-lexicon-popup-backdrop"; backdrop.addEventListener("click", closeLexiconEntry); const card = document.createElement("section"); card.className = "alpha-text-lexicon-popup-card"; card.setAttribute("role", "dialog"); card.setAttribute("aria-modal", "true"); card.setAttribute("aria-labelledby", "alpha-text-lexicon-popup-title"); card.setAttribute("tabindex", "-1"); const header = document.createElement("div"); header.className = "alpha-text-lexicon-popup-header"; const headingWrap = document.createElement("div"); headingWrap.className = "alpha-text-lexicon-popup-heading"; const title = document.createElement("h3"); title.id = "alpha-text-lexicon-popup-title"; title.textContent = "Lexicon Entry"; const subtitle = document.createElement("p"); subtitle.className = "alpha-text-lexicon-popup-subtitle"; subtitle.textContent = "Strong's definition"; headingWrap.append(title, subtitle); const closeButton = document.createElement("button"); closeButton.type = "button"; closeButton.className = "alpha-text-lexicon-popup-close"; closeButton.textContent = "Close"; closeButton.addEventListener("click", closeLexiconEntry); header.append(headingWrap, closeButton); const body = document.createElement("div"); body.className = "alpha-text-lexicon-popup-body"; card.append(header, body); popup.append(backdrop, card); document.body.appendChild(popup); lexiconPopupEl = popup; lexiconPopupTitleEl = title; lexiconPopupSubtitleEl = subtitle; lexiconPopupBodyEl = body; lexiconPopupCloseEl = closeButton; } function getSources() { return Array.isArray(state.catalog?.sources) ? state.catalog.sources : []; } function findById(entries, value) { const needle = normalizeId(value); return (Array.isArray(entries) ? entries : []).find((entry) => normalizeId(entry?.id) === needle) || null; } function getSelectedSource() { return findById(getSources(), state.selectedSourceId); } function getSelectedWork(source = getSelectedSource()) { return findById(source?.works, state.selectedWorkId); } function getSelectedSection(source = getSelectedSource(), work = getSelectedWork(source)) { return findById(work?.sections, state.selectedSectionId); } function normalizeTextValue(value) { return String(value || "").trim(); } const GREEK_TRANSLITERATION_MAP = { α: "a", β: "b", γ: "g", δ: "d", ε: "e", ζ: "z", η: "e", θ: "th", ι: "i", κ: "k", λ: "l", μ: "m", ν: "n", ξ: "x", ο: "o", π: "p", ρ: "r", σ: "s", ς: "s", τ: "t", υ: "u", φ: "ph", χ: "ch", ψ: "ps", ω: "o" }; const HEBREW_TRANSLITERATION_MAP = { א: "a", ב: "b", ג: "g", ד: "d", ה: "h", ו: "v", ז: "z", ח: "ch", ט: "t", י: "y", כ: "k", ך: "k", ל: "l", מ: "m", ם: "m", נ: "n", ן: "n", ס: "s", ע: "a", פ: "p", ף: "p", צ: "ts", ץ: "ts", ק: "q", ר: "r", ש: "sh", ת: "t" }; function stripSourceScriptMarks(value) { return String(value || "") .normalize("NFD") .replace(/[\u0300-\u036f\u0591-\u05c7]/g, ""); } function transliterateSourceScriptText(value) { const stripped = stripSourceScriptMarks(value); let result = ""; for (const character of stripped) { const lowerCharacter = character.toLowerCase(); const mapped = GREEK_TRANSLITERATION_MAP[lowerCharacter] || HEBREW_TRANSLITERATION_MAP[character] || HEBREW_TRANSLITERATION_MAP[lowerCharacter]; result += mapped != null ? mapped : character; } return result .replace(/\s+([,.;:!?])/g, "$1") .replace(/\s+/g, " ") .trim(); } function buildTokenDerivedTransliteration(verse) { const tokenText = (Array.isArray(verse?.tokens) ? verse.tokens : []) .map((token) => normalizeTextValue(token?.original)) .filter(Boolean) .join(" ") .replace(/\s+([,.;:!?])/g, "$1") .trim(); return tokenText ? transliterateSourceScriptText(tokenText) : ""; } function getVerseTransliteration(verse, source = null) { const metadata = verse?.metadata && typeof verse.metadata === "object" ? verse.metadata : {}; const explicit = [ verse?.transliteration, verse?.xlit, metadata?.transliteration, metadata?.transliterationText, metadata?.xlit, metadata?.romanizedText, metadata?.romanized ].map(normalizeTextValue).find(Boolean) || ""; if (explicit) { return explicit; } if (source?.features?.hasTokenAnnotations) { return buildTokenDerivedTransliteration(verse); } return ""; } function getSearchInput(scope) { return scope === "source" ? localSearchInputEl : globalSearchInputEl; } function getStoredSearchQuery(scope) { return scope === "source" ? state.localSearchQuery : state.globalSearchQuery; } function setStoredSearchQuery(scope, value) { if (scope === "source") { state.localSearchQuery = value; return; } state.globalSearchQuery = value; } function updateSearchControls() { return; } function clearActiveSearchUi(options = {}) { const preserveHighlight = options.preserveHighlight === true; const scope = state.activeSearchScope === "source" ? "source" : "global"; setStoredSearchQuery(scope, ""); const input = getSearchInput(scope); if (input instanceof HTMLInputElement) { input.value = ""; } state.searchQuery = ""; state.searchResults = null; state.searchLoading = false; state.searchError = ""; state.searchRequestId += 1; if (!preserveHighlight) { state.highlightedVerseId = ""; } updateSearchControls(); } function getSourceDisplayCapabilities(source, passage) { const verses = Array.isArray(passage?.verses) ? passage.verses : []; const hasOriginal = verses.some((verse) => normalizeTextValue(verse?.originalText)); const hasTransliteration = verses.some((verse) => getVerseTransliteration(verse, source)); const hasInterlinear = Boolean(source?.features?.hasTokenAnnotations); const textModeCount = 1 + (hasOriginal ? 1 : 0) + (hasTransliteration ? 1 : 0); return { hasTranslation: true, hasOriginal, hasTransliteration, hasInterlinear, hasAnyExtras: hasOriginal || hasTransliteration || hasInterlinear, supportsAllTextMode: textModeCount > 1 }; } function getDefaultTextDisplayMode(capabilities) { if (capabilities?.hasTranslation) { return "translation"; } if (capabilities?.hasOriginal) { return "original"; } if (capabilities?.hasTransliteration) { return "transliteration"; } return "translation"; } function getAvailableTextDisplayModes(capabilities) { const modes = []; if (capabilities?.hasTranslation) { modes.push("translation"); } if (capabilities?.hasOriginal) { modes.push("original"); } if (capabilities?.hasTransliteration) { modes.push("transliteration"); } if (capabilities?.supportsAllTextMode) { modes.push("all"); } return modes; } function getSourceDisplayPreferences(source, passage) { const sourceId = normalizeId(source?.id); const capabilities = getSourceDisplayCapabilities(source, passage); const availableTextModes = getAvailableTextDisplayModes(capabilities); const stored = sourceId ? state.displayPreferencesBySource[sourceId] : null; let textMode = stored?.textMode; if (!availableTextModes.includes(textMode)) { textMode = getDefaultTextDisplayMode(capabilities); } const preferences = { textMode, showInterlinear: capabilities.hasInterlinear ? Boolean(stored?.showInterlinear) : false }; if (sourceId) { state.displayPreferencesBySource[sourceId] = preferences; } return { ...preferences, capabilities, availableTextModes }; } function updateSourceDisplayPreferences(source, patch) { const sourceId = normalizeId(source?.id); if (!sourceId) { return; } const current = state.displayPreferencesBySource[sourceId] || {}; state.displayPreferencesBySource[sourceId] = { ...current, ...patch }; } function formatTextDisplayModeLabel(mode) { switch (mode) { case "translation": return "Translation"; case "original": return "Original"; case "transliteration": return "Transliteration"; case "all": return "All"; default: return "Display"; } } function clearSearchState() { state.searchQuery = ""; state.searchResults = null; state.searchLoading = false; state.searchError = ""; state.highlightedVerseId = ""; state.searchRequestId += 1; updateSearchControls(); } function clearScopedSearch(scope) { setStoredSearchQuery(scope, ""); const input = getSearchInput(scope); if (input instanceof HTMLInputElement) { input.value = ""; } if (state.activeSearchScope === scope) { clearSearchState(); } else { updateSearchControls(); } } function escapeRegExp(value) { return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function appendHighlightedText(target, text, query) { if (!(target instanceof HTMLElement)) { return; } const sourceText = String(text || ""); const normalizedQuery = String(query || "").trim(); target.replaceChildren(); if (!normalizedQuery) { target.textContent = sourceText; return; } const matcher = new RegExp(escapeRegExp(normalizedQuery), "ig"); let lastIndex = 0; let match = matcher.exec(sourceText); while (match) { if (match.index > lastIndex) { target.appendChild(document.createTextNode(sourceText.slice(lastIndex, match.index))); } const mark = document.createElement("mark"); mark.className = "alpha-text-mark"; mark.textContent = sourceText.slice(match.index, match.index + match[0].length); target.appendChild(mark); lastIndex = match.index + match[0].length; match = matcher.exec(sourceText); } if (lastIndex < sourceText.length) { target.appendChild(document.createTextNode(sourceText.slice(lastIndex))); } } function isHighlightedVerse(verse) { return normalizeId(verse?.id) && normalizeId(verse?.id) === normalizeId(state.highlightedVerseId); } function scrollHighlightedVerseIntoView() { const highlightedVerse = detailBodyEl?.querySelector?.(".alpha-text-verse.is-highlighted"); const detailPanel = highlightedVerse?.closest?.(".planet-detail-panel"); if (!(highlightedVerse instanceof HTMLElement) || !(detailPanel instanceof HTMLElement)) { return; } const verseRect = highlightedVerse.getBoundingClientRect(); const panelRect = detailPanel.getBoundingClientRect(); const targetTop = detailPanel.scrollTop + (verseRect.top - panelRect.top) - (detailPanel.clientHeight / 2) + (verseRect.height / 2); detailPanel.scrollTo({ top: Math.max(0, targetTop), behavior: "smooth" }); } function createCard(title) { const card = document.createElement("div"); card.className = "detail-meta-card planet-meta-card"; if (title) { const heading = document.createElement("strong"); heading.textContent = title; card.appendChild(heading); } return card; } function createEmptyMessage(text) { const message = document.createElement("div"); message.className = "alpha-text-empty"; message.textContent = text; return message; } function renderPlaceholder(title, subtitle, message) { if (detailNameEl) { detailNameEl.textContent = title; } if (detailSubEl) { detailSubEl.textContent = subtitle; } if (!detailBodyEl) { return; } detailBodyEl.replaceChildren(); const card = createCard("Text Reader"); card.appendChild(createEmptyMessage(message)); detailBodyEl.appendChild(card); } function navigateToPassageTarget(target) { if (!target) { return; } state.selectedWorkId = target.workId; state.selectedSectionId = target.sectionId; state.lexiconEntry = null; renderSelectors(); void loadSelectedPassage(); } function getPassageLocationLabel(passage) { const source = passage?.source || getSelectedSource(); const work = passage?.work || getSelectedWork(source); const section = passage?.section || getSelectedSection(source, work); return `${work?.title || "--"} · ${section?.title || section?.label || "--"}`; } function syncSelectionForSource(source) { const works = Array.isArray(source?.works) ? source.works : []; if (!works.length) { state.selectedWorkId = ""; state.selectedSectionId = ""; return; } if (!findById(works, state.selectedWorkId)) { state.selectedWorkId = works[0].id; } const work = getSelectedWork(source); const sections = Array.isArray(work?.sections) ? work.sections : []; if (!findById(sections, state.selectedSectionId)) { state.selectedSectionId = sections[0]?.id || ""; } } async function ensureCatalogLoaded(forceRefresh = false) { if (!forceRefresh && state.catalog) { return state.catalog; } const payload = await dataService.loadTextLibrary?.(forceRefresh); state.catalog = payload && typeof payload === "object" ? payload : { meta: {}, sources: [], lexicons: [] }; if (!state.selectedSourceId) { state.selectedSourceId = getSources()[0]?.id || ""; } syncSelectionForSource(getSelectedSource()); return state.catalog; } function fillSelect(selectEl, entries, selectedValue, labelBuilder) { if (!(selectEl instanceof HTMLSelectElement)) { return; } selectEl.replaceChildren(); (Array.isArray(entries) ? entries : []).forEach((entry) => { const option = document.createElement("option"); option.value = entry.id; option.textContent = typeof labelBuilder === "function" ? labelBuilder(entry) : String(entry?.label || entry?.title || entry?.id || ""); option.selected = normalizeId(entry.id) === normalizeId(selectedValue); selectEl.appendChild(option); }); selectEl.disabled = !selectEl.options.length; } function renderSourceList() { if (!sourceListEl) { return; } sourceListEl.replaceChildren(); const sources = getSources(); sources.forEach((source) => { const button = document.createElement("button"); button.type = "button"; button.className = "planet-list-item alpha-text-source-btn"; button.dataset.sourceId = source.id; button.setAttribute("role", "option"); const isSelected = normalizeId(source.id) === normalizeId(state.selectedSourceId); button.classList.toggle("is-selected", isSelected); button.setAttribute("aria-selected", isSelected ? "true" : "false"); const name = document.createElement("span"); name.className = "planet-list-name"; name.textContent = source.title; const meta = document.createElement("span"); meta.className = "alpha-text-source-meta"; const sectionLabel = source.sectionLabel || "Section"; meta.textContent = `${source.shortTitle || source.title} · ${source.stats?.workCount || 0} ${source.workLabel || "Works"} · ${source.stats?.sectionCount || 0} ${sectionLabel.toLowerCase()}s`; button.append(name, meta); button.addEventListener("click", () => { if (normalizeId(source.id) === normalizeId(state.selectedSourceId)) { return; } state.selectedSourceId = source.id; state.currentPassage = null; state.lexiconEntry = null; state.highlightedVerseId = ""; syncSelectionForSource(getSelectedSource()); renderSourceList(); renderSelectors(); if (state.searchQuery && state.activeSearchScope === "source") { void Promise.all([loadSelectedPassage(), runSearch("source")]); return; } void loadSelectedPassage(); }); sourceListEl.appendChild(button); }); if (!sources.length) { sourceListEl.appendChild(createEmptyMessage("No text sources are available.")); } if (sourceCountEl) { sourceCountEl.textContent = `${sources.length} sources`; } } function renderSelectors() { const source = getSelectedSource(); const work = getSelectedWork(source); const works = Array.isArray(source?.works) ? source.works : []; const sections = Array.isArray(work?.sections) ? work.sections : []; fillSelect(workSelectEl, works, state.selectedWorkId, (entry) => `${entry.title} (${entry.sectionCount} ${String(source?.sectionLabel || "section").toLowerCase()}s)`); fillSelect(sectionSelectEl, sections, state.selectedSectionId, (entry) => `${entry.label} · ${entry.verseCount} verses`); } function closeLexiconEntry() { dismissLexiconEntry(); } function clearLexiconOccurrenceState() { state.lexiconOccurrenceResults = null; state.lexiconOccurrenceLoading = false; state.lexiconOccurrenceError = ""; state.lexiconOccurrenceVisible = false; state.lexiconOccurrenceRequestId += 1; } function dismissLexiconEntry(options = {}) { const shouldRestoreFocus = options.restoreFocus !== false; state.lexiconRequestId += 1; state.lexiconEntry = null; clearLexiconOccurrenceState(); renderLexiconPopup(); const returnFocusEl = lexiconReturnFocusEl; lexiconReturnFocusEl = null; if (shouldRestoreFocus && returnFocusEl instanceof HTMLElement && returnFocusEl.isConnected) { requestAnimationFrame(() => { if (returnFocusEl.isConnected) { returnFocusEl.focus(); } }); } } async function toggleLexiconOccurrences() { const lexiconId = state.lexiconEntry?.lexicon?.id || state.lexiconEntry?.lexiconId || ""; const entryId = state.lexiconEntry?.entryId || ""; if (!lexiconId || !entryId) { return; } if (state.lexiconOccurrenceVisible && !state.lexiconOccurrenceLoading) { state.lexiconOccurrenceVisible = false; renderLexiconPopup(); return; } state.lexiconOccurrenceVisible = true; if (state.lexiconOccurrenceResults || state.lexiconOccurrenceError) { renderLexiconPopup(); return; } const requestId = state.lexiconOccurrenceRequestId + 1; state.lexiconOccurrenceRequestId = requestId; state.lexiconOccurrenceLoading = true; state.lexiconOccurrenceError = ""; renderLexiconPopup(); try { const payload = await dataService.loadTextLexiconOccurrences?.(lexiconId, entryId, { limit: 100 }); if (requestId !== state.lexiconOccurrenceRequestId) { return; } state.lexiconOccurrenceResults = payload; state.lexiconOccurrenceLoading = false; renderLexiconPopup(); } catch (error) { if (requestId !== state.lexiconOccurrenceRequestId) { return; } state.lexiconOccurrenceLoading = false; state.lexiconOccurrenceError = error?.message || "Unable to load verse occurrences for this Strong's entry."; renderLexiconPopup(); } } async function openLexiconOccurrence(result) { dismissLexiconEntry({ restoreFocus: false }); await openSearchResult(result); } function appendLexiconOccurrencePreview(target, result) { if (!(target instanceof HTMLElement)) { return; } target.replaceChildren(); const previewTokens = Array.isArray(result?.previewTokens) ? result.previewTokens : []; if (!previewTokens.length) { target.textContent = result?.preview || result?.reference || ""; return; } previewTokens.forEach((token, index) => { const text = String(token?.text || "").trim(); if (!text) { return; } const previousText = String(previewTokens[index - 1]?.text || "").trim(); if (index > 0 && text !== "..." && previousText !== "...") { target.appendChild(document.createTextNode(" ")); } if (token?.isMatch) { const mark = document.createElement("mark"); mark.className = "alpha-text-mark alpha-text-mark--lexicon"; mark.textContent = text; target.appendChild(mark); return; } target.appendChild(document.createTextNode(text)); }); } function renderLexiconPopup() { ensureLexiconPopup(); if (!(lexiconPopupEl instanceof HTMLElement) || !(lexiconPopupBodyEl instanceof HTMLElement)) { return; } const payload = state.lexiconEntry; const wasHidden = lexiconPopupEl.hidden; if (!payload) { lexiconPopupEl.hidden = true; lexiconPopupEl.setAttribute("aria-hidden", "true"); lexiconPopupTitleEl.textContent = "Lexicon Entry"; lexiconPopupSubtitleEl.textContent = "Strong's definition"; lexiconPopupBodyEl.replaceChildren(); return; } lexiconPopupTitleEl.textContent = payload.entryId ? `Strong's ${payload.entryId}` : "Lexicon Entry"; lexiconPopupSubtitleEl.textContent = payload.loading ? "Loading definition..." : "Strong's definition"; lexiconPopupBodyEl.replaceChildren(); if (payload.loading) { lexiconPopupBodyEl.appendChild(createEmptyMessage(`Loading ${payload.entryId}...`)); } else if (payload.error) { lexiconPopupBodyEl.appendChild(createEmptyMessage(payload.error)); } else { const entry = payload.entry || {}; const head = document.createElement("div"); head.className = "alpha-text-lexicon-head"; const idPill = document.createElement("button"); idPill.type = "button"; idPill.className = "alpha-text-lexicon-id alpha-text-lexicon-id--button"; idPill.textContent = payload.entryId || "--"; idPill.setAttribute("aria-expanded", state.lexiconOccurrenceVisible ? "true" : "false"); idPill.addEventListener("click", () => { void toggleLexiconOccurrences(); }); head.appendChild(idPill); if (entry.lemma) { const lemma = document.createElement("span"); lemma.className = "alpha-text-token-original"; lemma.textContent = entry.lemma; head.appendChild(lemma); } lexiconPopupBodyEl.appendChild(head); const rows = [ ["Transliteration", entry.xlit], ["Pronunciation", entry.pron], ["Derivation", entry.derivation], ["Strong's Definition", entry.strongs_def], ["KJV Definition", entry.kjv_def] ].filter(([, value]) => String(value || "").trim()); if (rows.length) { const dl = document.createElement("dl"); dl.className = "alpha-dl"; rows.forEach(([label, value]) => { const dt = document.createElement("dt"); dt.textContent = label; const dd = document.createElement("dd"); dd.textContent = String(value || "").trim(); dl.append(dt, dd); }); lexiconPopupBodyEl.appendChild(dl); } const occurrenceHint = document.createElement("p"); occurrenceHint.className = "alpha-text-lexicon-hint"; occurrenceHint.textContent = "Click the Strong's number to show verses that use this entry."; lexiconPopupBodyEl.appendChild(occurrenceHint); if (state.lexiconOccurrenceVisible) { const occurrenceSection = document.createElement("section"); occurrenceSection.className = "alpha-text-lexicon-occurrences"; const occurrenceTitle = document.createElement("strong"); occurrenceTitle.textContent = "Verse Occurrences"; occurrenceSection.appendChild(occurrenceTitle); if (state.lexiconOccurrenceLoading) { occurrenceSection.appendChild(createEmptyMessage(`Loading verses for ${payload.entryId}...`)); } else if (state.lexiconOccurrenceError) { occurrenceSection.appendChild(createEmptyMessage(state.lexiconOccurrenceError)); } else { const occurrencePayload = state.lexiconOccurrenceResults; const totalMatches = Number(occurrencePayload?.totalMatches) || 0; const summary = document.createElement("p"); summary.className = "alpha-text-search-summary"; summary.textContent = totalMatches ? `${totalMatches} verses use ${payload.entryId}.${occurrencePayload?.truncated ? ` Showing the first ${occurrencePayload.resultCount} results.` : ""}` : `No verses found for ${payload.entryId}.`; occurrenceSection.appendChild(summary); if (Array.isArray(occurrencePayload?.results) && occurrencePayload.results.length) { const occurrenceList = document.createElement("div"); occurrenceList.className = "alpha-text-lexicon-occurrence-list"; occurrencePayload.results.forEach((result) => { const button = document.createElement("button"); button.type = "button"; button.className = "alpha-text-lexicon-occurrence"; const headRow = document.createElement("div"); headRow.className = "alpha-text-search-result-head"; const reference = document.createElement("span"); reference.className = "alpha-text-search-reference"; reference.textContent = result.reference || `${result.workTitle} ${result.sectionLabel}:${result.verseNumber}`; const location = document.createElement("span"); location.className = "alpha-text-search-location"; location.textContent = `${result.sourceShortTitle || result.sourceTitle} · ${result.workTitle} · ${result.sectionLabel}`; const preview = document.createElement("p"); preview.className = "alpha-text-search-preview alpha-text-search-preview--compact"; appendLexiconOccurrencePreview(preview, result); button.addEventListener("click", () => { void openLexiconOccurrence(result); }); headRow.append(reference, location); button.append(headRow, preview); occurrenceList.appendChild(button); }); occurrenceSection.appendChild(occurrenceList); } } lexiconPopupBodyEl.appendChild(occurrenceSection); } } lexiconPopupEl.hidden = false; lexiconPopupEl.setAttribute("aria-hidden", "false"); if (wasHidden && lexiconPopupCloseEl instanceof HTMLButtonElement) { requestAnimationFrame(() => { lexiconPopupCloseEl.focus(); }); } } async function loadLexiconEntry(lexiconId, entryId, triggerElement) { if (!lexiconId || !entryId) { return; } if (triggerElement instanceof HTMLElement) { lexiconReturnFocusEl = triggerElement; } const requestId = state.lexiconRequestId + 1; state.lexiconRequestId = requestId; clearLexiconOccurrenceState(); state.lexiconEntry = { loading: true, lexiconId, entryId: String(entryId).toUpperCase() }; renderDetail(); try { const payload = await dataService.loadTextLexiconEntry?.(lexiconId, entryId); if (requestId !== state.lexiconRequestId) { return; } state.lexiconEntry = payload; renderDetail(); } catch (error) { if (requestId !== state.lexiconRequestId) { return; } state.lexiconEntry = { error: error?.message || "Unable to load lexicon entry.", lexiconId, entryId: String(entryId).toUpperCase() }; renderDetail(); } } function createMetaGrid(passage) { const source = passage?.source || getSelectedSource(); const work = passage?.work || getSelectedWork(source); const section = passage?.section || getSelectedSection(source, work); const displayPreferences = getSourceDisplayPreferences(source, passage); const metaGrid = document.createElement("div"); metaGrid.className = "alpha-text-meta-grid"; const overviewCard = createCard("Source Overview"); overviewCard.innerHTML += `