Files
EMI-ExpoAPP/utils/bibleReferences.js
T

466 lines
17 KiB
JavaScript

import API from '../API.js';
const BIBLE_TOKEN_REGEX = /@bible:([^\s]+)/gi;
const DEFAULT_TRANSLATION = "de4e12af7f28f599-01"; // KJV
export const BIBLE_BOOK_IDS = {
"Genesis": "GEN", "Exodus": "EXO", "Leviticus": "LEV", "Numbers": "NUM", "Deuteronomy": "DEU",
"Joshua": "JOS", "Judges": "JDG", "Ruth": "RUT", "1 Samuel": "1SA", "2 Samuel": "2SA",
"1 Kings": "1KI", "2 Kings": "2KI", "1 Chronicles": "1CH", "2 Chronicles": "2CH", "Ezra": "EZR",
"Nehemiah": "NEH", "Esther": "EST", "Job": "JOB", "Psalms": "PSA", "Proverbs": "PRO",
"Ecclesiastes": "ECC", "Song of Solomon": "SNG", "Isaiah": "ISA", "Jeremiah": "JER",
"Lamentations": "LAM", "Ezekiel": "EZK", "Daniel": "DAN", "Hosea": "HOS", "Joel": "JOL",
"Amos": "AMO", "Obadiah": "OBA", "Jonah": "JON", "Micah": "MIC", "Nahum": "NAM",
"Habakkuk": "HAB", "Zephaniah": "ZEP", "Haggai": "HAG", "Zechariah": "ZEC", "Malachi": "MAL",
"Matthew": "MAT", "Mark": "MRK", "Luke": "LUK", "John": "JHN", "Acts": "ACT",
"Romans": "ROM", "1 Corinthians": "1CO", "2 Corinthians": "2CO", "Galatians": "GAL",
"Ephesians": "EPH", "Philippians": "PHP", "Colossians": "COL", "1 Thessalonians": "1TH",
"2 Thessalonians": "2TH", "1 Timothy": "1TI", "2 Timothy": "2TI", "Titus": "TIT",
"Philemon": "PHM", "Hebrews": "HEB", "James": "JAS", "1 Peter": "1PE", "2 Peter": "2PE",
"1 John": "1JN", "2 John": "2JN", "3 John": "3JN", "Jude": "JUD", "Revelation": "REV"
};
const getNormalizedLocale = (locale = "") => {
return String(locale || "").toLowerCase().replace("_", "-").trim();
};
const getTranslationForLocale = (locale = "") => {
const normalized = getNormalizedLocale(locale);
if (!normalized) return "de4e12af7f28f599-01"; // KJV
if (normalized.startsWith("en")) return "de4e12af7f28f599-01"; // KJV
if (normalized.startsWith("es")) return "592420522e16049f-01"; // RVR1909
// Default to KJV
return DEFAULT_TRANSLATION;
};
const normalizeReference = (value = "") => {
try {
return decodeURIComponent(String(value || ""))
.replace(/_/g, " ")
.replace(/\s+/g, " ")
.trim();
} catch (_error) {
return String(value || "").replace(/_/g, " ").replace(/\s+/g, " ").trim();
}
};
export const encodeBibleReference = (reference = "") => {
return String(reference || "").replace(/\s+/g, "_").trim();
};
export const createBibleToken = (reference = "") => {
const encoded = encodeBibleReference(reference);
if (!encoded) return "";
return `@bible:${encoded}`;
};
export const extractBibleReferences = (content = "") => {
const seen = new Set();
const refs = [];
if (!content || typeof content !== "string") return refs;
let match;
while ((match = BIBLE_TOKEN_REGEX.exec(content)) !== null) {
const normalized = normalizeReference(match[1]);
if (!normalized || seen.has(normalized)) continue;
seen.add(normalized);
refs.push(normalized);
}
return refs;
};
export const stripBibleTokens = (content = "") => {
if (!content || typeof content !== "string") return "";
return content
.replace(BIBLE_TOKEN_REGEX, "")
.replace(/[ \t]{2,}/g, " ")
.replace(/[ \t]+\n/g, "\n")
.trim();
};
export const AVAILABLE_TRANSLATIONS = [
{ id: "de4e12af7f28f599-01", name: "King James Version (English)" },
{ id: "9879dbb7cfe39e4d-01", name: "World English Bible (English)" },
{ id: "592420522e16049f-01", name: "Reina Valera 1909 (Spanish)" },
{ id: "48acedcf8595c754-01", name: "Palabla de Dios para ti (Spanish)" }
];
const parseChapterHtml = (htmlStr) => {
const parts = String(htmlStr || "").split(/<span[^>]*data-number=\"/);
const versesMap = {};
for (let i = 1; i < parts.length; i++) {
const part = parts[i];
const numMatch = part.match(/^(\d+)\"/);
if (!numMatch) continue;
const vNum = numMatch[1];
let text = part.replace(/^[^>]+>/, '');
text = text.replace(/<[^>]+>/g, '');
text = text.replace(new RegExp('^' + vNum + '\\s*'), '').trim();
text = text.replace(/&nbsp;/g, ' ').replace(/&#160;/g, ' ');
if (!versesMap[vNum]) versesMap[vNum] = '';
versesMap[vNum] += text + ' ';
}
return versesMap;
};
const parseChapterJson = (items) => {
const versesMap = {};
let currentVerse = null;
const extract = (nodes) => {
if (!nodes) return;
for (const item of nodes) {
if (item.name === 'verse') {
currentVerse = item.attrs?.number || currentVerse;
if (currentVerse && !versesMap[currentVerse]) versesMap[currentVerse] = "";
} else if (item.type === 'text') {
if (item.attrs?.verseId) {
const vNum = item.attrs.verseId.split('.').pop();
if (!versesMap[vNum]) versesMap[vNum] = "";
versesMap[vNum] += item.text;
} else if (currentVerse) {
// Skip if the text is exactly the verse number to prevent duplicates
if (item.text.trim() === String(currentVerse)) {
continue;
}
versesMap[currentVerse] += item.text;
}
}
if (item.items) {
extract(item.items);
}
}
};
extract(items);
return versesMap;
};
export const fetchBibleChapter = async (chapterReference = "", locale = "", customTranslation = "") => {
const { book, chapter } = parseBibleReference(chapterReference);
const bookId = BIBLE_BOOK_IDS[book];
if (!bookId) throw new Error("Missing or unknown Bible reference book");
const chapterId = `${bookId}.${chapter}`;
const preferredTranslation = customTranslation || getTranslationForLocale(locale);
// Call our backend API which has the correct key and handles auth
let response;
try {
const url = `/bible/chapters/${chapterId}`;
const queryParams = { bibleId: preferredTranslation, "content-type": "json" };
let queryParamsString = "?";
Object.keys(queryParams).forEach(p => {
queryParamsString += p + "=" + queryParams[p] + "&";
});
const localBaseUrl = global.baseUrl ?? "https://emiapi.reynafamily.com";
// To bypass getCall's strict JSON structure mapping (since our backend just passes the API response directly),
// we can use standard fetch with the same cookie inclusion strategy.
const fetchRes = await fetch(localBaseUrl + url + queryParamsString, {
method: 'GET',
mode: 'cors',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept-Language': locale,
'x-app-language': locale,
}
});
if (!fetchRes.ok) throw new Error("Failed to load Bible passage");
response = await fetchRes.json();
} catch (error) {
throw new Error(error.message || "Failed to load Bible passage");
}
if (response?.error) throw new Error(response.error);
const payload = response.data;
if (!payload || !payload.content) throw new Error("Invalid Bible data");
let versesMap = {};
if (typeof payload.content === 'string') {
versesMap = parseChapterHtml(payload.content);
} else if (Array.isArray(payload.content)) {
versesMap = parseChapterJson(payload.content);
}
const verses = Object.keys(versesMap)
.sort((a, b) => Number(a) - Number(b))
.map(vNum => ({
verse: Number(vNum),
text: versesMap[vNum].replace(/\n/g, " ").trim()
}));
return {
reference: payload.reference || chapterReference,
verses: verses,
translation_id: preferredTranslation,
translation_name: AVAILABLE_TRANSLATIONS.find(t => t.id === preferredTranslation)?.name || "Translation",
};
};
export const fetchBiblePassage = async (reference = "", locale = "", customTranslation = "") => {
const { verse } = parseBibleReference(reference);
const chapterData = await fetchBibleChapter(reference, locale, customTranslation);
const verseData = chapterData.verses.find(v => v.verse === Number(verse));
if (!verseData) throw new Error("Verse not found");
return {
reference: `${chapterData.reference}:${verse}`,
text: verseData.text,
translation: chapterData.translation_name,
};
};
export const fetchBibleReference = async (reference = "", locale = "", customTranslation = "") => {
return fetchBibleChapter(reference, locale, customTranslation);
};
export const parseBibleReference = (reference = "") => {
const normalized = normalizeReference(reference);
const match = normalized.match(/^(.*)\s+(\d+)(?::(\d+)(?:-\d+)?)?$/);
if (!match) {
return {
reference: normalized,
book: normalized,
chapter: 1,
verse: 1,
chapterReference: normalized,
};
}
const book = (match[1] || "").trim();
const chapter = Number(match[2] || "1");
const verse = Number(match[3] || "1");
return {
reference: normalized,
book,
chapter,
verse,
chapterReference: `${book} ${chapter}`,
};
};
export const BIBLE_BOOKS = [
"Genesis",
"Exodus",
"Leviticus",
"Numbers",
"Deuteronomy",
"Joshua",
"Judges",
"Ruth",
"1 Samuel",
"2 Samuel",
"1 Kings",
"2 Kings",
"1 Chronicles",
"2 Chronicles",
"Ezra",
"Nehemiah",
"Esther",
"Job",
"Psalms",
"Proverbs",
"Ecclesiastes",
"Song of Solomon",
"Isaiah",
"Jeremiah",
"Lamentations",
"Ezekiel",
"Daniel",
"Hosea",
"Joel",
"Amos",
"Obadiah",
"Jonah",
"Micah",
"Nahum",
"Habakkuk",
"Zephaniah",
"Haggai",
"Zechariah",
"Malachi",
"Matthew",
"Mark",
"Luke",
"John",
"Acts",
"Romans",
"1 Corinthians",
"2 Corinthians",
"Galatians",
"Ephesians",
"Philippians",
"Colossians",
"1 Thessalonians",
"2 Thessalonians",
"1 Timothy",
"2 Timothy",
"Titus",
"Philemon",
"Hebrews",
"James",
"1 Peter",
"2 Peter",
"1 John",
"2 John",
"3 John",
"Jude",
"Revelation",
];
export const BIBLE_BOOK_CHAPTERS = {
Genesis: 50,
Exodus: 40,
Leviticus: 27,
Numbers: 36,
Deuteronomy: 34,
Joshua: 24,
Judges: 21,
Ruth: 4,
"1 Samuel": 31,
"2 Samuel": 24,
"1 Kings": 22,
"2 Kings": 25,
"1 Chronicles": 29,
"2 Chronicles": 36,
Ezra: 10,
Nehemiah: 13,
Esther: 10,
Job: 42,
Psalms: 150,
Proverbs: 31,
Ecclesiastes: 12,
"Song of Solomon": 8,
Isaiah: 66,
Jeremiah: 52,
Lamentations: 5,
Ezekiel: 48,
Daniel: 12,
Hosea: 14,
Joel: 3,
Amos: 9,
Obadiah: 1,
Jonah: 4,
Micah: 7,
Nahum: 3,
Habakkuk: 3,
Zephaniah: 3,
Haggai: 2,
Zechariah: 14,
Malachi: 4,
Matthew: 28,
Mark: 16,
Luke: 24,
John: 21,
Acts: 28,
Romans: 16,
"1 Corinthians": 16,
"2 Corinthians": 13,
Galatians: 6,
Ephesians: 6,
Philippians: 4,
Colossians: 4,
"1 Thessalonians": 5,
"2 Thessalonians": 3,
"1 Timothy": 6,
"2 Timothy": 4,
Titus: 3,
Philemon: 1,
Hebrews: 13,
James: 5,
"1 Peter": 5,
"2 Peter": 3,
"1 John": 5,
"2 John": 1,
"3 John": 1,
Jude: 1,
Revelation: 22,
};
export const getBookChapterCount = (book = "") => {
return BIBLE_BOOK_CHAPTERS[book] || 1;
};
const BIBLE_BOOK_TRANSLATIONS = {
"Genesis": { "es": "Génesis", "fr": "Genèse", "da": "1 Mosebog" },
"Exodus": { "es": "Éxodo", "fr": "Exode", "da": "2 Mosebog" },
"Leviticus": { "es": "Levítico", "fr": "Lévitique", "da": "3 Mosebog" },
"Numbers": { "es": "Números", "fr": "Nombres", "da": "4 Mosebog" },
"Deuteronomy": { "es": "Deuteronomio", "fr": "Deutéronome", "da": "5 Mosebog" },
"Joshua": { "es": "Josué", "fr": "Josué", "da": "Josva" },
"Judges": { "es": "Jueces", "fr": "Juges", "da": "Dommerne" },
"Ruth": { "es": "Rut", "fr": "Ruth", "da": "Ruth" },
"1 Samuel": { "es": "1 Samuel", "fr": "1 Samuel", "da": "1 Samuel" },
"2 Samuel": { "es": "2 Samuel", "fr": "2 Samuel", "da": "2 Samuel" },
"1 Kings": { "es": "1 Reyes", "fr": "1 Rois", "da": "1 Kongebog" },
"2 Kings": { "es": "2 Reyes", "fr": "2 Rois", "da": "2 Kongebog" },
"1 Chronicles": { "es": "1 Crónicas", "fr": "1 Chroniques", "da": "1 Krønikebog" },
"2 Chronicles": { "es": "2 Crónicas", "fr": "2 Chroniques", "da": "2 Krønikebog" },
"Ezra": { "es": "Esdras", "fr": "Esdras", "da": "Ezra" },
"Nehemiah": { "es": "Nehemías", "fr": "Néhémie", "da": "Nehemias" },
"Esther": { "es": "Ester", "fr": "Esther", "da": "Ester" },
"Job": { "es": "Job", "fr": "Job", "da": "Job" },
"Psalms": { "es": "Salmos", "fr": "Psaumes", "da": "Salmerne" },
"Proverbs": { "es": "Proverbios", "fr": "Proverbes", "da": "Ordsprogene" },
"Ecclesiastes": { "es": "Eclesiastés", "fr": "Ecclésiaste", "da": "Prædikeren" },
"Song of Solomon": { "es": "Cantares", "fr": "Cantique des Cantiques", "da": "Højsangen" },
"Isaiah": { "es": "Isaías", "fr": "Ésaïe", "da": "Esajas" },
"Jeremiah": { "es": "Jeremías", "fr": "Jérémie", "da": "Jeremias" },
"Lamentations": { "es": "Lamentaciones", "fr": "Lamentations", "da": "Klagesangene" },
"Ezekiel": { "es": "Ezequiel", "fr": "Ézéchiel", "da": "Ezekiel" },
"Daniel": { "es": "Daniel", "fr": "Daniel", "da": "Daniel" },
"Hosea": { "es": "Oseas", "fr": "Osée", "da": "Hoseas" },
"Joel": { "es": "Joel", "fr": "Joël", "da": "Joel" },
"Amos": { "es": "Amós", "fr": "Amos", "da": "Amos" },
"Obadiah": { "es": "Abdías", "fr": "Abdias", "da": "Obadias" },
"Jonah": { "es": "Jonás", "fr": "Jonas", "da": "Jonas" },
"Micah": { "es": "Miqueas", "fr": "Michée", "da": "Mika" },
"Nahum": { "es": "Nahúm", "fr": "Nahum", "da": "Nahum" },
"Habakkuk": { "es": "Habacuc", "fr": "Habacuc", "da": "Habakkuk" },
"Zephaniah": { "es": "Sofonías", "fr": "Sophonie", "da": "Sefanias" },
"Haggai": { "es": "Hageo", "fr": "Aggée", "da": "Haggaj" },
"Zechariah": { "es": "Zacarías", "fr": "Zacharie", "da": "Zakarias" },
"Malachi": { "es": "Malaquías", "fr": "Malachie", "da": "Malakias" },
"Matthew": { "es": "Mateo", "fr": "Matthieu", "da": "Matthæus" },
"Mark": { "es": "Marcos", "fr": "Marc", "da": "Markus" },
"Luke": { "es": "Lucas", "fr": "Luc", "da": "Lukas" },
"John": { "es": "Juan", "fr": "Jean", "da": "Johannes" },
"Acts": { "es": "Hechos", "fr": "Actes", "da": "Apostlenes Gerninger" },
"Romans": { "es": "Romanos", "fr": "Romains", "da": "Romerne" },
"1 Corinthians": { "es": "1 Corintios", "fr": "1 Corinthiens", "da": "1 Korinther" },
"2 Corinthians": { "es": "2 Corintios", "fr": "2 Corinthiens", "da": "2 Korinther" },
"Galatians": { "es": "Gálatas", "fr": "Galates", "da": "Galaterne" },
"Ephesians": { "es": "Efesios", "fr": "Éphésiens", "da": "Efeserne" },
"Philippians": { "es": "Filipenses", "fr": "Philippiens", "da": "Filipperne" },
"Colossians": { "es": "Colosenses", "fr": "Colossiens", "da": "Kolossenserne" },
"1 Thessalonians": { "es": "1 Tesalonicenses", "fr": "1 Thessaloniciens", "da": "1 Thessaloniker" },
"2 Thessalonians": { "es": "2 Tesalonicenses", "fr": "2 Thessaloniciens", "da": "2 Thessaloniker" },
"1 Timothy": { "es": "1 Timoteo", "fr": "1 Timothée", "da": "1 Timotheus" },
"2 Timothy": { "es": "2 Timoteo", "fr": "2 Timothée", "da": "2 Timotheus" },
"Titus": { "es": "Tito", "fr": "Tite", "da": "Titus" },
"Philemon": { "es": "Filemón", "fr": "Philémon", "da": "Filemon" },
"Hebrews": { "es": "Hebreos", "fr": "Hébreux", "da": "Hebræerne" },
"James": { "es": "Santiago", "fr": "Jacques", "da": "Jakob" },
"1 Peter": { "es": "1 Pedro", "fr": "1 Pierre", "da": "1 Peter" },
"2 Peter": { "es": "2 Pedro", "fr": "2 Pierre", "da": "2 Peter" },
"1 John": { "es": "1 Juan", "fr": "1 Jean", "da": "1 Johannes" },
"2 John": { "es": "2 Juan", "fr": "2 Jean", "da": "2 Johannes" },
"3 John": { "es": "3 Juan", "fr": "3 Jean", "da": "3 Johannes" },
"Jude": { "es": "Judas", "fr": "Jude", "da": "Judas" },
"Revelation": { "es": "Apocalipsis", "fr": "Apocalypse", "da": "Åbenbaringen" }
};
export const translateBibleReference = (reference = "", locale = "en") => {
const lang = String(locale || "en").substring(0, 2).toLowerCase();
if (lang === "en") return reference;
const parsed = parseBibleReference(reference);
if (!parsed || !parsed.book || !BIBLE_BOOK_TRANSLATIONS[parsed.book]) return reference;
const translatedBook = BIBLE_BOOK_TRANSLATIONS[parsed.book][lang] || parsed.book;
return reference.replace(parsed.book, translatedBook);
};