Files
bible-app/red-letter.js
T
christianandClaude Opus 5 0db5e9b07d Bible App 1.4.34 — full source
First commit on the local Gitea server, made after moving the tree off the
Windows machine. Build outputs, node_modules and the AI models stay out
(see .gitignore); everything the installer build needs is tracked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 13:05:20 -04:00

93 lines
4.2 KiB
JavaScript

/*
* red-letter.js — Words of Christ (red-letter) support. Isolated + additive.
*
* Loads the bundled verse-level dataset (red-letter.json, derived from the public-domain World
* English Bible \wj markers), merges any user reference overrides, and toggles visibility via a
* body class (body.red-letter-on) so on/off is instant with no re-render. The actual per-word
* marking lives in app.js (window.applyRedLetterMarks), which this module re-invokes once the data
* has loaded. Exposed on window.redLetter for the Settings page and (Phase 3) OSC.
*/
(function () {
'use strict';
var ENABLED_KEY = 'redLetterEnabled'; // 'true' | 'false' (default off)
var OVERRIDES_KEY = 'redLetterOverrides'; // free text, one reference per line
var 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'];
function isEnabled() { try { return localStorage.getItem(ENABLED_KEY) === 'true'; } catch (_) { return false; } }
function applyBodyClass() { try { document.body.classList.toggle('red-letter-on', isEnabled()); } catch (_) {} }
function normalizeBook(name) {
var n = String(name || '').trim().toLowerCase();
for (var i = 0; i < BOOKS.length; i++) if (BOOKS[i].toLowerCase() === n) return BOOKS[i];
// loose match (e.g. "song of songs", "psalm")
if (n === 'song of songs') return 'Song of Solomon';
if (n === 'psalm') return 'Psalms';
return null;
}
// Merge user overrides ("John 3:16", "Matthew 5:3-12", one per line) into the dataset.
function mergeOverrides(data) {
var raw = '';
try { raw = localStorage.getItem(OVERRIDES_KEY) || ''; } catch (_) {}
if (!raw.trim()) return;
raw.split(/\r?\n/).forEach(function (line) {
line = line.trim();
if (!line) return;
var m = line.match(/^(.+?)\s+(\d+):(\d+)(?:\s*-\s*(\d+))?$/);
if (!m) return;
var book = normalizeBook(m[1]);
if (!book) return;
var chapter = m[2], vStart = parseInt(m[3], 10), vEnd = m[4] ? parseInt(m[4], 10) : vStart;
if (!data[book]) data[book] = {};
if (!Array.isArray(data[book][chapter])) data[book][chapter] = [];
for (var v = vStart; v <= vEnd; v++) if (data[book][chapter].indexOf(v) === -1) data[book][chapter].push(v);
});
}
function reapply() {
try {
var app = window.bibleApp;
if (app && app.activeTabId != null && typeof window.applyRedLetterMarks === 'function') {
window.applyRedLetterMarks(app.activeTabId);
}
} catch (_) {}
}
function load() {
fetch('/red-letter.json', { cache: 'no-store' })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) {
if (!data || typeof data !== 'object') return;
try { mergeOverrides(data); } catch (_) {}
window.__redLetterData = data;
reapply(); // mark any already-rendered chapter now that data exists
})
.catch(function () { /* offline-safe: red-letter simply stays inactive */ });
}
// Public API (Settings + OSC)
window.redLetter = {
isEnabled: isEnabled,
setEnabled: function (on) {
try { localStorage.setItem(ENABLED_KEY, on ? 'true' : 'false'); } catch (_) {}
applyBodyClass();
},
toggle: function () { window.redLetter.setEnabled(!isEnabled()); return isEnabled(); },
// Re-read overrides + re-merge + re-mark (called after Settings saves references live, if ever)
reloadData: function () { load(); }
};
function init() { applyBodyClass(); load(); }
if (document.readyState !== 'loading') init();
else document.addEventListener('DOMContentLoaded', init);
})();