Fix
This commit is contained in:
1 parent
ba3b588ac5
commit
d74385ca3c
2 files changed
+80
-24
No files matched your search
+63
-17
@@ -311,9 +311,19 @@ function verseLabels(chapters, number) {
|
||||
// no verse anchors to offer.
|
||||
//
|
||||
// anchor says which chapter to show the verses of, and which verse to mark
|
||||
// selected. Null starts at 1:1, which is where every book starts: this says
|
||||
// where you are going, not where you are, and the address bar is the only
|
||||
// record of the latter.
|
||||
// selected. Anything this cannot read -- nothing at all, an empty select, a
|
||||
// footnote id, a chapter past the end of the book -- is chapter 1, and chapter
|
||||
// 1 is filled in as if it had been picked. There is no state where a chapter
|
||||
// is only sort of chosen: a select showing an option is a select that chose
|
||||
// it, and the verses beside it have to be that chapter's.
|
||||
//
|
||||
// Every book that gets a picker has verses in its first chapter -- the two
|
||||
// that do not, the front matter and the glossary, are turned away above -- so
|
||||
// falling back to 1 always fills the verse list rather than emptying it.
|
||||
//
|
||||
// The verse is left unselected when the anchor names only a chapter. That is
|
||||
// not the same as unfilled: the options are chapter 1's, and the select shows
|
||||
// the first of them. It says where you are going, not where you are.
|
||||
function referenceFor(map, code, anchor) {
|
||||
const chapters = map && Object.prototype.hasOwnProperty.call(map, code)
|
||||
? map[code] : null;
|
||||
@@ -328,12 +338,15 @@ function referenceFor(map, code, anchor) {
|
||||
|
||||
const at = parseAnchor(anchor) || { chapter: 1, verse: null };
|
||||
const number = at.chapter >= 1 && at.chapter <= chapters.length ? at.chapter : 1;
|
||||
// Only when the anchor got as far as naming this chapter. A fallback to 1
|
||||
// has not chosen a verse, it has just run out of anchor.
|
||||
const verse = at.chapter === number ? at.verse : null;
|
||||
|
||||
return {
|
||||
book: code,
|
||||
chapter: 'ch' + number,
|
||||
chapters: chapters.map((_, i) => ({ id: 'ch' + (i + 1), label: String(i + 1) })),
|
||||
verse: at.verse,
|
||||
verse: verse,
|
||||
verses: verseLabels(chapters, number)
|
||||
.map((label) => ({ id: 'v' + number + '_' + label, label: label }))
|
||||
};
|
||||
@@ -415,19 +428,30 @@ router.get('/api/bookmark', () => bookmarkFragment(''));
|
||||
|
||||
// The picker, and the two moves it makes.
|
||||
//
|
||||
// no parameters the whole picker, for whichever book is on screen. Asked
|
||||
// for by the picker itself, after a history restore -- which
|
||||
// swaps the history element straight in and processes no
|
||||
// out-of-band elements -- and when a worker first appears
|
||||
// under a page boot.html composed.
|
||||
// no parameters the whole picker, standing where the anchor in the address
|
||||
// bar puts it. Asked for by the picker itself: on load,
|
||||
// after a history restore -- which swaps the history element
|
||||
// straight in and processes no out-of-band elements -- and
|
||||
// when a worker first appears under a page boot.html
|
||||
// composed.
|
||||
// chapter=ch3 the verses of that chapter, and land on the chapter.
|
||||
// verse=v3_16 the same verses, that one selected, and land on the verse.
|
||||
//
|
||||
// The book is not in the URL, deliberately: it comes from HX-Current-URL,
|
||||
// which is the address the page is showing now rather than the address it was
|
||||
// showing when the picker was last rendered. The picker sits in the navbar and
|
||||
// outlives the page under it, so anything baked into its attributes is the one
|
||||
// thing here that can go stale.
|
||||
// Neither the book nor the anchor is in the URL, deliberately. Both come from
|
||||
// HX-Current-URL, which htmx sets from location.href on every request it
|
||||
// makes: the whole address, fragment included.
|
||||
//
|
||||
// For the book, because the picker sits in the navbar and outlives the page
|
||||
// under it -- an address baked into its attributes is the one thing here that
|
||||
// can go stale. For the anchor, because this is the only way a fragment
|
||||
// reaches this file at all. A browser opening /GEN#v3_16 asks for /GEN and
|
||||
// keeps the rest to itself, so the shell is composed without it and the picker
|
||||
// is rendered at 1:1 like any other. The page then asks this route for itself,
|
||||
// and that request is the one carrying the anchor.
|
||||
//
|
||||
// Which also puts the picker where it belongs after a back or forward, and
|
||||
// under a page boot.html composed once a worker turns up, for nothing extra:
|
||||
// those requests carry the address bar too.
|
||||
//
|
||||
// What the response headers are doing is the whole of what app.js used to do
|
||||
// on a change event, and htmx has a name for each part of it:
|
||||
@@ -442,11 +466,33 @@ router.get('/api/bookmark', () => bookmarkFragment(''));
|
||||
// history entry per option spun past.
|
||||
router.get('/api/reference', async (call) => {
|
||||
const params = call.url.searchParams;
|
||||
const anchor = params.get('verse') || params.get('chapter');
|
||||
const page = call.currentUrl();
|
||||
const reference = await referenceOf(bookOf(page), anchor);
|
||||
// Which select the reader moved, if either did -- by whether the parameter
|
||||
// is there, not by whether it says anything. A select posts an empty value
|
||||
// when it has no options, and reading that as "no select moved" answers it
|
||||
// with a whole picker: htmx swaps a <div> into a <select>, which renders as
|
||||
// nothing, and the select is then empty for good -- every change after that
|
||||
// posts the same empty value and gets the same answer.
|
||||
//
|
||||
// An empty value is still an answerable question. It names no chapter, so it
|
||||
// gets chapter 1, which is what everything here does with an anchor it
|
||||
// cannot read.
|
||||
const moved = params.has('verse') ? params.get('verse')
|
||||
: params.has('chapter') ? params.get('chapter')
|
||||
: null;
|
||||
|
||||
if (!anchor) return view.render('reference.njk', { reference: reference });
|
||||
// The address bar is the fallback, and only that: once a select has been
|
||||
// used it is the one saying where to go, and HX-Replace-Url has not caught
|
||||
// up yet.
|
||||
const reference = await referenceOf(
|
||||
bookOf(page), moved === null ? (page && page.hash.slice(1)) : moved);
|
||||
|
||||
// settled, because this picker is already standing where the anchor put it.
|
||||
// The one that came with the page is not, and carries a load trigger to ask
|
||||
// -- which this answer must not carry as well, or it would ask forever.
|
||||
if (moved === null) {
|
||||
return view.render('reference.njk', { reference: reference, settled: true });
|
||||
}
|
||||
// The options were rendered from this map, so failing to parse them back
|
||||
// means the map moved under an open page: leave the picker alone rather
|
||||
// than swapping in options for a book that is no longer on screen.
|
||||
|
||||
Reference in new issue
Block a user