
# Reference Manual

In-depth documentation of features like TEI Processing Model, HTML templating, themes and layout etc.

# Introduction

[TL;DR](tldr.xml): shortcut for the impatient reader

[Quickstart](quickstart.xml): gentle introduction for the beginner

TEI Publisher reference manual covers various aspects and technical layers of creation, customization and maintenance of publications based on data encoded primarily in XML. In particular, but not exclusively, we will be discussing _scholarly_ publications based on data encoded in TEI.

This introductory chapter provides a gentle overview of motivations and rationale behind Jinks and TEI Publisher design. For hands-on, technical discussion of particular topics, skip directly to the relevant chapters.

Bridging the gap between a computer-processable representation of sources and tangible, published edition is not a trivial step. It involves precise analysis and modeling, tedious work, and careful orchestration across multiple layers of the technological stack. From the raw data, to the publication - be it an online website or a printed volume - there are many ways to approach the challenge from the technical perspective. For the scholars and content curators, the challenge is rather seen as an obstacle: lengthy, cumbersome and risk-laden but unavoidable process.

![](tp-diagram.jpg) 

_XML data transformed to output with TEI Publisher_

It's not easy to find a way to reconcile the variety and allow for the flexibility within a shared, standards-based framework. From afar, there are significant overlaps between all scholarly publications. Yet the closer we look, the more we notice differences, from subtle to quite fundamental. After all, where is the common ground between a collection of Renaissance correspondence, Buddhist inscriptions, anthology of inaugural poems, and a lexicon of ancient individuals, of whom we only know a few words from their tombstones? Is there a way to accommodate the needs and wishes of editors, at the same time taking into account the technical and economic sustainability, as well as future re-use and interoperability of the scholarly datasets?

The motivation behind TEI Publisher was to provide a tool which enables scholars and editors to publish their materials without becoming programmers, but also does not force them into a one-size-fits-all framework. The solution is based on several foundational principles:

divide and conquer 
+  Split complex problems into smaller tasks. This is reflected in modular design at all levels.

do not reinvent the wheel 
+  Interface with existing systems and libraries where solutions already exist.

standardize where you can 
+  Based on open standards: from well-documented XML vocabularies (like TEI, JATS, DocBook) and formats (JSON) for the representation of data, to support for widely used protocols and specifications (e.g. Open API Specification, IIIF, DTS).

customize where you must 
+  Never force a concrete solution: all elements are designed as customizable.

community-driven 
+  TEI Publisher framework features and components are a response to real needs of user community: developed to provide a generalized solution to a repeated practical requirement.

open source 
+  All components are released under open licenses, assuring that all work continues to benefit the entire community.

## Jinks: edition in few clicks

