diff --git a/app/styles.css b/app/styles.css index 84dca07..71b89b9 100644 --- a/app/styles.css +++ b/app/styles.css @@ -3683,6 +3683,13 @@ letter-spacing: 0.03em; } + .alpha-text-verse-counts { + color: #a1a1aa; + font-size: 11px; + letter-spacing: 0.03em; + text-transform: uppercase; + } + .alpha-text-verse-text { margin: 0; color: #e4e4e7; diff --git a/app/ui-alphabet-text.js b/app/ui-alphabet-text.js index 8fa0bc5..c404585 100644 --- a/app/ui-alphabet-text.js +++ b/app/ui-alphabet-text.js @@ -165,6 +165,65 @@ return String(value || "").trim(); } + function extractVerseCountText(verse, source, displayPreferences, translationText = "") { + const mode = displayPreferences?.textMode || "translation"; + const originalText = normalizeTextValue(verse?.originalText); + const transliterationText = getVerseTransliteration(verse, source); + + if (mode === "original") { + return originalText || normalizeTextValue(translationText); + } + if (mode === "transliteration") { + return transliterationText || normalizeTextValue(translationText); + } + return normalizeTextValue(translationText) + || originalText + || transliterationText; + } + + function getTextCounts(value) { + const normalized = String(value || "") + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, ""); + const words = normalized.match(/[\p{L}\p{N}]+(?:['’-][\p{L}\p{N}]+)*/gu) || []; + const letters = normalized.match(/\p{L}/gu) || []; + const vowels = normalized.match(/[AEIOUYaeiouy]/g) || []; + const consonants = letters.length - vowels.length; + + return { + words: words.length, + letters: letters.length, + consonants: Math.max(0, consonants), + vowels: vowels.length + }; + } + + function formatCountSummary(counts) { + return `W:${counts.words} L:${counts.letters} C:${counts.consonants} V:${counts.vowels}`; + } + + function sumPassageCounts(passage, source, displayPreferences) { + const verses = Array.isArray(passage?.verses) ? passage.verses : []; + + return verses.reduce((totals, verse) => { + const translationText = source?.features?.hasTokenAnnotations + ? buildTokenTranslationText(verse?.tokens, verse?.text) + : verse?.text; + const counts = getTextCounts(extractVerseCountText(verse, source, displayPreferences, translationText)); + + totals.words += counts.words; + totals.letters += counts.letters; + totals.consonants += counts.consonants; + totals.vowels += counts.vowels; + return totals; + }, { + words: 0, + letters: 0, + consonants: 0, + vowels: 0 + }); + } + const GREEK_TRANSLITERATION_MAP = { α: "a", β: "b", γ: "g", δ: "d", ε: "e", ζ: "z", η: "e", θ: "th", ι: "i", κ: "k", λ: "l", μ: "m", ν: "n", ξ: "x", ο: "o", π: "p", @@ -982,6 +1041,18 @@ `; metaGrid.appendChild(overviewCard); + const totalsCard = createCard("Entry Totals"); + const totals = sumPassageCounts(passage, source, displayPreferences); + totalsCard.innerHTML += ` +
+
Words
${totals.words}
+
Letters
${totals.letters}
+
Consonants
${totals.consonants}
+
Vowels
${totals.vowels}
+
+ `; + metaGrid.appendChild(totalsCard); + if (displayPreferences.capabilities.hasAnyExtras) { const extraCard = createCard("Extra"); extraCard.classList.add("alpha-text-extra-card"); @@ -1057,6 +1128,8 @@ function createPlainVerse(verse) { const source = getSelectedSource(); const displayPreferences = getSourceDisplayPreferences(source, state.currentPassage); + const translationText = verse.text || ""; + const verseCounts = getTextCounts(extractVerseCountText(verse, source, displayPreferences, translationText)); const article = document.createElement("article"); article.className = "alpha-text-verse"; article.classList.toggle("is-highlighted", isHighlightedVerse(verse)); @@ -1068,9 +1141,13 @@ reference.className = "alpha-text-verse-reference"; reference.textContent = verse.reference || (verse.number ? `Verse ${verse.number}` : ""); - head.append(reference); + const stats = document.createElement("span"); + stats.className = "alpha-text-verse-counts"; + stats.textContent = formatCountSummary(verseCounts); + + head.append(reference, stats); article.append(head); - appendVerseTextLines(article, verse, source, displayPreferences, verse.text || ""); + appendVerseTextLines(article, verse, source, displayPreferences, translationText); return article; } @@ -1128,6 +1205,8 @@ } function createTokenVerse(verse, lexiconId, displayPreferences, source) { + const translationText = buildTokenTranslationText(verse?.tokens, verse?.text); + const verseCounts = getTextCounts(extractVerseCountText(verse, source, displayPreferences, translationText)); const article = document.createElement("article"); article.className = "alpha-text-verse"; article.classList.toggle("alpha-text-verse--interlinear", Boolean(displayPreferences?.showInterlinear)); @@ -1140,6 +1219,10 @@ reference.className = "alpha-text-verse-reference"; reference.textContent = verse.reference || (verse.number ? `Verse ${verse.number}` : ""); + const stats = document.createElement("span"); + stats.className = "alpha-text-verse-counts"; + stats.textContent = formatCountSummary(verseCounts); + const tokenGrid = document.createElement("div"); tokenGrid.className = "alpha-text-token-grid"; @@ -1174,9 +1257,9 @@ tokenGrid.appendChild(tokenEl); }); - head.append(reference); + head.append(reference, stats); article.append(head); - appendVerseTextLines(article, verse, source, displayPreferences, buildTokenTranslationText(verse?.tokens, verse?.text)); + appendVerseTextLines(article, verse, source, displayPreferences, translationText); if (displayPreferences?.showInterlinear) { article.appendChild(tokenGrid); } diff --git a/index.html b/index.html index 3dbe91a..93f3450 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ - +
@@ -997,7 +997,7 @@ - +