Skip to content

Resolving notes

phil/footnotes-sidenotes

NoteResolver::resolve() takes a document and gives a Resolution. The document should be normalized first, as Ujo::decode() and Ujo::normalize() do: normalization gives references their canonical form, the only one read.

What is collected

A note is a footnote block, {id, text}, wherever it stands: before its references, after them, nested in a column. A reference is <sup data-footnote="id">…</sup> in any text of any block, nested blocks and list items included. The content of the element is replaced at output, so a number stored there by a reader of Markdown counts for nothing. The text of code, mermaid and math blocks is not HTML: what looks like a reference there is left alone.

Reading order

The blocks are walked in document order, the values of a block in the order of its data, each nested block where it stands. A note takes its number at its first reference. A note referenced several times is one note, with one number and several NoteReference values.

Restarts

NoteOptions::$restart says where the numbering starts again:

RestartThe numbering starts again
DocumentNever: one numbering for the document
ChapterAt each header block whose level is chapterLevel or above; 1 by default
PageMarkerAt each block of a type in pageMarkers; ['pageBreak'] by default

A chapter or a page marker counts wherever it stands, nested or not. Note::$scope gives the run a note belongs to. A note referenced again in a later run keeps the number of its first reference.

Numbering

FactoryWrites
Numbering::decimal()1, 2, 3
Numbering::lowerRoman()i, ii, iii, decimal past 3999
Numbering::upperRoman()I, II, III, decimal past 3999
Numbering::symbols()*, †, ‡, §, ‖, , then **, ††, then ***, decimal past three rounds

Numbering::symbols(['a', 'b']) takes the caller's own sequence. NoteOptions::$numbering applies by default; $locales gives the numbering of a locale or of a language, and resolve($document, 'fr-CA') looks for fr-CA, then fr, then the default.

Problems

Resolution::$problems lists what was found, each a Problem with its kind, its noteId, the blockId where it stands and occurrences:

ProblemKindMeaningAt outputisError()
MissingNoteA reference names no noteThe reference is left outYes
UnreferencedNoteA note no reference namesThe note is left outYes
DuplicateIdA second note of an identifierThe first note is keptYes
ReferenceInNoteThe text of a note holds a referenceThe reference is left outYes
RepeatedReferenceA note is referenced several timesOne note, one backlink per referenceNo

Resolution::isClean() says that no problem loses anything; problemsOf($kind) reads one kind. A publishing flow can refuse a document that is not clean, a reader's page can show it anyway.

Cost

The resolver is one walk: the time grows with the size of the document, and a text without a reference costs one search for a substring. See Testing.