With the [Jinks](quickstart.xml?id=intro-jinks#intro-jinks) application manager, the core TEI Publisher idea of assembling an edition from the modular "lego" blocks is taken to the next level. The core of TEI Publisher is now decomposed into a set of small, modular profiles, which can be combined and configured to assemble concrete applications. Each profile provides end-to-end implementation for a particular aspect of a digital edition, e.g. support for a certain input format, integration of facsimile images, timelines or map display. Check the [hands-on tutorial](quickstart.xml?id=using-jinks#using-jinks) for a walk through.

Using Jinks is very simple on the surface, nevertheless it operates over the complex, heterogeneous spectrum of conceivable scholarly projects. Therefore it still requires from the user the usual intellectual and scholarly rigour to assure the end result matches the expectations. Similarly, to customize or create a new feature profile, the understanding of the orchestration across the technical layers is necessary.

# Data

Applications generated by Jinks/TEI Publisher will refer to a data collection, where XML files are expected to be stored. A default exists, placing this collection  within the application package, to make the prototyping fast and smooth,  nevertheless the setting can be easily changed in the  config.json configuration file.

Separating your data from application code has many benefits, particularly for actively developed applications and larger data sets.  This way the application code can be modified and deployed without redeploying and reindexing the data, and vice versa. It is also easier to maintain separate repositories (e.g. in Git) and differentiate access and privileges for editorial and development teams.

As a rule of thumb it is recommended to separate data and code, nevertheless some projects may prefer to keep their data and application integrated in a single package for the sake of marginally easier distribution.

A series of configuration properties within the `defaults` section determines where data is expected to be available:

`data` 
+  specifies the location of the entire data collection. It can be an absolute (starting with the database root) or a relative path (specified in relation to the application collection). By default this is set to `data` subcollection of the application package.

`data-default` 
+  Typically, the digital publication contains not only the primary dataset, for example the edited documents collection, but also a number of auxiliary resources: editorial introduction, bibliography, registers of places and people, as well as other ancilliary material. These complementary resources usually, should not appear in the document list the user is presented when browsing the main dataset. The `data-default` property exists to define the entry point to the edition, i.e. specify the subcollection which users should see. If such distinction is not necessary, this property can be set to the empty string. 

`register-root` 
+  Relative or absolute path pointing to the collection containing register data (entity lists) for people, places etc. If relative it will be resolved relative to `data`.

`data-exclude` 
+  An array of XPath expressions pointing to documents to be filtered out when browsing documents. Use this to hide certain documents in browsable collections.

Example: the _Serafin Correspondence_ blueprint comes with three subcollections under data: auxiliary contains a general project description, letters hosts the actual letters, and registers contains entity lists (as the _Entity Registers_ profile expects). Obviously users should only see the letters when browsing documents. Therefore, `data-default` is set to `letters` in config.json.

## Creating a data package

Keeping data and application code together is fine for initial prototyping. However, as mentioned before, it is highly recommended to separate the data from the application code.

From the database and development workflow perspective, separating data and application means that you maintain a dedicated data repository, distributed as a xar package for eXist-db and stored in a particular collection.  This collection will be referenced by your application. Just like the  application package, the data package can be  downloaded, distributed and installed into another eXist-db.

To create a data package, start a new app configuration in Jinks, fill out the abbreviation and the other two required form fields, and in the  Profiles tab, select Data Package as the base profile. Click on Apply and wait for the package to be created. Next, use the Files tab to add your data into its pregenerated data subcollection.

After preparing your data package, you'll have to configure your application to use it. Open the main application in Jinks and change the `data` property (and `data-default` if necessary) to use an absolute path pointing to the location of the data within the data package. For example:

```json
{ … "defaults": { "data": "/db/apps/barth-data/data", "data-default": "" }, … }
```

## Hierarchical data collections

Many editions will simply present a flat list of documents, e.g. a collection of letters, monographs or plays. The structure of the data is therefore very simple, all the files are stored on the same level in the `data-default` collection. 

Other publications, particularly those including heterogeneous material may require a more complex organization. The _TEI Publisher Documentation and Demo_ app is a good example – its data is further divided into a number of subcollections, containing the documentation, demo documents, JATS and DOCX samples etc. This is reflected in the user interface: the browsing view at the top level shows several panels and users can navigate to the corresponding subcollections by clicking on them.

In general, TEI Publisher applies the following rules for the browse page:

1.  if the collection to display contains a collection.html document, this file will be displayed

2.  otherwise, TEI Publisher compiles a paginated list of all documents stored in the collection directly and in any subcollections, no matter how deep they are nested

For example, the TEI Publisher Documentation and Demo adds a collection.html to the data root.

collection.html may contain an arbitrary HTML fragment (well-formed XML!) and may include template expressions. However, navigating to a collection is handled by Javascript, so links to subcollections require that a data-collection attribute is present, containing the relative link to the collection.

For example, let's assume a hypothetical application with two main collections: prints and manuscripts. It may include the following collection.html:

```html
<div class="collection"> <h3>Custom landing page demonstrating sub-subcollections.</h3> <ul class="documents thumbnails"> <li class="document"> <div class="thumbnail"><img src="prints.png" width="128"/></div> <div class="document-info"> <h3><a href="#" data-collection="prints">Prints</a></h3> <p>This one is for prints.</p> </div> </li> <li class="document"> <div class="thumbnail"><img src="manuscripts.png" width="128"/></div> <div class="document-info"> <h3><a href="#" data-collection="manuscripts">Manuscripts</a></h3> <p>This one is for manuscripts.</p> </div> </li> </ul> </div>
```

resulting in the following display:

![](data-subcollection-landing.png) 

_Fragment of the subcollections landing page_

## Processing instructions

The default view for a specific document can be configured via a processing instruction. Before displaying a document, TEI publisher will check if a processing instruction exists at the start of the document, telling it which ODD and view template to use (along with other configuration parameters). For example, the following processing instruction associates the document with the view template translation.html, the ODD dantiscus.odd, and switches to a page-by-page display (along TEI page break boundaries): 

```xml
<?teipublisher template="translation.html" odd="dantiscus.odd" view="page"?>
```

When viewing the document by structural divisions, two additional settings control the amount of content displayed at a time:

```xml
<?teipublisher depth="2" fill="6" odd="dta.odd"?>
```

odd 
+  The ODD file to use for rendering the document.

template 
+  The HTML view template to use. Default is `view.html` as configured in modules/config.xqm. 

view 
+  Default view to show when browsing the document. Supported values are  div, page or single : 

    1.  div : displays one structural division (TEI div, DocBook section …) at a time 

    2.  page : displays the document page by page. This requires page break indicators to be present (<pb> in TEI, not supported for DocBook). 

    3.  single : the entire document (or a selected fragment of it) is displayed at once 

depth 
+  When viewing entire divisions, the software tries to determine if it should show child divisions in separate pages or include them with the current div. <depth> indicates the nesting level up to which divisions should be shown separately. So setting it to "2" will result in divisions on level 3 or greater to be shown together with their enclosing div. 

fill 
+  If child divisions appear on separate pages, it may happen that the enclosing div contains just a heading or a single line of text. In this case, the algorithm will try to fill the page by showing the first child division as well. The <fill> paramter defines the number of elements which should at least be present on a page. If not, the software tries to fill it up. 

media 
+  A space separated list of output media types supported for this document. TEI Publisher 8 uses this information to limit the download options shown to the user. The output type `web` is always assumed to work and does not need to be specified. Other supported output media types are:

    +  epub

    +  print

    +  fo

    +  latex

    For example, this documentation uses the following settings in its processing instruction:

```xml
media="latex print fo epub"
```

# Development Workflows

This chapter discusses the different ways to interact with the database while you are developing your own digital edition. In essence, one can distinguish two basic approaches:
1.  development is primarily done inside the database using tools like eXide, Jinks or oXygen

2.  main development takes place in a local directory using whatever editor and tools you prefer

In reality, you will likely mix both. For example, even if you are mainly working in a directory, you may still want to use the visual ODD editor, occasionally update the application with Jinks etc. You therefore need ways to sync from database to file system and the other way around.

But whichever way you choose, it is important to always have an up to data backup in a safe place in case anything goes wrong. The best way to achieve this is to use a versioning system like git. We strongly recommend to set up a git repository whenever you start work on a digital edition. For beginners, a graphical git client like [GitHub Desktop](https://github.com/apps/desktop) might be the best choice.

## Approach 1: Developing inside the Database

This will be the easier approach as it does not require additional tooling (except for git – see below). It will work perfectly fine if:
+  you are mainly interested in changing the presentation of the data via ODD and TEI Processing Model

+  your application can rely on the existing profiles and requires only minor customization by changing the configuration in Jinks

+  you do not need (or only very little) custom CSS, javascript or additional API endpoints

If those conditions apply, you will mostly work with the visual ODD editor (for addressing the presentation) and Jinks for theming and other configuration changes. If you have to add single source files (like CSS styles), eXide provides a reliable editor.

However, it is vital to keep an up to date copy of your application somewhere save. Jinks itself provides two actions for retrieving a copy (see the bottom toolbar):

Download 
+  Packages the application into a single archive (a so called XAR), which you can later re-install into eXist-db using the Package Manager in the Dashboard. This is a quick choice to get a fully functional copy or for passing the application on to other people to try out.

Sync 
+  Synchronizes the file hierarchy into a local disk directory. Note that _local_ means the machine where eXist-db is running. If you are using docker, the directory will be _inside_ the docker image. If you connect to a remote server, it will be a directory on the server. Sync therefore is particularly convenient to use during development, to create snapshots to be committed to the source control repository, like Git.

Because Sync won't work for everyone, let's start with the Download option: running the action will result in a file ending with .xar being downloaded to your browser. This is a normal ZIP archive and you can use standard utilities to extract the files into a directory of your choice. If your system does not recognize the .xar as a ZIP and refuses to process it, simply change the file ending.

Contrary to an ordinary ZIP file, a .xar has a standardized internal structure and comes with metadata, which informs eXist-db about how to install the data, create indexes and so on.

The extracted archive contains everything, which would be required to restore the current state of the application. At this point, we strongly recommend to initialize this directory as a git repository and push it to a service like Github.

You can also rebuild the .xar – e.g. after making local changes to files – using the build scripts included in the archive. The build step will result in another, updated .xar being written into the build subdirectory and you can install this new archive into any _eXist-db_ using the _dashboard_ (or command line utility like _xst_, see below).

## Approach 2: Developing in a local directory

If an application reaches a certain complexity, you may prefer to switch to a directory-first approach. This is also how TEI Publisher itself is developed. The advantages:

+  you have a safe copy of your work in a local directory at any point

+  you can immediately commit changes to git or undo them later if needed

+  you can use any tools you like, including the editor of your choice

If you make your changes in a directory first, you obviously have to synchronize them to the database. The easiest way to do so is to rebuild the .xar every time and install it via the _dashboard_. However, reinstalling the entire application may take a while and therefore slow you down.

You can also upload single files using eXide, the file browser in jinks or oXygen's eXist-db integration. But again, this is a manual process.

Therefore, most TEI Publisher developers prefer using a development environment (IDE) like Visual Studio Code, Cursor or Antigravity. They all allow you to install an [eXist-db extension](https://open-vsx.org/extension/eXist-db/existdb-vscode), which – apart from providing XQuery language support – comes with a helpful synchronization utility. Applications generated by Jinks already contain the necessary configuration (in file .existdb.json) to run this synchronization utility.

![](cursor-sync.png) 

_Cursor editor with active sync task in bottom panel_

Once activated, the sync utility will continously watch for changes in the monitored directories and synchronize them to the database.

While convenient, there are some caveats of this approach:

+  if you use the visual ODD editor, you need to download the modified ODD yourself as it only exists in the database

+  if you modify the application configuration in jinks and apply it, there will likely be changes to files in the database, but not in your local directory

In those cases, use either the Sync action to automatically sync from database to local directory, or download a fresh .xar and unpack it manually.

Note, however, that if you have the sync task running in your editor, replaced files will be again written into the database. While this does usually not cause much damage, it is better to stop the task temporarily.

## Building

To build an application, you at least need to have [Java](installation.xml?id=java) and the build tool [Apache Ant](https://ant.apache.org/). Both should be installed so they are found in your environment if you open a terminal or shell. Typing `ant` should produce output similar to the following: 

```
Buildfile: /Users/wolfgang/Downloads/serafin/build.xml clean: release.true: release.false: [echo] Not a release, moving on ... check-release: git.revision: prepare: [mkdir] Created dir: /Users/wolfgang/Downloads/serafin/build/tp-serafin-1.0.0 [copy] Copying 245 files to /Users/wolfgang/Downloads/serafin/build/tp-serafin-1.0.0 xar: [zip] Building zip: /Users/wolfgang/Downloads/serafin/build/tp-serafin-1.0.0.xar all: BUILD SUCCESSFUL Total time: 0 seconds 
```

Expert: if you would like to run the test suite or provide a [local copy of TEI Publisher's webcomponents](web-components-loading), you'll also need to have [nodejs](https://nodejs.org/en) with the `npm` command  available in your terminal.

## Useful Resources

Here are links to some of the utilities mentioned above:

[xst](https://github.com/eXist-db/xst) 
+  A command line interface to eXist-db, which allows you to install/remove .xar packages, browse collections, upload single files and more.

[jinks-cli](https://www.npmjs.com/package/@teipublisher/jinks-cli) 
+  Control Jinks from the command line: supports creating/updating applications from profiles, running actions etc.

[eXist-db VSCode Extension](https://open-vsx.org/extension/eXist-db/existdb-vscode) 
+  Adds XQuery language support and a sync task to Visual Studio Code and other IDEs derived from it

[GitHub Desktop](https://github.com/apps/desktop) 
+  A graphical client to create and manage git repositories

# TEI Processing Model

TEI Publisher derives its name from TEI and the TEI Processing Model (TEI PM). TEI Processing Model is a part of the standard TEI vocabulary and TEI ODD specification format. It is described in the [Documentation Elements](https://tei-c.org/release/doc/tei-p5-doc/en/html/TD.html#TDPMPM) chapter of the TEI P5 Guidelines. Here we discuss it at length from a practical perspective, including TEI Publisher specific extensions and edge case examples. For hands-on introduction to this topic you may want to follow the [Quickstart](quickstart.xml?id=pm-tutorial) tutorial.

TEI Processing Model specification uses  TEI ODD syntax to define how an XML document should be transformed. TEI ODD (short for One Document Does it All) was originally designed to provide formal, machine actionable documentation of the project in the TEI language itself. Processing models in the ODD  add a powerful mechanism, through which you can control the transformation of the source XML documents to all  output formats: HTML, ePUB, PDF etc. 

The processing model approach is flexible and media-agnostic: behaviours and rendition styles are transparently translated into different output media types. A single ODD can handle a multitude of heterogeneous source documents and various output media types with very little adjustment.

![](pm-transformation.jpg) 

_TEI PM based transformation workflow_

## TEI Processing Model transformations

TEI Processing Model allows us to express even **very complex** requirements for processing, including the **distinctions in processing** based on the **document and application context**. For each of the markup tags we need a specific handling, we must only provide an `elementSpec` with one or more nested `model` specifications.

The code fragment below shows `elementSpec` definitions and `model`s that TEI Publisher uses for a few TEI elements: `ab`, `add` and `app`.

![](pm-teipublisher.jpg) 

_Example of TEI PM syntax_

### Models, behaviours and renditions

Every `model` must specify a `behaviour`, which often is one of the most common: `inline`, `block`, `alternate`, `link` or `note`. Many of the behaviour names are easily understandable, referring to basic web typesetting concepts: `link`, `block`, `heading` or `inline`. Others, e.g. `pass-through` or `webcomponent`, may require a word of explanation. [TEI Guidelines](https://tei-c.org/release/doc/tei-p5-doc/en/html/TD.html#TDPMPM) provide a list of suggested behaviours. TEI Publisher allows users to add their own custom behaviours, either within the ODD itself or by writing XQuery code.

Behaviours define what kind of transformation is expected for the given input. In this sense, behaviours are functions and they may accept a range of parameters, depending on the function in question. For example, the `link` behaviour needs at least two parameters specifying how the URL for the link should be constructed and what the visible link text should be. 

Behaviours, as abstract concepts, remain media agnostic. Their implementations in the TEI Processing Model library handle the mapping of the concept to the concrete output format, e.g. HTML or print. `inline` or `note` behaviours hardly differ across media, but obviously we cannot have hyperlinks on printed page, therefore `link` behaviour needs to be adjusted for print outputs.

Even with `behaviour`s, we still lack a way to describe the required formatting for our model, for example to declare that phrases marked as deleted should be rendered with a strike-through, titles should be italicized and lacunae marked with `[...]`. The CSS standard is extremely well suited to express such editorial decisions, therefore TEI Guideline specification highly recommends using CSS definitions for this purpose and TEI Publisher strictly _requires_ it. Follow to output styling section for details.

Please note that any electronic publication needs a general CSS design, regarding layout and styling of its user interface components. Such concerns should be separated as much as possible from the specifications of the processing models. TEI Publisher provides recommendations how to handle these aspects in custom CSS styling chapter.

Illustration below shows, how the same `inline behaviour` can be used to process elements that are semantically very different. Just by adjusting the `content` parameter and specifying the required rendition in CSS the editorial intention for processing can be easily and clearly expressed.

CSS specification for each `model` can be provided directly, via nested `outputRendition`, or, better, indirectly, setting the `cssClass` on the `model` and placing actual CSS rules in a separate file.

![](pm-inline.jpg) 

_Examples of inline behaviour_

### Context-differentiated models

As mentioned above, processing models are defined first and foremost at the level of an individual markup element (XML tag).  Requirements for the transformation are often relatively simple, for example, virtually all TEI-based editions will render `p` elements as paragraphs and `foreign` phrases as inline elements. `unclear` will be often followed by a `(?)` and `sic` by `(!)`. For substantial majority of TEI, DocBook and JATS elements a single model suffices to process it.

On the other hand, a much smaller, but significant, number, particularly for TEI tags, require variant handling. It may be depending on the project conventions, the context in which the element appears in the source XML structure, and also the application context it is presented in. Let's illustrate these three situations with examples.

Community convention 
+  Words or phrases supplied by the editor where they are illegible or erroneously omitted in the source are encoded in TEI by `supplied` tag. Typical rendition of this phenomenon uses square brackets `[]` to mark the supplied phrase, while the dominant convention in Polish textual scholarship calls for angle brackets `<>`. Similar differences can be observed in typesetting conventions for expansion of abbreviations, marking of lacunae etc.

XML context 
+  TEI element `title` may be used in various locations in the TEI file. It is often used in the transcription, where any kind of work might have been mentioned in the source text. `title` may also occur in various metadata sections, from bibliography to the title of the TEI file itself. Depending on the context, it will be presented differently: the title of the entire TEI document will often be rendered as top-level heading, while one of the titles mentioned in the transcribed documents will typically become just an italicized inline phrase.

Application context 
+  The same source fragment may require different handling depending what is the purpose of the presentation. Let's assume that in our source document we have heavily encoded section heading (`head`), which includes a reference to a person (`persName`), but due to the damage to the manuscript witness, it is not fully legible. Nevertheless, the name can be easily reconstructed (`supplied`) based on other evidence available to the editor, who duly provides an explanatory note about the reconstruction process (`note`).

    In the reading view of the edition, we want all the encoded information available to the reader, with the conjecture marked with brackets according to the convention, editorial note signaled with a note anchor in superscript and provided as a footnote, and possibly additional information about the identification of the person accessible as a tooltip or margin note.

    On the other hand, for the table of contents, all the above would be just an irritating distraction, and in this scenario we want simply the plain, reading content of the heading.

Illustration below depicts a case where requirements for the _reading_ view are drastically different from the _table of content_: the former including extensive critical apparatus and commentary, as well as some information about the layout of the primary source, while table of content focuses on chapter number with supplementary information regarding the range of paragraphs belonging to it. Chapter heading in this case is only available as a tooltip and rendered without any scholarly apparatus.

![](pm-c7s.jpg) 

_Processing requirements differing in various application contexts_

Example above originates from the [Canoniser les Sept Sages](https://www.unige.ch/c7s/) project.

## Processing Model Syntax

TEI Guidelines introduced a small number of new elements for the TEI Processing Model: `model`, `modelSequence`, `modelGrp`, `param` and `outputRendition`. It also extended the content model of `elementSpec` to allow to nest processing models in it.  In practice, we need just the small number of elements below to fully express intended transformation for any XML document:

`model` 
+  describes the processing intended for a specified element

`param` 
+  provides a parameter for a model behaviour by supplying its name and value

`modelSequence` 
+  any sequence of `model` or nested `modelSequence` elements which is to be processed as a sequence, not mutually exclusive alternatives

`outputRendition` 
+  describes the intended appearance for the model behaviour's output

`pb:template`* 
+  code template replacing the default `content` parameter accepted by all behaviours

`pb:behaviour`* 
+  custom behaviour definition

Asterisks (*) mark TEI Publisher extensions to the TEI Processing Model syntax. These elements are therefore not in the TEI namespace, but the TEI Publisher namespace, denoted with the `pb:` prefix.

### <model> element

<model> element is primarily used to document the intended processing for a given element. One or more of these elements may appear directly within an <elementSpec> element specification to define the processing anticipated for that element. Where multiple <model> elements appear, they are understood to document mutually exclusive processing scenarios, possibly for different outputs or applicable in different contexts. 

A processing model defines on an abstract level how a given element may be transformed to produce one or more outputs. The model is expressed in terms of **behaviours** and their parameters, using high-level formatting concepts, such as block, inline,  note or heading. A processing model is thus a template description, used to generate the code needed by the publishing application to process the source document into required output. 

The example below depicts a situation where a single model is defined for the <app> element. As no @predicate or @output are specified, this model applies for all contexts in which <app> may appear and all possible outputs. Thus all <app> elements will be transformed into inline chunks of text containing only contents of <app> 's <lem> child and omitting any possible <rdg> children. 

```xml
<elementSpec mode="change" ident="app"> <model behaviour="inline"> <param name="content" value="lem"/> </model> </elementSpec>
```

Processing model for `app` presented above would be most likely insufficient for a scholarly publication. For practical examples consult the TEI Publisher ODD and custom ODD files available in TEI Publisher Documentation & Demo.

###  <model> children and attributes

+  @predicate : the condition under which this model applies, given as an XPath Predicate Expression 

+  @behaviour : names the function which this processing model uses in order to produce output; possible values include: alternate, block, figure, heading, inline, link, list, note, paragraph 

+  @cssClass : one or more CSS class names which should be added to the resulting output element where applicable 

+  @output : identifier of the output for which this model applies; applies to all output if no @output is present on a <model> 

+  @useSourceRendition : whether to obey any rendition attribute which is present in the source document

+  @pb:mode : processing mode to set to any subsequently called models

+  <param> : allows to pass parameters to @behaviour function; parameters available depend on the behaviour in question; when parameters are not explicitly passed, default values for those are assumed; all behaviour functions use current element as default content 

+  <outputRendition> : supplies information about the desired output rendition in CSS; its attribute @scope provides a way of defining ‘pseudo-elements’ eg: first-line, first-letter, before, after 

    NB: this element should be used with care, it is **strongly recommended to use `@cssClass`** wherever possible

### <model> output styling

The intended rendering for the output of a processing model may be controlled in one of the following ways.

+  `model`'s `@cssClass` attribute may be used to specify the name of a CSS style class in an associated CSS stylesheet (read more on specifying CSS styles in the ODD) which is to be applied,

+  the attribute @useSourceRendition may be used to indicate that the rendition defined in the source document should be applied (applicable only to TEI documents using pointers to [CSS rendition schemes](https://tei-c.org/release/doc/tei-p5-doc/en/html/ref-rendition.html)), 

+  CSS definition for the default model CSS class (`tei-{ident}`) may be provided

+  <outputRendition> element, specifying the styling to be applied in CSS. This option should be used with care,  it is strongly recommended to use `@cssClass` wherever possible to separate concerns and avoid redundancy.

It is strongly recommended that use <outputRendition> is strictly limited to editorial decisions, such as '_conjectures are to be displayed in square brackets_' or avoided altogether. Under no circumstances it should be used as means to record general typesetting and layout specific design choices. The latter are discussed in the Custom CSS styling chapter. 

When more than one of these options is used, they are understood to be combined in accordance with the rules for multiple declaration of the styling language used. 

All TEI Processing Model behaviours (except omit and pass-through) add two CSS classes to the generated HTML element: one consisting of the name of the element preceded by the vocabulary prefix (e.g. `tei-div`) and the other additionally suffixed with the number of the model that has been applied (e.g. `tei-div2`). The former allows to specify CSS rules for all the elements belonging to this class. TEI Publisher provides the `tp.css` CSS stylesheet accompanying the default `teipublisher.odd`, with definitions for some of the default CSS classes, for example to ensure default italicization of `hi` elements or titles.

Link to an external CSS stylesheet can be defined in the `encodingDesc/tagsDecl/rendition` section of the ODD. Just specify a relative link in the @source attribute, e.g.: 

```xml
<rendition source="docbook.css"/> 
```

The file should be stored in the same collection as the source ODD it is referenced from. The linked file should be a standard CSS stylesheet.

When necessary, the processing model library translates the CSS styles into the target media format, e.g. for LaTeX. Restrictions apply due to differences between the output formats. Not all CSS properties are supported for every format. Please refer to the section on Output media settings for further information. 

As an alternative to linked CSS files, the <rendition> element can be used with the @selector attribute to embed CSS rules directly in the ODD.

```xml
<rendition selector="h3"> font-family: serif; font-weight: 400; </rendition>
```

Choose one of the two approaches, but do not mix them. In both cases make sure to recompile the ODD after changes as the CSS is merged into the generated code!

### output styling example

Model specifying desired rendition via `cssClass`: contents of <ex> elements are to be displayed in italic and wrapped in parentheses: 

```xml
<elementSpec mode="change" ident="ex"> <model behaviour="inline" cssClass="ex"/> </elementSpec>
```

The ODD file must declare a corresponding CSS stylesheet in `rendition` element in the `encodingDesc` section of the `teiHeader`. In the example below, a file called `tp.css` is expected to contain definitions for CSS classes used in the ODD processing models.

```xml
<TEI> <teiHeader> ... <encodingDesc> <tagsDecl> <rendition source="tp.css"/> </tagsDecl> </encodingDesc> </teiHeader> ... </TEI> 
```

CSS definitions in the `tp.css` file declared in the ODD example above could be formulated as follows: 

```css
.ex { font-style: italic; } .ex::before { content: '('; } .ex::after {                    content: ')'; } 
```

Alternative rendering specification using `outputRendition` is presented below. This method is not recommended, as it leads to verbose and redundant CSS definitions inlined in the ODD. It tends to bloat the ODD and makes it harder to maintain for complex projects, mixing up presentational and functional concerns.

```xml
<elementSpec mode="change" ident="ex"> <model behaviour="inline"> <outputRendition>font-style: italic;</outputRendition> <outputRendition scope="before">content:"(";</outputRendition> <outputRendition scope="after">content:")";</outputRendition> </model> </elementSpec>
```

### multiple `model`s

As discussed in an earlier section, sometimes several processing models are required for the same element depending on the context.  

Set of multiple <model> statements is regarded as **mutually exclusive alternation** and **only the first** model with @predicate **matching** the current context **is applied**.

`model` and `modelSequence` elements are evaluated **top to bottom**, in the TEI ODD document order. The first one for which the `@predicate` condition is satisfied will be applied. Further processing models will not be evaluated at all.

`@predicate` can be specified as any XPath expression which will be evaluated to logically _true_ or _false_. Typically behaviour predicates test one or more of the following characteristics:

the presence or a specific value of element's attribute 
+  `predicate="@target"` : true if current element has a `target` attribute

    `predicate="@type='chapter'"` : true if current element has a `type` attribute and its value is specifically _chapter_ 

presence of a child or further descendant 
+  `predicate="lem"` : true if current element has a `lem` child element

    `predicate="descendant::pb"` : true if current element has a page beginning nested anywhere down the XML tree hierarchy

presence of a specific parent or ancestor 
+  `predicate="ancestor::table"` : true if current element is somewhere within a table

    `predicate="parent::div"` : true if current element is a direct child of a division

    `predicate="parent::div[@type='chapter']"` : true if current element is a direct child of a _chapter_ level division

value of an external parameter passed from the outside* 
+  `$parameters?mode=('toc', 'breadcrumb')` : true if the external parameter called `mode` is set to _toc_ or _breadcrumb_ 

    * Convention for referring to, and evaluating, external parameters, is dependant on the processing model engine. _TEI Processing Model library_, used in TEI Publisher, employs the XPath syntax and passes external parameters via `$parameters` map structure.

Predicate tests may use logical operators of conjunction (`and`) or alternative (`or`) to create more complex expressions, following the rules of [XPath expression language](https://www.w3.org/TR/xpath-31/).

### matching `model`s example

For a simple case where single model is insufficient, let's focus on quotes. We may wish to process the <quote> element as an inline italic phrase when it appears inside a paragraph (<p> element), but as an indented block when it appears elsewhere. To achieve this, we need to change the specification for the <quote> element to include two <model> elements as follows: 

```xml
<elementSpec mode="change" ident="quote"> <model predicate="ancestor::p" behaviour="inline"/> <model behaviour="block"/> </elementSpec>
```

The first processing model will be matched and used only for <quote> elements which match the XPath expression given as the value of the @predicate attribute. Other element occurrences will use the second processing model.  

Given that quotes are ubiquitous in TEI and may occur in a number of contexts, a more appropriate predicate could test for other contexts than paragraph ancestor, as it happens in the [TEI Publisher ODD](https://github.com/eeditiones/jinks/blob/b6523a09fbbdee6c5141a2d762ff52f11fc27be2/profiles/base10/resources/odd/teipublisher.odd#L616).

###  <modelSequence> 

The <modelSequence> element is provided for the case where a sequence of models is to be processed, functioning as a single unit.

As discussed earlier, when multiple `model` elements occur as direct children of the `elementSpec`, they are treated as mutually exclusive and only one of the models will be applied during the transformation.

Nevertheless, sometimes it is necessary to apply two or more behaviour functions to the same source element. Let's consider a verse line, where apart from the content of the line we may want the verse number presented to the side (typically not for each verse, but for every fifth or so).

To achieve this effect, we might want to apply two models in a sequence: first one using the `n` attribute as the `parameter` to produce the ordinal, and a second one, to transform the actual content of the line.

`modelSequence` element is a wrapper that can be used around any group of nested `model` (and `modelSequence`) elements, to ensure that they are all applied. Models are evaluated (checking if predicate tests are satisfied in the current context), and applied in the sequence determined by the ODD document order.

Please note that since predicate rules still apply, therefore in the example below, the first model in the sequence will only be applied for every 5th verse.

```xml
<elementSpec mode="change" ident="l"> <modelSequence> <model predicate="(number(@n) mod 5) = 0" behaviour="inline"> <param name="content" value="@n"/> </model> <model behaviour="block"/> <modelSequence> </elementSpec>
```

`modelSequence` is not the only way to achieve similar effect, depending on the precise requirements, alternative approaches would modify the `content` parameter, or use `pb:template` extension. Particularly to produce nested markup structures the latter method is most useful.

![](pm-verse.jpg) 

_Numbered verse lines example from [Neolatina Sarmatica](https://neolatina.dhlab.uj.edu.pl/exist/apps/neolatina/s48-1)._

Another use case would be to use modelSequence to generate table of contents preceding the reading text as shown in the example below: 

```xml
<elementSpec mode="change" ident="body"> <modelSequence> <model behaviour="index"> <param name="type" value="'toc'"/> </model> <model behaviour="block"/> </modelSequence> </elementSpec>
```

###  <modelGrp> (processing model group) 

The <modelGrp> element may be used to group <model>s for better readability of the code, for example elements intended for the same output format.

This element, though present in the specification, has little use in practice and we [do not recommend](https://github.com/TEIC/TEI/issues/2856) its use.

## Behaviours

The [TEI Guidelines](http://www.tei-c.org/release/doc/tei-p5-doc/en/html/ref-model.html) list a number of suggested behaviours. TEI Publisher implements all these, supplemented with a few _additional_ ones. Users may also add their own custom behaviours, either within the ODD itself, or by implementing custom behaviours in XQuery. 

Illustration below shows relative frequency of use of behaviours in the TEI Publisher ODD. `inline` and `block` behaviours are used in more than 50% of models. Close to 10% of models uses `pass-through` behaviour which in majority of cases corresponds with the use of `pb:template` extension.

Several other behaviours are used relatively frequently: `alternate`, `link`, `heading`, `list` and `listItem` as well as `omit`, `webcomponent`, `paragraph` and `break`.

Other behaviours are very closely tied to the specific elements they tend to be solely used for, e.g. `table` and related `row` and `cell` behaviours are used exclusively by their eponymous elements in TEI (and by `table`, `tr`, `td` and  `th` elements in DocBook, respectively).

What follows, in ODD customization practice, we'll typically need to use just a handful of most frequent behaviours, therefore it pays off to familiarize yourself with these first.

![](behaviour-distribution.jpg) 

_Behaviour distribution in TEI Publisher ODD_

### Standard behaviours

This section lists the behaviours supported by TEI Processing Model library out of the box.  These include all behaviours suggested by the TEI Guidelines and several additional ones, commonly used in TEI Publisher-based editorial projects. 

Behaviour functions accept a range of parameters, depending on the function in question. Where these parameters are not explicitly specified in the <model>, default values are used.

All behaviour functions have at least one parameter called  content (with a slight exception of `alternate` behaviour, where equivalent parameter is called default). By default the `content` is set to XPath expression `.`, which means the currently processed node. You may adjust this by explicitly changing the content parameter value in the `model`. 

In the parameter lists below we skip the content parameter as it is available for every behaviour. Optional parameters are marked as _optional_ in parentheses, followed by the output mode they apply to, if relevant. 

alternate 
+  Present _default_ and _alternative_ content. The concrete implementation depends on the output format. Typical web-oriented implementation relies on using a dynamic tooltip to display the alternate content.

| Parameter | Description | 
| ---|--- |
| default | the content to display by default | 
| ---|--- |
| alternate | alternate content | 
| persistent | (optional, web) show a persistent popup on click instead of a tooltip on hover if parameter evaluates to an effective boolean value of true | 

anchor 
+  Create an anchor to which you can link, identified by the given id.

| Parameter | Description | 
| ---|--- |
| id | the id | 
| ---|--- |

block 
+  Create a block structure, usually a div in HTML or fo:block in fo.

body 
+  Create the body of a document. In HTML this will result in a <body> tag.

break 
+  Create a line, column, or page break according to type.

| Parameter | Description | 
| ---|--- |
| type | e.g. "page", "column", "line" | 
| ---|--- |
| label | e.g. "p. 13v" | 

cell 
+  Create a table cell. If the @cols or @rows attribute is specified, the cell may span several columns/rows.

cit 
+  Show a citation, with an indication of the source.

| Parameter | Description | 
| ---|--- |
| source | the citation source | 
| ---|--- |

document 
+  Create output document wrapper. The concrete implementation depends heavily on the output format. For example, in the case of LaTeX output this  behaviour sets the document preamble, where options for the paper size, document layout, packages used and so on can be defined.

figure 
+  Make a figure with provided title argument as caption.

| Parameter | Description | 
| ---|--- |
| title | a caption | 
| ---|--- |

graphic 
+  Display the graphic retrieved from the given url.

| Parameter | Description | 
| ---|--- |
| url | the url to load the graphic from | 
| ---|--- |
| width | the width of the graphic, e.g. "300px", "50%" ... | 
| height | the height of the graphic, e.g. "300px", "50%" ... | 
| scale | a scaling factor to apply. If specified, width and height will be output as percentage based on the scaling factor, which should be a number between 0 and 1. | 
| title | a title for the graphics element. Usually not shown directly. | 

heading 
+  Create a heading.

| Parameter | Description | 
| ---|--- |
| level | the structural level of this heading. In HTML mode, this translates to <h1>, <h2> etc. | 
| ---|--- |

inline 
+  Output an inline element.

link 
+  Create a hyperlink.

| Parameter | Description | 
| ---|--- |
| uri | the link url | 
| ---|--- |
| target | identifier of the tab to open the link in (only web output) | 

list 
+  Create an ordered or unordered list, depending on the type attribute (e.g. type="ordered"). If a label is present before each item, a description list is output instead, using the label as definition term. 

| Parameter | Description | 
| ---|--- |
| type | The type of list: use "ordered" for an enumerated list, or "custom" to specify item labels in combination with the n parameter on each `listItem`. The default is "unordered" for a list of bullet points.  | 
| ---|--- |

listItem 
+  Output an item in a list.

| Parameter | Description | 
| ---|--- |
| n | a label to use for the item | 
| ---|--- |

metadata 
+  Output a metadata section, e.g. a <head> in HTML.

note 
+  create a note, often out of line, depending on the value of `place` ; could be "margin", "footnote", "endnote", "inline" 

| Parameter | Description | 
| ---|--- |
| place | defines the placement of the note, e.g. "margin", "footnote" ... | 
| ---|--- |
| label | the label to use for the footnote reference, usually a number. | 

omit 
+  Do nothing: skip this element, do not process children.

paragraph 
+  Create a paragraph. 

pass-through 
+  Process the content without creating any kind of wrapper. Most often used together with `pb:template` to create more complex output markup or just with `content` parameter, to limit processing to selected content. The latter can be also used to specify arbitrary order for processing source fragments.

row 
+  Create a table row. 

section 
+  Create a new section in the output document. In HTML mode, this translates to a <section> element being output.

table 
+  Create a table.

text 
+  Output literal text.

title 
+  Output the document title. In HTML mode, this creates a <title> element. In LaTeX, it adds the title to the document metadata.

webcomponent (TEI Publisher extension) 
+  Output a custom HTML element using the value of parameter name as the custom tag name. All other parameters are translated into corresponding attributes (properties of the webcomponent). 

| Parameter | Description | 
| ---|--- |
| name | the tag name to use for the custom element. Must be a string value. | 
| ---|--- |

### Custom behaviours in the ODD

The two dozen behaviours supported by TEI Processing Model library out of the box are enough to cover most standard tasks, but sometimes a custom behaviour may make the ODD code clearer and easier to maintain.

By combining code templates with parameters we can create a very simple mechanism to define new behaviours right inside the ODD!

Take the TEI Publisher documentation as an example: it is written in Docbook 5 and transformed via ODD. The documentation includes some videos which are hosted on youtube. In DocBook these are represented by <videodata> elements inside a <videoobject>.

```
<figure xml:id="edit-odd"> <title>Screencast</title> <mediaobject> <videoobject> <videodata fileref="https://www.youtube.com/embed/avRO-b2BwUI?rel=0" width="853" depth="480"/> </videoobject> </mediaobject> </figure>
```

In the HTML output we would need to transform this into an <iframe>, so the reader can view the video embedded in the page. We can achieve this with a < pb:template> as sketched in the previous section, but it would be nice to turn this into a general-purpose behaviour, which we can re-use in other situations requiring an iframe. The TEI Publisher Processing Model library allows us to define a behaviour right inside the ODD as follows: 

```
<pb:behaviour ident="iframe" output="web"> <pb:param name="src"/> <pb:param name="width"/> <pb:param name="height"/> <pb:template xmlns=""> <iframe src="[[src]]" width="[[width]]" height="[[height]]" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen="allowfullscreen"></iframe> </pb:template> </pb:behaviour>
```

#### Note

Note how we have to reset the namespace on the <pb:template>? This is required because the default namespace in an ODD document is the TEI namespace. You therefore need to reset it whenever you want to output elements in another or no namespace inside a template. Without this, the `iframe` would end up in the TEI namespace. Web browsers will usually ignore it, but it would be incorrect nevertheless.

All behaviours should be defined in the TEI header of the ODD file (or – to be exact – the < tagsDecl> part of the <encodingDesc>). You may create multiple behaviour declarations with the same @ident, provided that they apply to different @output modes. Parameters declared via <pb:param> without @value attribute are expected to be passed to the behaviour from the calling model. A parameter may be empty though. If you define an XPath expression as @value attribute, the result of the XPath evaluation will be used as value for the parameter. 

The new behaviour defined above will be named iframe and takes three parameters: src, width and  height. It can now be used by a <model> as follows: 

```
<model behaviour="iframe"> <param name="width" value="@width"/> <param name="height" value="@depth"/> <param name="src" value="@fileref"/> </model>
```

For further code examples, please have a look at [docbook.odd](odd/docbook.odd), which is used for viewing the documentation. 

#### Note

The graphical ODD editor in TEI Publisher does not yet support defining your own behaviours via <pb:behaviour>. You have to make these changes in the source XML using an XML editor. You can, however, use the graphical editor to continue editing the ODD afterwards. It makes sure not to overwrite your hand-written code upon save. 

### Custom Behaviours in XQuery

While TEI Publisher does provide ways to create custom behaviours in XQuery  this should only be used when well justified: custom XQuery behaviours limit the portability of the ODD and negatively influence maintenance. It is recommended  to rely on standard behaviours, <pb:behaviour> and <pb:template> extensions of the ODD syntax wherever possible.

Avoiding custom behaviours works quite well for HTML output, nevertheless edge cases exist where creating custom behaviours in XQuery makes sense. Follow instructions from the XQuery extension modules chapter for this approach.

Avoiding custom behaviours works quite well for HTML output and we have realized complex projects with no extension behaviours. Things may become more difficult for LaTeX output: there are hundreds of packages to use, and users typically define their own macros or environments for all recurring typesetting tasks. For example, to print a TEI <persName>, experienced LaTeX users would normally create a corresponding `\persName` macro and handle the formatting details there. Similarly, processing highly specific XML sources, for example to produce the reference pages for the TEI Guidelines themselves, is best handled via custom XQuery behaviours.

## Custom markup templates with <pb:template> 

The standard supported behaviours are sufficient to cover most typical tasks, but more complex HTML markup or procedural output formats like LaTeX require more customization and control over the generated output. The TEI Publisher library therefore extends the processing model syntax with a custom element for defining code templates. The `pb:template` element belongs to TEI Publisher, not TEI namespace ( http://teipublisher.com/1.0).

Within the ODD, a <model> may define a <pb:template> element containing a code template. The template is expanded first, and the result is passed to the behaviour specified for the model, in place of the content parameter accepted by all behaviours. The very simple case of outputting a <persName> in LaTeX could therefore be written as: 

```xml
<elementSpec ident="persName" mode="add"> <model behaviour="inline" output="latex"> <pb:template>\persName{[[content]]}</pb:template> </model> </elementSpec>
```

The template can reference other parameters defined within the <model> by enclosing the parameter name in double brackets. In the example above we're referencing the default parameter content, which does not need to be explicitly specified. Here it therefore retains its default value, which in this situation is the content of the <persName> tag. The parameter will be processed in a normal way before it is passed into the template, so if <persName> contains any nested TEI markup, the matching processing models will be applied first.

The result of expanding the template is then passed to the behaviour ( `inline` in the example above).

You may also specify additional parameters to be included in the template. For example, the TEI document may contain a glossary of terms which are referenced in the text using `<term ref="#termid">text</term>`. In LaTeX this would translate to `\glslink{ref}{text}`, which can be easily produced by the following <model> : 

```xml
<elementSpec ident="term" mode="add"> <model behaviour="inline" output="latex"> <param name="ref" value="substring-after(@ref, '#')"/> <pb:template>\glslink{[[ref]]}{[[content]]}</pb:template> </model> </elementSpec>
```

We define an additional parameter ref, which contains the id string from the @ref attribute, stripping out the leading '#'. 

The templating mechanism is not limited to LaTeX, but may be used to generate HTML or FO markup, for example, if you have to generate a complex, nested HTML fragment for a single TEI element. This is difficult, and sometimes impossible, to achieve without templates.

The following example uses the pb-popover webcomponent to display the `tei:persName` from a person register for a given `tei:persName/@key` of your TEI document. 

```xml
<elementSpec ident="persName" mode="add"> <model behaviour="inline" cssClass="popover"> <param name="content" value="."/> <param name="key" value="@key"/> <param name="person-from-register" value="doc('/path/to/person.xml')//person[@id = @key]/persName[@type='full']/string()"> <pb:template xmlns="" xml:space="preserve"> <pb-popover data-ref="[[key]]"> <span slot="default">[[content]]</span> <span slot="alternate">[[person-from-register]]</span> </pb-popover> </pb:template> </model> 
```

## XQuery extension modules

Where possible, developers should stick to the standard behaviours, or use the <pb:behaviour> and <pb:template> extensions of the ODD syntax. However, there might be situations in which it is necessary to generate a specific type of complex output, which requires the full power of XQuery. To facilitate this, the implementation allows extension modules to be configured, with definitions of custom functions and behaviours which may be then used in the ODD. 

Configuration is done via an XML file which must reside in the same collection as the source ODD files. It contains a series of <output> elements, representing particular output modes (e.g. web or print) via @mode attribute. < output> element without a mode groups modules available for all output modes. 

Each <output> element lists modules to be loaded for specified output mode. Each definition may optionally be limited to a specific ODD, name of which is specified in the @odd attribute. 

```xml
<modules> <!-- functions or behaviours common to all output modes --> <output> <module uri="http://www.tei-c.org/tei-simple/xquery/common-functions" prefix="tc" at="xmldb:exist:///db/apps/my-app/modules/common.xql"/> </output> <!-- General fo extension functions --> <output mode="print"> <module uri="http://www.tei-c.org/tei-simple/xquery/ext-fo" prefix="ext-fo" at="xmldb:exist:///db/apps/tei-publisher/modules/ext-fo.xql"/> </output> <!-- Special web configuration for the documentation (to handle <code>) --> <output mode="web" odd="documentation"> <module uri="http://www.tei-c.org/tei-simple/xquery/ext-html" prefix="ext-html" at="xmldb:exist:///db/apps/tei-publisher/modules/ext-html.xql"/> </output> </modules>
```

Whenever the library tries to locate a processing model function for a given behaviour, it will **first** check any extension module it knows to see if it contains a matching function. One can thus **overwrite** the default functions as well as define new ones. 

An extension module may also contain general purpose XQuery functions you want to call from within an ODD parameter, e.g. for formatting a date, outputting a number etc. To make these functions available to all output modes, just skip the @mode attribute. 

Starting with tei-publisher-lib 3.1.0, the module import path given in the "at" attribute no longer needs to be absolute. A relative path (e.g. ext-html.xql) will be interpreted relative to the modules collection. 

### Implementing Custom Functions and Behaviours

To be recognized by the library, an extension function must accept at least 4 default arguments, plus any number of custom parameters. The required parameters are:

$config 
+  a map containing configuration information as well as function references to be called. The most important ones are $config?apply($config, $node) and $config?apply-children($config, $node, $content). Both are function items and when called, continue processing with either a single  $node or a sequence of nodes in $content. 

$node 
+  the TEI element being processed at the moment

$class 
+  a list of HTML class names to be used. This includes automatically generated class names as well as those passed via @cssClass on a model item. 

$content 
+  because content is defined for every model rule, it is always passed to a behaviour function (though it might be empty) 

For all additional parameters, the processing model implementation tries to fill each custom parameter with a corresponding value by looking through the <param> children of the <model> in the ODD to find one with a name matching the variable name. If no matching parameter can be found, the function argument will be set to the empty sequence. You should not enforce a type or cardinality for any of the custom parameters as this may lead to unexpected errors. The parameters may be empty or contain more than one item. 

For example, a custom behaviour called `code` for syntax highlighting in an extension module named ext-html.xql might look as follows: 

```xquery
xquery version "3.1"; (:~ : Non-standard extension functions, mainly used for the documentation. :) module namespace pmf="http://www.tei-c.org/tei-simple/xquery/ext-html"; declare namespace tei="http://www.tei-c.org/ns/1.0"; declare function pmf:code($config as map(*), $node as element(), $class as xs:string+, $content as node()*, $lang as item()?) { <pre class="code {$class}" data-language="{if ($lang) then $lang else 'xquery'}"> {replace(string-join($content/node()), "^\s+?(.*)\s+$", "$1")} </pre> };
```

It defines one function, pmf:code, which can be called from the ODD as follows, provided that the ext-html.xql module has been configured as described in the previous section. 

```xml
<model behaviour="code">  <param name="lang" value="@lang"/>  </model> 
```

### Custom Behaviours Accepting User-Defined Parameters

Sometimes you may like to implement a generic behaviour which takes arbitrary parameters from the user. This means the parameter list of your behaviour will not be fixed.

To facilitate this, a behaviour function may declare a final parameter `$optional as map(*)`. If the processor finds <param> children in the model which cannot be mapped to an explicitly declared parameter, it stores all such extra parameters as a key/value pairs in the $optional map. 

## Setting ODD parameters

There are several scenarios, where it's useful to pass a parameter to the ODD, or set it at some point in the processing, to be retrieved later: 

+ to access global configuration parameters, e.g. to determine the path to the registers or primary data collection to avoid redundance in hard-coding it in the ODD

+ external parameters, informing the ODD processor about elements of the application context, e.g. user-selected language, or if we are in the table of content, breadcrumbs, register or perhaps a reading view

+  to influence processing of the subsequently called models for particular use case by setting a processing mode

+  to set a parameter value in a processing model and have it available to all subsequently processed models to avoid repeated computation of a value, that can be re-used, e.g. expanding the facsimile prefix into reusable URL fragment

The last two scenarios have a lot in common, in fact setting a processing mode is a special case of setting an arbitrary parameter. Both features are intended for relatively rare cases, where the standard mechanisms for handling the flow of processing are not enough. This mainly applies to cases in which you have to process the same document fragment multiple times in the same processing context (usually the edition view).

### Global Configuration Variables

Sometimes you may want to access global configuration parameters, e.g. to determine the data root collection of the current application.

TEI Publisher imports some commonly needed elements of the global application configuration from the `odd-global.xqm` module.

```xml
<modules> <!-- Import global variables and functions from modules/odd-global.xqm --> <module uri="http://e-editiones.org/tei-publisher/odd-global" prefix="global" at="odd-global.xqm"/> ... </modules>
```

### External Parameters

The script calling the processing model may pass external parameters into the ODD. They will be available in the variable $parameters, which is an XQuery map. Access parameters using `?`, the XQuery lookup operator. 

For example, one can use this feature to control how specific parts of the document are output, without having to define a separate output mode, which would result in much more code. Below we display a shortened header for the document, containing simply its title, but only if the parameter "header" is set to "short":

```xml
<elementSpec mode="change" ident="fileDesc"> <model predicate="$parameters?header='short' behaviour="block" cssClass="header-short"> <param name="content" value="titleStmt"/> </model> ... </elementSpec>
```

The <pb-view> webcomponent also lets you define arbitrary parameters to be passed to the ODD via <pb-param>. For example, the breadcrumbs shown above this documentation page are realized by setting a parameter mode and can be queried in model predicates with $parameters?mode='breadcrumbs' . 

```xml
<section class="breadcrumbs"> <pb-view id="title-view1" src="document1" subscribe="transcription"> <pb-param name="mode" value="breadcrumbs"/> </pb-view> </section>
```

If the parameter is set, the processing model rules in the ODD will output the headings of all ancestor sections of the current division only, ignoring everything else. This approach helps to reuse the same ODD for viewing specific aspects of the document.

A dedicated user interface webcomponent [ <pb-toggle-feature> ](https://cdn.tei-publisher.com/api.html#pb-toggle-feature.0) exists for toggling between two values of a parameter. Example below would produce a checkbox which when on results in the value of  $parameters?mode set to diplomatic, otherwise to norm. 

```xml
<pb-toggle-feature name="mode" on="diplomatic" off="norm">Diplomatic View</pb-toggle-feature>
```

### Processing Modes 

Distinguishing between processing modes is a common feature in XSLT. It comes handy if you need to process the same XML more than once for different purposes.  

One example would be to render a person reference once in the running text, and again, as a footnote, but in that case enhanced with the associated commentary but stripped of any information regarding textual features such as scribal errrors in the spelling of the name, or gaps and stains on the parchment. 

As a rule of thumb, setting a mode is helpful where it would not be possible to distinguish the scenario to apply neither by the context in which the element appears in the XML source nor by the external parameter passed into the ODD.

TEI Publisher supports an extension attribute, `@pb:mode`, on a `model`. The value of the attribute will be taken as the name of the mode, and subsequently called models can test for it in a `predicate` in via special variable, `$mode`

For example, if you set `pb:mode` attribute to `register` on a model with `<model behaviour="block" pb:mode="register">`, then subsequently called models (i.e. models for all  nodes nested within the current element) can be tested for the mode in a predicate, e.g. `<model predicate="$mode='register'" .../>`. 

None of the examples shipping with TEI Publisher needs this feature, but in some specific editions we do encounter situations which cannot be handled without them. Illustration below presents such a case, where in the edition text only one of the possible expansions is presented (_Dat(ae)_), but the apparatus below gives a rendition of all potential readings (_Dat(ae) or Dat(um)_). Both the edited text and the apparatus are generated from the same TEI fragment, but processed twice, under two models differentiated by mode, one resulting in an inline phrase, and the other in a footnote.

![](dantiscus-processing-modes.jpg) 

_Use case for applying different processing modes to the same TEI element [in Dantiscus' correspondence edition](http://dantiscus.al.uw.edu.pl/?f=letterSummary&letter=116&view=6#ftntid286509376)_

### Setting parameters within the ODD

A logical follow-up for setting the processing mode described in the previous section and passing external parameters, this extension allows to set an arbitrary parameter which is passed on to all models subsequently called as a result of processing the current element. 

The syntax is almost the same as for the standard `param`, which would be set on the `model`.  Just the name of the element changes to `<pb:set-param>`, marking this as a special parameter which is _"inherited"_ by subsequent models. 

Common motivation to use this feature would be for processing efficiency, to compute certain value once and just use it many times afterwards. Let's imagine that every paragraph exposes a citation button which always starts with the author, title and other information from the publication statement part of the TEI file and only ends with a paragraph number. We could easily prepare the initial sequence on the entire text or a division level and have it available to all nested paragraphs, thus saving the processing time required to look up from the paragraph to the TEI header every time. 

Example below shows relevant ODD models for setting the `iiif` and `iiif-prefix` parameters on the `div` level, and later reusing these precomputed values to generate links to facsimile for every word. Due to data structure, reflecting the complex history of the manuscripts, scattered across archives and available from different IIIF services around the globe, the computation involves various lookups and benefits greatly from this optimization.

![](pm-erabbinica.jpg) 

_Use case for pre-setting a parameter to optimize generation of IIIF links for every word, line and column in [eRabbinica](http://www.erabbinica.org/midrash/MekhY/transcription_MekhY_Ox24.xml?panels=0.1)_

```xml
<elementSpec ident="div"> <model behaviour="block"> <pb:set-param name="iiif" value=" let $facs := (($parameters?root/ancestor::TEI)//pb[@facs])[1]  let $prefix := substring-before($facs/@facs, ':')  let $iiif := doc('/db/apps/erabbinica-data/data/sources/taxonomy/taxonomy.xml')/id($prefix)    return $iiif//*:ptr/@target"/> <pb:set-param name="iiif-prefix" value=" let $facs := (($parameters?root/ancestor::TEI)//pb[@facs])[1]  return substring-before($facs/@facs, ':')"/> <outputRendition xml:space="preserve"> direction: rtl; </outputRendition> </model> </elementSpec> 
```

```xml
<elementSpec ident="w" mode="change"> <model behaviour="inline"> <param name="default" value="."/> <param name="facs" value=" let $pattern := '^'||$parameters?iiif-prefix || ':' || '([^/]+).*$'    return   $parameters?iiif || replace(@facs, $pattern, '$1')"/> <param name="coords" value="let $pattern := '^'||$parameters?iiif-prefix || ':' || '[^/]+/(.*)$'   return  ('[', replace(@facs, $pattern, '$1'), ']')"/> <param name="ana" value="@ana"/> <pb:template xmlns="" xml:space="preserve"><span> <pb-facs-link emit="transcription" facs="[[facs]]" coordinates="[[coords]]">[[default]]</pb-facs-link> </span></pb:template> </model> </elementSpec> 
```

## XQuery or XPath

The implementation directly translates processing model instructions into an XQuery 3.1 module by generating executable XQuery code. This is straightforward as the resulting XQuery will closely resemble the specification in the ODD, thus being easy to debug. It also leads to very efficient code, which is as fast or even faster as a hand-written, optimized transformation.

As a welcome side effect, any valid XQuery expression might be used wherever the spec expects an XPath expression, e.g. in predicates or parameters. For example, one can define variables inside a parameter using a standard XQuery `let $x := ... return ...` syntax. 

## Default Processing Model Rules

It is possible to define a default <elementSpec> to be applied to all elements which are not already matched by another <elementSpec>'s model. TEI Processing Model default specifies that where no <elementSpec> is present for an element, processing descends down through child nodes,  as if `pass-through`behaviour was applied. To change this default and omit content elements  without specification, you may want to define a default <elementSpec> as shown below: 

```xml
<elementSpec ident="*"> <model behaviour="omit"/>  </elementSpec>
```

You can also define models to match text nodes, e.g. if you need to normalize certain nodes:

```xml
<elementSpec ident="text()"> <model behaviour="text"/> </elementSpec>
```

Note that outputting text nodes is a performance critical operation, so use with care. Complex processing may dramatically affect performance.

## Chaining the ODDs

In the spirit of reuse, it is considered best practice to chain the ODD files together and only change or add project specific rules to a custom ODD extending the base as necessary. Chaining allows for  future upgrades, as your base ODDs are updated by standardization bodies or communities maintaining them.  This way the custom ODD usually stays short and simple. TEI Publisher is shipping with an extensive base ODD (called `teipublisher.odd`) already supporting a wide range of use case scenarios. Custom ODDs would usually extend this ODD.

While the base ODD provides a sensible rendition out of the box, concrete projects often require certain adjustments to fully meet its specific needs. It has been one of primary concerns in Publisher's design that customization is not only possible on all levels but actively encouraged and as simple as possible.

Very broadly TEI PM customizations involve two aspects: adjusting the processing logic (how the source document is translated into the output format), and changing the styling and typesetting of the presented output. In this chapter we'll discuss both, as they primarily require modification of the ODD with the TEI Processing Model. As mentioned above, it is usually best to choose as your starting point an already existing ODD, e.g. teipublisher.odd and create a customization chained to it. 

## Output Specific Settings

The library supports various output media formats and translates styles into the corresponding format. Currently the following output modes are supported and can be used in the @output attribute: 

web 
+  Produces HTML output

fo 
+  Generates a PDF via XSL:FO

latex 
+  Creates a PDF via LaTeX

print 
+  Extends web output with a focus on Print and Paged Media (PrintCSS)

epub 
+  A specialization of the web output mode targetted at epub documents

markdown 
+  Plain text enhanced with markdown

By default, the base10 profile only enables the output modes `web`, `print` and `epub`. The _markdown_ profile obviously adds `markdown` to the list. If you would like to use the `latex` or `fo` mode, you have to configure this in the config.json:

```json
{ … "defaults": { "media": ["web", "print", "fo", "latex", "epub"] } … }
```

The quality of the generated output may vary a lot for the fo and latex modes, depending on the type of input document. The following section provides more details on the configuration of the FO output option:

### FO Output

When generating XSL:FO output, the implementation tries to translate the CSS rules specified for renditions into the corresponding XSL:FO formatting properties. Not all CSS properties are recognized or can be mapped to FO properties. Unknown properties defined in a rendition will be ignored.

The default rendering for headings, paragraphs and the like is defined by a separate CSS file. The implementation merges those defaults with the custom renditions given in the ODD.

The library searches for default CSS styles in a file named  <odd-name>.fo.css inside the specified output collection (in which the generated XQuery files are stored). The style definitions are copied literally into attributes on the output XSL:FO elements, so any property which is a valid attribute for the corresponding element may be used. For example, teipublisher.fo.css contains: 

```css
.tei-text { font-family: "Junicode"; hyphenate: true; } .tei-floatingText { padding: 6pt; } .tei-p { text-align: justify; } 
```

Every XSL:FO document needs a master layout and a page sequence definition. Because those tend to be rather verbose as they include things like page margins etc., they are read from two XML files:

master.fo.xml 
+  Contains the layout master set

page-sequence.fo.xml 
+  Defines the main page sequence

The mechanisms for configuring FO output are still very much under development and we welcome suggestions by users.

### LaTeX Output

The latex output mode produces good results for longer texts which fit well into the pre-defined LaTeX environments. The number of supported CSS properties is limited though:

+  font-weight

+  font-style

+  font-variant

+  font-size

+  color

+  text-decoration

+  text-align

+  text-indent

To create arbitary complex LaTeX output, you may want to use the  <pb:template> extension  to the ODD syntax. It is heavily used to e.g. generate the LaTeX version of this documentation. See also [serafin.odd](odd/serafin.odd) or [vangogh.odd](odd/vangogh.odd) for examples. 

TEI Publisher creates a default LaTeX prolog based on standard packages and settings. You may overwrite the defaults by providing your own template within the ODD element spec for the TEI root element. See the example ODDs mentioned above. Note that TEI Publisher will generate some LaTeX macros for styles defined in <outputRendition> which should be imported into the prolog. The styles are added to the default configuration map and can be accessed via `$config('latex-styles')`. Refer to the example ODDs and just copy/paste the corresponding lines. 

This output mode requires a local installation of LaTeX on the machine running TEI Publisher. The examples have been tested on a default installation of MacTeX 2018. If you are not running MacTeX, you likely need to adjust the path to the LaTeX binary in the XQuery configuration module modules/config.xqm. Search for the variable $config:tex-command and adjust it to point to a binary of ` xelatex`, `pdflatex` or `lualatex` . 

### ePub Output

The epub output mode extends the HTML mode. You may define general styling in an extra CSS file, located in resources/css/epub.css. This external stylesheet is included into all generated epub files and may be used to configure general settings like page breaks, hyphenation, font sizes etc. 

### Print CSS

This output mode targets printed HTML via the CSS Paged Media specification (often also called Print CSS). Though recently improved, browser support is still incomplete. However, there are numerous tools to fill the gap. Select the print CSS download option for any of the documents in TEI Publisher which support it and you will be directed to a preview page. This page uses a library called [paged.js](https://pagedjs.org/) to "_teach_" the browser how to handle certain layout aspects. Thanks to this, using the `print page` dialog of your browser will result in a much nicer page layout than if you just tried to print TEI Publisher's document view.

You can find a more detailed discussion of the feature along with some CSS examples in this [article on the e-editiones homepage](https://www.e-editiones.org/posts/print-css/).

The preview page uses TEI Publisher's `pb-print-preview` web component. This component will retrieve the full HTML of the document, enhanced with additional CSS stylesheets for print, and passes it to paged.js for the page formatting. For testing purposes you can also click on the bottom-most button with the eye icon to see the raw HTML (without paged.js applied). This is handy if you want to inspect the HTML node hierarchy or the CSS classes assigned.

To generate the HTML of the document, the component calls Publisher's API endpoint:

```
/api/document/doc%2Fdocumentation.xml/print
```

The following (optional) parameters are supported:

base 
+  specifies the base URI for resolving relative links, e.g. to locate images

style 
+  one or more additional CSS stylesheet URLs to be imported into the page.

script 
+  one or more Javascript files to be loaded via <script>.

wc 
+  if set, include TEI Publisher's web component libraries. You may need this if you are using components like <pb-code-highlight> (as in the documentation)

As an open source project, paged.js does not support every fancy layout you may wish for. For example, support for formatting footnotes will be limited. There are other, mainly commercial tools, which you may use to generate PDF out of your HTML. Those include e.g. [Prince XML](https://www.princexml.com/), [PDF Reactor](https://www.pdfreactor.com/) or [Antennahouse](https://antennahouse.com/). A full list can be found on [print-css.rocks](https://www.print-css.rocks/tools). Each of those may have different strengths and weaknesses. Please try yourself.

For command line tools, you can use the API endpoint described above to retrieve the full HTML of the page and feed this to PrinceXML or whatever you want to use. To simplify this, the preview page includes a button at the bottom: clicking it will copy the API endpoint URL (excluding the page.js components, which would interfere with external tools). The screenshot below shows princeXML's dialog into which you can paste the URL provided by the preview page:

![](prince-screenshot.png) 

_Processing Print HTML with PrinceXML_

# Anatomy of a profile

Everything in TEI Publisher 10 is a profile, which also includes any application you generate with jinks. Applications follow the same structure and hierarchical organization as other profiles. The difference is only that an application includes the merged content of all the profiles it extends. When you update an application, changes contributed by the various dependant profiles will be injected into the correct place within the collection hierarchy. To customize an application, it is therefore helpful to know the basic anatomy. It is also vital to respect the naming scheme where one is documented to avoid conflicts and guarantee smooth updates.

The most important locations inside the application hierarchy are:

config.json 
+  Contains the configuration for the profile or application and declares its dependencies on other profiles

.jinks.json 
+  This file is maintained by jinks to keep track of changes and avoid that local changes are overwritten by updates. You should not need to modify it by hand. However, it is important that this file is present. Do not remove it and make sure it is available in the root collection of your application when installed, e.g. if you deploy from a xar package.

context.json 
+  Contains the expanded configuration of all profiles used. Maintained by Jinks. **Do not remove or modify**.

modules 
+  The actual application code, written in XQuery, can be found here. Files below the lib collection should in general not be touched.

    However, profiles can contribute additional XQuery files. Likewise your application may need functionality not available out of the box. In general, there are two main areas in which a profile or application may contribute XQuery code:

API endpoints 
    +  Those always come combined with a JSON definition of the endpoint (using the Open API standard), and the naming convention is [profile-or-app]-api.xql and [profile-or-app]-api.json. For example, the registers profile installs registers-api.xql and registers-api.json.

Templating function modules 
    +  include helper or utility functions to be used in HTML templates and should go into modules/templates.

templates 
+  HTML templates come in two flavours:

page templates 
    +  contribute a custom HTML page to the application, e.g. a document view for a particular type of edition (see serafin blueprint) adding special features, or a custom browse or landing page. They will usually extend and inherit from one of the base templates.

template blocks 
    +  provide fragments of HTML to be injected into other views. For example, the iiif profile is only adding a IIIF viewer into the right sidebar.

resources 
+  this collection/directory contains CSS stylesheets, Javascript files, i18n translation files and most important: ODD files.

doc 
+  contains documentation for the profile. This will be shown within jinks if users click on the "I" information icon next to a profile.

static 
+  some profiles allow the application to be transformed into a static snapshot, which does not require a database or any other server-side logic. Those profiles usually need to provide customized templates for this static version, replacing dynamic functionality with static counterparts where possible.

    The collection hierarchy below static otherwise mirrors the structure described in this chapter.

For most of the files mentioned above, defined extension points exist in config.json. This means you have to register the added resource in the appropriate location within config.json. We'll discuss this in the sections below.

Every profile or application may also add its own configuration parameters to config.json. By convention those should go into an object below the features property. For example, the iiif profile reads its configuration items from `features.iiif`:

```json
"features": { "iiif": { "viewer": "pb-tify", "base_uri": "https://apps.existsolutions.com/cantaloupe/iiif/2/", "enabled": false } } 
```

## Templated source files

If you browse through the source code of the profiles shipping with Jinks, you will also note a bunch of files having .tpl in their name, e.g. profiles/theme-base10/resources/css/jinks-variables.tpl.css.

Those are templated files and Jinks will automatically process and expand them while copying files. You may rename any file to match the pattern .tpl.suffix to have it expanded upon installation. What templates are and how you use them is described in the next section.

## XQuery Callbacks

Whenever generating or updating an application, Jinks will check each profile for an XQuery module named setup.xql. This module controls the generation or update process for the profile. It consists of one or more annotated XQuery functions, which are called during the different stages of the process:

%generator:prepare 
+  receives the merged configuration map - including all changes applied by other profiles earlier in the extension chain - and may return a modified map. Use this to compute and set custom properties needed by your profile.

%generator:write 
+  performs the actual installation. Receives the configuration map as only parameter.

%generator:after-write 
+  is called after the application has been updated (or installed if it was not generated before). Receives the configuration map as first, the path to the root of the target application as second parameter.

If a profile does not have a setup.xql, the default behavious is to recursively copy everything in the profile's source folder to the destination, expanding templates on the way.

In TEI Publisher 10, only two profiles: _base10_ and _theme-base10_ need a setup.xql. Both plug into `%generator:after-write` to perform additional post-processing steps.

## Bootstrap your own profile

At the time of writing, all TEI Publisher default profiles are shipped with _Jinks_. However, it is also possible to create an external profile in the form of an _eXist-db_ installable package. Upon startup, _Jinks_ will search through all packages in the database. If a package has a config.json with a declared `"type"` property of either `"blueprint"`, `"feature"`, `"theme"` or `"base"`, it will be added to the list of available profiles in the Profiles tab. Other than that, a profile's `config.json` looks very much like that of an application, with one exception: where applications list the profiles they extend in the `"extends"` property, a profile does not declare this. Instead, it lists the profiles it depends on in the `"depends"` property.

_Jinks_ can help to bootstrap the profile: fill out the required fields on the Application Configuration tab as usual. On the Profiles tab, scroll to the very bottom and enable the Create a new profile checkbox. Next to it, you should select the type of the profile. Afterwards, choose whatever profiles you would like to add as dependencies and change theme or configuration settings as needed.

![](jinks-create-profile.png) 

_New profile option on Profiles tab_

Clicking Apply will bootstrap the new profile inside the database and automatically downloads it as an installable .xar package. Now you have two options to further work on the profile:

1.  inside the database using _eXide_

2.  unzip the .xar to a directory

Using option 1, you'll still have to make sure you backup your profile to the file system every now and then (use Download App in the Application menu). With option 2, you need to build and deploy the profile whenever you made changes. The generated Ant build script should generate a new .xar, which you can then upload to the dashboard.

If you open the generated config.json of the new profile, it may look like this:

```json
{ "pkg": { "abbrev": "my-profile" }, "overwrite": "default", "label": "my-profile", "type": "blueprint", "version": "1.0.0", "depends": ["base10","demo-data","theme-base10"], "skipSource": [ "repo.xml", "expath-pkg.xml", "build.xml" ] }
```

Important here is that this configuration has a `"type"` property, which identifies  it as a profile, and the other features used are declared as dependencies.

# Jinks Templates

Templates are a key building block of TEI Publisher 10 and appear in two different contexts:

1.  at generation time to expand the files contributed by the different profiles

2.  at runtime to dynamically render the HTML pages of an application

Templates in TEI Publisher 10 use the [jinks-templates](https://github.com/eeditiones/jinks-templates) library, which provides a powerful templating system with inheritance, blocks, and XQuery integration. Templates are processed server-side. Unlike the templating in earlier TEI Publisher version, _jinks-templates_ can operate on XML as well as plain text files, i.e. the processor can deal with (X)HTML as well as CSS, Javascript or XQuery code. However, the following sections will concentrate on HTML templates, because those are the files user will be most interested in changing.

## Templating Syntax

The jinks-templates library uses a simple but powerful syntax:

+  `[% ... %]` - Template directives for control structures, includes, blocks, and templates

+  `[[ ... ]]` - Variable interpolation using XQuery expressions

+  `---json` - Frontmatter markers for configuration (between `---json` and `---`)

Template directives support common control structures like `[% if %]`, `[% for %]`, `[% include %]`, and more. Variable interpolation allows you to embed XQuery expressions directly in your templates, with access to the full context map and any imported XQuery modules.

For a full documentation of directives, please refer to the [jinks-templating README](https://github.com/eeditiones/jinks-templates).

## The Context Map

Every template receives a context map (a JSON object), which it can use to look up configuration parameters. The context map is populated from two sources:

+  the merged config.json of the application and all profiles it extends. You can see this context in jinks itself if you expand the corresponding Merged Configuration panel below the current application's configuration editor.

+  frontmatter given in the template and any other template it inherits from

Template inheritance and context merging are central concepts in TEI Publisher 10: most profiles contribute just a few little fragments to the application, determined by configuration variables received in the context. In the simplest case, a page template may only contain frontmatter, i.e. it just reconfigures the various features and content blocks coming from inherited templates or profiles.

### Accessing the Context

Within template expressions, the context map can be accessed in two ways:

1.  via the `$context` variable – this is an XQuery map and you can use the lookup operator "?" to query for keys, nested maps and arrays. For example, to check if the table of contents is enabled, you can write:

```
[% if $context?features?toc?enabled %]
```

2.  via variable mapping – each top level property in the context map is also available as a local variable, so above expression could be shortened to:

```
[% if $features?toc?enabled %]
```

Note that the second approach will result in an XQuery error if there's no property called features in the current context map. Therefore, if you are not absolutely sure that the property will always be there, use approach 1, which will never fail.

## Template Inheritance

Templates can extend other templates, creating an inheritance chain. This allows you to build complex page layouts by composing simpler templates. The inheritance is specified in the JSON frontmatter using the `extends` directive.

For example, the inheritance chain for the documentation page you're currently reading is as follows:

+  templates/pages/documentation.html – Is loaded first, but contains only frontmatter and one block to overwrite how breadcrumbs are shown. Everything else is inherited via the extended basic.html.

+  templates/pages/basic.html – Adds the main text view into the central content area

+  templates/layouts/content.html - Contributes content-specific layout with breadcrumbs and table of contents

+  templates/layouts/base.html - The root template, defines the overall page structure with HTML head, body, and layout areas

The template processor will walk through the entire chain, in the order shown above, expanding each template on the way. The initial template at the start of the chain, templates/pages/documentation.html, looks like this:

```html
<template> ---json { "templating": { "extends": "templates/pages/basic.html" }, "styles": [ "resources/css/documentation.css" ], "defaults": { "base": "doc/documentation.xml" }, "urls": { "template": "documentation/:id?", "ignore": "odd,view,path" }, "features": { "toc": { "enabled": true } } } --- [% template! breadcrumb %] <li> <pb-view class="breadcrumbs" src="document1" subscribe="transcription" disable-history="" on-update=""> <pb-param name="mode" value="breadcrumb"/> </pb-view> </li> [% endtemplate %] </template> 
```

As you can see, it mainly adds to the configuration, this way changing how inherited templates up the chain will process, e.g. by indicating that a table of contents should be shown (by default there won't be any).

There is one template expression, `[% template! breadcrumb %]`, which overrides the default HTML for the breadcrumbs in the toolbar: for the documentation we would like to see hierarchical breadcrumbs and we would like to output those via the ODD – hence the <pb-view> webcomponent.

## Frontmatter Configuration Merging

As we just learned, each template in the inheritance chain can include JSON frontmatter (between `---json` and `---` markers). These configurations are merged recursively as the inheritance chain is processed. Initially the configuration (passed to the first template in the chain) corresponds to the config.json of the root application, merged with all the config.json of the profiles it extends.

It is therefore important to understand how the merging works:

+  _Atomic values_ (strings, numbers, booleans) - Later templates overwrite values from earlier templates

+  _Maps/Objects_ - Maps will be processed recursively by merging the properties of each incoming into the outgoing map.

+  _Arrays_ - Values are appended with duplicates removed. For arrays of maps, deduplication uses the `id` property if present.

This merging behavior allows child templates to extend or override configuration from parent templates. For example, a child template can append additional stylesheets to the `styles` array. Existing array items coming from inherited templates or profiles are preserved.

We can also override a single property in a nested configuration entry. For example, to disable the navigation buttons in the toolbar, we can use:

```json
---json { "features": { "toolbar": { "navigation": false } } } ---
```

This will be recursively merged into the initial `features.toolbar` entry, resulting in:

```json
---json { "features": { "toolbar": { "enabled": true, "zoom": true, "navigation": false, "progress": { "enabled": true, "subscribe": "transcription" } } } } ---
```

For sure there will be edge cases when this kind of property merging is not desirable. The jinks-templates library provides [special properties](https://github.com/eeditiones/jinks-templates?tab=readme-ov-file#how-context-maps-are-merged) for that (see `"$replace"`).

## Preloaded XML Data

XML documents stored in the application can be preloaded into variables (or context properties) to access them in template expressions: if the frontmatter of the template contains a `data` property, it will be processed as an object, mapping variable (or property) names to relative paths into the application. The templating tries to resolve each relative path to an XML document and declares a top-level property in `$context` with the corresponding name. For example, the frontmatter of profiles/landing-page/templates/index.html includes:

```json
"data": { "landing": "landing/landing.xml" }
```

This defines a single mapping, though more can be added. landing/landing.xml is a TEI document containing the sections to be rendered for different parts of the landing page. Within the template, information about the document as well as its content can be accessed with `$landing` or `$context?landing`. The value of the variable will be a map with the following properties:

`content` 
+  The root of the document as a document node or the root element of a fragment (when using addressing by id)

`path` 
+  The relative path to the document

`odd` 
+  The ODD configured for rendering this document (without the '.odd' suffix)

`view` 
+  The preferred view (_div_, _page_ …) configured for the document

Within the template, you can use all XQuery expressions to further process the content. To render all or parts of it via ODD, call the utility function `page:transform`, e.g.:

```xml
<div>[[ page:transform($landing?content, map { (: parameters :) }, $landing?odd) ]]</div>
```

If the template is evaluated as part of a route addressing an XML document, the associated document will be preloaded by default and will be available as `$doc` or `$context?doc`. In this case, you do not need to declare an explicit mapping in the frontmatter.

For example, profiles/docs/templates/pages/osinski.html will be processed when you click on the link to the document _Bogactwa mowy polskiej_ in the _TEI Publisher 10 Documentation and Demo_ application. The template includes metadata about the document in the right sidebar, which is rendered with the following templating fragment:

```xml
[% template after %] <details> [[ page:transform($doc?content//tei:teiHeader, map { "mode": "sidebar" }, $doc?odd || ".odd") ]] </details> [% endtemplate %]
```

## Blocks and Templates

To make template inheritance really useful, we need a way for templates further down the inheritance chain to contribute content to defined locations or placeholders within the inherited templates. The template system uses two complementary concepts for content composition:

`[% block name %]` 
+  Defines a placeholder in a parent template where content can be injected. Blocks are typically empty in the base template and get filled by child templates. Multiple templates in the inheritance chain can contribute content to the same block, which will be concatenated.

    If a block is not empty, its content will be used as a fallback in case no other template is provided.

`[% template name %]` 
+  Defines content that will be injected into a corresponding `[% block name %]` placeholder. When a template defines a template with a matching name, its content fills that block. Multiple templates can contribute to the same block, and their content will be concatenated in the order they appear in the inheritance hierarchy.

    You may append an additional integer to the template directive to change the position at which the content will appear in the block if there's more than one template, e.g.:

```
[% template after 1 %]
```

    The integer is used for ordering: templates defining an order number will be output first, sorted by the given number. Other templates will come after in the order they appear.

`[% template! name %]` 
+  The exclamation mark indicates an override. This replaces any content provided by earlier templates in the inheritance chain for this block, rather than appending to it. The last `[% template! %]` always wins and replaces other templates occurring before.

For example, in templates/layouts/base.html, you'll find various block placeholders like in this snippet:

```html
<aside class="before hidden-mobile"> [% block before %][% endblock %] </aside> <header class="content-top hidden-mobile"> [% if $features?toolbar?enabled %] [% include "templates/toolbar.html" %] [% endif %] [% block content-top %][% endblock %] [% if $features?toolbar?progress?enabled %] <pb-progress subscribe="[[ $features?toolbar?progress?subscribe ]]" indeterminate="indeterminate" /> [% endif %] </header> [% block content %][% endblock %]
```

This defines three blocks for: the left sidebar, the top of the content area and the main content area itself. The content block is treated specially in that it will be filled by all remaining content not associated with another block. So to add something into the left sidebar as well as some main content, we can use the following child template:

```html
<template> [% template before %] <h1>About this document</h1> [% endtemplate %] <p>This is the main content of the document.</p> </template>
```

## Template-Only Files for Dynamic Content Injection

Some profiles use template-only HTML files to add specific panels or components to pages without modifying the base templates. These files are wrapped in a <template> element and contain only `[% template %]` definitions, no page structure.

These template-only files are loaded using the `use` directive in the profile's config.json. When a profile specifies files in the `use` array, those templates are automatically loaded and their template definitions become available to any page that has the corresponding block placeholders.

For example, the iiif profile uses this pattern to inject a IIIF viewer into the right sidebar. The profile's config.json includes:

```json
"templating": { "use": [ "templates/iiif-blocks.html" ] }
```

And templates/iiif-blocks.html contains:

```html
<template> [% template scripts %] [% if ($context?isStatic or exists($context?doc)) and $context?features?iiif?enabled and $context?features?iiif?viewer = "pb-tify" %] <script type="module" src="..."></script> [% endif %] [% endtemplate %] [% template after %] [% if ($context?isStatic or exists($context?doc)) and $context?features?iiif?enabled %] [% if $context?features?iiif?viewer = "pb-tify" %] <pb-tify subscribe="transcription" emit="transcription"></pb-tify> [% else %] <pb-facsimile .../> [% endif %] [% endif %] [% endtemplate %] </template>
```

When the IIIF feature is enabled, this template automatically injects the IIIF viewer component into the `after` block (right sidebar) and adds necessary scripts to the `scripts` block, without requiring any changes to the base templates. This pattern allows features to be added or removed by simply enabling or disabling them in the configuration, making the system highly modular.

Other profiles that use this pattern include dark-mode, timeline, metadata-panel, and annotate, each adding their own specific functionality to pages through template-only files.

## Importing Helper Functions

Templates can call XQuery functions from custom modules to perform complex operations or access application-specific logic. To make XQuery modules available to templates, you need to register them in the profile's config.json file under the `templating.modules` property.

Within a profile, helper modules should be placed in the modules/templates directory. For example, the base10 profile includes two helper modules: modules/templates/page.xqm and modules/templates/browse.xqm.

To register a module, add an entry to the `templating.modules` object in config.json. Each module entry uses the module's namespace URI as the key, and specifies a `prefix` (used to call functions in templates) and an `at` path (the location of the module file relative to the profile root):

```json
"templating": { "modules": { "http://teipublisher.com/ns/templates/page": { "prefix": "page", "at": "modules/templates/page.xqm" }, "http://teipublisher.com/ns/templates/browse": { "prefix": "browse", "at": "modules/templates/browse.xqm" } } }
```

Once registered, functions from these modules can be called directly in templates using the prefix. For example, the `page:parameter()` function from page.xqm can be used to reliably look up a request parameter:

```html
<input name="query" type="search" value="[[ page:parameter($context, 'query', ()) ]]"/>
```

While not required, it is good practice to pass the template's `$context` map to the helper functions as first parameter, giving them access to all configuration values, request parameters, and other context data. This allows them to perform operations that would be difficult or impossible to express directly in template syntax, such as complex data transformations, permission checks, or API calls.

# Layout, Themes and Styling

In TEI Publisher 10, HTML templates and styling are covered by different profiles. Templates are always part of the profile they functionally belong to. The base10 profile contains the root templates, which create the general layout of the page, while other profiles contribute the views targeted at a particular use case or feature.

Styling, on the other hand, is entirely handled by the selected theme profile. By default this is theme-base10. If you remove the dependency on this profile from your app, you'll get an unstyled page with just raw HTML.

## Default Page Layout Areas

To achieve a consistent look and feel across the generated website, the root template in base10 imposes a certain logical structure in the form of predefined areas, which can be filled with content. The assumption is that most pages will obviously need a header (for the page menu), a content area, and two areas before and after the central view for auxiliary information. Each of the main areas may also need some kind of header (e.g. for breadcrumbs or a toolbar).

However, the base profile does not state how those areas should be laid out on the page. This is the responsibility of the theme, which by default renders the areas in a three-column layout.

In detail, the base template (templates/layouts/base.html) defines the following areas, each corresponding to a templating block, which can be populated by templates:

Before Sidebar (`before`) 
+  Contains blocks for the left sidebar area: 
    +  `before-top` - Navigation items above the sidebar

    +  `before` - Main sidebar content (commonly used for table of contents)

Main Content (`content`) 
+  Contains blocks for the main content area: 
    +  `content-top` - Header area above main content (often contains toolbar)

    +  `above-content` - Content directly above the main content block

    +  `content` - The main content area (this is where document views are typically rendered)

    +  `below-content` - Content directly below the main content block

After Sidebar (`after`) 
+  Contains blocks for the right sidebar area: 
    +  `after-top` - Navigation items above the sidebar

    +  `after` - Main sidebar content (commonly used for facsimiles, metadata, or other supplementary content)

Additionally, there are blocks for the page header (`header`, `hero`) and for adding styles and scripts to the HTML head (`styles`, `scripts`).

### Injecting Content into Layout Areas

As described in the section on templates, child templates use the `[% template %]` directive with the appropriate block name to inject content into any of the layout areas. The content defined in the template will be inserted into the corresponding `[% block %]` placeholder in the parent template.

For example, templates/layouts/content.html injects a table of contents into the left sidebar by defining a template for the `before` block:

```html
[% template before %] [% if $context?features?toc?enabled and exists($context?doc)%] <h5> <pb-i18n key="document.contents">Contents</pb-i18n> </h5> <pb-load class="toc" url="api/document/{doc}/contents?target=transcription&amp;icons=true" .../> [% endif %] [% endtemplate %]
```

This content will appear in the left sidebar defined in base.html at the location where `[% block before %][% endblock %]` is placed.

Similarly, templates/pages/basic.html injects the main document view into the `content` block by placing the <pb-view> element directly in the template body (content outside of any `[% template %]` directive goes into the `content` block by default).

Multiple templates in the inheritance chain can contribute to the same block as explained in the previous section.

## Themes and custom CSS styling

In TEI Publisher 10, all styling is done by one or more _theme_ profiles, optionally combined with additional styles required by other profiles for the user interface fragments they contribute. After generating an app, the folder `resources/css` will contain all the CSS stylesheets files.

Out of the box, TEI Publisher 10 comes with a single default theme, _theme-base10_, carefully prepared by a professional designer. The future will bring additional themes and users are invited to develop and contribute their own._theme-base10_ is currently based on a custom build of Pico CSS, a minimal CSS framework, which we further stripped down. It provides sensible defaults for the most important HTML elements. s

To facilitate the modification of frequently customized styling aspects, there are several styling definitions that can be be directly configured in config.json when generating/updating the application, such as the logo, the color palette or the fonts. You can find the full list of configurable settings in the documentation of the [theme-base10 profile](../../jinks/profile/theme-base10). Note that this list is not exhaustive and likely to change over time.

You can overwrite any styling property using the JSON configuration editor in Jinks (or by directly editing the config.json of your generated application. For example, the _serafin_ blueprint changes the styles as follows:

```json
"theme": { "logo": { "width": "48px", "dark": "../images/base_icon_transparent_background.svg", "splash": { "dark": "../images/animation_serafin.gif", "light": "../images/animation_serafin.gif" }, "light": "../images/base_icon_transparent_background.svg" }, "layout": { "search": "before", "before": { "width": "min(30vw, 420px)" } }, "colors": { "palettes": { "dark": "palette-dark.css" }, "palette": "dark" } }
```

This snippet changes the logo, the splash screen, the width of the left sidebar and the color palette. Note that we avoid using color codes, but instead refer to a color palette, which has a balanced selection of matching colors. In the example above, a new palette named dark is added via  `colors.palettes`.

### Adding CSS Stylesheets

While some styling aspects can be customized via configuration only, you will likely encounter cases for which you would like to add your own CSS. We strongly recommend to not change any of the default stylesheets coming from the theme or other profiles. Instead, create your own CSS stylesheet and overwrite the default styles where necessary. Not doing so will very likely result in conflicts during future updates.

You can register additional stylesheets by adding them to the top-level styles property, which is a JSON array. This can be done directly in config.json, or via frontmatter. The latter, adding styles via frontmatter, has the advantage that the styles will only be loaded for this particular view and not pollute the styling of other pages.

For example, the _Documentation + Demo_ profile does this as follows:

```json
<template> ---json { "templating": { "extends": "templates/pages/basic.html" }, "styles": [ "resources/css/documentation.css" ], … } --- … </template>
```

### External stylesheets

ODD specification allows for explicit declaration of an external CSS file which may define styles and CSS classes to be applied to tranformed sources (in ` encodingDesc/tagsDecl/rendition`), e.g. 

```xml
<rendition source="docbook.css"/> 
```

Styles and classes from that file are loaded into `pb-view` component and thus accessible for its content. 

External stylesheets for `pb-view` can also be specified via `load-css` component configuration attribute. In this scenario, unlike with ODD `rendition`, regenerating the ODD is not required for changes to the CSS file to be applied, otherwise both methods are functionally equivalent. 

```html
<pb-view id="view1" src="document1" load-css="odd/docbook.css"/> 
```

Broader discussion of using <rendition> for custom styles can be found in this section. 

# Supported XML vocabularies

TEI Publisher started as a publishing toolbox for TEI but the principles of the TEI Processing Model were never limited to a single vocabulary. Publisher very quickly extended support to other XML formats. Currently TEI, DocBook, JATS and MS Word DOCX are supported out of the box (DOCX via automated conversion to TEI on upload).

Few specificities of _TEI_ and _DocBook_ are listed below, while _DOCX_ is discussed at length in the following section. 

## TEI

In principle, any TEI document will be supported by any TEI Publisher-based app, and can be displayed with any of the pre-configured blueprints and profiles.

Nevertheless, certain assumptions are made about encoding of the basic structure of the TEI documents for the purpose of navigation:

+  page beginnings are encoded with <pb> 

+  column beginnings are encoded with <cb> 

+  structural divisions in the document are encoded with <div> elements 

We acknowledge that TEI offers other ways to encode these features, e.g. generic <milestone> element or specialized numbered division elements like <div1>, <div2>. TEI documents using alternative encodings will be still displayed as specified in the ODD, it is only for the sake of navigation or division-based full text search that we had to assume certain conventions to be able to decide what to show as the next page, column or division. 

We believe our choice represents the most common way of using TEI but, for those who followed the path less travelled, the chapter on  customization briefly discusses how to change relevant functionalities. 

## DocBook

DocBook support is demonstrated by this very document you are now reading,  documentation.xml. It is written in DocBook and presented via dedicated feature, _Docbook support_ which includes as a default ODD  docbook.odd.

## JATS (Journal Publishing Tag Library)

The _Jats support_ feature includes the JATS ODD, which currently supports [NISO JATS Version 1.2](https://jats.nlm.nih.gov/publishing/tag-library/1.2/). It covers many elements commonly used for journal articles in the humanities, and can be easily customized for more specific use cases or encoding flavours.

## MS Word DOCX format conversion

Starting with the version 5.0.0 of the TEI Publisher a new docx handling module is available to allow for ingesting documents in _docx_ format. The goal of this module is to provide a way to import Word documents, preserving their textual content, structure and basic semantics of the text, not to provide an authoritative mapping of complete set of MS Word features to TEI. 

Docx format is relatively flat, thus reconstructing logical document structure like divisions, lists and similar can be only based on certain heuristics. Likewise it is impossible to deduce semantics attributed to certain formatting decisions. For that reason TEI Publisher by intention ignores many style properties — trying to preserve as much as possible would likely just add unnecessary "noise" and result in low-quality TEI. 

### A word about Word

A Word document is essentially a zip archive of several different XML files. These files store various parts – the text content, styles, embedded media files etc. Information most relevant for the import process have been extracted into a map, which is passed as a parameter to the ODD, so it is available for every element. Thus information about numbering styles can be accessed via `$parameters?nstyle(.)` function and testing if a list is bulleted could be done checking the value of ` $parameters?nstyle(.)/numFmt/@w:val`. Full list of available functions and some hints how to customize default conversion ODD are provided at the end of this chapter. 

![](wordStructure.png) 

_MS Word archive structure_

Named tei:* styles 
+  Named styles can be strong indicators for the semantics of the text fragment. Styles whose name starts with `tei:` are thus recognized as TEI elements with the same name. If a character sequence uses a style called `tei:persName`, it will be wrapped into a TEI <persName> element in the output, e.g. `<persName>Johann Wolfgang Goethe</persName>`. A place name should be marked with a style ` tei:placeName` and reconstructed text could be encoded by applying a style ` tei:supplied`. 

Headings and divisions 
+  Since Word does not have a concept for text division, instead storing just flat lists of paragraphs, so the only way to reconstruct the logical structure is to use Word headings and outline level associated with these to determine division boundaries. 

    In the first pass, all paragraph styles starting with `heading`, ` title` or `subtitle` generate a <tei:head> element. The outline level assigned to the heading is recorded as well. 

    Subsequently, in a second pass through the generated output, divisions are generated based on the outline level: a <div> spans all text from the heading to the next heading on the same outline level and the process is repeated for all headings within the division on a lower outline level. 

Lists 
+  Lists structure needs to be reconstructed, very much like divisions, taking into consideration the list level associated with every item which can be accessed via a call to `$parameters?pstyle(.)//outlineLvl/@w:val`. 

Foot- and endnotes 
+  Footnotes are translated into TEI note elements. Endnotes are also supported and transformed into `<note type="endnote">`. 

Tables 
+  Processing of simple tables works very well as well as cells spanning multiple colums. Row spans are not implemented yet. 

Images 
+  Embedded images are stored into a subcollection starting with the name of the docx file being processed and suffixed with `.media`, eg. `<graphic url="test.docx.media/image1.png"/>` 

### ODD for docx

The ODD used for docx processing can be found in [docx.odd](../odd-editor.html?odd=docx.odd). Users are free to extend the default ODD with additional heuristics. For example, a paragraph being entirely bold could also be treated as a heading, or a left text indent may indicate a quote. 

For testing purposes there is a Word document provided in  data/doc/test.docx which includes samples of most important features like headings, lists, tables, notes and embedded images. Try uploading it via upload panel as described in the [upload section](quickstart.xml#upload) and check the conversion results. Behaviour of the conversion mostly follows the approach used in TEI Stylesheets docx-to-tei transformation module and has been tested on test files included there. 

#### Parameter functions

Functions below can be used to retrieve styles or other information related to a current node. For more usage examples see [docx.odd](../odd-editor.html?odd=docx.odd) 

cstyle 
+  phrase level (characters, words or phrases) styles associated with the current node

    Returns: `w:style` 

    Usage example: `$parameters?cstyle(.)/name[starts-with(@w:val, 'tei:')]` 

endnote 
+  content of the endnote

    Returns: `w:endnote/w:p` 

footnote 
+  content of the footnote

    Returns: `w:footnote/w:p` 

link 
+  external link

    Returns: `rel:Relationship` 

    Usage example: `$parameters?link(.)/@Target` 

nstyle 
+  list style information associated with the current node

    Returns: `w:lvl` 

    Usage example: `$parameters?nstyle(.)/numFmt` 

pstyle 
+  paragraph level styles associated with the current node

    Returns: `w:style` 

    Usage example: `$parameters?pstyle(.)/name[matches(@w:val, 'quote';, 'i')]` 

## Adding a custom vocabulary

As discussed in the opening chapter, publishing a corpus of documents online is much more than just transforming a single source document into the desired output format. To fully support a new XML vocabulary in TEI Publisher several aspects need to be adressed:

+  ODD with processing models

+  default view and page template

+  navigation, breadcrumbs and table of contents

+  search and filtering: full text index definitions, facets and fields

ODD and processing models within it govern how the document in your new vocabulary will be transformed into a range of available output formats: HTML, ePub etc.

All other aspects are interconnected and depend on the understanding what constitutes the basic unit of the text: TEI primarily considers _divisions_ or _pages_, DocBook rather _sections_. Therefore navigation for TEI document will be switching between <div> s or reconstructed XML fragments between subsequent <pb> s, while talking about pages in DocBook documents makes no sense and < section> s are the main structural units. 

This structure has its consequences for further aspects: generating TOC in TEI will analyze nested <div> / <head> structures but only <section> / <title> in DocBook. Likewise, KWIC display in TEI will be showing matches in <div> context but <section> in DocBook, so Lucene indexes need to be defined on these elements in their appropriate namespaces. Ditto for facets and fields used for sorting and filtering. 

Sections below will explain where and how to customize these aspects in more detail.

### ODD

Create a new, blank ODD file which is not chained to any other ODD and add < elementSpec> s for its elements. Make sure that you specify correct namespace for your vocabulary. Optionally, you can also specify an external CSS file with style declarations for classes and elements you will be using in processing models of your ODD. 

See how it looks in the ODD for DocBook.

![](new-vocabulary-odd.png) 

_ODD for DocBook_

When adding models into the custom ODD for your vocabulary it is recommended that at least one element applies the document behaviour. Usually it will be the top level element or the main content-bearing one (like <article> in DocBook). 

This is not strictly required but for print output via LaTeX or FO the  document behaviour specifies default prologue governing basic setup for PDF. If you refer to the docbook.odd you will note that the same effect is achieved by explicitly defining the prologue in the <pb:template> for  article. 

#### Case study: foo vocabulary

Let's consider an imaginary vocabulary called _foo_. All documents in this vocabulary will belong to the `http://foo.io` namespace. Simple document could look as follows: 

```xml
<fooStart xmlns="http://foo.io"> <foo>My foo document</foo> <bar>About something very important for foo community.</bar> </fooStart> 
```

Let's save this document in the playground collection as foo.xml. 

Unfortunately a request to retrieve this document in Publisher fails with _the server did not return any content_ error message. 

```
http://localhost:8080/exist/apps/tei-publisher/playground/foo.xml
```

Fixing this will require creating a new ODD for _foo_ vocabulary. 

Create a new ODD as described previously. Use document behaviour for <fooStart> element and perhaps <inline> with an < outputRendition> set to italic for <foo> element. Please note to specify the ODD for your vocabulary. 

Very bare bones ODD file for a fictional _Foo_ vocabulary could look as follows: 

![](foo-odd.png) 

_ODD for Foo_

Or, in XML form:

```xml
<schemaSpec start="fooStart" ident="foo" ns="http://foo.io">  <elementSpec ident="foo" mode="add"> <model behaviour="inline"> <outputRendition>font-style: italic;</outputRendition> </model> </elementSpec> <elementSpec ident="fooStart" mode="add"> <model behaviour="document"/> </elementSpec> </schemaSpec> 
```

With this in place we could reformulate our request to explicitly specify the ODD to use and a single view. 

```
http://localhost:8080/exist/apps/tei-publisher/playground/foo.xml?odd=foo&view=single
```

![](foo-single-view.png) 

_foo.xml rendered with foo.odd in single view_

#### Note

It is necessary to use single view along with the foo.odd. Otherwise, the app default view would be used, which in TEI Publisher is normally set to div. As we already mentioned, implementation of view parameters needs to be vocabulary-specific to work. Since _foo_ vocabulary doesn't yet have navigation customized, TEI Publisher will fall back to TEI and try to locate <tei:div> elements, which obviously cannot be found in our test document in foo namespace. 

### Default view and page template

Rendering of the document is governed by a number of parameters, particularly view, ODD and template : 

Even when these parameters are not explicitly specified, TEI Publisher and apps generated from it, will fall back to the default values specified in  modules/config.xqm. 

+  ODD: `$config:default-view` 

+  view: `$config:default-view` 

+  template: `$config:default-template` 

Alternative way to specify these would be using a processing instruction in the foo.xml document itself. 

```xml
<?teipublisher odd="foo.odd" view="single" template="view.html"?> 
```

### Lucene configuration

eXist-db and TEI Publisher make extensive use of Lucene indexing engine. In particular search, navigation, sorting and filtering heavily depend on full text indexes, facets and fields. It is therefore paramount to specify these correctly for your data collection.

Supporting a new vocabulary, make sure to add its namespace on the <index> element in collection.xconf. 

```xml
<index xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dbk="http://docbook.org/ns/docbook"> 
```

Beyond this minor adjustment, adding a new vocabulary does not differ in creation and use of facets and fields.

### Navigation

We have extensively covered modifications to the ODD and page templates in earlier chapters. In case of vocabularies without out-of-the-box Publisher support it is necessary to customize the navigation as well. Tacit understanding of document's structure is critical for many kinds of user interactions – from browsing through pages to creating the table of contents.

modules/navigation.xql is the main "control room" for all tasks related to navigation. You will note that it imports custom modules for all supported vocabularies: _TEI_, _JATS_ and _DocBook_. All requests are dispatched to specialized modules, depending on the namespace of the document (cf.  config:document-type function). 

```xquery
module namespace nav="http://www.tei-c.org/tei-simple/navigation"; import module namespace tei-nav="http://www.tei-c.org/tei-simple/navigation/tei" at "navigation-tei.xql"; import module namespace jats-nav="http://www.tei-c.org/tei-simple/navigation/jats" at "navigation-jats.xql"; import module namespace docbook-nav="http://www.tei-c.org/tei-simple/navigation/docbook" at "navigation-dbk.xql"; 
```

Customizing yet unsupported vocabulary will require:

+  create a new navigation module for the new vocabulary (e.g.  navigation-foo.xql ; it should implement all the functions that  navigation.xql dispatches to; you can use navigation-tei as a starting point for customization 

+  import it into navigation.xql 

+  adjust config:document-type function 

+  adjust nav:get-root 

### Search

Full text search is realized via the same modular approach that governs navigation. modules/query.xql is the main "control room", dispatching requests to functions in specialized modules. See implementation of query-db.xql or  query-tei.xql before creating a dedicated module for your vocabulary. Make sure to import your module into query.xql. 

# Server-side API

Many of the user interface web components need to communicate with the server to request certain data to be retrieved, processed and returned before the client-side components can display it to the user. For example, when user clicks the Table of Contents button, the corresponding pb-load component sends an ajax request to retrieve the list of chapters. Similarly, when user types something in the autocomplete field, a request is sent after each keystroke to find matching terms in the index. These requests need to be received and processed by the server with the eXist instance running and only then data will be returned to the browser. 

TEI Publisher, and all applications created with it, use a formal API specification [(click this link to see it)](../api.html), based on the [Open API standard](http://spec.openapis.org/oas/v3.0.3), which defines all the server-side API endpoints available in Publisher. The library responsible for interpreting the Open API specification and routing requests to XQuery endpoints is called [roaster](https://github.com/eeditiones/roaster) and can also be used independently.

The image below presents a section of the core TEI Publisher API handling all ODD-related operations: creating, retrieving, updating, deleting, recompiling; syntax check for a code fragment (lint); retrieving a list of all available ODDs.

![](api-spec.png) 

_TEI Publisher API page – the_odd_endpoints_

## Advantages of the API-based approach

+  **clear specification and easy overview** 

    users and developers will benefit from the clear specification for available functionality

+  **customizable** 

    users can easily overwrite and extend existing API endpoints as well as add custom ones for project-specific functionality

+  **separation of concerns** 

    server-side functionality can be used directly by any software system, not necessarily within the context of the TEI Publisher or using Publisher's UI components; further changes in internal API implementation do not require any adjustments of the UI components

+  **standard-based** 

    OAS compliance means we are using a well-known and documented standard and can benefit from existing tools for writing, testing and generating documentation

## Extending the API

Every Jinks profile or custom application may add additional API endpoints on top of the core API. For example, the _IIIF profile_ adds an API endpoint to generate a IIIF manifest from a TEI document, and the _timeline_ profile provides endpoints for retrieving timeline data. Custom applications and blueprints may implement endpoints in the same way.

TEI Publisher allows an arbitrary number of Open API configurations, each defining different endpoints. Simply provide one or more relevant .json and .xql files for your application or profile and register them via the configuration in config.json.

To learn more about the Open API functionality supported by roaster and the different fields and parameters available through the passed in `$request` parameter, see the [README](https://github.com/eeditiones/roaster) of _roaster_.

### Registering custom API endpoints in config.json

To make TEI Publisher aware of your custom Open API specifications, and ensure the endpoints are available, you must register the relevant .json Open API definition files in your config.json profile or application configuration file. Each entry should specify both the path to your Open API specification file (.json) and the implementation module (XQuery route handler).

As a concrete example, see the snippet from profiles/iiif/config.json below. Here, the IIIF profile registers its Open API routes as follows:

```json
{ ... "api": [ { "spec": "iiif-api.json", "prefix": "iiif", "path": "iiif-api.xql", "id": "https://e-editiones.org/api/iiif" } ] ... } 
```

In this example, each object in the `api` array registers an Open API specification with:

id 
+  The namespace URI declared by the XQuery module implementing the endpoints.

prefix 
+  A symbolic name used within the system to refer to the endpoint group, and as namespace prefix for the XQuery functions to be called. If no XQuery module is connected, provide a unique string for the documentation.

spec 
+  The filename of the Open API specification (.json file), relative to the configuration file.

path 
+  The path to the corresponding XQuery implementation that handles the routes defined in the specification.

Sometimes you may just want to add an additional endpoint, but reuse existing XQuery functions already known to the system. In this case, specify spec and prefix only, but leave out path and id. For example, the _markdown_ profile contributes an endpoint listening on route `/markdown/{docid}`, but calls the existing view operation from module profiles/base10/modules/lib/api/view.xql, only changing the parameters passed to the function by setting a different default for the HTML template to be used. Reusing existing library functions to define custom routes is a common pattern, which we will cover in more detail in the next section.

Open API specifications will be evaluated in the order in which they are given in the configuration. Therefore, if two specifications define an endpoint for the same route, the second route will win. This way you can overwrite how a route is handled.

# URL Routing

## Creating custom URLs

By default, TEI Publisher addresses the different example pages by the name of the XML document displayed, which means users will see the relative path to the document in the URL. A request to `/test/adagia.xml` will be handled by the Open API route matching the path `"/{docid}"`, which then calls the XQuery function vapi:view to retrieve the HTML template associated with the TEI document and return it to the browser:

```javascript
"/{docid}": { "get": { "summary": "Retrieve the HTML template used for displaying a document", "description": "Get the HTML template associated with the given document. This is called whenever the users tries to view a document. The actual content of the document will then be loaded by the template.", "tags": ["view"], "operationId": "vapi:view", "x-error-handler": "vapi:handle-error", ... } } 
```

This default can be easily changed to _addressing by id_ in Jinks configuration. Nevertheless  some editions may prefer other addressing schemes, e.g. using a virtual collection and identifier. For example, the Alfred Escher Briefedition uses URLs like [https://briefedition.alfred-escher.ch/briefe/B0017](https://briefedition.alfred-escher.ch/briefe/B0017). Using an abstract identifier has the advantage that bookmarked URLs remain accessible even if the implementation changes (as happened for Escher), given that the identifier is stable. An even more complex addressing scheme can be found in the [Johann Conrad Fischer](https://www.johannconradfischer.com) edition. Here the different travel journals are addressed by the year they cover, and the chapter to show is given as a number. Additionally, the language is given in the first path parameter, so the resulting URLs have the form: `/language/year/chapter`, e.g. `/de/1794/9`. Obviously implementing a scheme like this requires a bit more tweaking, so we'll concentrate on simpler cases like the one in Escher. 

The _TEI Publisher Documentation and Demo_ application includes one example, which deviates from the default addressing scheme: the [Wörterbuch der Philosophischen Grundbegriffe](../encyclopedia) represents an early 19th century encyclopedia in German (encoded in [TEI-Lex0](https://dariah-eric.github.io/lexicalresources/pages/TEILex0/TEILex0.html), a customization of TEI targeted at dictionaries and encyclopedias). An encyclopedia or dictionary is usually not read page by page. Instead users will want to browse to a particular headword they are interested in. Accordingly, the URL should reflect the chosen headword rather than the document. URLs are thus of the form `/encyclopedia/{headword}`.

This example needs two route definitions: the first corresponding to the root page displayed, i.e. if the user has not selected a headword, the second to represent the detail view of a headword. The Open API specification, [modules/docs-api.json](modules/docs-api.json), thus has configurations for `/encyclopedia` as well as `/encyclopedia/{headword}`. In both cases we simply copied and modified the configuration for the standard path, i.e. `"/{docid}"`, from [modules/lib/api.json](modules/lib/api.json).

The default endpoint takes various parameter, including two required ones: docid for the path to the TEI document, and file to specify the HTML template to use. For the encyclopedia those will always be the same, i.e. demo/Kirchner-Michaelis-1907.xml and pages/tei-lex, so we can pass them as a default value. This way we can reuse the existing operation (vapi:view) and don't have to write a custom handler function in XQuery!

The complete definition of the route for a headword in [modules/docs-api.json](modules/docs-api.json) looks like this:

```javascript
"/encyclopedia/{search}": { "get": { "summary": "Show encyclopedia entry matching {search}", "description": "Search endpoint used for the encyclopedia example (Damen Conversations Lexikon)", "operationId": "vapi:view", "x-error-handler": "vapi:handle-error", "tags": ["encyclopedia"], "parameters": [ { "name": "file", "in": "query", "schema": { "type": "string", "default": "pages/tei-lex" } }, { "name": "docid", "in": "query", "description": "Relative path to the document", "required": true, "schema": { "type": "string", "default": "demo/Kirchner-Michaelis-1907.xml" }, "allowReserved": true }, { "name": "search", "in": "path", "description": "headword query", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "HTML of the page", "content": { "text/html": { "schema": { "type": "string" } } } }, "404": { "description": "The document was not found", "content": { "text/html": { "schema": { "type": "string" } } } } } } }
```

This handles the server-side routing of requests. However, there's also a client-side aspect, which we'll cover in the next chapter.

## Adjusting the client-side URL handling

With the two routes we set up for the encyclopedia in the previous section, we cover the cases in which the entire page is loaded or reloaded by the browser, either by following a link, selecting a bookmark or pasting an URL into the location bar. However, TEI Publisher is a highly dynamic application and users can interact with the page without reloading it, e.g. by navigating to the next page of the document, selecting a toggle, clickling a link in the table of contents etc. Client-side interactions like this also need to be recorded and should be reflected in the URL, so users can copy or bookmark it. And the bookmarked link should later reconstitute the page in the state it was in when the bookmark was created.

All client-side web components in TEI Publisher therefore use a central state registry to track their state and reflect it in the URL. You can see this in action when you open any of the larger sample documents and navigate between its pages using the navigation buttons: the URL shown in the browser's location bar will change. For simple documents like [Kant's Kritik](../test/kant_rvernunft_1781.TEI-P5.xml), the current page displayed will be encoded in the root parameter. If the divisions or pages to navigate by have an `xml:id`, this will be used instead and placed into the id parameter. The path to the TEI document is also tracked (it's the `test/kant_rvernunft_1781.TEI-P5.xml` part of the URL), though you won't see it change while navigating.

For the encyclopedia example we deviate from this default pattern with the relative URL becoming `encyclopedia/{headword}`. We thus need to inform the client-side components about the change, so they know how to store their state into the URL. The Jinks configuration schema provides a property `urls` for this, which has two subproperties:

`template` 
+  A template expression containing placeholders for the parameters to be filled in. The template uses the same syntax and library as the popular [Express javascript framework](https://expressjs.com/en/guide/routing.html#route-paths).

`ignore` 
+  A comma-separated list of parameter names which should _not_ be reflected in the URL.

The expression passed to url-template may reference any of the default parameters known to TEI Publisher components, in particular path, odd, view or id. Additionally, any parameter supplied to a <pb-view> via the <pb-param> element can be referenced. Just have a look at the constructed URLs in the browser to see which parameters are being tracked for a specific page. Within TEI Publisher, URL templates will usually be rather simple. For the full syntax supported check the underlying [javascript library](https://www.npmjs.com/package/path-to-regexp).

To handle the URL for the encyclopedia example, we set the URL template to `encyclopedia/:search?`. This injects the search parameter as part of the path, e.g. `/encyclopedia/Egoismus`. The ? after the parameter indicates that it is optional. We also do not want the `path` to the document to appear anywhere (it is always the same in the case), so we add it to `ignore`. The resulting configuration snippet would thus look as follows:

```json
"urls": { "template": "encyclopedia/:search?", "ignore": "path,odd,view,userParams" }
```

and it is added to the frontmatter of the corresponding HTML template, [tei-lex.html](templates/pages/tei-lex.html).

#### A note on history handling

It is important that only one central text component on the page writes and reads the URL. Usually this will be the main <pb-view>. Other components, e.g. for the translation, should be made dependent and restore their state based on the state of the main component. If multiple components write their state, browsing back via the browser history may not work properly, or you may see undesired effects due to conflicting parameters.

TEI Publisher supports different ways to make one <pb-view> depend on another, e.g. via a mapping function (map attribute). A mapped view will never write to the browser history.

In other cases, to explicitely prevent a dependent <pb-view> from writing to the URL, use the attribute disable-history. To make a <pb-view> load its contents _after_ the main text view, set attribute on-update and make sure, the dependent component listens to the event channel written by the main component.

# Language versions

TEI Publisher is already available in twenty languages and this number increases thanks to the engagement of our user community.

With version 6.0 i18n support has been greatly extended to cover not only labels and attribute values in HTML templates but also within web components. A mechanism for project specific language files extending the default Publisher label collection has been added.

Thanks to community contributions via [ Crowdin](https://crwd.in/tei-publisher) a number of languages has been added and existing ones are updated when needed. We welcome and encourage additions and amendments. Please consider Crowdin a master repository for translations and publish your contributions there instead of submitting PR directly. Do not hesitate to get in touch if a language you'd like to support is not yet listed. 

Crowdin users are only exposed to form-based graphical user interface but i18n files are using JSON format to preserve their logical structure. Initial portion of German localization file de.json is shown below for illustration. 

![](i18n-json-structure.png) 

_Translation JSON file structure_

In TEI Publisher and in generated apps translation files are by default loaded from CDN with `pb-components`. When it is necessary for a project to add new labels or change wording of existing ones, a customization mechanism is described below. 

## Using i18n in page templates

There are several scenarios for using i18n labels:

+  directly within HTML element

+  in an attribute

+  passed in component-specific structure

###  HTML text node with `pb-i18n` 

Any text fragment within HTML element can be considered a target for i18n when wrapped in <pb-i18n> component. <pb-i18n> elements are listening for events emitted by <pb-lang>. 

```html
<h3 slot="collapse-trigger"> <pb-i18n key="menu.documentation">Documentation</pb-i18n> </h3> 
```

### Attributes (on HTML elements or custom web components)

When it is necessary to translate the value of an attribute a mechanism based on ` data-` attributes is used. Supply additional `data-i18n` attribute specifying the key of the i18n label to use preceded by the name of the attribute that needs to be translated. In the example below it's the `heading` attribute that needs to be filled with the translated version of _ODD Files_ label. The label is stored in JSON file under `odd.files`, so ` [heading]odd.files` needs to be used for the `data-i18n` attribute. 

```xml
<paper-card data-i18n="[heading]odd.files" class="odds" heading="ODD Files"> <div class="card-content"> ... content of the ODD list </div> </paper-card> 
```

When <pb-lang> is switched to Spanish, the <paper-card> heading will read _Archivos ODD_ instead of _ODD Files_. 

![](i18n-attribute.png) 

_Output for <paper-card> with translated `@heading`_

### Special web component properties configured via attributes

Some web components accept more complex configuration options via arrays passed in attributes. For example, <pb-browse-docs> component allows to specify a number of label/value pairs for dropdown menus used for ordering and filtering the document list. Understandably, the labels should switch in line with changes to the language chosen via < pb-lang>. Therefore i18n translation keys like _browse.title_ or _browse.author_ are used instead of text values. Note that web components may have particular expectations for the data format expected so consult API documentation for each component. 

```html
<pb-browse-docs id="document-list" url="collection/"  sort-options='[{"label": "browse.title", "value": "title"},{"label": "browse.author", "value": "author"}]'  sort-by="title" filter-options='[{"label": "browse.title", "value": "title"},{"label": "browse.author", "value": "author"},{"label": "browse.file", "value": "file"}]'  filter-by="title"  auto="auto" history="history" login="login" emit="docs" subscribe="docs"> <pb-paginate slot="toolbar" id="paginate" per-page="10" range="5" emit="docs" subscribe="docs"></pb-paginate> </pb-browse-docs> 
```

## Project specific i18n files

It is a common need that projects will need their own internationalized labels for menu items, dialogs and other user interface elements.

All these can be stored in JSON language files, following the same naming conventions and basic structure as TEI Publisher ones. To use custom language files, you need to specify the path under which they can be found relative to your application:

```html
<pb-page locales="resources/i18n/{{ns}}/{{lng}}.json">
```

The path expression requires placeholders for two parameters ns and lng : 

lng 
+  the language code for a selected language, e.g. `de` or `fr` 

ns 
+  the namespace prefix used to distinguish between different collections of language files. By default, TEI Publisher expects custom language files to be in a namespace called `app`, though this can be configured. 

So given above configuration, TEI Publisher will search for a custom language file for, say, French in `resources/i18n/app/fr.json`. If you prefer a flat directory structure, you could change the locale to ` resources/i18n/{{ns}}_{{lng}}.json` and TEI Publisher will look for a file ` resources/i18n/app_fr.json`. 

You may also define additional namespaces to be searched with the  locale-fallback-ns parameter: 

```html
<pb-page locales="resources/i18n/{{ns}}/{{lng}}.json" locale-fallback-ns="app my-module">
```

which means that TEI Publisher will search for labels in the `my-module` namespace first, then falling back to the `app` namespace, and if the label could still not be found, using TEI Publisher's default namespace. The latter is called ` common` and should not be overwritten. 

Listing below demonstrates a fictional en.js with a custom set of labels. A new top-level key has been added as well as 3 subkeys for the `menu` section. Now the project has access to a number of new i18n keys: `menu.about`, ` menu.contact`, `menu.statute` and `greeting`. 

Furthermore, a value for `menu.documentation` has been specified. That key already existed in Publisher (set to "Documentation") but the version from custom file will take precedence and be used in the custom app. 

```js
{ "menu": { "documentation": "Docu", "about": "About", "contact": "Contact", "statute": "Statute" }, "greeting": "Welcome" } 
```

# Search

TEI Publisher (as eXist-db) make extensive use of the _Lucene_ indexing engine to implement filtering and ordering of data. In particular, all search-related features heavily depend on full text, facets and fields of the Lucene index. It is therefore paramount to specify these correctly for your data collection.

In the context of the TEI Publisher application, when searching, we are applying certain criteria to the collection of all available entries. As a result, we select only entries matching the filtering criteria. By entries we usually mean elements from the `data-default` collection, typically represented by individual TEI files, less often _virtual documents_. Either way, the data collection needs an index configuration, matching the kinds of queries we expect to run.

From the eXist database perspective there are 3 types of query criteria, which can be applied in conjunction:

_full text_ queries 
+  

    matching the text content of the entry, for which the full text index is defined; therefore, if we want to search in the entire TEI document (both metadata and transcription), we need an index on `TEI`, if only the transcription part, on `TEI/text`

    full text queries can match entire words exactly, but we may also use wildcard characters (`?` and `*`) to search for patterns, e.g. `ben*` would match anything starting with _ben_, like _benefactor_, _benevolent_ or the name _Ben_

_field_ queries 
+  similar to the full text query, but searching only through the predefined field, which can be defined as necessary; for example, we can associate a field called `abstract` with a `TEI/text` node and define it as `<field name="abstract" expression="ancestor::tei:TEI//tei:abstract"/> `

    Please note that the field is always defined in relation to the node, for which the index is defined

facet queries 
+  like a fields, a facet can be defined with an arbitrary content, which will be attached to the indexed parent node; main difference is it can be only used for exact checks: only if the indexed node has the exact facet value associated, it will be matched by the query

Illustration above shows user interface elements related to all query types. Facets are selected with checkboxes, while full-text and field queries are input in the Search box at the top of the sidebar. The Query scope allows users to choose between default full-text search (here called just _content_, and more constrained field searches.

![](search-options.jpg) 

_Search options: full-text, fields and facets_

## Index configuration

The collection.xconf tells eXist database how to index the collection. The default configuration in TEI Publisher creates several Lucene full-text indexes, the most  important one for TEI associated with <tei:text>. It has a number of fields and facets attached. 

Every field and facet must have an expression attribute. The expression is an arbitrary XPath/XQuery expression. For every element being indexed, the expression is evaluated once and the result defines the values which will be associated with the indexed elements. 

Apart from expression, fields require a name,  while facets need a dimension.

For a detailed description of how full-text indexes and facets are defined in the  collection.xconf, please refer to the [eXist documentation](http://exist-db.org/exist/apps/doc/lucene). 

If you open the default collection.xconf, you'll see that most facet or field expressions call a function nav:get-metadata. This function is declared in index.xql helper file. By externalizing most of the code into a separate function, we can keep the index configuration clean and short. 

The default index.xql already does some advanced preprocessing, for example for the "genre" facet: each of the sample documents references a central taxonomy (contained in data/taxonomy.xml). The references are resolved at indexing time by following the <catRef> element's @target attribute. Note that we create a hierarchical facet, because e.g. "Philosophy" is a sub-category of "Prose". The code in function  idx:get-genre will automatically include the super-category. 

Example below shows an excerpt from TEI Publisher standard index for the `TEI/text` nodes.  Please note that sometimes both fields and facets are defined under the same field name as the facet dimension.  They may, but don't necessary need to be computed in the same way, for example the date field holds a string value,  while the facet dimension is a sequence of year-month-day items, which is used for the hierarchical facet.

```xml
<text match="/tei:TEI/tei:text"> <field name="language" expression="nav:get-metadata(ancestor::tei:TEI, 'language')"/> <field name="person" expression="nav:get-metadata(ancestor::tei:TEI, 'person')"/> <field name="date" expression="nav:get-metadata(ancestor::tei:TEI, 'date')"/> <field name="file" expression="util:document-name(.)"/> <field name="text" expression="."/> <facet dimension="genre" expression="nav:get-metadata(ancestor::tei:TEI, 'genre')" hierarchical="yes"/> <facet dimension="language" expression="nav:get-metadata(ancestor::tei:TEI, 'language')"/> <facet dimension="person" expression="nav:get-metadata(ancestor::tei:TEI, 'person')"/> <facet dimension="date" expression="nav:get-metadata(ancestor::tei:TEI, 'date') => tokenize('-')" hierarchical="yes"/> </text> 
```

## Facets

Facets allow users to quickly navigate through a set of documents or query results by selecting from predefined categories or properties. This way, users can "drill down" into the set, reducing the number of displayed items with every step. TEI Publisher provides default index configuration which can be extended to match custom-defined facets.

From a user perspective, the main concept behind facets is the _drill down_ : initially the user sees all facet values associated with the set of search results displayed. The number behind each value denotes the number of items in the set, matching this particular facet. As the user selects a facet value, the set necessarily becomes smaller, as the non-matching facet values disappear and the numbers adjust accordingly. 

Facets are super fast because eXist will create them when indexing the document. No extra computation is needed when the user clicks on a facet to drill down into a displayed set: all information is already available in the index. To see an example of facets in action, just go to the [Browse](/exist/apps/tei-publisher/browse.html) page of Documentation and Demo. 

If you would like to configure additional facets, you need to prepare relevant indexes as described in  index configuration chapter and additionally edit the `facets-config.xqm`

While the index configuration is responsible for the server-side creation of facets, we need a place to define how facets should be displayed in the user interface. The `facets-config` configuration module: config.xqm declares a variable  $config:facets. It should contain an array of maps, where each map defines the settings for one dimension, e.g.: 

```xquery
(: : Display configuration for facets to be shown in the sidebar. The facets themselves : are configured in the index configuration, collection.xconf. :) declare variable $facets-config:facets := [ map { "dimension": "genre", "heading": "Genre", "max": 5, "hierarchical": true() }, map { "dimension": "language", "heading": "Language", "max": 5, "hierarchical": false(), "output": function($label) { switch($label) case "de" return "German" case "es" return "Spanish" case "la" return "Latin" case "fr" return "French" case "en" return "English" default return $label } }, map { "dimension": "feature", "heading": "facets.feature", "source": "api/search/facets/feature", "max": 5, "hierarchical": false() }, ]; 
```

The map properties are as follows:

dimension 
+  The name of the dimension. Should correspond to the value of the  @dimension attribute used in collection.xconf 

heading 
+  The heading to display above the facet values

max 
+  Maximum number of facet values to be displayed initially. More can be shown if the user clicks on the Show All checkbox. Pass an empty sequence, i.e. (), to not limit the number and hide the checkbox. 

hierarchical 
+  Defines if the facet is hierarchical, which means that only the top-level facet values in the hierarchy will be shown initially. If the user selects one top-level value, the interface will expand and show the sub-categories. For this to work the facet must be configured as "hierarchical" in  collection.xconf 

output 
+  A function which can be used to process the facet value before display. The facet label will be replaced by whatever the function returns.

    The function may accept a single (taking the current facet value), or two parameters (current facet value and selected UI language).

source 
+  In addition to checkboxes, an autocomplete _combo box_ can be displayed into which users can type to search for and select a facet. This is in particular useful for long lists of possible facet values. The `source` property should point to an API endpoint from which the current list of matching facets is being retrieved. TEI Publisher provides a default implementation of this API function in profiles/base10/modules/lib/api/search.xql, `sapi:list-facets`.

    If an `output` function is provided, facet labels are passed to it before display. Note that the combo box will not work with hierarchical facets.

# Hosting

Regardless of the installation type, TEI Publisher always runs on an [eXist-db database](https://exist-db.org) and there are a few things to keep in mind for a production system, i.e. one that is publicly available to users. We strongly suggest following the recommendations outlined in the [Production Use – Good Practice](https://exist-db.org/exist/apps/doc/production_good_practice) of the eXist-db documentation. One important point is to always run eXist-db in production behind a proxy server, which serves as an intermediary to control and protect access to a server on which the database runs.

## Running behind a proxy

To set up a proxy, we suggest looking at the docker compose configuration we provide. It includes [configuration templates](https://github.com/eeditiones/teipublisher-docker-compose/tree/master/conf) for the nginx proxy. More general information about proxy servers and eXist-db can be found in the article [Proxying eXist-db behind a Web Server](https://exist-db.org/exist/apps/doc/production_web_proxying).

What does a proxy do for you? It can:

1.  handle domains and protect parts of the database you don't want external users to access. For example, you can access the Escher edition using its [public URL](https://www.briefedition.alfred-escher.ch/) and you will directly land on the home page of the edition. What you can't see is that the Escher is just one out of several editions hosted within the same eXist database. It is the proxy's job to direct you to the correct application matching the public URL, so it appears to you as if there were only one edition. Other editions in the same database are hidden and effectively protected.

2.  enforce a secured connection: communication between the users browser and the proxy will be secured by HTTPS. And because all traffic is routed through the proxy, it is safe to keep the eXist database running on HTTP.

3.  cache specific pages, so TEI Publisher does not need to transform TEI to HTML every time

In the following sections we'll discuss specific settings you should pay attention to when running TEI Publisher behind a proxy.

### Context paths

If you would like the proxy to directly route traffic for a public URL to a specific application within eXist, you also need to make sure that all internal application links are correctly set, depending on the context in which the app operates. The link to "Start" (i.e. the home page of the edition) in the menu would be an example. While you are developing the app, you want this link to point to `/exist/apps/my-edition/` (we call this the _context path_). When deploying the app to production (i.e. behind a proxy), this link should change and just point to the root of the host. The context path should therefore be empty.

TEI Publisher based apps allow developers to control this behaviour via a special variable in `modules/config.xqm` called `$config:context-path`.

The default setting for `$config:context-path` is determined as follows:

1.  if a Java system property called `teipublisher.context-path` is set, use its value as context path

2.  otherwise look at the HTTP request header `X-Forwarded-Host` and if present, assume that we're running behind a proxy and the context path should be empty

3.  finally, try to determine the context path by checking the install location of the application within eXist. This will lead to a context path like `/exist/apps/my-edition/`

This procedure should work for most cases, so manually adjusting `$config:context-path` should rarely be necessary.

### Caching

While you can use a proxy to cache arbitrary contents based on timeouts and cache expiration, TEI Publisher 8 includes some support for a more intelligent caching approach: if enabled, TEI Publisher will answer to the `If-Modified-Since` HTTP header sent by the proxy. If any of the resources targetted by the request have been modified since the given timestamp, TEI Publisher will process the request as usual. However, if it finds that no modifications were applied in the meantime, it simply returns a 304 response code, telling the proxy that it can safely use its cached version.

To enable this feature, set the variable `$config:enable-proxy-caching` to true.

At the time of writing, only the most important API calls in TEI Publisher support `If-Modified-Since`. Those include the API call for displaying a single page or division, which is the most frequently called operation.

## Hosting Options

Where and how to host a custom edition built with TEI Publisher is one of the most frequent questions we receive. Fortunately there are various options and we're doing our best to help academic users by providing various tools to simplify the task, as well as cooperating with providers.

Some options are:

dedicated server 
+  means a physical or virtual machine on which eXist-db, TEI Publisher and a proxy server can be installed and configured. Many academic providers and research infrastructures (e.g. Huma-Num) routinely support dedicated eXist-db and TEI Publisher installations while others can do so upon request.

dockerized installation 
+  we provide a ready-to-use configuration including TEI Publisher, a proxy and the named entity recognition service in the [teipublisher-docker-compose](https://github.com/eeditiones/teipublisher-docker-compose) Github repository. The readme describes how to customize this configuration for your own custom edition.

    To use this option you need to find a hosting provider, which supports running a dockerized architecture with _docker compose_ (just search for a virtual cloud server with docker preinstalled). Fortunately this is quite common and - unless you intend to run a heavy duty service – the costs are very acceptable. With this option, rebuilding the entire setup is quick and easy, which makes it the perfect solution for sites still being under development. On the downside, performance may not be as good as with a dedicated server, but it is hard to give any reliable figures. You'll have to see yourself. 

static site 
+  the newest kid on the block: instead of running TEI Publisher, eXist etc., you can transform your custom edition into a static site. This means that the entire application is converted to a pregenerated set of static HTML pages and helper files. The generated files can be hosted on any web server, including free services like GitHub Pages. It is a particularly fitting solution for smaller editions without extensive search features. The article [Generating a high voltage site by going static](https://www.e-editiones.org/posts/community-event-going-static/) describes how such a static site can be generated and discusses in detail the advantages and drawbacks of this solution.

all-inclusive hosting options 
+  in cooperation with e-editiones, [Archives Online](https://www.archives-online.org/) provides a complete hosting solution for scholarly editions based on TEI Publisher and IIIF on [sources-online.org](https://sources-online.org/). This covers long-term maintenance and support of an edition project. If you are interested in having an edition hosted, please contact [Archives Online](info@archives-online.org).

