A Processing-Model @predicate decides when a <model> applies: an element
takes the first of its models, in document order, whose predicate holds. Params
(<param value>) are XPath too. How far each target reads them:
Any XPath 3.1. Both generators copy predicates and params verbatim into the
generated XSLT 3.0 stylesheet, where the XSLT processor evaluates them. The
stylesheet sets xpath-default-namespace to TEI's, so the ODD's unprefixed
names are TEI elements. A prefix the ODD binds where the expression stands is
resolved when the ODD is read, to Q{uri}local, or to the unprefixed name for
TEI's namespace, so the expression needs no declaration where it is carried.
A subset of XPath, implemented in runtime/xpath.mjs.
It ships beside the generated handlers, which evaluate predicates and params
with it, and is inlined in the CETEIcean pages, which evaluate them in the
browser:
- Paths:
.,..,@name,name,*,node(),text(),/,//, absolute paths, and[predicate](a number is a position). - Axes:
self,child,parent,ancestor,ancestor-or-self,descendant,descendant-or-self,preceding-sibling,following-sibling,preceding,following. - Names: unprefixed or
tei:, TEI's;Q{uri}localanother namespace's, as a prefix the ODD binds becomes. A prefix it does not bind is outside the subset.x:*,*:nameandnamespace-uri()are too. - Operators:
or,and, the general comparisons (= != < <= > >=), the value comparisons (eq ne lt le gt ge),+ -,mod,if (…) then … else …, and sequences(a, b). - Functions:
position,last,count,not,exists,empty,string,concat,contains,contains-token,starts-with,ends-with,matches,replace,translate,normalize-space,string-length,local-name,true,false.
What it accepts follows XPath 3.1: a path gives each node once, in document
order; a general comparison is numeric only against a number; a value comparison
with more than one item on a side is an error when evaluated, as is a call with
the wrong number of arguments when compiled. Two known differences: regular
expressions are JavaScript's, and @n eq 1 compares numerically where XPath
raises a type error.
odd-to-unified.mjs and odd-to-ceteicean.mjs compile every expression once.
A CETEIcean page compiles them again when it loads.
An expression the subset cannot evaluate — an eXist function, an XQuery let
chain, a union — is compiled to what a failing evaluation would have returned
anyway: false() for a predicate, so the model never fires and the next one
decides, and the empty sequence for a param. Someone else's ODD therefore runs
here, degraded where it has to be, instead of being rejected.
What was degraded is recorded, never silent: each generator writes
odd-unsupported.json beside what it generates —
{ "element": "p", "model": "p-1", "where": "predicate", "expression": "@a | @b",
"reason": "unexpected \"|\"", "odd": "bad.odd", "line": 3 }— prints the count in the build log, and findings.mjs lists the entries with
the rest of what the ODD does not foresee.
--strict fails the build instead, naming the element, the model and the line.
Our own ODDs are compiled with it (see package.json), so a regression in them
stays a build error.
The CSS needs no predicates: the renderers put the selected model's classes on
the element, and edition.css styles those classes.