This commit is contained in:
mark committed 2026-09-04 08:08:32 -04:00
1 parent 77bf8d9f2e
commit ea193e1264
53 files changed
+545 -347

No files matched your search

+52 -12
View File
@@ -1,7 +1,14 @@
// Verse modal wiring. The modal markup lives in base.njk and the bookmark
// lives in the service worker, so all this file does is decide which verse
// was clicked, fill the form, and show it -- Bootstrap handles opening,
// dismissing and focus, and htmx carries the save.
// Page startup and verse modal wiring.
//
// shell.html is static and ships with an empty #content, so the first thing
// every load does is fetch the fragment its URL names and swap it in. That is
// the same request a boosted link makes, so there is one path into #content
// and nothing special about a first visit.
//
// The rest is the modal: the markup lives in shell.html and the bookmark lives
// in the service worker, so all this file does is decide which verse was
// clicked, fill the form, and show it -- Bootstrap handles opening, dismissing
// and focus, and htmx carries the save.
(function () {
'use strict';
@@ -65,7 +72,7 @@
if (!el || !window.bootstrap) return;
var info = verseInfo(span);
currentUrl = info.url;
el.querySelector('.verse-dialog-ref').textContent = info.ref;
document.getElementById('verse-dialog-ref').textContent = info.ref;
el.querySelector('input[name="ref"]').value = info.ref;
el.querySelector('input[name="href"]').value = info.href;
status('');
@@ -121,13 +128,46 @@
}
});
markVerses(document);
// -- startup ------------------------------------------------------------
// The shell already carries the bookmark, so this is only for the case where
// the worker started controlling the page after it was rendered.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(function () {
document.body.dispatchEvent(new CustomEvent('bookmark-load'));
}).catch(function () {});
function fragmentUrl() {
return (location.pathname.split('/').pop() || 'index.html') + location.search;
}
// The content arrives after the document has been parsed, so the browser has
// long given up on the fragment identifier by the time the verse exists.
// Doing it here is what makes a link to GEN.html#v1_1 land on the verse.
function scrollToHash() {
if (!location.hash) return;
var el = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (el) el.scrollIntoView();
}
// api/bookmark has no origin server -- it is answered by the worker -- so
// there is nothing to ask until the worker is controlling this page.
// controller, not ready: ready can resolve a tick before clients.claim()
// takes effect, and a request sent in that gap reaches the network and 404s.
function whenControlled(fn) {
if (!('serviceWorker' in navigator)) return;
if (navigator.serviceWorker.controller) return fn();
navigator.serviceWorker.addEventListener('controllerchange', fn, { once: true });
}
// Deferred scripts run before DOMContentLoaded fires, so this always lands,
// and it lands after htmx has processed the shell.
document.addEventListener('DOMContentLoaded', function () {
htmx.ajax('GET', fragmentUrl(), { target: '#content', swap: 'innerHTML' })
.then(scrollToHash);
// Registered from the page it is going to serve, not from a page of its
// own: there is nothing to wait for and nothing to reload. This load is
// already the real app -- the worker installs alongside it, and takes over
// for the bookmark, the offline download and every request from then on.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').catch(function () {});
}
whenControlled(function () {
document.body.dispatchEvent(new CustomEvent('worker-ready'));
});
});
})();