|
14 | 14 | /* ============================================================ |
15 | 15 | * Constants |
16 | 16 | * ============================================================ */ |
| 17 | + // Translations with full per-book JSON data available under bible-data/{value}/ |
17 | 18 | const EN_TRANSLATIONS = [ |
18 | 19 | { value: "kjv", label: "KJV — King James Version", full: true }, |
19 | 20 | { value: "nkjv", label: "NKJV — New King James Version", full: false }, |
|
22 | 23 | ]; |
23 | 24 |
|
24 | 25 | const TA_TRANSLATIONS = [ |
25 | | - { value: "pv", label: "PV — பரிசுத்த வேதாகமம்" } |
| 26 | + { value: "pv", label: "PV — பரிசுத்த வேதாகமம் (Tamil OV)" } |
26 | 27 | ]; |
27 | 28 |
|
28 | 29 | const ALL_TRANSLATIONS = EN_TRANSLATIONS.concat(TA_TRANSLATIONS); |
29 | 30 |
|
| 31 | + // Translations whose per-book files are currently just placeholder stubs |
| 32 | + const STUB_TRANSLATIONS = new Set(["nkjv", "niv", "esv"]); |
| 33 | + |
30 | 34 | /* ============================================================ |
31 | 35 | * App State |
32 | 36 | * ============================================================ */ |
|
209 | 213 | * Helper: Toast notifications |
210 | 214 | * ============================================================ */ |
211 | 215 | function toast(msg, color) { |
212 | | - // Simple toast notification |
213 | | - console.log(msg); |
| 216 | + var existing = document.getElementById("_zgm-toast"); |
| 217 | + if (existing) existing.remove(); |
| 218 | + var t = document.createElement("div"); |
| 219 | + t.id = "_zgm-toast"; |
| 220 | + t.textContent = msg; |
| 221 | + t.style.cssText = [ |
| 222 | + "position:fixed", "bottom:1.5rem", "right:1.5rem", "z-index:9999", |
| 223 | + "background:" + (color || "#2e7d32"), "color:#fff", |
| 224 | + "padding:0.7rem 1.2rem", "border-radius:8px", |
| 225 | + "font-size:0.9rem", "box-shadow:0 4px 14px rgba(0,0,0,0.25)", |
| 226 | + "opacity:0", "transition:opacity 0.2s" |
| 227 | + ].join(";"); |
| 228 | + document.body.appendChild(t); |
| 229 | + requestAnimationFrame(function () { t.style.opacity = "1"; }); |
| 230 | + setTimeout(function () { |
| 231 | + t.style.opacity = "0"; |
| 232 | + setTimeout(function () { t.remove(); }, 200); |
| 233 | + }, 2800); |
214 | 234 | } |
215 | 235 |
|
216 | | - async function loadBooks() { |
217 | | - try { |
218 | | - const res = await fetch("assets/data/bible-books.json"); |
219 | | - if (!res.ok) throw new Error("books list not found"); |
220 | | - const list = await res.json(); |
221 | | - state.books = list; |
222 | | - list.forEach(function (book) { |
223 | | - state.booksMap[book.name] = book; |
224 | | - }); |
225 | | - } catch (_) { |
226 | | - const sample = (window.SAMPLE_BIBLE_DATA || {}).books || []; |
227 | | - state.books = sample; |
228 | | - sample.forEach(function (book) { |
229 | | - state.booksMap[book.name] = book; |
| 236 | + async function tryFetchJson(paths) { |
| 237 | + for (var i = 0; i < paths.length; i++) { |
| 238 | + try { |
| 239 | + var res = await fetch(paths[i]); |
| 240 | + if (!res.ok) continue; |
| 241 | + var data = await res.json(); |
| 242 | + return data; |
| 243 | + } catch (_) { |
| 244 | + // Continue trying remaining paths. |
| 245 | + } |
| 246 | + } |
| 247 | + return null; |
| 248 | + } |
| 249 | + |
| 250 | + function bookNameToFile(bookName) { |
| 251 | + return String(bookName || "").replace(/\s+/g, "") + ".json"; |
| 252 | + } |
| 253 | + |
| 254 | + function setBooks(list) { |
| 255 | + state.books = Array.isArray(list) ? list : []; |
| 256 | + state.booksMap = {}; |
| 257 | + state.books.forEach(function (book) { |
| 258 | + if (book && book.name) state.booksMap[book.name] = book; |
| 259 | + }); |
| 260 | + } |
| 261 | + |
| 262 | + async function buildBooksFromNames(bookNames) { |
| 263 | + var out = []; |
| 264 | + for (var i = 0; i < bookNames.length; i++) { |
| 265 | + var name = String(bookNames[i] || "").trim(); |
| 266 | + if (!name) continue; |
| 267 | + |
| 268 | + var file = bookNameToFile(name); |
| 269 | + var chapterCount = 1; |
| 270 | + var raw = await tryFetchJson([ |
| 271 | + "bible-data/kjv/" + file, |
| 272 | + "./bible-data/kjv/" + file, |
| 273 | + "/bible-data/kjv/" + file |
| 274 | + ]); |
| 275 | + |
| 276 | + if (raw && Array.isArray(raw.chapters) && raw.chapters.length) { |
| 277 | + chapterCount = raw.chapters.length; |
| 278 | + } |
| 279 | + |
| 280 | + out.push({ |
| 281 | + name: name, |
| 282 | + slug: name.toLowerCase().replace(/\s+/g, "-"), |
| 283 | + file: file, |
| 284 | + testament: i < 39 ? "ot" : "nt", |
| 285 | + chapters: chapterCount |
230 | 286 | }); |
231 | 287 | } |
| 288 | + return out; |
| 289 | + } |
| 290 | + |
| 291 | + async function loadBooks() { |
| 292 | + var list = await tryFetchJson([ |
| 293 | + "assets/data/bible-books.json", |
| 294 | + "./assets/data/bible-books.json", |
| 295 | + "/assets/data/bible-books.json" |
| 296 | + ]); |
| 297 | + |
| 298 | + if (Array.isArray(list) && list.length && list[0] && list[0].name) { |
| 299 | + setBooks(list); |
| 300 | + return; |
| 301 | + } |
| 302 | + |
| 303 | + var names = await tryFetchJson([ |
| 304 | + "bible-data/Books.json", |
| 305 | + "./bible-data/Books.json", |
| 306 | + "/bible-data/Books.json" |
| 307 | + ]); |
| 308 | + |
| 309 | + if (Array.isArray(names) && names.length) { |
| 310 | + var rebuilt = await buildBooksFromNames(names); |
| 311 | + setBooks(rebuilt); |
| 312 | + return; |
| 313 | + } |
| 314 | + |
| 315 | + var sample = (window.SAMPLE_BIBLE_DATA || {}).books || []; |
| 316 | + if (Array.isArray(sample) && sample.length) { |
| 317 | + setBooks(sample); |
| 318 | + return; |
| 319 | + } |
| 320 | + |
| 321 | + setBooks([]); |
| 322 | + toast("Unable to load Bible books. Please refresh.", "#e53935"); |
232 | 323 | } |
233 | 324 |
|
234 | 325 | function syncTileActive(gridId, value) { |
|
329 | 420 | var cacheKey = bookName + "|" + chapter + "|" + translationKey; |
330 | 421 | if (state.chapterCache[cacheKey]) return state.chapterCache[cacheKey]; |
331 | 422 |
|
332 | | - var sample = (window.SAMPLE_BIBLE_DATA || {}).translations || {}; |
333 | | - |
334 | | - if (translationKey === "kjv") { |
335 | | - var bookMeta = state.booksMap[bookName]; |
336 | | - if (bookMeta) { |
337 | | - try { |
338 | | - var res = await fetch("bible-data/" + bookMeta.file); |
339 | | - if (res.ok) { |
340 | | - var raw = await res.json(); |
341 | | - var data = extractChapter(raw, bookName, chapter); |
342 | | - if (data && Object.keys(data).length) { |
343 | | - state.chapterCache[cacheKey] = data; |
344 | | - return data; |
345 | | - } |
346 | | - } |
347 | | - } catch (_) { } |
348 | | - } |
| 423 | + var bookMeta = state.booksMap[bookName]; |
| 424 | + if (!bookMeta) return null; |
| 425 | + |
| 426 | + // All translations now live at bible-data/{translationKey}/{Book}.json |
| 427 | + // NKJV / NIV / ESV files are stubs (chapters:[]) until full data is added |
| 428 | + if (STUB_TRANSLATIONS.has(translationKey)) { |
| 429 | + return null; // stubs have no verse content yet |
349 | 430 | } |
350 | 431 |
|
351 | | - var translationData = sample[translationKey] || {}; |
352 | | - var bookData = translationData[bookName] || {}; |
353 | | - var chData = bookData[String(chapter)] || null; |
| 432 | + var filePath = "bible-data/" + translationKey + "/" + bookMeta.file; |
| 433 | + try { |
| 434 | + var res = await fetch(filePath); |
| 435 | + if (!res.ok) return null; |
| 436 | + var raw = await res.json(); |
354 | 437 |
|
355 | | - if (chData) { |
356 | | - state.chapterCache[cacheKey] = chData; |
357 | | - return chData; |
358 | | - } |
| 438 | + // Stub check: placeholder files have no chapters |
| 439 | + if (raw && raw.status === "placeholder") return null; |
359 | 440 |
|
360 | | - return null; |
361 | | - } |
| 441 | + var data = extractChapter(raw, bookName, chapter); |
| 442 | + if (data && Object.keys(data).length) { |
| 443 | + state.chapterCache[cacheKey] = data; |
| 444 | + return data; |
| 445 | + } |
| 446 | + } catch (_) {} |
362 | 447 |
|
363 | | - function setCombinedSelection(bookName, chapter) { |
364 | | - const sel = q("book-chapter-select"); |
365 | | - if (!sel || !bookName || !chapter) return; |
366 | | - sel.value = bookName + "|" + chapter; |
| 448 | + return null; |
367 | 449 | } |
368 | 450 |
|
369 | 451 | /* ============================================================ |
|
1067 | 1149 | * Bootstrap |
1068 | 1150 | * ============================================================ */ |
1069 | 1151 | async function init() { |
1070 | | - const sampleData = window.SAMPLE_BIBLE_DATA || {}; |
1071 | | - state.sermons = sampleData.sermons || []; |
1072 | | - state.verses = sampleData.verses || []; |
1073 | | - |
1074 | 1152 | await loadBooks(); |
1075 | 1153 |
|
1076 | | - const translationSel = q("translation-select"); |
| 1154 | + // Load sermons for the related-content panel |
| 1155 | + try { |
| 1156 | + var sermonsRes = await fetch("assets/data/sermons.json"); |
| 1157 | + if (sermonsRes.ok) state.sermons = await sermonsRes.json(); |
| 1158 | + } catch (_) {} |
| 1159 | + |
| 1160 | + // Build daily-verse pool from devotions |
| 1161 | + try { |
| 1162 | + var devRes = await fetch("assets/data/devotions.json"); |
| 1163 | + if (devRes.ok) { |
| 1164 | + var devotions = await devRes.json(); |
| 1165 | + state.verses = Array.isArray(devotions) |
| 1166 | + ? devotions.map(function (d) { |
| 1167 | + return { reference: d.scripture || d.reference || "", text: d.reflection || d.text || "" }; |
| 1168 | + }) |
| 1169 | + : []; |
| 1170 | + } |
| 1171 | + } catch (_) {} |
| 1172 | + |
| 1173 | + state.tamilData = null; // Loaded lazily on first Tamil PV request |
| 1174 | + |
| 1175 | + var translationSel = q("translation-select"); |
1077 | 1176 | if (translationSel) translationSel.value = state.primaryTranslation; |
1078 | 1177 |
|
1079 | 1178 | populateBookSelect(); |
1080 | 1179 | bindEvents(); |
1081 | 1180 | renderDailyVerse(); |
1082 | 1181 | updateRelatedSermons(); |
1083 | 1182 |
|
1084 | | - // Populate related sermons placeholder with default text |
1085 | | - const rSermons = q("related-sermons"); |
| 1183 | + var rSermons = q("related-sermons"); |
1086 | 1184 | if (rSermons) rSermons.innerHTML = '<p class="muted">Select a passage to see related sermons.</p>'; |
1087 | 1185 | } |
1088 | 1186 |
|
|
0 commit comments