XSLT Explorer: docbook.xsl

7 imports, 42 includes, 776 templates, 232 functions, 101 variables, 214 params, 2 FIXME: comments

List of Imports
List of Templates
tp:orderedlist-properties
tp:process-list
tp:apply-localization-template
tp:format-number
tp:group-or-arg
t:inline
tp:compute-lines
tp:filter-callouts
tp:verbatim-array
t:generate-index
t:index-zone-reference
tp:indexed-section
t:mediaobject-img
tp:viewport
t:table-footnotes
tp:resolve-persistent-toc-uris
t:chunk-cleanup
t:chunk-output
t:docbook
tp:cals-colspec
tp:cell
tp:list-of-titles
tp:toc
tp:tocentry-link
tp:link
tp:xref
t:person-name
t:person-name-family-given
t:person-name-first-last
t:person-name-last-first
t:person-name-list
t:glossary-divisions
t:biblioentry
tp:out-of-line-xlink
tp:simple-xlink
t:xlink
t:bottom-nav
t:chunk-footnotes
t:top-nav
List of Functions
fp:estimated-term-length()
fp:select-vert-members()
f:l10n-token()
f:l10n-token()
fp:l10n-token()
fp:minified-css()
f:attributes()
f:attributes()
f:conditional-orientation-class()
f:date-format()
f:generate-id()
f:generate-id()
f:gentext-letters()
f:gentext-letters-for-language()
f:global-syntax-highlighter()
f:href()
f:id()
f:is-true()
f:l10n-language()
f:label-separator()
f:orderedlist-item-number()
f:orderedlist-item-numeration()
f:orderedlist-startingnumber()
f:orientation-class()
fp:css-properties()
fp:lookup-string()
fp:parse-key-value-pairs()
fp:parse-key-value-pairs()
fp:properties()
fp:replace-element()
fp:replace-element()
fp:separator()
f:refsection()
f:relative-path()
f:section()
f:section-depth()
f:spaces()
f:step-number()
f:step-numeration()
f:target()
f:tokenize-on-char()
f:translate-attribute()
f:unique-id()
f:uri-scheme()
fp:common-attributes()
fp:common-attributes()
fp:nearest-relevant-ancestor()
fp:title-properties()
fp:title-properties-override()
f:absolute-length()
f:empty-length()
f:equal-lengths()
f:is-empty-length()
f:length-string()
f:length-units()
f:make-length()
f:make-length()
f:make-length()
f:parse-length()
f:relative-length()
fp:in-verbatim()
fp:array-append()
fp:array-pad()
fp:balance-line()
fp:balance-markup()
fp:balance-markup()
fp:contains()
fp:following()
fp:inject()
fp:inject-array()
fp:inject-into-chars()
fp:inject-into-line()
fp:injection-array()
fp:line-number()
fp:line-to-chars()
fp:make-lines()
fp:make-lines-array()
fp:open()
fp:unflatten()
fp:unflatten()
fp:unflatten-line()
fp:unflatten-line()
fp:up-to()
fp:validate-injection-array()
fp:verbatim-properties()
fp:verbatim-syntax-highlight()
fp:vpi()
fp:vpi()
f:verbatim-callout()
f:verbatim-numbered()
f:verbatim-style()
f:verbatim-syntax-highlighter()
f:verbatim-trim-leading()
f:verbatim-trim-trailing()
fp:fix-text()
fp:iso690()
fp:optional-sep()
f:available-bibliographies()
f:available-bibliographies()
f:available-glossaries()
f:available-glossaries()
f:biblioentries()
f:biblioentries()
f:citations()
f:citations()
f:glossentries()
f:glossentries()
f:glossrefs()
f:glossrefs()
f:pi()
f:pi()
f:pi-attributes()
fp:available-bibliographies()
fp:available-glossaries()
fp:baseform()
fp:pi-attributes()
fp:pi-from-list()
fp:pi-pi-attributes()
fp:group-index()
fp:group-label()
fp:nearest-section()
fp:nearest-section-id()
fp:primary()
fp:scope()
fp:secondary()
fp:tertiary()
f:chunk()
f:chunk-filename()
fp:chunk-exclude()
fp:chunk-include()
fp:chunk-navigation()
fp:matches-expr()
fp:root-base-uri()
fp:trim-common-parts()
fp:trim-common-prefix()
f:css-length()
f:css-property()
f:mediaobject-amend-uri()
f:mediaobject-input-base-uri()
f:mediaobject-type()
f:mediaobject-viewport()
f:object-align()
f:object-contentheight()
f:object-contentwidth()
f:object-height()
f:object-properties()
f:object-properties()
f:object-scale()
f:object-scalefit()
f:object-valign()
f:object-width()
f:in-scope-language()
f:languages()
fp:localization()
fp:localization-list()
fp:localization-template()
fp:lookup-localization-list()
fp:lookup-localization-template()
fp:footnote-mark()
fp:footnote-number()
fp:resolve-persistent-toc()
fp:resolve-persistent-toc-prefix()
fp:run-transforms()
fp:run-transforms()
f:cals-colsep()
f:cals-rowsep()
fp:align-char-pad()
fcals:align()
fcals:align-colspec()
fcals:align-spanspec()
fcals:cell()
fcals:cell-decoration()
fcals:cell-overhang()
fcals:char()
fcals:char-colspec()
fcals:char-spanspec()
fcals:colsep()
fcals:colsep-colspec()
fcals:colsep-spanspec()
fcals:colspan()
fcals:colspec()
fcals:colspec-column-number()
fcals:colspec-for-column()
fcals:column-number()
fcals:decrement-overhang()
fcals:empty-cell-colsep()
fcals:empty-cell-rowsep()
fcals:next-empty-cell()
fcals:overhang()
fcals:overhang-into-row()
fcals:rowsep()
fcals:rowsep-colspec()
fcals:rowsep-spanspec()
fcals:rowspan()
fcals:spanspec()
fcals:table-columns()
fcals:tgroup()
fcals:valign()
fcals:zeros()
fp:colspec-for-colnum()
fp:only-initial-pis()
fp:number()
fp:localization-template-from-xrefstyle()
fp:collapse-years()
fp:collapse-years()
fp:syntax-highlight()
fp:syntax-highlight()
f:syntax-highlight()
f:syntax-highlight()
f:syntax-highlight()
fp:construct-templates()
fp:pick-template()
f:template()
fp:find-xlink-nodes()
fp:pmuj()
fp:pmuj-enabled()
fp:xlink-sources()
fp:xlink-targets()
fp:xlink-xmlns-scheme()
fp:xlink-xpath-scheme()
f:xlink-style()
f:xpointer-idref()
f:chunk-title()
fp:chunk-output-filename()
fp:footnote-mark()
fp:footnote-number()
fp:navigable()
fp:relative-link()
fp:relative-uri()
fp:root-base-uri()
fp:trim-common-parts()
fp:trim-common-prefix()

docbook.xsl

1 import

main.xsl

5 imports, 41 includes

param.xsl

2 variables (2 used only in one other module), 213 params (1 unused)

Instructions
Param $additional-languages
Param $align-char-default as xs:string
Param $align-char-pad
Param $align-char-width
Param $allow-eval as xs:string
Param $annotate-toc
Param $annotation-collection as xs:string
Param $annotation-mark
Param $annotation-placement
Param $annotation-style
Param $annotations-js
Param $auto-toc as xs:string
Param $bibliography-collection as xs:string
Param $bibliography-style as xs:string
Param $callout-default-column
Param $chunk-exclude as xs:string*
Param $chunk-include as xs:string*
Param $chunk-nav as xs:string
Param $chunk-nav-js as xs:string
Param $chunk-output-base-uri as xs:string
Param $chunk-renumber-footnotes
Param $chunk-section-depth
Param $classsynopsis-indent
Param $component-numbers as xs:string
Param $component-numbers-inherit as xs:string
Param $control-js as xs:string
Param $copyright-collapse-years
Param $copyright-year-range-separator
Param $copyright-year-separator
Param $dc-metadata as xs:string
Param $debug as xs:string [static]
Param $default-float-style
Param $default-length-magnitude
Param $default-length-unit
Param $personal-name-style
Param $default-theme as xs:string
Param $division-numbers as xs:string
Param $division-numbers-inherit as xs:string
Param $docbook-transclusion
Param $dynamic-profile-error
Param $dynamic-profiles as xs:string
Param $experimental-pmuj
Param $formal-object-title-placement
Param $mediaobject-details-placement
Param $formalgroup-nested-object-title-placement
Param $funcsynopsis-default-style
Param $funcsynopsis-table-threshold
Param $funcsynopsis-trailing-punctuation
Param $generate-html-page as xs:string
Param $generate-index
Param $generate-nested-toc as xs:string
Param $generate-toc as xs:string
Param $generate-trivial-toc as xs:string
Param $generated-id-root
Param $generated-id-sep
Param $generator-metadata as xs:string
Param $glossary-collection as xs:string
Param $glossary-sort-entries
Param $glossary-automatic-divisions
Param $image-ignore-scaling as xs:boolean
Param $image-nominal-height
Param $image-nominal-width
Param $image-property-warning
Param $index-on-type
Param $index-on-role
Param $index-show-entries
Param $indexed-section-groups
Param $lists-of-equations as xs:string
Param $lists-of-examples as xs:string
Param $lists-of-figures as xs:string
Param $lists-of-procedures as xs:string
Param $lists-of-tables as xs:string
Param $local-conventions as xs:string?
Param $mathml-js
Param $mediaobject-accessibility as xs:string
Param $mediaobject-exclude-extensions as xs:string
Param $mediaobject-input-base-uri as xs:string?
Param $mediaobject-grouped-by-type as xs:string
Param $mediaobject-output-base-uri as xs:string?
Param $mediaobject-output-paths as xs:string
Param $mediaobject-video-element as xs:string
Param $number-single-appendix
Param $orderedlist-item-numeration
Param $othername-in-middle
Param $output-media
Unused
Param $oxy-markup
Param $pagetoc-elements
Param $pagetoc-dynamic
Param $pagetoc-js
Param $page-style as xs:string
Param $paper-size as xs:string?
Param $persistent-toc-css
Param $persistent-toc-filename as xs:string?
Param $persistent-toc-js
Param $pixels-per-inch
Param $procedure-step-numeration
Param $productionset-lhs-rhs-separator
Param $profile-arch
Param $profile-audience
Param $profile-condition
Param $profile-conformance
Param $profile-lang
Param $profile-os
Param $profile-outputformat
Param $profile-revision
Param $profile-revisionflag
Param $profile-role
Param $profile-security
Param $profile-separator
Param $profile-userlevel
Param $profile-vendor
Param $profile-wordsize
Param $theme-picker as xs:string
Param $transclusion-id-fixup as xs:string
Param $transclusion-prefix-separator as xs:string
Param $transclusion-suffix as xs:string
Param $qandadiv-default-toc
Param $relax-ng-grammar as xs:string?
Param $refentry-generate-name
Param $refentry-generate-title
Param $revhistory-style
Param $section-numbers as xs:string
Param $section-numbers-inherit
Param $section-toc-depth
Param $segmentedlist-style
Param $show-remarks
Param $sidebar-as-aside
Param $sort-collation
Param $table-accessibility as xs:string
Param $use-docbook-css as xs:string
Param $use-minified-css as xs:string
Param $variablelist-termlength-threshold
Param $verbatim-callouts as xs:string
Param $verbatim-line-style
Param $verbatim-number-every-nth
Param $verbatim-number-first-line
Param $verbatim-number-minlines
Param $verbatim-number-separator
Param $verbatim-numbered-elements
Param $verbatim-plain-style as xs:string
Param $verbatim-space
Param $verbatim-style-default
Param $verbatim-syntax-highlight-css
Param $verbatim-syntax-highlight-languages
Param $verbatim-syntax-highlighter as xs:string
Param $verbatim-table-style
Param $verbatim-trim-leading-blank-lines
Param $verbatim-trim-trailing-blank-lines
Param $xspec as xs:string
Param $transform-original
Param $transform-before
Param $transform-after
Param $dynamic-profile-variables as map(xs:QName, item()*)?
Param $warn-about-missing-localizations as xs:string
Param $sets-number-from as xs:string
Param $books-number-from as xs:string
Param $divisions-number-from as xs:string
Param $components-number-from as xs:string
Param $sections-number-from as xs:string
Param $formal-objects-number-from as xs:string
Param $sets-inherit-from as xs:string
Param $books-inherit-from as xs:string
Param $divisions-inherit-from as xs:string
Param $components-inherit-from as xs:string
Param $sections-inherit-from as xs:string
Param $formal-objects-inherit-from as xs:string
Param $unwrap-paragraphs as xs:string
Param $copy-verbatim-js
Param $translate-suppress-elements
Param $varlistentry-separate-terms
Param $presentation-mode
Param $presentation-js
Param $presentation-css
Param $transformed-docbook-input as xs:string?
Param $transformed-docbook-output as xs:string?
Param $titleabbrev-passthrough as xs:string
Param $variablelist-panelset as xs:string
Param $vendor-css as xs:string*
Param $verbatim-embellish-linenumbers
Param $verbatim-default-language
Param $use-id-as-filename as xs:string
Param $on-unhandled-elements as xs:string
Variable $vp:static-parameters as map(xs:QName, item()*)
Uses: $debug
Used in: «root»
Variable $vp:dynamic-parameters as map(xs:QName, item()*)
Uses: $additional-languages, $align-char-default, $align-char-pad, $align-char-width, $allow-eval, $annotate-toc, $annotation-collection, $annotation-mark, $annotation-placement, $annotation-style, $annotations-js, $auto-toc, $bibliography-collection, $bibliography-style, $books-inherit-from, $books-number-from, $callout-default-column, $callout-unicode-start, $chunk, $chunk-exclude, $chunk-include, $chunk-nav, $chunk-nav-js, $chunk-output-base-uri, $chunk-renumber-footnotes, $chunk-section-depth, $classsynopsis-indent, $component-numbers, $component-numbers-inherit, $components-inherit-from, $components-number-from, $control-js, $copy-verbatim-js, $copyright-collapse-years, $copyright-year-range-separator, $copyright-year-separator, $date-date-format, $date-dateTime-format, $dc-metadata, $default-float-style, $default-language, $default-length-magnitude, $default-length-unit, $default-theme, $division-numbers, $division-numbers-inherit, $divisions-inherit-from, $divisions-number-from, $docbook-transclusion, $dynamic-profile-error, $dynamic-profile-variables, $dynamic-profiles, $experimental-pmuj, $fallback-js, $footnote-numeration, $formal-object-title-placement, $formal-objects-inherit-from, $formal-objects-number-from, $formalgroup-nested-object-title-placement, $funcsynopsis-default-style, $funcsynopsis-table-threshold, $funcsynopsis-trailing-punctuation, $generate-html-page, $generate-index, $generate-nested-toc, $generate-toc, $generate-trivial-toc, $generated-id-root, $generated-id-sep, $generator-metadata, $gentext-language, $glossary-automatic-divisions, $glossary-collection, $glossary-sort-entries, $html-extension, $image-ignore-scaling, $image-nominal-height, $image-nominal-width, $image-property-warning, $index-on-role, $index-on-type, $index-show-entries, $indexed-section-groups, $inline-xlink-href, $lists-of-equations, $lists-of-examples, $lists-of-figures, $lists-of-procedures, $lists-of-tables, $local-conventions, $mathml-js, $mediaobject-accessibility, $mediaobject-details-placement, $mediaobject-exclude-extensions, $mediaobject-grouped-by-type, $mediaobject-input-base-uri, $mediaobject-output-base-uri, $mediaobject-output-paths, $mediaobject-video-element, $message-level, $nominal-page-width, $number-single-appendix, $olink-databases, $on-unhandled-elements, $orderedlist-item-numeration, $othername-in-middle, $output-media, $oxy-markup, $page-style, $pagetoc-dynamic, $pagetoc-elements, $pagetoc-js, $paper-size, $persistent-toc, $persistent-toc-css, $persistent-toc-filename, $persistent-toc-js, $persistent-toc-search, $personal-name-style, $pixels-per-inch, $presentation-css, $presentation-js, $presentation-mode, $procedure-step-numeration, $productionset-lhs-rhs-separator, $profile-arch, $profile-audience, $profile-condition, $profile-conformance, $profile-lang, $profile-os, $profile-outputformat, $profile-revision, $profile-revisionflag, $profile-role, $profile-security, $profile-separator, $profile-userlevel, $profile-vendor, $profile-wordsize, $qandadiv-default-toc, $qandaset-default-label, $qandaset-default-toc, $refentry-generate-name, $refentry-generate-title, $relax-ng-grammar, $resource-base-uri, $revhistory-style, $section-numbers, $section-numbers-inherit, $section-toc-depth, $sections-inherit-from, $sections-number-from, $segmentedlist-style, $sets-inherit-from, $sets-number-from, $show-remarks, $sidebar-as-aside, $sort-collation, $table-accessibility, $table-footnote-numeration, $theme-picker, $titleabbrev-passthrough, $transclusion-id-fixup, $transclusion-link-scope, $transclusion-prefix-separator, $transclusion-suffix, $transform-after, $transform-before, $transform-original, $transformed-docbook-input, $transformed-docbook-output, $translate-suppress-elements, $unwrap-paragraphs, $use-docbook-css, $use-id-as-filename, $use-minified-css, $user-css-links, $variablelist-panelset, $variablelist-termlength-threshold, $varlistentry-separate-terms, $vendor-css, $verbatim-callouts, $verbatim-default-language, $verbatim-embellish-linenumbers, $verbatim-embellishments, $verbatim-line-style, $verbatim-number-every-nth, $verbatim-number-first-line, $verbatim-number-minlines, $verbatim-number-separator, $verbatim-numbered-elements, $verbatim-plain-style, $verbatim-space, $verbatim-style-default, $verbatim-syntax-highlight-css, $verbatim-syntax-highlight-languages, $verbatim-syntax-highlighter, $verbatim-table-style, $verbatim-trim-leading-blank-lines, $verbatim-trim-trailing-blank-lines, $warn-about-missing-localizations, $xlink-arclist-after, $xlink-arclist-before, $xlink-arclist-sep, $xlink-arclist-titlesep, $xlink-icon-closed, $xlink-icon-open, $xlink-js, $xlink-style, $xlink-style-default, $xspec
Used in: «root»
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns="http://www.w3.org/1999/xhtml"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:ext="http://docbook.org/extensions/xslt"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<!-- **********************************************************************
     This file is generated automatically from the parameters
     reference in the guide. Do not edit this file by hand.
     ********************************************************************** -->

<!-- Note: Some of these parameters are initialized using content
     instead of a select attribute in order to make the reference page
     in the Guide work better. -->

<!-- Many of these parameters are shadowed by variables (see
     variable.xsl) for use in the stylesheets. Often, they're defined
     as strings here and as more useful data types in the variables. -->
   <xsl:param name="additional-languages" select="()"/>
   <xsl:param name="align-char-default" as="xs:string" select="'.'"/>
   <xsl:param name="align-char-pad" select="' '"/>
   <xsl:param name="align-char-width" select="2"/>
   <xsl:param name="allow-eval" as="xs:string" select="'true'"/>
   <xsl:param name="annotate-toc" select="'true'"/>
   <xsl:param name="annotation-collection" as="xs:string" select="''"/>
   <xsl:param name="annotation-mark">
      <sup>⌖</sup>
   </xsl:param>
   <xsl:param name="annotation-placement" select="'after'"/>
   <xsl:param name="annotation-style" select="'javascript'"/>
   <xsl:param name="annotations-js" select="'js/annotations.js'"/>
   <xsl:param name="auto-toc" as="xs:string" select="'true'"/>
   <xsl:param name="bibliography-collection" as="xs:string" select="''"/>
   <xsl:param name="bibliography-style" as="xs:string" select="'default'"/>
   <xsl:param name="callout-default-column" select="60"/>
   <xsl:param name="callout-unicode-start" select="9311"/>
   <xsl:param name="chunk" as="xs:string?" select="()"/>
   <xsl:param name="chunk-exclude"
              as="xs:string*"
              select="('self::db:partintro',&#xA; 'self::*[ancestor::db:partintro]',&#xA; 'self::db:annotation',&#xA; 'self::db:section[not(preceding-sibling::db:section)]',&#xA; 'self::db:sect1[not(preceding-sibling::db:sect1)]',&#xA; 'self::db:toc')"/>
   <xsl:param name="chunk-include"
              as="xs:string*"
              select="('parent::db:set',&#xA; 'parent::db:book',&#xA; 'parent::db:part',&#xA; 'parent::db:reference',&#xA; 'self::db:refentry',&#xA; 'self::db:section',&#xA; 'self::db:sect1')"/>
   <xsl:param name="chunk-nav" as="xs:string" select="'true'"/>
   <xsl:param name="chunk-nav-js" as="xs:string" select="'js/chunk-nav.js'"/>
   <xsl:param name="chunk-output-base-uri" as="xs:string">
      <xsl:choose>
         <xsl:when test="not($v:chunk)">
    <!-- it doesn't actually matter -->
            <xsl:sequence select="''"/>
         </xsl:when>
         <xsl:when use-when="function-available('ext:cwd')" test="true()">
            <xsl:sequence select="ext:cwd()"/>
         </xsl:when>
         <xsl:otherwise>
            <xsl:message terminate="yes" select="'You must specify the $chunk-output-base-uri'"/>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:param>
   <xsl:param name="chunk-renumber-footnotes" select="'true'"/>
   <xsl:param name="chunk-section-depth" select="1"/>
   <xsl:param name="classsynopsis-indent" select="'  '"/>
   <xsl:param name="component-numbers" as="xs:string" select="'true'"/>
   <xsl:param name="component-numbers-inherit" as="xs:string" select="'false'"/>
   <xsl:param name="control-js" as="xs:string" select="'js/controls.js'"/>
   <xsl:param name="copyright-collapse-years" select="true()"/>
   <xsl:param name="copyright-year-range-separator" select="'–'"/>
   <xsl:param name="copyright-year-separator" select="', '"/>
   <xsl:param name="date-date-format" select="'[D01] [MNn,*-3] [Y0001]'"/>
   <xsl:param name="date-dateTime-format"
              select="'[H01]:[m01] [D01] [MNn,*-3] [Y0001]'"/>
   <xsl:param name="dc-metadata" as="xs:string" select="'true'"/>
   <xsl:param name="debug" static="yes" as="xs:string" select="''"/>
   <xsl:param name="default-float-style" select="'left'"/>
   <xsl:param name="default-language" select="'en'"/>
   <xsl:param name="default-length-magnitude" select="25.0"/>
   <xsl:param name="default-length-unit" select="'%'"/>
   <xsl:param name="personal-name-style" select="()"/>
   <xsl:param name="default-theme" as="xs:string" select="''"/>
   <xsl:param name="division-numbers" as="xs:string" select="'true'"/>
   <xsl:param name="division-numbers-inherit" as="xs:string" select="'false'"/>
   <xsl:param name="docbook-transclusion" select="'false'"/>
   <xsl:param name="dynamic-profile-error" select="'ignore'"/>
   <xsl:param name="dynamic-profiles" as="xs:string" select="'false'"/>
   <xsl:param name="experimental-pmuj" select="'false'"/>
   <xsl:param name="footnote-numeration" select="('1')"/>
   <xsl:param name="formal-object-title-placement"
              select="'after table:before formalgroup:before'"/>
   <xsl:param name="mediaobject-details-placement" select="'before'"/>
   <xsl:param name="formalgroup-nested-object-title-placement" select="'after'"/>
   <xsl:param name="funcsynopsis-default-style" select="'kr'"/>
   <xsl:param name="funcsynopsis-table-threshold" select="40"/>
   <xsl:param name="funcsynopsis-trailing-punctuation" select="';'"/>
   <xsl:param name="generate-html-page" as="xs:string" select="'true'"/>
   <xsl:param name="generate-index" select="'true'"/>
   <xsl:param name="generate-nested-toc"
              as="xs:string"
              select="'not(f:section(.))&#xA;or (f:section(.) and f:section-depth(.) le $vp:section-toc-depth)'"/>
   <xsl:param name="generate-toc"
              as="xs:string"
              select="'(empty(parent::*) and self::db:article)&#xA;or self::db:set or self::db:book&#xA;or self::db:part or self::db:reference'"/>
   <xsl:param name="generate-trivial-toc" as="xs:string" select="'false'"/>
   <xsl:param name="generated-id-root" select="'R'"/>
   <xsl:param name="generated-id-sep" select="'_'"/>
   <xsl:param name="generator-metadata" as="xs:string" select="'true'"/>
   <xsl:param name="gentext-language" select="()"/>
   <xsl:param name="glossary-collection" as="xs:string" select="''"/>
   <xsl:param name="glossary-sort-entries" select="'true'"/>
   <xsl:param name="glossary-automatic-divisions" select="'false'"/>
   <xsl:param name="html-extension" select="'.html'"/>
   <xsl:param name="image-ignore-scaling" as="xs:boolean" select="false()"/>
   <xsl:param name="image-nominal-height" select="'4in'"/>
   <xsl:param name="image-nominal-width" select="$nominal-page-width"/>
   <xsl:param name="image-property-warning" select="true()"/>
   <xsl:param name="index-on-type" select="'true'"/>
   <xsl:param name="index-on-role" select="'true'"/>
   <xsl:param name="index-show-entries" select="()"/>
   <xsl:param name="indexed-section-groups" select="'true'"/>
   <xsl:param name="lists-of-equations" as="xs:string" select="'false'"/>
   <xsl:param name="lists-of-examples" as="xs:string" select="'true'"/>
   <xsl:param name="lists-of-figures" as="xs:string" select="'true'"/>
   <xsl:param name="lists-of-procedures" as="xs:string" select="'false'"/>
   <xsl:param name="lists-of-tables" as="xs:string" select="'true'"/>
   <xsl:param name="local-conventions" as="xs:string?" select="()"/>
   <xsl:param name="mathml-js"
              select="'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=MML_CHTML'"/>
   <xsl:param name="mediaobject-accessibility"
              as="xs:string"
              select="'summary details'"/>
   <xsl:param name="mediaobject-exclude-extensions"
              as="xs:string"
              select="&#34;.eps .ps .pdf&#34;"/>
   <xsl:param name="mediaobject-input-base-uri" as="xs:string?" select="()"/>
   <xsl:param name="mediaobject-grouped-by-type" as="xs:string" select="'false'"/>
   <xsl:param name="mediaobject-output-base-uri" as="xs:string?" select="()"/>
   <xsl:param name="mediaobject-output-paths" as="xs:string" select="'true'"/>
   <xsl:param name="mediaobject-video-element" as="xs:string" select="'video'"/>
   <xsl:param name="nominal-page-width" select="'6in'"/>
   <xsl:param name="number-single-appendix" select="'true'"/>
   <xsl:param name="olink-databases" as="xs:string" select="''"/>
   <xsl:param name="orderedlist-item-numeration" select="'1aiAI'"/>
   <xsl:param name="othername-in-middle" select="'true'"/>
   <xsl:param name="output-media" select="'screen'"/>
   <xsl:param name="oxy-markup" select="'false'"/>
   <xsl:param name="pagetoc-elements" select="''"/>
   <xsl:param name="pagetoc-dynamic" select="'true'"/>
   <xsl:param name="pagetoc-js" select="'js/pagetoc.js'"/>
   <xsl:param name="page-style" as="xs:string" select="'article'"/>
   <xsl:param name="paper-size" as="xs:string?" select="()"/>
   <xsl:param name="persistent-toc" select="'false'"/>
   <xsl:param name="persistent-toc-css" select="'css/docbook-toc.css'"/>
   <xsl:param name="persistent-toc-filename"
              as="xs:string?"
              select="'persistent-toc.html'"/>
   <xsl:param name="persistent-toc-js" select="'js/persistent-toc.js'"/>
   <xsl:param name="persistent-toc-search" select="'true'"/>
   <xsl:param name="pixels-per-inch" select="96.0"/>
   <xsl:param name="procedure-step-numeration" select="'1aiAI'"/>
   <xsl:param name="productionset-lhs-rhs-separator" select="':='"/>
   <xsl:param name="profile-arch" select="''"/>
   <xsl:param name="profile-audience" select="''"/>
   <xsl:param name="profile-condition" select="''"/>
   <xsl:param name="profile-conformance" select="''"/>
   <xsl:param name="profile-lang" select="''"/>
   <xsl:param name="profile-os" select="''"/>
   <xsl:param name="profile-outputformat" select="''"/>
   <xsl:param name="profile-revision" select="''"/>
   <xsl:param name="profile-revisionflag" select="''"/>
   <xsl:param name="profile-role" select="''"/>
   <xsl:param name="profile-security" select="''"/>
   <xsl:param name="profile-separator" select="';'"/>
   <xsl:param name="profile-userlevel" select="''"/>
   <xsl:param name="profile-vendor" select="''"/>
   <xsl:param name="profile-wordsize" select="''"/>
   <xsl:param name="theme-picker" as="xs:string" select="'false'"/>
   <xsl:param name="transclusion-id-fixup" as="xs:string" select="'none'"/>
   <xsl:param name="transclusion-link-scope" as="xs:string" select="'global'"/>
   <xsl:param name="transclusion-prefix-separator" as="xs:string" select="'---'"/>
   <xsl:param name="transclusion-suffix" as="xs:string" select="''"/>
   <xsl:param name="qandadiv-default-toc" select="$qandaset-default-toc"/>
   <xsl:param name="qandaset-default-label" select="'number'"/>
   <xsl:param name="qandaset-default-toc" select="'true'"/>
   <xsl:param name="relax-ng-grammar" as="xs:string?" select="()"/>
   <xsl:param name="refentry-generate-name" select="true()"/>
   <xsl:param name="refentry-generate-title" select="true()"/>
   <xsl:param name="resource-base-uri" select="'./'"/>
   <xsl:param name="revhistory-style" select="'table'"/>
   <xsl:param name="section-numbers" as="xs:string" select="'true'"/>
   <xsl:param name="section-numbers-inherit" select="'true'"/>
   <xsl:param name="section-toc-depth" select="'unbounded'"/>
   <xsl:param name="segmentedlist-style" select="'table'"/>
   <xsl:param name="show-remarks" select="'false'"/>
   <xsl:param name="sidebar-as-aside" select="false()"/>
   <xsl:param name="sort-collation"
              select="'http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive'"/>
   <xsl:param name="table-accessibility"
              as="xs:string"
              select="'summary details'"/>
   <xsl:param name="table-footnote-numeration" select="('a')"/>
   <xsl:param name="use-docbook-css" as="xs:string" select="'true'"/>
   <xsl:param name="use-minified-css" as="xs:string" select="'false'"/>
   <xsl:param name="user-css-links" select="()"/>
   <xsl:param name="variablelist-termlength-threshold" select="20"/>
   <xsl:param name="verbatim-callouts"
              as="xs:string"
              select="'linecolumn lines lineranges-first'"/>
   <xsl:param name="verbatim-line-style"
              select="'programlisting programlistingco&#xA; screen screenco synopsis'"/>
   <xsl:param name="verbatim-number-every-nth" select="5"/>
   <xsl:param name="verbatim-number-first-line" select="'true'"/>
   <xsl:param name="verbatim-number-minlines" select="'5'"/>
   <xsl:param name="verbatim-number-separator" select="'|'"/>
   <xsl:param name="verbatim-numbered-elements"
              select="'programlisting programlistingco'"/>
   <xsl:param name="verbatim-plain-style"
              as="xs:string"
              select="'address literallayout funcsynopsisinfo classsynopsisinfo'"/>
   <xsl:param name="verbatim-space" select="' '"/>
   <xsl:param name="verbatim-style-default" select="'lines'"/>
   <xsl:param name="verbatim-syntax-highlight-css" select="'css/pygments.css'"/>
   <xsl:param name="verbatim-syntax-highlight-languages"
              select="'python perl html xml xslt xquery javascript json css'"/>
   <xsl:param name="verbatim-syntax-highlighter"
              as="xs:string"
              select="'pygments'"/>
   <xsl:param name="verbatim-table-style" select="''"/>
   <xsl:param name="verbatim-trim-leading-blank-lines" select="'false'"/>
   <xsl:param name="verbatim-trim-trailing-blank-lines" select="'true'"/>
   <xsl:param name="xlink-arclist-before" select="' ['"/>
   <xsl:param name="xlink-arclist-after" select="'] '"/>
   <xsl:param name="xlink-arclist-titlesep" select="': '"/>
   <xsl:param name="xlink-arclist-sep" select="', '"/>
   <xsl:param name="xlink-icon-closed" select="()"/>
   <xsl:param name="xlink-icon-open" select="()"/>
   <xsl:param name="xlink-js" select="'js/xlink.js'"/>
   <xsl:param name="xlink-style" select="'document'"/>
   <xsl:param name="xlink-style-default" select="'inline'"/>
   <xsl:param name="xspec" as="xs:string" select="'false'"/>
   <xsl:param name="transform-original" select="()"/>
   <xsl:param name="transform-before" select="()"/>
   <xsl:param name="transform-after" select="()"/>
   <xsl:param name="dynamic-profile-variables"
              as="map(xs:QName, item()*)?"
              select="()"/>
   <xsl:param name="warn-about-missing-localizations"
              as="xs:string"
              select="'true'"/>
   <xsl:param name="sets-number-from" as="xs:string" select="'set'"/>
   <xsl:param name="books-number-from" as="xs:string" select="'set'"/>
   <xsl:param name="divisions-number-from" as="xs:string" select="'book'"/>
   <xsl:param name="components-number-from" as="xs:string" select="'book'"/>
   <xsl:param name="sections-number-from" as="xs:string" select="'component'"/>
   <xsl:param name="formal-objects-number-from"
              as="xs:string"
              select="'component'"/>
   <xsl:param name="sets-inherit-from" as="xs:string" select="''"/>
   <xsl:param name="books-inherit-from" as="xs:string" select="''"/>
   <xsl:param name="divisions-inherit-from" as="xs:string" select="''"/>
   <xsl:param name="components-inherit-from" as="xs:string" select="''"/>
   <xsl:param name="sections-inherit-from" as="xs:string" select="'section'"/>
   <xsl:param name="formal-objects-inherit-from"
              as="xs:string"
              select="'component'"/>
   <xsl:param name="unwrap-paragraphs" as="xs:string" select="'false'"/>
   <xsl:param name="fallback-js" select="'js/fallback.js'"/>
   <xsl:param name="message-level" as="xs:integer" select="1"/>
   <xsl:param name="copy-verbatim-js" select="'js/copy-verbatim.js'"/>
   <xsl:param name="translate-suppress-elements" select="''"/>
   <xsl:param name="varlistentry-separate-terms" select="'false'"/>
   <xsl:param name="presentation-mode" select="'false'"/>
   <xsl:param name="presentation-js" select="'js/presentation.js'"/>
   <xsl:param name="presentation-css" select="'css/presentation.css'"/>
   <xsl:param name="inline-xlink-href" as="xs:string" select="'false'"/>
   <xsl:param name="transformed-docbook-input" as="xs:string?" select="()"/>
   <xsl:param name="transformed-docbook-output" as="xs:string?" select="()"/>
   <xsl:param name="titleabbrev-passthrough" as="xs:string" select="'true'"/>
   <xsl:param name="variablelist-panelset" as="xs:string" select="'true'"/>
   <xsl:param name="vendor-css" as="xs:string*" select="()"/>
   <xsl:param name="verbatim-embellishments" select="'true'"/>
   <xsl:param name="verbatim-embellish-linenumbers" select="'true'"/>
   <xsl:param name="verbatim-default-language" select="'none'"/>
   <xsl:param name="use-id-as-filename" as="xs:string" select="'false'"/>
   <xsl:param name="on-unhandled-elements" as="xs:string" select="'fail'"/>
   <xsl:variable name="vp:static-parameters" as="map(xs:QName, item()*)">
      <xsl:map>
         <xsl:map-entry key="QName('', 'debug')" select="$debug"/>
      </xsl:map>
   </xsl:variable>
   <xsl:variable name="vp:dynamic-parameters" as="map(xs:QName, item()*)">
      <xsl:map>
         <xsl:map-entry key="QName('', 'additional-languages')" select="$additional-languages"/>
         <xsl:map-entry key="QName('', 'align-char-default')" select="$align-char-default"/>
         <xsl:map-entry key="QName('', 'align-char-pad')" select="$align-char-pad"/>
         <xsl:map-entry key="QName('', 'align-char-width')" select="$align-char-width"/>
         <xsl:map-entry key="QName('', 'allow-eval')" select="$allow-eval"/>
         <xsl:map-entry key="QName('', 'annotate-toc')" select="$annotate-toc"/>
         <xsl:map-entry key="QName('', 'annotation-collection')"
                        select="$annotation-collection"/>
         <xsl:map-entry key="QName('', 'annotation-mark')" select="$annotation-mark"/>
         <xsl:map-entry key="QName('', 'annotation-placement')" select="$annotation-placement"/>
         <xsl:map-entry key="QName('', 'annotation-style')" select="$annotation-style"/>
         <xsl:map-entry key="QName('', 'annotations-js')" select="$annotations-js"/>
         <xsl:map-entry key="QName('', 'auto-toc')" select="$auto-toc"/>
         <xsl:map-entry key="QName('', 'bibliography-collection')"
                        select="$bibliography-collection"/>
         <xsl:map-entry key="QName('', 'bibliography-style')" select="$bibliography-style"/>
         <xsl:map-entry key="QName('', 'callout-default-column')"
                        select="$callout-default-column"/>
         <xsl:map-entry key="QName('', 'callout-unicode-start')"
                        select="$callout-unicode-start"/>
         <xsl:map-entry key="QName('', 'chunk')" select="$chunk"/>
         <xsl:map-entry key="QName('', 'chunk-exclude')" select="$chunk-exclude"/>
         <xsl:map-entry key="QName('', 'chunk-include')" select="$chunk-include"/>
         <xsl:map-entry key="QName('', 'chunk-nav')" select="$chunk-nav"/>
         <xsl:map-entry key="QName('', 'chunk-nav-js')" select="$chunk-nav-js"/>
         <xsl:map-entry key="QName('', 'chunk-output-base-uri')"
                        select="$chunk-output-base-uri"/>
         <xsl:map-entry key="QName('', 'chunk-renumber-footnotes')"
                        select="$chunk-renumber-footnotes"/>
         <xsl:map-entry key="QName('', 'chunk-section-depth')" select="$chunk-section-depth"/>
         <xsl:map-entry key="QName('', 'classsynopsis-indent')" select="$classsynopsis-indent"/>
         <xsl:map-entry key="QName('', 'component-numbers')" select="$component-numbers"/>
         <xsl:map-entry key="QName('', 'component-numbers-inherit')"
                        select="$component-numbers-inherit"/>
         <xsl:map-entry key="QName('', 'control-js')" select="$control-js"/>
         <xsl:map-entry key="QName('', 'copyright-collapse-years')"
                        select="$copyright-collapse-years"/>
         <xsl:map-entry key="QName('', 'copyright-year-range-separator')"
                        select="$copyright-year-range-separator"/>
         <xsl:map-entry key="QName('', 'copyright-year-separator')"
                        select="$copyright-year-separator"/>
         <xsl:map-entry key="QName('', 'date-date-format')" select="$date-date-format"/>
         <xsl:map-entry key="QName('', 'date-dateTime-format')" select="$date-dateTime-format"/>
         <xsl:map-entry key="QName('', 'dc-metadata')" select="$dc-metadata"/>
         <xsl:map-entry key="QName('', 'default-float-style')" select="$default-float-style"/>
         <xsl:map-entry key="QName('', 'default-language')" select="$default-language"/>
         <xsl:map-entry key="QName('', 'default-length-magnitude')"
                        select="$default-length-magnitude"/>
         <xsl:map-entry key="QName('', 'default-length-unit')" select="$default-length-unit"/>
         <xsl:map-entry key="QName('', 'personal-name-style')" select="$personal-name-style"/>
         <xsl:map-entry key="QName('', 'default-theme')" select="$default-theme"/>
         <xsl:map-entry key="QName('', 'division-numbers')" select="$division-numbers"/>
         <xsl:map-entry key="QName('', 'division-numbers-inherit')"
                        select="$division-numbers-inherit"/>
         <xsl:map-entry key="QName('', 'docbook-transclusion')" select="$docbook-transclusion"/>
         <xsl:map-entry key="QName('', 'dynamic-profile-error')"
                        select="$dynamic-profile-error"/>
         <xsl:map-entry key="QName('', 'dynamic-profiles')" select="$dynamic-profiles"/>
         <xsl:map-entry key="QName('', 'experimental-pmuj')" select="$experimental-pmuj"/>
         <xsl:map-entry key="QName('', 'footnote-numeration')" select="$footnote-numeration"/>
         <xsl:map-entry key="QName('', 'formal-object-title-placement')"
                        select="$formal-object-title-placement"/>
         <xsl:map-entry key="QName('', 'mediaobject-details-placement')"
                        select="$mediaobject-details-placement"/>
         <xsl:map-entry key="QName('', 'formalgroup-nested-object-title-placement')"
                        select="$formalgroup-nested-object-title-placement"/>
         <xsl:map-entry key="QName('', 'funcsynopsis-default-style')"
                        select="$funcsynopsis-default-style"/>
         <xsl:map-entry key="QName('', 'funcsynopsis-table-threshold')"
                        select="$funcsynopsis-table-threshold"/>
         <xsl:map-entry key="QName('', 'funcsynopsis-trailing-punctuation')"
                        select="$funcsynopsis-trailing-punctuation"/>
         <xsl:map-entry key="QName('', 'generate-html-page')" select="$generate-html-page"/>
         <xsl:map-entry key="QName('', 'generate-index')" select="$generate-index"/>
         <xsl:map-entry key="QName('', 'generate-nested-toc')" select="$generate-nested-toc"/>
         <xsl:map-entry key="QName('', 'generate-toc')" select="$generate-toc"/>
         <xsl:map-entry key="QName('', 'generate-trivial-toc')" select="$generate-trivial-toc"/>
         <xsl:map-entry key="QName('', 'generated-id-root')" select="$generated-id-root"/>
         <xsl:map-entry key="QName('', 'generated-id-sep')" select="$generated-id-sep"/>
         <xsl:map-entry key="QName('', 'generator-metadata')" select="$generator-metadata"/>
         <xsl:map-entry key="QName('', 'gentext-language')" select="$gentext-language"/>
         <xsl:map-entry key="QName('', 'glossary-collection')" select="$glossary-collection"/>
         <xsl:map-entry key="QName('', 'glossary-sort-entries')"
                        select="$glossary-sort-entries"/>
         <xsl:map-entry key="QName('', 'glossary-automatic-divisions')"
                        select="$glossary-automatic-divisions"/>
         <xsl:map-entry key="QName('', 'html-extension')" select="$html-extension"/>
         <xsl:map-entry key="QName('', 'image-ignore-scaling')" select="$image-ignore-scaling"/>
         <xsl:map-entry key="QName('', 'image-nominal-height')" select="$image-nominal-height"/>
         <xsl:map-entry key="QName('', 'image-nominal-width')" select="$image-nominal-width"/>
         <xsl:map-entry key="QName('', 'image-property-warning')"
                        select="$image-property-warning"/>
         <xsl:map-entry key="QName('', 'index-on-type')" select="$index-on-type"/>
         <xsl:map-entry key="QName('', 'index-on-role')" select="$index-on-role"/>
         <xsl:map-entry key="QName('', 'index-show-entries')" select="$index-show-entries"/>
         <xsl:map-entry key="QName('', 'indexed-section-groups')"
                        select="$indexed-section-groups"/>
         <xsl:map-entry key="QName('', 'lists-of-equations')" select="$lists-of-equations"/>
         <xsl:map-entry key="QName('', 'lists-of-examples')" select="$lists-of-examples"/>
         <xsl:map-entry key="QName('', 'lists-of-figures')" select="$lists-of-figures"/>
         <xsl:map-entry key="QName('', 'lists-of-procedures')" select="$lists-of-procedures"/>
         <xsl:map-entry key="QName('', 'lists-of-tables')" select="$lists-of-tables"/>
         <xsl:map-entry key="QName('', 'local-conventions')" select="$local-conventions"/>
         <xsl:map-entry key="QName('', 'mathml-js')" select="$mathml-js"/>
         <xsl:map-entry key="QName('', 'mediaobject-accessibility')"
                        select="$mediaobject-accessibility"/>
         <xsl:map-entry key="QName('', 'mediaobject-exclude-extensions')"
                        select="$mediaobject-exclude-extensions"/>
         <xsl:map-entry key="QName('', 'mediaobject-input-base-uri')"
                        select="$mediaobject-input-base-uri"/>
         <xsl:map-entry key="QName('', 'mediaobject-grouped-by-type')"
                        select="$mediaobject-grouped-by-type"/>
         <xsl:map-entry key="QName('', 'mediaobject-output-base-uri')"
                        select="$mediaobject-output-base-uri"/>
         <xsl:map-entry key="QName('', 'mediaobject-output-paths')"
                        select="$mediaobject-output-paths"/>
         <xsl:map-entry key="QName('', 'mediaobject-video-element')"
                        select="$mediaobject-video-element"/>
         <xsl:map-entry key="QName('', 'nominal-page-width')" select="$nominal-page-width"/>
         <xsl:map-entry key="QName('', 'number-single-appendix')"
                        select="$number-single-appendix"/>
         <xsl:map-entry key="QName('', 'olink-databases')" select="$olink-databases"/>
         <xsl:map-entry key="QName('', 'orderedlist-item-numeration')"
                        select="$orderedlist-item-numeration"/>
         <xsl:map-entry key="QName('', 'othername-in-middle')" select="$othername-in-middle"/>
         <xsl:map-entry key="QName('', 'output-media')" select="$output-media"/>
         <xsl:map-entry key="QName('', 'oxy-markup')" select="$oxy-markup"/>
         <xsl:map-entry key="QName('', 'pagetoc-elements')" select="$pagetoc-elements"/>
         <xsl:map-entry key="QName('', 'pagetoc-dynamic')" select="$pagetoc-dynamic"/>
         <xsl:map-entry key="QName('', 'pagetoc-js')" select="$pagetoc-js"/>
         <xsl:map-entry key="QName('', 'page-style')" select="$page-style"/>
         <xsl:map-entry key="QName('', 'paper-size')" select="$paper-size"/>
         <xsl:map-entry key="QName('', 'persistent-toc')" select="$persistent-toc"/>
         <xsl:map-entry key="QName('', 'persistent-toc-css')" select="$persistent-toc-css"/>
         <xsl:map-entry key="QName('', 'persistent-toc-filename')"
                        select="$persistent-toc-filename"/>
         <xsl:map-entry key="QName('', 'persistent-toc-js')" select="$persistent-toc-js"/>
         <xsl:map-entry key="QName('', 'persistent-toc-search')"
                        select="$persistent-toc-search"/>
         <xsl:map-entry key="QName('', 'pixels-per-inch')" select="$pixels-per-inch"/>
         <xsl:map-entry key="QName('', 'procedure-step-numeration')"
                        select="$procedure-step-numeration"/>
         <xsl:map-entry key="QName('', 'productionset-lhs-rhs-separator')"
                        select="$productionset-lhs-rhs-separator"/>
         <xsl:map-entry key="QName('', 'profile-arch')" select="$profile-arch"/>
         <xsl:map-entry key="QName('', 'profile-audience')" select="$profile-audience"/>
         <xsl:map-entry key="QName('', 'profile-condition')" select="$profile-condition"/>
         <xsl:map-entry key="QName('', 'profile-conformance')" select="$profile-conformance"/>
         <xsl:map-entry key="QName('', 'profile-lang')" select="$profile-lang"/>
         <xsl:map-entry key="QName('', 'profile-os')" select="$profile-os"/>
         <xsl:map-entry key="QName('', 'profile-outputformat')" select="$profile-outputformat"/>
         <xsl:map-entry key="QName('', 'profile-revision')" select="$profile-revision"/>
         <xsl:map-entry key="QName('', 'profile-revisionflag')" select="$profile-revisionflag"/>
         <xsl:map-entry key="QName('', 'profile-role')" select="$profile-role"/>
         <xsl:map-entry key="QName('', 'profile-security')" select="$profile-security"/>
         <xsl:map-entry key="QName('', 'profile-separator')" select="$profile-separator"/>
         <xsl:map-entry key="QName('', 'profile-userlevel')" select="$profile-userlevel"/>
         <xsl:map-entry key="QName('', 'profile-vendor')" select="$profile-vendor"/>
         <xsl:map-entry key="QName('', 'profile-wordsize')" select="$profile-wordsize"/>
         <xsl:map-entry key="QName('', 'theme-picker')" select="$theme-picker"/>
         <xsl:map-entry key="QName('', 'transclusion-id-fixup')"
                        select="$transclusion-id-fixup"/>
         <xsl:map-entry key="QName('', 'transclusion-link-scope')"
                        select="$transclusion-link-scope"/>
         <xsl:map-entry key="QName('', 'transclusion-prefix-separator')"
                        select="$transclusion-prefix-separator"/>
         <xsl:map-entry key="QName('', 'transclusion-suffix')" select="$transclusion-suffix"/>
         <xsl:map-entry key="QName('', 'qandadiv-default-toc')" select="$qandadiv-default-toc"/>
         <xsl:map-entry key="QName('', 'qandaset-default-label')"
                        select="$qandaset-default-label"/>
         <xsl:map-entry key="QName('', 'qandaset-default-toc')" select="$qandaset-default-toc"/>
         <xsl:map-entry key="QName('', 'relax-ng-grammar')" select="$relax-ng-grammar"/>
         <xsl:map-entry key="QName('', 'refentry-generate-name')"
                        select="$refentry-generate-name"/>
         <xsl:map-entry key="QName('', 'refentry-generate-title')"
                        select="$refentry-generate-title"/>
         <xsl:map-entry key="QName('', 'resource-base-uri')" select="$resource-base-uri"/>
         <xsl:map-entry key="QName('', 'revhistory-style')" select="$revhistory-style"/>
         <xsl:map-entry key="QName('', 'section-numbers')" select="$section-numbers"/>
         <xsl:map-entry key="QName('', 'section-numbers-inherit')"
                        select="$section-numbers-inherit"/>
         <xsl:map-entry key="QName('', 'section-toc-depth')" select="$section-toc-depth"/>
         <xsl:map-entry key="QName('', 'segmentedlist-style')" select="$segmentedlist-style"/>
         <xsl:map-entry key="QName('', 'show-remarks')" select="$show-remarks"/>
         <xsl:map-entry key="QName('', 'sidebar-as-aside')" select="$sidebar-as-aside"/>
         <xsl:map-entry key="QName('', 'sort-collation')" select="$sort-collation"/>
         <xsl:map-entry key="QName('', 'table-accessibility')" select="$table-accessibility"/>
         <xsl:map-entry key="QName('', 'table-footnote-numeration')"
                        select="$table-footnote-numeration"/>
         <xsl:map-entry key="QName('', 'use-docbook-css')" select="$use-docbook-css"/>
         <xsl:map-entry key="QName('', 'use-minified-css')" select="$use-minified-css"/>
         <xsl:map-entry key="QName('', 'user-css-links')" select="$user-css-links"/>
         <xsl:map-entry key="QName('', 'variablelist-termlength-threshold')"
                        select="$variablelist-termlength-threshold"/>
         <xsl:map-entry key="QName('', 'verbatim-callouts')" select="$verbatim-callouts"/>
         <xsl:map-entry key="QName('', 'verbatim-line-style')" select="$verbatim-line-style"/>
         <xsl:map-entry key="QName('', 'verbatim-number-every-nth')"
                        select="$verbatim-number-every-nth"/>
         <xsl:map-entry key="QName('', 'verbatim-number-first-line')"
                        select="$verbatim-number-first-line"/>
         <xsl:map-entry key="QName('', 'verbatim-number-minlines')"
                        select="$verbatim-number-minlines"/>
         <xsl:map-entry key="QName('', 'verbatim-number-separator')"
                        select="$verbatim-number-separator"/>
         <xsl:map-entry key="QName('', 'verbatim-numbered-elements')"
                        select="$verbatim-numbered-elements"/>
         <xsl:map-entry key="QName('', 'verbatim-plain-style')" select="$verbatim-plain-style"/>
         <xsl:map-entry key="QName('', 'verbatim-space')" select="$verbatim-space"/>
         <xsl:map-entry key="QName('', 'verbatim-style-default')"
                        select="$verbatim-style-default"/>
         <xsl:map-entry key="QName('', 'verbatim-syntax-highlight-css')"
                        select="$verbatim-syntax-highlight-css"/>
         <xsl:map-entry key="QName('', 'verbatim-syntax-highlight-languages')"
                        select="$verbatim-syntax-highlight-languages"/>
         <xsl:map-entry key="QName('', 'verbatim-syntax-highlighter')"
                        select="$verbatim-syntax-highlighter"/>
         <xsl:map-entry key="QName('', 'verbatim-table-style')" select="$verbatim-table-style"/>
         <xsl:map-entry key="QName('', 'verbatim-trim-leading-blank-lines')"
                        select="$verbatim-trim-leading-blank-lines"/>
         <xsl:map-entry key="QName('', 'verbatim-trim-trailing-blank-lines')"
                        select="$verbatim-trim-trailing-blank-lines"/>
         <xsl:map-entry key="QName('', 'xlink-arclist-before')" select="$xlink-arclist-before"/>
         <xsl:map-entry key="QName('', 'xlink-arclist-after')" select="$xlink-arclist-after"/>
         <xsl:map-entry key="QName('', 'xlink-arclist-titlesep')"
                        select="$xlink-arclist-titlesep"/>
         <xsl:map-entry key="QName('', 'xlink-arclist-sep')" select="$xlink-arclist-sep"/>
         <xsl:map-entry key="QName('', 'xlink-icon-closed')" select="$xlink-icon-closed"/>
         <xsl:map-entry key="QName('', 'xlink-icon-open')" select="$xlink-icon-open"/>
         <xsl:map-entry key="QName('', 'xlink-js')" select="$xlink-js"/>
         <xsl:map-entry key="QName('', 'xlink-style')" select="$xlink-style"/>
         <xsl:map-entry key="QName('', 'xlink-style-default')" select="$xlink-style-default"/>
         <xsl:map-entry key="QName('', 'xspec')" select="$xspec"/>
         <xsl:map-entry key="QName('', 'transform-original')" select="$transform-original"/>
         <xsl:map-entry key="QName('', 'transform-before')" select="$transform-before"/>
         <xsl:map-entry key="QName('', 'transform-after')" select="$transform-after"/>
         <xsl:map-entry key="QName('', 'dynamic-profile-variables')"
                        select="$dynamic-profile-variables"/>
         <xsl:map-entry key="QName('', 'warn-about-missing-localizations')"
                        select="$warn-about-missing-localizations"/>
         <xsl:map-entry key="QName('', 'sets-number-from')" select="$sets-number-from"/>
         <xsl:map-entry key="QName('', 'books-number-from')" select="$books-number-from"/>
         <xsl:map-entry key="QName('', 'divisions-number-from')"
                        select="$divisions-number-from"/>
         <xsl:map-entry key="QName('', 'components-number-from')"
                        select="$components-number-from"/>
         <xsl:map-entry key="QName('', 'sections-number-from')" select="$sections-number-from"/>
         <xsl:map-entry key="QName('', 'formal-objects-number-from')"
                        select="$formal-objects-number-from"/>
         <xsl:map-entry key="QName('', 'sets-inherit-from')" select="$sets-inherit-from"/>
         <xsl:map-entry key="QName('', 'books-inherit-from')" select="$books-inherit-from"/>
         <xsl:map-entry key="QName('', 'divisions-inherit-from')"
                        select="$divisions-inherit-from"/>
         <xsl:map-entry key="QName('', 'components-inherit-from')"
                        select="$components-inherit-from"/>
         <xsl:map-entry key="QName('', 'sections-inherit-from')"
                        select="$sections-inherit-from"/>
         <xsl:map-entry key="QName('', 'formal-objects-inherit-from')"
                        select="$formal-objects-inherit-from"/>
         <xsl:map-entry key="QName('', 'unwrap-paragraphs')" select="$unwrap-paragraphs"/>
         <xsl:map-entry key="QName('', 'fallback-js')" select="$fallback-js"/>
         <xsl:map-entry key="QName('', 'message-level')" select="$message-level"/>
         <xsl:map-entry key="QName('', 'copy-verbatim-js')" select="$copy-verbatim-js"/>
         <xsl:map-entry key="QName('', 'translate-suppress-elements')"
                        select="$translate-suppress-elements"/>
         <xsl:map-entry key="QName('', 'varlistentry-separate-terms')"
                        select="$varlistentry-separate-terms"/>
         <xsl:map-entry key="QName('', 'presentation-mode')" select="$presentation-mode"/>
         <xsl:map-entry key="QName('', 'presentation-js')" select="$presentation-js"/>
         <xsl:map-entry key="QName('', 'presentation-css')" select="$presentation-css"/>
         <xsl:map-entry key="QName('', 'inline-xlink-href')" select="$inline-xlink-href"/>
         <xsl:map-entry key="QName('', 'transformed-docbook-input')"
                        select="$transformed-docbook-input"/>
         <xsl:map-entry key="QName('', 'transformed-docbook-output')"
                        select="$transformed-docbook-output"/>
         <xsl:map-entry key="QName('', 'titleabbrev-passthrough')"
                        select="$titleabbrev-passthrough"/>
         <xsl:map-entry key="QName('', 'variablelist-panelset')"
                        select="$variablelist-panelset"/>
         <xsl:map-entry key="QName('', 'vendor-css')" select="$vendor-css"/>
         <xsl:map-entry key="QName('', 'verbatim-embellishments')"
                        select="$verbatim-embellishments"/>
         <xsl:map-entry key="QName('', 'verbatim-embellish-linenumbers')"
                        select="$verbatim-embellish-linenumbers"/>
         <xsl:map-entry key="QName('', 'verbatim-default-language')"
                        select="$verbatim-default-language"/>
         <xsl:map-entry key="QName('', 'use-id-as-filename')" select="$use-id-as-filename"/>
         <xsl:map-entry key="QName('', 'on-unhandled-elements')"
                        select="$on-unhandled-elements"/>
      </xsl:map>
   </xsl:variable>
</xsl:stylesheet>

VERSION.xsl

2 variables (2 used only in one other module)

Instructions
Variable $v:VERSION
Variable $v:VERSION-ID
Used in: main.xsl
Source code
1
2
3
4
5
6
7
<xsl:stylesheet xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="3.0">
   <xsl:variable name="v:VERSION" select="'2.5.0-SNAPSHOT'"/>
   <xsl:variable name="v:VERSION-ID" select="'4ff16dca'"/>
</xsl:stylesheet>

variable.xsl

60 variables (1 unused, 56 used only in one other module)

Instructions
Variable $v:as-json
Used in: main.xsl
Variable $v:as-xml
Used in: main.xsl
Variable $v:custom-localizations as document-node()?
Used in: main.xsl
Variable $v:localization-base-uri
Used in: main.xsl
Variable $vp:section-toc-depth as xs:integer
Variable $v:verbatim-table-style
Variable $v:verbatim-line-style
Variable $v:verbatim-plain-style as xs:string*
Variable $v:verbatim-space as node()
Variable $v:verbatim-numbered-elements as xs:string*
Variable $v:verbatim-number-minlines
Variable $v:verbatim-number-every-nth
Variable $v:verbatim-number-first-line
Variable $v:verbatim-callouts as xs:string*
Variable $v:verbatim-syntax-highlight-languages
Variable $v:verbatim-syntax-highlight-options
Used in: main.xsl
Variable $v:verbatim-syntax-highlight-pygments-options
Used in: main.xsl
Variable $v:mediaobject-output-base-uri as xs:string?
Variable $v:mediaobject-exclude-extensions
Variable $vp:mediaobject-accessibility
Variable $vp:table-accessibility
Variable $v:personal-name-styles
Used in: main.xsl
Variable $v:formal-object-title-placement as map(xs:string,xs:string)
Variable $v:formalgroup-nested-object-title-placement as map(xs:string,xs:string)
Variable $v:mediaobject-details-placement as map(xs:string,xs:string)
Variable $v:arg-choice-opt-open-str
Used in: main.xsl
Variable $v:arg-choice-opt-close-str
Used in: main.xsl
Variable $v:arg-choice-req-open-str
Used in: main.xsl
Variable $v:arg-choice-req-close-str
Used in: main.xsl
Variable $v:arg-choice-plain-open-str
Used in: main.xsl
Variable $v:arg-choice-plain-close-str
Used in: main.xsl
Variable $v:arg-choice-def-open-str
Used in: main.xsl
Variable $v:arg-choice-def-close-str
Used in: main.xsl
Variable $v:arg-rep-repeat-str
Used in: main.xsl
Variable $v:arg-rep-norepeat-str
Used in: main.xsl
Variable $v:arg-rep-def-str
Used in: main.xsl
Variable $v:arg-or-sep
Used in: main.xsl
Variable $v:chunk-renumber-footnotes as xs:boolean
Variable $v:chunk-filter-namespaces as namespace-node()*
Variable $v:admonition-icons
Used in: main.xsl
Variable $v:annotation-close as element()
Used by: template
Used in: main.xsl
Variable $v:nominal-page-width
Variable $v:image-nominal-width
Variable $v:image-nominal-height
Variable $v:toc-open as element()
Variable $v:toc-close as element()
Variable $vp:pagetoc-elements as xs:string*
Variable $vp:olinkdb
Used in: main.xsl
Variable $v:theme-list as element()*
Used in: main.xsl
Variable $vp:random-prefix as xs:string
Used in: main.xsl
Variable $vp:js-controls as element()*
Variable $v:highlight-js-head-elements as element()*
Variable $v:prism-js-head-elements as element()*
Variable $v:media-type-default as xs:string
Used in: main.xsl
Variable $v:media-type-map as map(xs:string, xs:string)
Used in: main.xsl
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:ext="http://docbook.org/extensions/xslt"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:ls="http://docbook.org/ns/docbook/l10n/source"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<!-- Note: These are variables used by the stylesheet. Many are
     initialized by parsing parameters defined in param.xsl. They're
     in a separate stylesheet to make the pipelines cleaner. -->

<!-- Some them are initialized using content instead of a select
     attribute in order to make the reference page in the Guide work
     better. -->

<xsl:variable name="v:as-json" select="map {'method':'json','indent':true()}"/>
<xsl:variable name="v:as-xml" select="map {'method':'xml','indent':true()}"/>

<xsl:variable name="v:custom-localizations" as="document-node()?"
              select="()"/>

<xsl:variable name="v:localization-base-uri"
              select="resolve-uri('../locale/', static-base-uri())"/>

<xsl:variable name="v:chunk" as="xs:boolean"
              select="not(normalize-space($chunk) = '')"/>

<xsl:variable name="vp:section-toc-depth" as="xs:integer">
  <xsl:choose>
    <xsl:when test="$section-toc-depth instance of xs:integer">
      <xsl:sequence select="max((0, $section-toc-depth))"/>
    </xsl:when>
    <xsl:when test="$section-toc-depth castable as xs:integer">
      <xsl:sequence select="max((0, xs:integer($section-toc-depth)))"/>
    </xsl:when>
    <xsl:when test="string($section-toc-depth) = 'unbounded'">
      <xsl:sequence select="2147483647"/> <!-- 0x7fffffff -->
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:variable name="v:debug" static="yes" as="xs:string*"
              select="tokenize($debug, '[,\s]+') ! normalize-space(.)"/>

<xsl:variable name="v:verbatim-table-style"
              select="tokenize($verbatim-table-style, '\s+')"/>

<xsl:variable name="v:verbatim-line-style"
              select="tokenize($verbatim-line-style, '\s+')"/>

<xsl:variable name="v:verbatim-plain-style" as="xs:string*"
              select="tokenize($verbatim-plain-style, '\s+')"/>

<xsl:variable name="v:verbatim-space" as="node()">
  <xsl:value-of select="substring($verbatim-space || ' ', 1, 1)"/>
</xsl:variable>

<xsl:variable name="v:verbatim-numbered-elements" as="xs:string*"
              select="tokenize($verbatim-numbered-elements, '\s+')"/>

<xsl:variable name="v:verbatim-number-minlines"
              select="xs:integer($verbatim-number-minlines)"/>

<xsl:variable name="v:verbatim-number-every-nth"
              select="xs:integer($verbatim-number-every-nth)"/>

<xsl:variable name="v:verbatim-number-first-line"
              select="f:is-true($verbatim-number-first-line)"/>

<xsl:variable name="v:verbatim-callouts" as="xs:string*"
              select="tokenize($verbatim-callouts, '\s+')"/>

<xsl:variable name="v:verbatim-syntax-highlight-languages"
              select="tokenize($verbatim-syntax-highlight-languages, '\s+')"/>
<xsl:variable name="v:verbatim-syntax-highlight-options"
           select="map { }"/>
<xsl:variable name="v:verbatim-syntax-highlight-pygments-options"
           select="map { }"/>

<xsl:variable name="v:mediaobject-output-base-uri" as="xs:string?">
  <xsl:message use-when="'mediaobject-uris' = $v:debug"
               select="'Mediaobject out. base URI:',
                       if (empty($mediaobject-output-base-uri))
                       then ()
                       else if (ends-with($mediaobject-output-base-uri, '/'))
                            then $mediaobject-output-base-uri
                            else $mediaobject-output-base-uri || '/'"/>
  <xsl:sequence select="if (empty($mediaobject-output-base-uri))
                        then ()
                        else if (ends-with($mediaobject-output-base-uri, '/'))
                             then $mediaobject-output-base-uri
                             else $mediaobject-output-base-uri || '/'"/>
</xsl:variable>

<xsl:variable name="v:mediaobject-exclude-extensions"
              select="tokenize($mediaobject-exclude-extensions, '\s+')"/>

<xsl:variable name="vp:mediaobject-accessibility"
              select="tokenize($mediaobject-accessibility, '\s+')"/>

<xsl:variable name="vp:table-accessibility"
              select="tokenize($table-accessibility, '\s+')"/>

<xsl:variable name="v:personal-name-styles"
              select="('first-last', 'last-first', 'FAMILY-given')"/>

<xsl:variable name="v:formal-object-title-placement" as="map(xs:string,xs:string)"
              select="fp:parse-key-value-pairs(
                        tokenize($formal-object-title-placement, '\s+'))"/>

<xsl:variable name="v:formalgroup-nested-object-title-placement" as="map(xs:string,xs:string)"
              select="fp:parse-key-value-pairs(
                        tokenize($formal-object-title-placement, '\s+'))"/>

<xsl:variable name="v:mediaobject-details-placement" as="map(xs:string,xs:string)"
              select="fp:parse-key-value-pairs(
                        tokenize($mediaobject-details-placement, '\s+'))"/>

<xsl:variable name="v:arg-choice-opt-open-str"><span class="cmdpunct">[</span></xsl:variable>
<xsl:variable name="v:arg-choice-opt-close-str"><span class="cmdpunct">]</span></xsl:variable>
<xsl:variable name="v:arg-choice-req-open-str"><span class="cmdpunct">{</span></xsl:variable>
<xsl:variable name="v:arg-choice-req-close-str"><span class="cmdpunct">}</span></xsl:variable>
<xsl:variable name="v:arg-choice-plain-open-str"><xsl:text></xsl:text></xsl:variable>
<xsl:variable name="v:arg-choice-plain-close-str"><xsl:text></xsl:text></xsl:variable>
<xsl:variable name="v:arg-choice-def-open-str"><span class="cmdpunct">[</span></xsl:variable>
<xsl:variable name="v:arg-choice-def-close-str"><span class="cmdpunct">]</span></xsl:variable>
<xsl:variable name="v:arg-rep-repeat-str"><span class="cmdpunct">…</span></xsl:variable>
<xsl:variable name="v:arg-rep-norepeat-str"><xsl:text></xsl:text></xsl:variable>
<xsl:variable name="v:arg-rep-def-str"><xsl:text></xsl:text></xsl:variable>
<xsl:variable name="v:arg-or-sep"><span class="cmdpunct"> | </span></xsl:variable>

<xsl:variable name="vp:user-css-links"
              select="tokenize(normalize-space($user-css-links), '\s+')"/>

<xsl:variable name="v:chunk-renumber-footnotes" as="xs:boolean"
              select="f:is-true($chunk-renumber-footnotes)"/>

<xsl:variable name="v:chunk-filter-namespaces" as="namespace-node()*">
  <xsl:namespace name="db" select="'http://docbook.org/ns/docbook'"/>
</xsl:variable>

<!-- Make sure we've resolved it so that file:///, file://, file:/, etc.
     get normalized because later on we're going to want to compare
     the prefix of this base URI with the prefix of another URI. -->
<xsl:variable name="vp:chunk-output-base-uri" as="xs:anyURI?">
  <xsl:choose>
    <xsl:when use-when="function-available('ext:cwd')"
              test="true()">
      <xsl:if test="'chunks' = $v:debug">
        <xsl:message select="'Chunk output base uri:',
                             resolve-uri($chunk-output-base-uri, ext:cwd())"/>
      </xsl:if>
      <xsl:sequence select="resolve-uri($chunk-output-base-uri, ext:cwd())"/>
    </xsl:when>
    <xsl:when test="$v:chunk">
      <xsl:if test="'chunks' = $v:debug">
        <xsl:message select="'Chunk output base uri:', $chunk-output-base-uri"/>
      </xsl:if>
      <xsl:sequence select="xs:anyURI($chunk-output-base-uri)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="xs:anyURI($chunk-output-base-uri)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<!-- I tinkered a bit to find images that would display across
     a variety of devices. YMMV. Beware: Pygmentize on Windows
     doesn't use UTF-8 by default, so bad can happen. -->
<xsl:variable name="v:admonition-icons">
  <db:tip>☞</db:tip>
  <db:note>ⓘ</db:note>
  <db:important>☝</db:important>
  <db:caution>⚠</db:caution>
  <db:warning>🛑</db:warning>
  <db:danger>⚡</db:danger>
</xsl:variable>

<xsl:variable name="v:annotation-close" as="element()">
  <span>╳</span>
</xsl:variable>

<xsl:variable name="v:nominal-page-width"
              select="f:parse-length($nominal-page-width)"/>
<xsl:variable name="v:image-nominal-width"
              select="f:parse-length($image-nominal-width)"/>
<xsl:variable name="v:image-nominal-height"
              select="f:parse-length($image-nominal-height)"/>

<xsl:variable name="v:toc-open" as="element()">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAABDklEQVQ4je2UUW3EMAyGv0wDUAhlkDBIyuAYXCEclEJoGZRBUgYOgzJoGHgPW6o7rWq35+2TIkWW9duW7N+oKuM46jiOGGMAaNsW5xzee5xzn8EvRESXZUFEWNcVAFWl73v6vjcGUIAYIyEEANZ1RUSY55lSCo/HA4BhGGiahtvthnOOtm0BSCnRdd1eVAGNMeoR27aptVattbpt22FOjFGrzjsX1I7q/4q3y4xf8i/4FwT3Pcw5Y4zBWvujfQMopZBzJuf8XbCeVyWEgHOO+/2+x0SEaZpIKSEih0X2Wz6jdlxKuezaqL7qpZS0lLKbw/M4ANba3RyapiGE8OJGqOrpizGq916999VATvM/AGFn1sxsVjL5AAAAAElFTkSuQmCC"
       alt="[toc]"/>
</xsl:variable>

<xsl:variable name="v:toc-close" as="element()">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAtklEQVQ4jaWT0Q3DIAxEz52AEbyB2cDZhBEY3SNcPyqqBEwqpZaQAHNP5jBCEiJCPAiSIgAeiUe8zoveO9x9e9jd0XtfKwHA1hpHtNY49rO8u59zn4mqMiJSyFkcEVTVFQCAtdYFMotrrXN111JnyA/xCsggN2JeXmGEmaGU8l2XUmBm29fZuh0RW2PTK2SGZcamADPb3nmGmNkKOI7j1rAzJG2kcWhqkstQ1QX+/2ciKU/FJOUN6JFZYWpmK3sAAAAASUVORK5CYII="
       alt="X"/>
</xsl:variable>

<xsl:variable name="vp:pagetoc-elements" as="xs:string*"
              select="tokenize($pagetoc-elements, '\s+')"/>

<xsl:variable name="vp:olinkdb" select="false()"/>

<xsl:variable name="v:olink-databases" as="element(h:targetdb)*">
  <xsl:if test="normalize-space($olink-databases) != ''">
    <xsl:for-each select="tokenize($olink-databases, ',\s*') ! normalize-space(.)">
      <xsl:variable name="db" select="resolve-uri(., static-base-uri())"/>
      <xsl:try>
        <xsl:variable name="olinkdb" select="doc($db)/h:targetdb"/>
        <xsl:if test="empty($olinkdb)">
          <xsl:message select="'No targets in olinkdb:', $db"/>
        </xsl:if>
        <xsl:sequence select="$olinkdb"/>
        <xsl:catch>
          <xsl:message select="'Failed to load olinkdb:', $db"/>
        </xsl:catch>
      </xsl:try>
    </xsl:for-each>
  </xsl:if>
</xsl:variable>

<xsl:variable name="v:theme-list" as="element()*">
  <theme name="Materials dark" id="materials-dark" dark="true"/>
  <theme name="Materials light" id="materials-light" dark="false"/>
</xsl:variable>

<xsl:variable name="vp:random-prefix" as="xs:string">
  <xsl:variable name="chars"
                select="('a','b','c','d','e','f','_','_','_','1','2','3','4','5','6')"/>
  <xsl:sequence select="string-join(random-number-generator()?permute($chars), '')"/>
</xsl:variable>

<xsl:variable name="vp:js-controls" as="element()*">
  <xsl:variable name="random" select="$vp:random-prefix"/>
  <span class="controls-open">☰</span>
  <div class="js-controls-wrapper">
    <xsl:if test="$v:theme-list[@dark='true']">
      <xsl:attribute name="db-dark-theme"
                     select="($v:theme-list[@dark='true'])[1]/@id"/>
    </xsl:if>
    <div class="js-controls-body">
      <div class="js-controls-header">
        <div class="js-controls-close">╳</div>
        <div class="js-controls-title">
          <xsl:text>Settings</xsl:text>
        </div>
      </div>
      <div class="js-controls-content">
        <fieldset db-random="{$random}" class="js-controls-toggles">
          <legend>Select options:</legend>
          <input id="db-js-annotations_{$random}" type="checkbox" value="js" />
          <label for="db-js-annotations_{$random}">JavaScript annotations</label><br/>
          <input id="db-js-xlinks_{$random}" type="checkbox" value="js" />
          <label for="db-js-xlinks_{$random}">JavaScript extended links</label><br/>
        </fieldset>
        <div id="db-js-controls-reload_{$random}" class="js-controls-reload"></div>
        <fieldset>
          <legend>Choose a theme:</legend>
          <input id="db-default-theme_{$random}" type="radio" name="db-theme" value="default" />
          <label for="db-default-theme_{$random}">Default</label><br/>
          <xsl:for-each select="$v:theme-list">
            <input id="db-theme-{@id}-{$random}"
                   type="radio" name="db-theme" value="{@id}" />
            <label for="db-theme-{@id}-{$random}">
              <xsl:sequence select="@name/string()"/>
            </label>
            <br/>
          </xsl:for-each>
        </fieldset>
      </div>
      <div class="js-controls-buttons">
        <button id="js-controls-cancel">✗</button>
        <xsl:text> </xsl:text>
        <button id="js-controls-ok">✓</button>
      </div>
    </div>
  </div>
</xsl:variable>

<!-- N.B. There's a bit of a hack in the variables below. We put the
     link and script elements in the same variable, but we use them in
     two different places so that the h:link element(s) precede the
     DocBook CSS files (in order to support overrides with cascade).
     -->

<xsl:variable name="v:highlight-js-head-elements" as="element()*">
  <link rel="stylesheet"
        href="{$resource-base-uri}css/highlight-11.10.0.min.css" />
  <script src="{$resource-base-uri}js/highlight-11.10.0.min.js"></script>
  <xsl:choose>
    <xsl:when test="f:is-true($verbatim-embellish-linenumbers)">
      <script src="{$resource-base-uri}js/highlightjs-line-numbers-2.9.0.min.js"></script>
      <script>hljs.configure({languages:[]}); hljs.highlightAll(); hljs.initLineNumbersOnLoad();</script>
    </xsl:when>
    <xsl:otherwise>
      <script>hljs.configure({languages:[]}); hljs.highlightAll();</script>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:variable name="v:prism-js-head-elements" as="element()*">
  <link rel="stylesheet" href="{$resource-base-uri}css/prism.css"/>
  <script src="{$resource-base-uri}js/prism.js"></script>
</xsl:variable>

<!-- ============================================================ -->

<xsl:variable name="v:media-type-default" as="xs:string"
              select="'application/octet-stream'"/>

  <!-- Credit: https://developer.mozilla.org/en-US/docs/Web/HTTP/\
       Basics_of_HTTP/MIME_types/Common_types -->
  <!-- I left out the .3gp and .3g2 extensions because they're either audio or
       video depending on what they actually contain and that's more complicated
       than just an extension lookup
       I added .text, .mov, .m3u8, .wmv
  -->
<xsl:variable name="v:media-type-map" as="map(xs:string, xs:string)">
  <xsl:map>
    <xsl:map-entry key="'.aac'" select="'audio/aac'"/>
    <xsl:map-entry key="'.abw'" select="'application/x-abiword'"/>
    <xsl:map-entry key="'.arc'" select="'application/x-freearc'"/>
    <xsl:map-entry key="'.avif'" select="'image/avif'"/>
    <xsl:map-entry key="'.avi'" select="'video/x-msvideo'"/>
    <xsl:map-entry key="'.azw'" select="'application/vnd.amazon.ebook'"/>
    <xsl:map-entry key="'.bin'" select="'application/octet-stream'"/>
    <xsl:map-entry key="'.bmp'" select="'image/bmp'"/>
    <xsl:map-entry key="'.bz'" select="'application/x-bzip'"/>
    <xsl:map-entry key="'.bz2'" select="'application/x-bzip2'"/>
    <xsl:map-entry key="'.cda'" select="'application/x-cdf'"/>
    <xsl:map-entry key="'.csh'" select="'application/x-csh'"/>
    <xsl:map-entry key="'.css'" select="'text/css'"/>
    <xsl:map-entry key="'.csv'" select="'text/csv'"/>
    <xsl:map-entry key="'.doc'" select="'application/msword'"/>
    <xsl:map-entry key="'.docx'"
                   select="'application/vnd.openxmlformats-officedocument.wordprocessingml.document'"/>
    <xsl:map-entry key="'.eot'" select="'application/vnd.ms-fontobject'"/>
    <xsl:map-entry key="'.epub'" select="'application/epub+zip'"/>
    <xsl:map-entry key="'.gz'" select="'application/gzip'"/>
    <xsl:map-entry key="'.gif'" select="'image/gif'"/>
    <xsl:map-entry key="'.htm'" select="'text/html'"/>
    <xsl:map-entry key="'.html'" select="'text/html'"/>
    <xsl:map-entry key="'.ico'" select="'image/vnd.microsoft.icon'"/>
    <xsl:map-entry key="'.ics'" select="'text/calendar'"/>
    <xsl:map-entry key="'.jar'" select="'application/java-archive'"/>
    <xsl:map-entry key="'.jpeg'" select="'image/jpeg'"/>
    <xsl:map-entry key="'.jpg'" select="'image/jpeg'"/>
    <xsl:map-entry key="'.js'" select="'text/javascript'"/>
    <xsl:map-entry key="'.json'" select="'application/json'"/>
    <xsl:map-entry key="'.jsonld'" select="'application/ld+json'"/>
    <xsl:map-entry key="'.mid'" select="'audio/midi'"/>
    <xsl:map-entry key="'.midi'" select="'audio/midi'"/>
    <xsl:map-entry key="'.mjs'" select="'text/javascript'"/>
    <xsl:map-entry key="'.m3u8'" select="'application/x-mpegURL'"/>
    <xsl:map-entry key="'.mp3'" select="'audio/mpeg'"/>
    <xsl:map-entry key="'.mp4'" select="'video/mp4'"/>
    <xsl:map-entry key="'.mpeg'" select="'video/mpeg'"/>
    <xsl:map-entry key="'.mpkg'" select="'application/vnd.apple.installer+xml'"/>
    <xsl:map-entry key="'.mov'" select="'video/quicktime'"/>
    <xsl:map-entry key="'.odp'" select="'application/vnd.oasis.opendocument.presentation'"/>
    <xsl:map-entry key="'.ods'" select="'application/vnd.oasis.opendocument.spreadsheet'"/>
    <xsl:map-entry key="'.odt'" select="'application/vnd.oasis.opendocument.text'"/>
    <xsl:map-entry key="'.oga'" select="'audio/ogg'"/>
    <xsl:map-entry key="'.ogv'" select="'video/ogg'"/>
    <xsl:map-entry key="'.ogx'" select="'application/ogg'"/>
    <xsl:map-entry key="'.opus'" select="'audio/opus'"/>
    <xsl:map-entry key="'.otf'" select="'font/otf'"/>
    <xsl:map-entry key="'.png'" select="'image/png'"/>
    <xsl:map-entry key="'.pdf'" select="'application/pdf'"/>
    <xsl:map-entry key="'.php'" select="'application/x-httpd-php'"/>
    <xsl:map-entry key="'.ppt'" select="'application/vnd.ms-powerpoint'"/>
    <xsl:map-entry key="'.pptx'"
                   select="'application/vnd.openxmlformats-officedocument.presentationml.presentation'"/>
    <xsl:map-entry key="'.rar'" select="'application/vnd.rar'"/>
    <xsl:map-entry key="'.rtf'" select="'application/rtf'"/>
    <xsl:map-entry key="'.sh'" select="'application/x-sh'"/>
    <xsl:map-entry key="'.svg'" select="'image/svg'"/>          <!-- not image/svg+xml for epub -->
    <xsl:map-entry key="'.tar'" select="'application/x-tar'"/>
    <xsl:map-entry key="'.tif'" select="'image/tiff'"/>
    <xsl:map-entry key="'.tiff'" select="'image/tiff'"/>
    <xsl:map-entry key="'.ts'" select="'video/mp2t'"/>
    <xsl:map-entry key="'.ttf'" select="'font/ttf'"/>
    <xsl:map-entry key="'.txt'" select="'text/plain'"/>
    <xsl:map-entry key="'.text'" select="'text/plain'"/>
    <xsl:map-entry key="'.vsd'" select="'application/vnd.visio'"/>
    <xsl:map-entry key="'.wav'" select="'audio/wav'"/>
    <xsl:map-entry key="'.weba'" select="'audio/webm'"/>
    <xsl:map-entry key="'.webm'" select="'video/webm'"/>
    <xsl:map-entry key="'.webp'" select="'image/webp'"/>
    <xsl:map-entry key="'.wmv'" select="'video/x-ms-wmv'"/>
    <xsl:map-entry key="'.woff'" select="'font/woff'"/>
    <xsl:map-entry key="'.woff2'" select="'font/woff2'"/>
    <xsl:map-entry key="'.xhtml'" select="'application/xhtml+xml'"/>
    <xsl:map-entry key="'.xls'" select="'application/vnd.ms-excel'"/>
    <xsl:map-entry key="'.xlsx'"
                   select="'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'"/>
    <xsl:map-entry key="'.xml'" select="'application/xml'"/>
    <xsl:map-entry key="'.xul'" select="'application/vnd.mozilla.xul+xml'"/>
    <xsl:map-entry key="'.zip'" select="'application/zip'"/>
    <xsl:map-entry key="'.7z'" select="'application/x-7z-compressed'"/>
  </xsl:map>
</xsl:variable>

</xsl:stylesheet>

space.xsl

Instructions
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db m xs"
                version="3.0">

<xsl:preserve-space elements="*"/>

<xsl:strip-space elements="
db:abstract
db:affiliation
db:anchor
db:answer
db:appendix
db:area
db:areaset
db:areaspec
db:artheader
db:article
db:audiodata
db:audioobject
db:author
db:authorblurb
db:authorgroup
db:beginpage
db:bibliodiv
db:biblioentry
db:bibliography
db:biblioset
db:blockquote
db:book
db:bookbiblio
db:bookinfo
db:callout
db:calloutlist
db:caption
db:caution
db:chapter
db:citerefentry
db:classsynopsis
db:cmdsynopsis
db:co
db:colgroup
db:collab
db:colophon
db:colspec
db:confgroup
db:constructorsynopsis
db:copyright
db:danger
db:dedication
db:destructorsynopsis
db:docinfo
db:editor
db:entrytbl
db:epigraph
db:equation
db:example
db:fieldsynopsis
db:figure
db:footnote
db:formalgroup
db:formalpara
db:funcprototype
db:funcsynopsis
db:glossary
db:glossdef
db:glossdiv
db:glossentry
db:glosslist
db:graphicco
db:group
db:highlights
db:imagedata
db:imageobject
db:imageobjectco
db:important
db:index
db:indexdiv
db:indexentry
db:indexterm
db:info
db:informalequation
db:informalexample
db:informalfigure
db:informaltable
db:inlineequation
db:inlinemediaobject
db:itemizedlist
db:itermset
db:keycombo
db:keywordset
db:legalnotice
db:listitem
db:lot
db:mediaobject
db:mediaobjectco
db:menuchoice
db:methodparam
db:methodsynopsis
db:msg
db:msgentry
db:msgexplan
db:msginfo
db:msgmain
db:msgrel
db:msgset
db:msgsub
db:msgtext
db:note
db:objectinfo
db:ooclass
db:ooexception
db:oointerface
db:orderedlist
db:othercredit
db:part
db:partintro
db:preface
db:printhistory
db:procedure
db:productionset
db:programlistingco
db:publisher
db:qandadiv
db:qandaentry
db:qandaset
db:question
db:refentry
db:reference
db:refmeta
db:refnamediv
db:refsect1
db:refsect1info
db:refsect2
db:refsect2info
db:refsect3
db:refsect3info
db:refsynopsisdiv
db:refsynopsisdivinfo
db:revhistory
db:revision
db:row
db:sbr
db:screenco
db:screenshot
db:sect1
db:sect1info
db:sect2
db:sect2info
db:sect3
db:sect3info
db:sect4
db:sect4info
db:sect5
db:sect5info
db:section
db:sectioninfo
db:seglistitem
db:segmentedlist
db:seriesinfo
db:set
db:setindex
db:setinfo
db:shortcut
db:sidebar
db:simplelist
db:simplemsgentry
db:simplesect
db:spanspec
db:step
db:subject
db:subjectset
db:subjectset
db:substeps
db:synopfragment
db:table
db:tr
db:tbody
db:textobject
db:tfoot
db:tgroup
db:thead
db:tip
db:toc
db:tocchap
db:toclevel1
db:toclevel2
db:toclevel3
db:toclevel4
db:toclevel5
db:tocpart
db:varargs
db:variablelist
db:varlistentry
db:videodata
db:videoobject
db:void
db:warning

db:dialogue
db:poetry
db:linegroup
db:speaker
db:person
"/>

</xsl:stylesheet>

unhandled.xsl

2 templates

Instructions
Template match ≅ *
Mode: m:docbook
Matches: *
Template match ≅ h:*
Mode: m:docbook
Matches: h:*
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="f m xs"
                version="3.0">

<xsl:template match="*">
  <xsl:choose>
    <xsl:when test="$on-unhandled-elements = 'fail'">
      <xsl:message terminate="yes"
                   select="'No template for ' || node-name(.) || ': ' || f:generate-id(.)"/>
    </xsl:when>
    <xsl:when test="$on-unhandled-elements = 'render'">
      <xsl:message select="'No template for ' || node-name(.) || ': ' || f:generate-id(.)"/>

      <xsl:variable name="inline"
                    select="normalize-space(string-join(text(),'')) != ''"/>

      <xsl:element namespace="http://www.w3.org/1999/xhtml"
                   name="{if ($inline) then 'span' else 'div'}">
        
        <xsl:element namespace="http://www.w3.org/1999/xhtml"
                     name="{if ($inline) then 'span' else 'div'}">
          <xsl:attribute name="class" select="'unhandled'"/>
          <xsl:text>&lt;</xsl:text>
          <xsl:value-of select="node-name(.)"/>
          <xsl:text>&gt;</xsl:text>
        </xsl:element>

        <xsl:apply-templates/>

        <xsl:element namespace="http://www.w3.org/1999/xhtml"
                     name="{if ($inline) then 'span' else 'div'}">
          <xsl:attribute name="class" select="'unhandled'"/>
          <xsl:text>&lt;/</xsl:text>
          <xsl:value-of select="node-name(.)"/>
          <xsl:text>&gt;</xsl:text>
        </xsl:element>
      </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="." mode="m:unhandled"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template xmlns:h="http://www.w3.org/1999/xhtml"
              match="h:*">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="node()"/>
  </xsl:element>
</xsl:template>

</xsl:stylesheet>

errors.xsl

14 variables (3 unused, 11 used only in one other module)

Instructions
Variable $dbe:INVALID-INJECT
Used in: main.xsl
Variable $dbe:INVALID-CALS
Used in: main.xsl
Variable $dbe:INVALID-AREAREFS
Used in: main.xsl
Variable $dbe:INVALID-PRODUCTIONRECAP
Used in: main.xsl
Variable $dbe:INVALID-CONSTRAINT
Used in: main.xsl
Variable $dbe:INVALID-TEMPLATE
Used in: main.xsl
Variable $dbe:INTERNAL-RENUMBER-ERROR
Variable $dbe:INTERNAL-HIGHLIGHT-ERROR
Variable $dbe:INVALID-NAME-STYLE
Used in: main.xsl
Variable $dbe:DYNAMIC-PROFILE-SYNTAX-ERROR
Unused
Variable $dbe:DYNAMIC-PROFILE-EVAL-ERROR
Unused
Variable $dbe:INVALID-DYNAMIC-PROFILE-ERROR
Unused
Variable $dbe:INVALID-TRANSFORM
Used in: «root»
Variable $dbe:INVALID-RESULTS-REQUESTED
Used by: t:docbook
Used in: «root»
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db dbe f m xs"
                version="3.0">

<xsl:variable name="dbe:INVALID-INJECT" select="xs:QName('dbe:INVALID-INJECT')"/>
<xsl:variable name="dbe:INVALID-CALS" select="xs:QName('dbe:INVALID-CALS')"/>
<xsl:variable name="dbe:INVALID-AREAREFS" select="xs:QName('dbe:INVALID-AREAREFS')"/>
<xsl:variable name="dbe:INVALID-PRODUCTIONRECAP"
              select="xs:QName('dbe:INVALID-PRODUCTIONRECAP')"/>
<xsl:variable name="dbe:INVALID-CONSTRAINT"
              select="xs:QName('dbe:INVALID-CONSTRAINT')"/>
<xsl:variable name="dbe:INVALID-TEMPLATE"
              select="xs:QName('dbe:INVALID-TEMPLATE')"/>
<xsl:variable name="dbe:INTERNAL-RENUMBER-ERROR"
              select="xs:QName('dbe:INTERNAL-RENUMBER-ERROR')"/>
<xsl:variable name="dbe:INTERNAL-HIGHLIGHT-ERROR"
              select="xs:QName('dbe:INTERNAL-HIGHLIGHT-ERROR')"/>
<xsl:variable name="dbe:INVALID-NAME-STYLE"
              select="xs:QName('dbe:INVALID-NAME-STYLE')"/>
<xsl:variable name="dbe:DYNAMIC-PROFILE-SYNTAX-ERROR"
              select="xs:QName('dbe:DYNAMIC-PROFILE-SYNTAX-ERROR')"/>
<xsl:variable name="dbe:DYNAMIC-PROFILE-EVAL-ERROR"
              select="xs:QName('dbe:DYNAMIC-PROFILE-EVAL-ERROR')"/>
<xsl:variable name="dbe:INVALID-DYNAMIC-PROFILE-ERROR"
              select="xs:QName('dbe:INVALID-DYNAMIC-PROFILE-ERROR')"/>
<xsl:variable name="dbe:INVALID-TRANSFORM"
              select="xs:QName('dbe:INVALID-TRANSFORM')"/>
<xsl:variable name="dbe:INVALID-RESULTS-REQUESTED"
              select="xs:QName('dbe:INVALID-RESULTS-REQUESTED')"/>

</xsl:stylesheet>

head.xsl

13 templates, 1 function (1 used only in one other module)

Instructions
Template match ≅ db:keywordset
Mode: m:html-head
Matches: db:keywordset
Template match ≅ db:subjectset
Mode: m:html-head
Matches: db:subjectset
Template match ≅ db:meta
Mode: m:html-head
Matches: db:meta
Template match ≅ h:*
Mode: m:html-head
Matches: h:*
Template match ≅ *
Mode: mp:html-head-meta
Matches: *
Template match ≅ *
Mode: m:html-head-script
Matches: *
Function fp:minified-css($css as xs:string) as xs:string
Template match ≅ *
Mode: m:html-head-links
Matches: *
Template match ≅ *
Mode: m:html-head-last
Matches: *
Template match ≅ *
Mode: m:head-additions
Matches: *
Template match ≅ *
Mode: m:html-body-script
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:mode name="m:html-head" on-no-match="shallow-copy"/>

<xsl:template match="*" mode="m:html-head" as="element(h:head)">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>
      <xsl:variable name="title">
        <xsl:apply-templates select="/*" mode="m:headline-title"/>
      </xsl:variable>
      <xsl:value-of select="$title"/>
    </title>

    <xsl:apply-templates select="db:keywordset|db:subjectset|db:meta"
                         mode="m:html-head"/>

  <xsl:variable name="global-highlighter" select="f:global-syntax-highlighter(.)"/>

    <xsl:choose>
      <xsl:when test="not(f:is-true($verbatim-embellishments))">
        <!-- nop -->
      </xsl:when>
      <xsl:when test="$global-highlighter = 'highlight.js'">
        <xsl:sequence select="$v:highlight-js-head-elements/self::h:link"/>
      </xsl:when>
      <xsl:when test="$global-highlighter = ('prism', 'prism.js')">
        <xsl:sequence select="$v:prism-js-head-elements/self::h:link"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- nop -->
      </xsl:otherwise>
    </xsl:choose>

    <xsl:if test="exists($v:verbatim-syntax-highlight-languages)
                  and normalize-space($verbatim-syntax-highlight-css) != ''">
      <link rel="stylesheet"
            href="{$resource-base-uri}{$verbatim-syntax-highlight-css}"/>
    </xsl:if>

    <xsl:apply-templates select="." mode="mp:html-head-meta"/>
    <xsl:apply-templates select="." mode="mp:html-head-script"/>
    <xsl:apply-templates select="." mode="m:html-head-script"/>
    <xsl:apply-templates select="." mode="mp:html-head-links"/>
    <xsl:apply-templates select="." mode="m:html-head-links"/>
    <xsl:apply-templates select="h:*" mode="m:html-head"/>

    <xsl:for-each select="$vp:user-css-links">
      <link rel="stylesheet" href="{$resource-base-uri}{.}"/>
    </xsl:for-each>

    <xsl:apply-templates select="." mode="m:html-head-last"/>
  </head>
</xsl:template>

<xsl:template match="db:keywordset" mode="m:html-head">
  <xsl:variable name="keywords" as="xs:string*">
    <xsl:for-each select="db:keyword">
      <xsl:sort select="normalize-space(.)"/>
      <xsl:sequence select="normalize-space(.)"/>
    </xsl:for-each>
  </xsl:variable>

  <meta name="keywords" content="{string-join($keywords,',')}"/>
</xsl:template>

<xsl:template match="db:subjectset" mode="m:html-head">
  <xsl:variable name="keywords" as="xs:string*">
    <xsl:for-each select="db:subject/db:subjectterm">
      <xsl:sort select="normalize-space(.)"/>
      <xsl:sequence select="normalize-space(.)"/>
    </xsl:for-each>
  </xsl:variable>

  <meta name="keywords" content="{string-join($keywords,',')}"/>
</xsl:template>

<xsl:template match="db:meta" mode="m:html-head">
  <xsl:if test="@name and @content">
    <meta name="{@name}" content="{@content}"/>
  </xsl:if>
</xsl:template>

<xsl:template match="h:*" mode="m:html-head">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="@*,node()" mode="m:html-head"/>
  </xsl:element>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="mp:html-head-meta">
  <xsl:variable name="Z" select="xs:dayTimeDuration('PT0H')"/>

  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>

  <xsl:if test="f:is-true($dc-metadata)">
    <link rel="schema.dc" href="https://purl.org/dc/elements/1.1/"/>
    <meta name="dc.modified"
          content="{format-dateTime(
                      adjust-dateTime-to-timezone(current-dateTime(), $Z),
                      '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]Z')}"/>
    <xsl:choose>
      <xsl:when test="empty(/*/db:info/db:pubdate)"/>
      <xsl:when test="empty(/*/db:info/db:pubdate/node())">
        <meta name="dc.created"
              content="{format-dateTime(
                         adjust-dateTime-to-timezone(current-dateTime(), $Z),
                         '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]Z')}"/>
      </xsl:when>
      <xsl:when test="/*/db:info/db:pubdate/string() castable as xs:dateTime">
        <xsl:variable name="date"
                      select="xs:dateTime(/*/db:info/db:pubdate/string())"/>
        <meta name="dc.created"
              content="{format-dateTime(
                         adjust-dateTime-to-timezone($date, $Z),
                         '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]Z')}"/>
      </xsl:when>
      <xsl:when test="/*/db:info/db:pubdate/string() castable as xs:date">
        <xsl:variable name="date"
                      select="xs:date(/*/db:info/db:pubdate/string())"/>
        <meta name="dc.created"
              content="{format-date($date, '[Y0001]-[M01]-[D01]')}"/>
      </xsl:when>
      <xsl:otherwise>
        <meta name="dc.created" content="{string(/*/db:info/db:pubdate)}"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>

  <xsl:if test="f:is-true($generator-metadata)">
    <meta name="generator"
          content="{'DocBook xslTNG version ' || $v:VERSION
                    || ' / ' || $v:VERSION-ID
                    || ' / ' || system-property('xsl:product-name')
                    || ' ' || system-property('xsl:product-version')}"/>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="mp:html-head-script">
  <xsl:if test="f:is-true($persistent-toc)">
    <link rel="stylesheet"
          href="{$resource-base-uri}{fp:minified-css($persistent-toc-css)}"/>
  </xsl:if>

  <xsl:variable name="global-highlighter" select="f:global-syntax-highlighter(.)"/>

  <xsl:choose>
    <xsl:when test="$global-highlighter = ('', 'none')"/>
    <xsl:when test="$global-highlighter = 'pygments'"/>
    <xsl:when test="$global-highlighter = 'highlight.js'">
      <xsl:sequence select="$v:highlight-js-head-elements/self::*
                            except $v:highlight-js-head-elements/self::h:link"/>
    </xsl:when>
    <xsl:when test="$global-highlighter = ('prism', 'prism.js')">
      <xsl:sequence select="$v:prism-js-head-elements/self::*
                            except $v:prism-js-head-elements/self::h:link"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Unrecognized syntax highlighter:', $global-highlighter"/>
    </xsl:otherwise>
  </xsl:choose>

  <xsl:if test="f:is-true($presentation-mode)">
    <script src="{$presentation-js}" defer="defer"/>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="m:html-head-script">
</xsl:template>

<xsl:template match="*" mode="mp:html-head-links">
  <xsl:if test="f:is-true($use-docbook-css)">
    <link href="{$resource-base-uri}{fp:minified-css('css/docbook.css')}"
          rel="stylesheet" media="screen"/>
    <!-- This stylesheet was made conditional in 2.1.0 because modern
         browsers generate a lot of warnings for print-specific features. -->
    <xsl:if test="$output-media = 'print'">
      <link href="{$resource-base-uri}{fp:minified-css('css/docbook-paged.css')}"
            rel="stylesheet" media="print"/>
    </xsl:if>
    <xsl:for-each select="$vendor-css">
      <xsl:choose>
        <xsl:when test="contains(., '/') or contains(., '\')">
          <link href="{$resource-base-uri}{.}"
                rel="stylesheet"/>
        </xsl:when>
        <xsl:otherwise>
          <link href="{$resource-base-uri}{fp:minified-css('css/' || .)}"
                rel="stylesheet"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:if>

  <xsl:if test="f:is-true($presentation-mode)">
    <link rel="stylesheet"
          href="{$presentation-css}"/>
  </xsl:if>
</xsl:template>

<xsl:function name="fp:minified-css" as="xs:string">
  <xsl:param name="css" as="xs:string"/>
  <xsl:sequence select="if (f:is-true($use-minified-css))
                        then replace($css, '\.css$', '.min.css')
                        else $css"/>
</xsl:function>

<xsl:template match="*" mode="m:html-head-links">
</xsl:template>

<xsl:template match="*" mode="m:html-head-last">
</xsl:template>

<xsl:template match="*" mode="m:head-additions">
</xsl:template>

<xsl:template match="*" mode="m:html-body-script">
  <xsl:param name="rootbaseuri" as="xs:anyURI" required="yes"/>
  <xsl:param name="chunkbaseuri" as="xs:anyURI" required="yes"/>
</xsl:template>

</xsl:stylesheet>

titles.xsl

37 templates (1 used only in one other module), 3 functions (1 unused, 3 used only in one other module), 2 variables (2 used only in one other module)

Instructions
Variable $v:user-title-groups as element()*
Used in: main.xsl
Variable $v:title-groups as element()+
Function fp:title-properties($this as element()) as element()?
Function fp:title-properties-override($context as element(), $properties as element()) as element()
Used in: main.xsl
Template match ≅ * as item()*
Mode: mp:compute-headline-label
Matches: *
Template match ≅ *
Template match ≅ *
Mode: m:headline-label
Matches: *
Template match ≅ db:appendix
Mode: m:headline-label
Matches: db:appendix
Template match ≅ db:qandaentry
Mode: m:headline-label
Matches: db:qandaentry
Template match ≅ db:question
Mode: m:headline-label
Matches: db:question
Template match ≅ db:answer
Mode: m:headline-label
Matches: db:answer
Template match ≅ * as item()*
Mode: m:headline-number
Matches: *
Template match ≅ db:orderedlist/db:listitem as item()*
Mode: m:headline-number
Matches: db:orderedlist/db:listitem
Template match ≅ db:step as item()*
Mode: m:headline-number
Matches: db:step
Template match ≅ db:qandadiv as item()*
Mode: m:headline-number
Matches: db:qandadiv
Template match ≅ db:equation|db:example|db:figu… as item()*
Mode: m:headline-number
Matches: db:equation, db:example, db:figure, db:formalgroup, db:procedure, db:qandaset, db:table
Template match ≅ db:sect1|db:sect2|db:sect3|db:… as item()*
Mode: m:headline-number
Matches: db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section
Template match ≅ db:acknowledgements|db:appendi… as item()*
Mode: m:headline-number
Matches: db:acknowledgements, db:appendix, db:article, db:bibliography, db:chapter, db:colophon, db:dedication, db:glossary, db:index, db:partintro, db:preface, db:setindex
Template match ≅ db:part|db:reference as item()*
Mode: m:headline-number
Matches: db:part, db:reference
Template match ≅ db:book as item()*
Mode: m:headline-number
Matches: db:book
Template match ≅ db:set as item()*
Mode: m:headline-number
Matches: db:set
Template match ≅ db:equation|db:example|db:figu…
Mode: mp:format-headline-number
Matches: db:equation, db:example, db:figure, db:table
Template match ≅ *
Mode: mp:format-headline-number
Matches: *
Template tp:format-number match ≅ as item()*
Used in: main.xsl
Mode: m:docbook
Template match ≅ * as item()*
Mode: mp:headline-number-prefix
Matches: *
Template match ≅ db:sect1|db:sect2|db:sect3|db:… as item()*
Mode: mp:headline-number-prefix
Matches: db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section
Template match ≅ db:acknowledgements|db:appendi… as item()*
Mode: mp:headline-number-prefix
Matches: db:acknowledgements, db:appendix, db:article, db:bibliography, db:chapter, db:colophon, db:dedication, db:glossary, db:index, db:partintro, db:preface, db:setindex
Template match ≅ db:part|db:reference as item()*
Mode: mp:headline-number-prefix
Matches: db:part, db:reference
Template match ≅ db:book as item()*
Mode: mp:headline-number-prefix
Matches: db:book
Template match ≅ db:set as item()*
Mode: mp:headline-number-prefix
Matches: db:set
Function fp:nearest-relevant-ancestor($elem as element()) as element()
Used in: main.xsl
Template match ≅ *
Mode: m:headline-title
Matches: *
Template match ≅ db:refentry
Mode: m:headline-title
Matches: db:refentry
Template match ≅ db:refmeta
Mode: m:headline-title
Matches: db:refmeta
Template match ≅ db:refnamediv
Mode: m:headline-title
Matches: db:refnamediv
Template match ≅ db:refname
Mode: m:headline-title
Matches: db:refname
Template match ≅ db:question
Mode: m:headline-title
Matches: db:question
Template match ≅ db:title|db:titleabbrev
Mode: m:title
Matches: db:title, db:titleabbrev
Template match ≅ h:db-annotation|h:db-footnote
Mode: mp:strip-links
Matches: h:db-annotation, h:db-footnote
Template match ≅ h:a
Mode: mp:strip-links
Matches: h:a
Template match ≅ element()
Mode: mp:strip-links
Matches: element()
Template match ≅ attribute()|comment()|processi…
Mode: mp:strip-links
Matches: attribute(), comment(), processing-instruction(), text()
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:variable name="v:user-title-groups" as="element()*"/>

<xsl:variable name="v:title-groups" as="element()+"
              xmlns:db="http://docbook.org/ns/docbook">
  <xsl:sequence select="$v:user-title-groups"/>

  <title xpath="self::db:section|self::db:sect1
                |self::db:sect2|self::db:sect3|self::db:sect4|self::db:sect5
                |self::db:refsection|self::db:refsect1|self::db:refsect2|self::db:refsect3"
         group="{if (f:is-true($section-numbers))
                 then 'title-numbered'
                 else 'title-unnumbered'}"/>

  <title xpath="self::db:article|self::db:preface|self::db:chapter|self::db:appendix"
         group="{if (f:is-true($component-numbers))
                 then 'title-numbered'
                 else 'title-unnumbered'}"/>

  <title xpath="self::db:set" group="title-unnumbered"/>

  <title xpath="self::db:book|self::db:part|self::db:reference"
         group="{if (f:is-true($division-numbers))
                 then 'title-numbered'
                 else 'title-unnumbered'}"/>

  <title xpath="self::db:figure[parent::db:formalgroup]
                |self::db:table[parent::db:formalgroup]
                |self::db:equation[parent::db:formalgroup]
                |self::db:example[parent::db:formalgroup]"
         group="subfigure-title"/>

  <title xpath="self::db:figure|self::db:table|self::db:equation|self::db:example|self::db:procedure"
         group="title-numbered"/>

  <title xpath="self::db:formalgroup"
         group="title-numbered"/>

  <title xpath="self::db:step|self::db:listitem[parent::db:orderedlist]"
         group="title-unnumbered"/>

  <title xpath="self::db:glosssee|self::db:glossseealso"
         group="title-unnumbered"/>

  <title xpath="self::db:see|self::db:seealso"
         group="title-unnumbered"/>

  <title xpath="self::db:question|self::db:answer"
         group="title-numbered"/>

  <title xpath="self::*"
         group="title-unnumbered"/>
</xsl:variable>

<!-- ============================================================ -->

<xsl:function name="fp:title-properties" as="element()?" cache="yes">
  <xsl:param name="this" as="element()"/>

  <xsl:iterate select="$v:title-groups">
    <xsl:variable name="test" as="element()*">
      <xsl:evaluate context-item="$this" xpath="@xpath"/>
    </xsl:variable>

    <xsl:choose>
      <xsl:when test="$test">
        <xsl:sequence select="fp:title-properties-override($test, .)"/>
        <xsl:break/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:next-iteration/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:function>

<xsl:function name="fp:title-properties-override" as="element()" cache="yes">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="properties" as="element()"/>

  <xsl:variable name="numbered-pi"
                select="($context/ancestor-or-self::* ! f:pi(., 'numbered'))[last()]"/>

  <!--
  <xsl:message select="node-name($context), $numbered-pi"/>
  -->

  <xsl:element name="{node-name($properties)}" namespace="{namespace-uri($properties)}">
    <xsl:copy select="$properties/@* except $properties/@group"/>
    <xsl:attribute name="group">
      <xsl:choose>
        <xsl:when test="empty($numbered-pi)">
          <xsl:sequence select="$properties/@group"/>
        </xsl:when>
        <xsl:when test="$numbered-pi[1] = 'true'">
          <xsl:sequence select="'title-numbered'"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="'title-unnumbered'"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
  </xsl:element>
</xsl:function>


<xsl:template match="*" mode="mp:compute-headline-label" as="item()*">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="prop" select="fp:title-properties(.)"/>

  <!--
  <xsl:message select="node-name(.), $purpose, $prop/@group/string()"/>
  -->

  <xsl:variable name="template"
                select="if ($purpose = 'lot')
                        then fp:localization-template(., 'list-of-titles')
                        else fp:localization-template(., $prop/@group)"/>

  <xsl:if test="$template/lt:label">
    <xsl:apply-templates select="." mode="m:headline-label">
      <xsl:with-param name="purpose" select="$purpose"/>
    </xsl:apply-templates>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:headline">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="prop" select="fp:title-properties(.)"/>

  <!-- There's a little bit of a hack here. Turning off numbers
       (e.g., $division-numbers, $component-numbers, or $section-numbers)
       should (usually) effect the list-of-titles as well. But it
       doesn't unless you also override the list-of-titles templates in
       the localization, and that's more work. Surely the flags should
       apply?

       So to support that, if the property group contains 'unnumbered',
       we look for list-of-titles-unnumbered, otherwise we look for
       'list-of-titles'. It's a bit of a hack, but...those boolean
       params are arguably the hack, so...
   -->
  <!--
  <xsl:message select="local-name(.), $purpose, $prop/@group/string()"/>
  <xsl:message select="$prop"/>
  -->

  <xsl:variable name="template" as="element(l:template)">
    <xsl:choose>
      <xsl:when test="$purpose = 'lot' and contains($prop/@group, 'unnumbered')">
        <xsl:sequence select="fp:localization-template(., 'list-of-titles-unnumbered')"/>
      </xsl:when>
      <xsl:when test="$purpose = 'lot'">
        <xsl:sequence select="fp:localization-template(., 'list-of-titles')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="fp:localization-template(., $prop/@group)"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!--
  <xsl:message select="local-name(.), $purpose, $template"/>
  -->

  <xsl:variable name="label" as="item()*">
    <xsl:apply-templates select="." mode="mp:compute-headline-label">
      <xsl:with-param name="purpose" select="$purpose"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="$vp:olinkdb">
    <xsl:attribute name="db-label" select="$label"/>
  </xsl:if>

  <xsl:variable name="title" as="node()*">
    <xsl:if test="$template/lt:content">
      <xsl:apply-templates select="." mode="m:headline-title">
        <xsl:with-param name="purpose" select="$purpose"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:variable>

  <xsl:if test="f:is-true($titleabbrev-passthrough)
                and db:info/db:titleabbrev and not($purpose = 'lot')">
    <script type="text/html" class="titleabbrev">
      <xsl:apply-templates select="$template" mode="mp:localization">
        <xsl:with-param name="context" select="."/>
        <xsl:with-param name="label" select="$label"/>
        <xsl:with-param name="content">
          <xsl:apply-templates select="." mode="m:headline-title">
            <xsl:with-param name="purpose" select="'abbrev'"/>
          </xsl:apply-templates>
        </xsl:with-param>
      </xsl:apply-templates>
    </script>
  </xsl:if>

  <xsl:apply-templates select="$template" mode="mp:localization">
    <xsl:with-param name="context" select="."/>
    <xsl:with-param name="label" select="$label"/>
    <xsl:with-param name="content" select="$title"/>
  </xsl:apply-templates>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:headline-label">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:apply-templates select="." mode="m:headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:appendix" mode="m:headline-label">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:choose>
    <xsl:when test="not(f:is-true($number-single-appendix))
                    and empty(preceding-sibling::db:appendix)
                    and empty(following-sibling::db:appendix)">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match>
        <xsl:with-param name="purpose" select="$purpose"/>
      </xsl:next-match>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:qandaentry" mode="m:headline-label">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:apply-templates select="db:question" mode="m:headline-label">
    <xsl:with-param name="purpose" select="$purpose"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:question" mode="m:headline-label">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="label"
                select="ancestor::db:qandaset[@defaultlabel][1]/@defaultlabel/string()"/>
  <xsl:variable name="label"
                select="if ($label)
                        then $label
                        else $qandaset-default-label"/>

  <xsl:choose>
    <xsl:when test="db:label">
      <xsl:apply-templates select="db:label"/>
    </xsl:when>
    <xsl:when test="$label = 'none'"/>
    <xsl:when test="$label = 'number'">
      <xsl:number from="db:qandaset" level="multiple" select=".."
                  count="db:qandaentry|db:qandadiv"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="$label != 'qanda'">
        <xsl:message
            select="'Unexpected qandaset label: ' || $label || ', using qanda'"/>
      </xsl:if>

      <xsl:sequence select="f:l10n-token(., 'question')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:answer" mode="m:headline-label">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="label"
                select="ancestor::db:qandaset[@defaultlabel][1]/@defaultlabel/string()"/>
  <xsl:variable name="label"
                select="if ($label)
                        then $label
                        else $qandaset-default-label"/>

  <xsl:choose>
    <xsl:when test="db:label">
      <xsl:apply-templates select="db:label"/>
    </xsl:when>
    <xsl:when test="$label = 'none' or $label='number'"/>
    <xsl:when test="$label = 'qanda'">
      <xsl:text>A:</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message
          select="'Unexpected qandaset label: ' || $label || ', using qanda'"/>
      <xsl:text>A:</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" as="item()*" mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:message use-when="$v:debug = 'numeration'"
               select="'No headline number for', local-name(.)"/>
  <!-- just a default... -->
  <xsl:number level="single"/>
</xsl:template>

<xsl:template match="db:orderedlist/db:listitem" as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="prefix" as="item()*">
    <xsl:apply-templates select="parent::*/ancestor::db:listitem[parent::db:orderedlist][1]"
                         mode="m:headline-number">
      <xsl:with-param name="purpose" select="$purpose"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="number" as="xs:integer"
                select="f:orderedlist-item-number(.)[last()]"/>

  <xsl:variable name="format"
                select="f:orderedlist-item-numeration(.)"/>

  <xsl:variable name="formatted-number" as="xs:string?">
    <xsl:if test="exists($format)">
      <xsl:number value="$number" format="{$format}"/>
    </xsl:if>
  </xsl:variable>

  <xsl:if test="exists($formatted-number)">
    <xsl:if test="exists($prefix)">
      <xsl:sequence select="$prefix"/>
      <span class="sep">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="'number-separator'"/>
        </xsl:apply-templates>
      </span>
    </xsl:if>
    <xsl:sequence select="$formatted-number"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:step" as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="prefix" as="item()*">
    <xsl:apply-templates select="ancestor::db:step[1]"
                         mode="m:headline-number">
      <xsl:with-param name="purpose" select="$purpose"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="formatted-number" as="item()*">
    <xsl:number value="fp:number(.)" format="{f:step-numeration(.)}"/>
  </xsl:variable>

  <xsl:if test="exists($formatted-number)">
    <xsl:if test="exists($prefix)">
      <xsl:sequence select="$prefix"/>
      <span class="sep">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="'number-separator'"/>
        </xsl:apply-templates>
      </span>
    </xsl:if>
    <xsl:sequence select="$formatted-number"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:qandadiv" as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:variable name="prefix" as="item()*">
    <xsl:apply-templates select="parent::*"
                         mode="m:headline-number">
      <xsl:with-param name="purpose" select="$purpose"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="number" select="fp:number(.)"/>

  <xsl:variable name="format">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'number-format'"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="exists($number) and exists($format)">
    <xsl:if test="exists($prefix)">
      <xsl:sequence select="$prefix"/>
      <span class="sep">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="'number-separator'"/>
        </xsl:apply-templates>
      </span>
    </xsl:if>
    <xsl:number value="$number" format="{$format}"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:figure[not(parent::db:formal-group)]
                     |db:example[not(parent::db:formal-group)]
                     |db:table[not(parent::db:formal-group)]
                     |db:equation[not(parent::db:formal-group)]
                     |db:procedure
                     |db:qandaset
                     |db:formalgroup"
              as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" select="tokenize($formal-objects-inherit-from, '\s+')"/>

  <xsl:apply-templates select="."
                       mode="mp:format-headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
    <xsl:with-param name="inherit-from" select="$inherit-from"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
              as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" select="tokenize($sections-inherit-from, '\s+')"/>

  <xsl:apply-templates select="."
                       mode="mp:format-headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
    <xsl:with-param name="inherit-from" select="$inherit-from"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:preface|db:chapter|db:appendix|db:partintro
                     |db:dedication|db:colophon|db:acknowledgements
                     |db:article
                     |db:glossary|db:bibliography
                     |db:index|db:setindex"
              mode="m:headline-number"
              as="item()*">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" select="tokenize($components-inherit-from, '\s+')"/>

  <!--
  <xsl:message select="local-name(.), $inherit-from,
                       '============================================================'"/>
  -->

  <xsl:apply-templates select="."
                       mode="mp:format-headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
    <xsl:with-param name="inherit-from" select="$inherit-from"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:part|db:reference" as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" select="tokenize($divisions-inherit-from, '\s+')"/>

  <xsl:apply-templates select="."
                       mode="mp:format-headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
    <xsl:with-param name="inherit-from" select="$inherit-from"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:book" as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" select="tokenize($books-inherit-from, '\s+')"/>
  <xsl:apply-templates select="."
                       mode="mp:format-headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
    <xsl:with-param name="inherit-from" select="$inherit-from"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:set" as="item()*"
              mode="m:headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" select="tokenize($sets-inherit-from, '\s+')"/>
  <xsl:apply-templates select="."
                       mode="mp:format-headline-number">
    <xsl:with-param name="purpose" select="$purpose"/>
    <xsl:with-param name="inherit-from" select="$inherit-from"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:figure[parent::db:formalgroup]
                     |db:table[parent::db:formalgroup]
                     |db:example[parent::db:formalgroup]
                     |db:equation[parent::db:formalgroup]"
              mode="mp:format-headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" as="xs:string*"/>

  <xsl:variable name="prefix" as="item()*">
    <xsl:apply-templates select=".." mode="mp:format-headline-number">
      <xsl:with-param name="purpose" select="$purpose"/>
      <xsl:with-param name="inherit-from" select="$inherit-from"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="number" as="item()*"
                select="fp:number(.)"/>

  <xsl:variable name="format">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'number-format'"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="exists($number) and exists($format)">
    <xsl:if test="exists($prefix)">
      <xsl:sequence select="$prefix"/>
      <span class="sep">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="'number-separator'"/>
        </xsl:apply-templates>
      </span>
    </xsl:if>
    <xsl:number value="$number" format="{$format}"/>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="mp:format-headline-number">
  <xsl:param name="purpose" as="xs:string" required="yes"/>
  <xsl:param name="inherit-from" as="xs:string*"/>

  <xsl:variable name="from" as="element()?">
    <xsl:choose>
      <xsl:when test="$inherit-from = 'section'
                      and (ancestor::db:section|ancestor::db:sect1|ancestor::db:sect2
                           |ancestor::db:sect3|ancestor::db:sect4|ancestor::db:sect5)">
        <xsl:sequence
            select="(ancestor::db:section|ancestor::db:sect1|ancestor::db:sect2
                     |ancestor::db:sect3|ancestor::db:sect4|ancestor::db:sect5)[last()]"/>
      </xsl:when>
      <xsl:when test="$inherit-from = 'component'
                      and (ancestor::db:preface|ancestor::db:chapter
                           |ancestor::db:appendix|ancestor::db:partintro
                           |ancestor::db:dedication|ancestor::db:colophon
                           |ancestor::db:acknowledgements
                           |ancestor::db:article|ancestor::db:refentry
                           |ancestor::db:glossary|ancestor::db:bibliography
                           |ancestor::db:index|ancestor::db:setindex)">
        <xsl:sequence select="(ancestor::db:preface|ancestor::db:chapter
                               |ancestor::db:appendix|ancestor::db:partintro
                               |ancestor::db:dedication|ancestor::db:colophon
                               |ancestor::db:acknowledgements
                               |ancestor::db:article|ancestor::db:refentry
                               |ancestor::db:glossary|ancestor::db:bibliography
                               |ancestor::db:index|ancestor::db:setindex)[last()]"/>
      </xsl:when>
      <xsl:when test="$inherit-from = 'division'
                      and (ancestor::db:part|ancestor::db:reference)">
        <xsl:sequence select="(ancestor::db:part|ancestor::db:reference)[last()]"/>
      </xsl:when>
      <xsl:when test="$inherit-from = 'book' and ancestor::db:book">
        <xsl:sequence select="ancestor::db:book[1]"/>
      </xsl:when>
      <xsl:when test="$inherit-from = 'set' and ancestor::db:set">
        <xsl:sequence select="ancestor::db:set[1]"/>
      </xsl:when>
      <xsl:otherwise>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="label" as="item()*">
    <xsl:apply-templates select="$from" mode="mp:compute-headline-label">
      <xsl:with-param name="purpose" select="$purpose"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="prefix" as="item()*">
    <xsl:apply-templates select="$from" mode="mp:format-headline-number">
      <xsl:with-param name="purpose" select="$purpose"/>
      <xsl:with-param name="inherit-from" select="$inherit-from"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="number" as="item()*"
                select="fp:number(.)"/>

  <!--
  <xsl:message select="'FHN:', local-name(.), 'F:&quot;'||local-name($from)||'&quot;', 
                       'L:&quot;'||string-join($label, ' ')|| '&quot;',
                       'P:', $prefix, ' N:', $number, ':', $inherit-from"/>
  -->

  <xsl:variable name="format">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'number-format'"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="exists($number) and exists($format)">
    <xsl:if test="exists($label) and exists($prefix)">
      <xsl:sequence select="$prefix"/>
      <span class="sep">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="'number-separator'"/>
        </xsl:apply-templates>
      </span>
    </xsl:if>
    <xsl:number value="$number" format="{$format}"/>
  </xsl:if>
</xsl:template>

<xsl:template name="tp:format-number" as="item()*">
  <xsl:param name="prefix" as="item()*"/>

  <xsl:variable name="number" select="fp:number(.)"/>

  <xsl:variable name="format">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'number-format'"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="exists($prefix) and exists($number)">
    <xsl:sequence select="$prefix"/>
    <span class="sep">
      <xsl:apply-templates select="." mode="m:gentext">
        <xsl:with-param name="group" select="'number-separator'"/>
      </xsl:apply-templates>
    </span>
  </xsl:if>

  <xsl:if test="exists($number) and exists($format)">
    <xsl:number value="$number" format="{$format}"/>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" as="item()*"
              mode="mp:headline-number-prefix">
  <xsl:param name="inherit-from" select="()"/>
  <xsl:message>No headline number prefix for <xsl:value-of select="local-name(.)"/></xsl:message>
  <xsl:sequence select="()"/>
</xsl:template>

<xsl:template match="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
              as="item()*" mode="mp:headline-number-prefix">
  <xsl:param name="inherit-from" select="$sections-inherit-from"/>
  <xsl:call-template name="tp:format-number">
    <xsl:with-param name="prefix" as="item()*">
      <xsl:if test="$inherit-from = ('section', 'component', 'division', 'book', 'set')">
        <xsl:apply-templates select="parent::*" mode="mp:headline-number-prefix">
          <xsl:with-param name="inherit-from" select="$inherit-from"/>
        </xsl:apply-templates>
      </xsl:if>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:preface|db:chapter|db:appendix|db:partintro
                     |db:dedication|db:colophon|db:acknowledgements
                     |db:article
                     |db:glossary|db:bibliography
                     |db:index|db:setindex"
              mode="mp:headline-number-prefix"
              as="item()*">
  <xsl:param name="inherit-from" select="$components-inherit-from"/>
  <xsl:if test="$inherit-from = ('component', 'division', 'book', 'set')">
    <xsl:apply-templates select="parent::*" mode="mp:headline-number-prefix">
      <xsl:with-param name="inherit-from" select="$inherit-from"/>
    </xsl:apply-templates>
    <xsl:value-of select="fp:number(.)"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:part|db:reference"
              mode="mp:headline-number-prefix"
              as="item()*">
  <xsl:param name="inherit-from" select="$divisions-inherit-from"/>
  <xsl:if test="$inherit-from = ('division', 'book', 'set')">
    <xsl:apply-templates select="parent::*" mode="mp:headline-number-prefix">
      <xsl:with-param name="inherit-from" select="$inherit-from"/>
    </xsl:apply-templates>
    <xsl:value-of select="fp:number(.)"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:book"
              mode="mp:headline-number-prefix"
              as="item()*">
  <xsl:param name="inherit-from" select="$divisions-inherit-from"/>
  <xsl:if test="$inherit-from = ('book', 'set')">
    <xsl:apply-templates select="parent::*" mode="mp:headline-number-prefix">
      <xsl:with-param name="inherit-from" select="$inherit-from"/>
    </xsl:apply-templates>
    <xsl:value-of select="fp:number(.)"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:set"
              mode="mp:headline-number-prefix"
              as="item()*">
  <xsl:param name="inherit-from" select="$divisions-inherit-from"/>
  <xsl:if test="$inherit-from = ('set')">
    <xsl:apply-templates select="parent::*" mode="mp:headline-number-prefix">
      <xsl:with-param name="inherit-from" select="$inherit-from"/>
    </xsl:apply-templates>
    <xsl:value-of select="fp:number(.)"/>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<!-- Is there a clever XPath I'm overlooking? -->
<xsl:function name="fp:nearest-relevant-ancestor" as="element()">
  <xsl:param name="elem" as="element()"/>
  <xsl:choose>
    <xsl:when test="$elem/self::db:chapter|$elem/self::db:appendix
                    |$elem/self::db:sect1|$elem/self::db:sect2|$elem/self::db:sect3
                    |$elem/self::db:sect4|$elem/self::db:sect5|$elem/self::db:section">
      <xsl:sequence select="$elem"/>
    </xsl:when>
    <xsl:when test="empty($elem/parent::*)">
      <xsl:sequence select="$elem"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:nearest-relevant-ancestor($elem/parent::*)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:headline-title">
  <xsl:param name="purpose" as="xs:string" select="'title'"/>

  <xsl:choose>
    <xsl:when test="$purpose = 'title' or not(db:info/db:titleabbrev)">
      <xsl:apply-templates select="db:info/db:title" mode="m:title">
        <xsl:with-param name="purpose" select="$purpose"/>
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="db:info/db:titleabbrev" mode="m:title">
        <xsl:with-param name="purpose" select="$purpose"/>
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:refentry" mode="m:headline-title">
  <xsl:param name="purpose" as="xs:string" select="'title'"/>
  <xsl:choose>
    <xsl:when test="db:refmeta">
      <xsl:apply-templates select="db:refmeta" mode="m:headline-title">
        <xsl:with-param name="purpose" select="$purpose"/>
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="db:refnamediv/db:refname[1]" mode="m:headline-title">
        <xsl:with-param name="purpose" select="$purpose"/>
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:refmeta" mode="m:headline-title">
  <xsl:param name="purpose" as="xs:string" select="'title'"/>
  <xsl:apply-templates select="db:refentrytitle/node()"/>
  <xsl:apply-templates select="db:manvolnum"/>
</xsl:template>

<xsl:template match="db:refnamediv" mode="m:headline-title">
  <xsl:param name="purpose" as="xs:string" select="'title'"/>
  <xsl:apply-templates select="db:refname[1]" mode="m:headline-title">
    <xsl:with-param name="purpose" select="$purpose"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:refname" mode="m:headline-title">
  <xsl:param name="purpose" as="xs:string" select="'title'"/>
  <xsl:apply-templates mode="m:title">
    <xsl:with-param name="purpose" select="$purpose"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:question" mode="m:headline-title">
  <xsl:param name="purpose" as="xs:string" select="'title'"/>
  <xsl:apply-templates mode="m:title"
      select="(* except (db:label|db:info|db:tip|db:note|db:danger|db:important
                         |db:caution|db:sidebar|db:figure|db:example
                         |db:procedure|db:table|db:equation)
              )[1]">
    <xsl:with-param name="purpose" select="$purpose"/>
  </xsl:apply-templates>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:title|db:titleabbrev" mode="m:title">
  <xsl:param name="purpose" as="xs:string" required="yes"/>

  <xsl:choose>
    <xsl:when test="$purpose = 'title'">
      <xsl:apply-templates/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="title" as="item()*">
        <xsl:apply-templates/>
      </xsl:variable>
      <xsl:apply-templates select="$title" mode="mp:strip-links"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- ============================================================ -->

<xsl:template xmlns:h="http://www.w3.org/1999/xhtml"
              match="h:db-footnote|h:db-annotation" mode="mp:strip-links"/>

<xsl:template xmlns:h="http://www.w3.org/1999/xhtml"
              match="h:a" mode="mp:strip-links">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="element()" mode="mp:strip-links">
  <xsl:copy>
    <xsl:apply-templates select="@*,node()" mode="mp:strip-links"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="attribute()|text()|comment()|processing-instruction()"
              mode="mp:strip-links">
  <xsl:copy/>
</xsl:template>

</xsl:stylesheet>

numbers.xsl

21 templates, 1 function (1 used only in one other module)

Instructions
Function fp:number($node as element()) as xs:integer?
Used in: main.xsl
Template match ≅ *
Mode: mp:label-number
Matches: *
Template match ≅ db:set
Mode: mp:label-number
Matches: db:set
Template match ≅ db:book
Mode: mp:label-number
Matches: db:book
Template match ≅ db:part|db:reference
Mode: mp:label-number
Matches: db:part, db:reference
Template match ≅ db:acknowledgements|db:appendi…
Mode: mp:label-number
Matches: db:acknowledgements, db:appendix, db:article, db:bibliography, db:chapter, db:colophon, db:dedication, db:glossary, db:index, db:partintro, db:preface, db:setindex
Template match ≅ db:refentry|db:refsect1|db:ref…
Mode: mp:label-number
Matches: db:refentry, db:refsect1, db:refsect2, db:refsect3, db:refsection
Template match ≅ db:sect1|db:sect2|db:sect3|db:…
Mode: mp:label-number
Matches: db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section, db:simplesect
Template match ≅ db:figure|db:formalgroup
Mode: mp:label-number
Matches: db:figure, db:formalgroup
Template match ≅ db:formalgroup|db:table
Mode: mp:label-number
Matches: db:formalgroup, db:table
Template match ≅ db:example|db:formalgroup
Mode: mp:label-number
Matches: db:example, db:formalgroup
Template match ≅ db:equation|db:formalgroup
Mode: mp:label-number
Matches: db:equation, db:formalgroup
Template match ≅ db:equation|db:example|db:figu…
Mode: mp:label-number
Matches: db:equation, db:example, db:figure, db:table
Template match ≅ db:procedure
Mode: mp:label-number
Matches: db:procedure
Template match ≅ db:figure
Mode: mp:label-number
Priority: 10
Matches: db:figure
Template match ≅ db:table
Mode: mp:label-number
Priority: 10
Matches: db:table
Template match ≅ db:example
Mode: mp:label-number
Priority: 10
Matches: db:example
Template match ≅ db:equation
Mode: mp:label-number
Priority: 10
Matches: db:equation
Template match ≅ db:procedure
Mode: mp:label-number
Priority: 10
Matches: db:procedure
Template match ≅ db:step as xs:integer?
Mode: mp:label-number
Matches: db:step
Template match ≅ db:qandaset as xs:integer?
Mode: mp:label-number
Matches: db:qandaset
Template match ≅ db:qandadiv as xs:integer?
Mode: mp:label-number
Matches: db:qandadiv
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<!-- ============================================================ -->

<xsl:function name="fp:number" as="xs:integer?" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:apply-templates select="$node" mode="mp:label-number"/>
</xsl:function>

<xsl:template match="*" mode="mp:label-number">
  <xsl:message select="'Error: no numeration scheme for ' || local-name(.)"/>
  <xsl:sequence select="0"/>
</xsl:template>

<xsl:template match="db:set" mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$sets-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number from="db:set" level="single"/>
    </xsl:when>
    <xsl:when test="$sets-number-from = ('set', 'root')">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: sets-number-from='||$sets-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:book" mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$books-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$books-number-from = ('set', 'root')">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: books-number-from='||$books-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:part|db:reference" mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$divisions-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$divisions-number-from = 'set'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:when test="$divisions-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$divisions-number-from = 'book'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:when test="$divisions-number-from = 'root'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: divisions-number-from='||$divisions-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:preface|db:chapter|db:appendix|db:partintro
                     |db:dedication|db:colophon|db:acknowledgements
                     |db:article
                     |db:glossary|db:bibliography
                     |db:index|db:setindex" mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$components-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'set'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'book'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'division'
                    and ancestor::db:part">
      <xsl:number from="db:part" level="any"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'division'
                    and ancestor::db:book">
      <xsl:number from="db:book" level="single"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'division'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:when test="$components-number-from = 'root'">
      <xsl:number level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: components-number-from='||$components-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:refentry|db:refsection|db:refsect1|db:refsect2|db:refsect3"
              mode="mp:label-number">
  <!-- not usually numbered -->
  <xsl:sequence select="()"/>
</xsl:template>

<xsl:template match="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5
                     |db:simplesect"
              mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="parent::db:section|parent::db:sect1|parent::db:sect2
                    | parent::db:sect3|parent::db:sect4|parent::db:sect5">
      <xsl:number from="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                  level="single"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'set'">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'book'">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'division'
                    and ancestor::db:part">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  from="db:part" level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'division'">
      <xsl:number count="db:section[not(parent::db:section)
                                    and not(ancestor::db:part)]
                         |db:sect1[not(ancestor::db:part)]"
                  from="db:book" level="any"/>
    </xsl:when>
    <!-- bibliography and glossary are special because they can be either
         ancestors or preceding elements which impacts out level=any works -->
    <xsl:when test="$sections-number-from = 'component'
                    and (ancestor::db:glossary|ancestor::db:bibliography)">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  from="db:glossary|db:bibliography"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'component'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:section[not(parent::db:section)
                                    and not(ancestor::db:bibliography
                                            |ancestor::db:glossary)]
                         |db:sect1[not(ancestor::db:bibliography
                                       |ancestor::db:glossary)]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'component'">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$sections-number-from = 'root'">
      <xsl:number count="db:section[not(parent::db:section)]|db:sect1"
                  level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: sections-number-from='||$sections-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:figure[not(parent::db:formalgroup)]|db:formalgroup[db:figure]"
              mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$formal-objects-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'set'">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'
                    and (ancestor::db:part|ancestor::db:reference)">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:part|db:reference" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)
                                   and not(ancestor::db:part|ancestor::db:reference)]
                         |db:formalgroup[db:figure
                                         and not(ancestor::db:part|ancestor::db:reference)]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:section|ancestor::db:sect1
                         |ancestor::db:sect2|ancestor::db:sect3
                         |ancestor::db:sect4|ancestor::db:sect5)">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                  level="single"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'root'">
      <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:figure]"
                  level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: formal-objects-number-from='||$formal-objects-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:table[not(parent::db:formalgroup)]|db:formalgroup[db:table]"
              mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$formal-objects-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'set'">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'
                    and (ancestor::db:part|ancestor::db:reference)">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:part|db:reference" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)
                                   and not(ancestor::db:part|ancestor::db:reference)]
                         |db:formalgroup[db:table
                                         and not(ancestor::db:part|ancestor::db:reference)]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:section|ancestor::db:sect1
                         |ancestor::db:sect2|ancestor::db:sect3
                         |ancestor::db:sect4|ancestor::db:sect5)">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                  level="single"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'root'">
      <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:table]"
                  level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: formal-objects-number-from='||$formal-objects-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:example[not(parent::db:formalgroup)]|db:formalgroup[db:example]"
              mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$formal-objects-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'set'">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'
                    and (ancestor::db:part|ancestor::db:reference)">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:part|db:reference" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)
                                   and not(ancestor::db:part|ancestor::db:reference)]
                         |db:formalgroup[db:example
                                         and not(ancestor::db:part|ancestor::db:reference)]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:section|ancestor::db:sect1
                         |ancestor::db:sect2|ancestor::db:sect3
                         |ancestor::db:sect4|ancestor::db:sect5)">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                  level="single"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'root'">
      <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:example]"
                  level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: formal-objects-number-from='||$formal-objects-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:equation[db:info/db:title and not(parent::db:formalgroup)]
                     |db:formalgroup[db:equation]"
              mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$formal-objects-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'set'">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'
                    and (ancestor::db:part|ancestor::db:reference)">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:part|db:reference" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'">
      <xsl:number count="db:equation[db:info/db:title
                                      and not(ancestor::db:formalgroup)
                                      and not(ancestor::db:part|ancestor::db:reference)]
                         |db:formalgroup[db:equation
                                         and not(ancestor::db:part|ancestor::db:reference)]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:section|ancestor::db:sect1
                         |ancestor::db:sect2|ancestor::db:sect3
                         |ancestor::db:sect4|ancestor::db:sect5)">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                  level="single"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'root'">
      <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                         |db:formalgroup[db:equation]"
                  level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: formal-objects-number-from='||$formal-objects-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:figure[parent::db:formalgroup]
                     |db:table[parent::db:formalgroup]
                     |db:example[parent::db:formalgroup]
                     |db:equation[parent::db:formalgroup]"
              mode="mp:label-number">
  <xsl:number from="db:formalgroup"/>
</xsl:template>

<xsl:template match="db:procedure[db:info/db:title]"
              mode="mp:label-number">
  <xsl:choose>
    <xsl:when test="$formal-objects-number-from = 'set'
                    and ancestor::db:set">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:set" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'set'">
      <xsl:number count="db:procedure[db:info/db:title]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'
                    and ancestor::db:book">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'book'">
      <xsl:number count="db:procedure[db:info/db:title]"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'
                    and (ancestor::db:part|ancestor::db:reference)">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:part|db:reference" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'division'">
      <xsl:number count="db:procedure[db:info/db:title
                                      and not(ancestor::db:part|ancestor::db:reference)]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'component'">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:section|ancestor::db:sect1
                         |ancestor::db:sect2|ancestor::db:sect3
                         |ancestor::db:sect4|ancestor::db:sect5)">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                  level="single"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'
                    and (ancestor::db:preface|ancestor::db:chapter
                         |ancestor::db:appendix|ancestor::db:partintro
                         |ancestor::db:dedication|ancestor::db:colophon
                         |ancestor::db:acknowledgements
                         |ancestor::db:article|ancestor::db:refentry
                         |ancestor::db:glossary|ancestor::db:bibliography
                         |ancestor::db:index|ancestor::db:setindex)">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:preface|db:chapter|db:appendix|db:partintro
                        |db:dedication|db:colophon|db:acknowledgements
                        |db:article|db:refentry
                        |db:glossary|db:bibliography
                        |db:index|db:setindex"
                  level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'section'">
      <xsl:number count="db:procedure[db:info/db:title]"
                  from="db:book" level="any"/>
    </xsl:when>
    <xsl:when test="$formal-objects-number-from = 'root'">
      <xsl:number count="db:procedure[db:info/db:title]"
                  level="any"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Error: formal-objects-number-from='||$formal-objects-number-from"/>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- These are for backwards compatibility without adding a bunch of
     new conditions for qandasets. -->
<xsl:template match="db:figure[not(parent::db:formalgroup) and ancestor::db:qandadiv]"
              mode="mp:label-number" priority="10">
  <xsl:number count="db:figure[not(ancestor::db:formalgroup)]
                     |db:formalgroup[db:figure]"
              from="db:qandadiv" level="any"/>
</xsl:template>

<xsl:template match="db:table[not(parent::db:formalgroup) and ancestor::db:qandadiv]"
              mode="mp:label-number" priority="10">
  <xsl:number count="db:table[not(ancestor::db:formalgroup)]
                     |db:formalgroup[db:table]"
              from="db:qandadiv" level="any"/>
</xsl:template>

<xsl:template match="db:example[not(parent::db:formalgroup) and ancestor::db:qandadiv]"
              mode="mp:label-number" priority="10">
  <xsl:number count="db:example[not(ancestor::db:formalgroup)]
                     |db:formalgroup[db:example]"
              from="db:qandadiv" level="any"/>
</xsl:template>

<xsl:template match="db:equation[db:info/db:title
                                 and not(parent::db:formalgroup)
                                 and ancestor::db:qandadiv]"
              mode="mp:label-number" priority="10">
  <xsl:number count="db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                     |db:formalgroup[db:equation]"
              from="db:qandadiv" level="any"/>
</xsl:template>

<xsl:template match="db:procedure[db:info/db:title and ancestor::db:qandadiv]"
              mode="mp:label-number" priority="10">
  <xsl:number count="db:procedure[db:info/db:title]"
              from="db:qandadiv" level="any"/>
</xsl:template>

<xsl:template match="db:step" mode="mp:label-number" as="xs:integer?">
  <xsl:number level="single"/>
</xsl:template>

<xsl:template match="db:qandaset" mode="mp:label-number" as="xs:integer?">
  <xsl:sequence select="()"/>
</xsl:template>

<xsl:template match="db:qandadiv" mode="mp:label-number" as="xs:integer?">
  <xsl:number level="single"/>
</xsl:template>

</xsl:stylesheet>

units.xsl

11 functions (3 unused, 8 used only in one other module), 4 variables (1 unused, 3 used only in one other module)

Instructions
Variable $v:unit-scale as map(*)
Variable $vp:length-regex
Used in: main.xsl
Variable $vp:percent-regex
Unused
Variable $vp:relative-regex
Used in: main.xsl
Function f:length-units($length as xs:string?) as xs:string?
Unused
Function f:absolute-length($length as map(*)) as xs:double
Function f:relative-length($length as map(*)) as xs:double
Used in: main.xsl
Function f:empty-length() as map(*)
Function f:is-empty-length($length as map(*)?) as xs:boolean
Function f:equal-lengths($a as map(*)?, $b as map(*)?) as xs:boolean
Used in: main.xsl
Function f:make-length#1($relative as xs:double) as map(*)
Unused
Function f:make-length#2($magnitude as xs:double, $unit as xs:string) as map(*)
Used in: main.xsl
Function f:make-length#3($relative as xs:double, $magnitude as xs:double, $unit as xs:string) as map(*)
Used in: main.xsl
Function f:length-string($length as map(*)?) as xs:string?
Unused
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m map v vp xs"
                version="3.0">

<xsl:variable name="v:unit-scale" as="map(*)">
  <xsl:map>
    <xsl:map-entry key="'px'" select="1.0"/>
    <xsl:map-entry key="'in'" select="$pixels-per-inch"/>
    <xsl:map-entry key="'m'" select="$pixels-per-inch div 2.54 * 100.0"/>
    <xsl:map-entry key="'cm'" select="$pixels-per-inch div 2.54"/>
    <xsl:map-entry key="'mm'" select="$pixels-per-inch div 25.4"/>
    <xsl:map-entry key="'pt'" select="$pixels-per-inch div 72.0"/>
    <xsl:map-entry key="'pc'" select="$pixels-per-inch div 6.0"/>
    <xsl:map-entry key="'em'" select="$pixels-per-inch div 6.0"/>
    <xsl:map-entry key="'barleycorn'" select="$pixels-per-inch div 3.0"/>
  </xsl:map>
</xsl:variable>

<xsl:variable name="vp:length-regex" select="'^(\d+(\.\d+)?)\s*(\S+)?$'"/>
<xsl:variable name="vp:percent-regex" select="'^(\d+(\.\d+)?)\s*%$'"/>
<xsl:variable name="vp:relative-regex" select="'^(\d+(\.\d+)?)\s*\*(\s*\+\s*(.*))?$'"/>

<xsl:function name="f:parse-length" as="map(*)">
  <xsl:param name="length" as="xs:string?"/>

  <xsl:choose>
    <xsl:when test="empty($length)">
      <xsl:sequence select="f:empty-length()"/>
    </xsl:when>
    <xsl:otherwise>
      <!--
      <xsl:variable name="x" select="'3*+0.25pt'"/>
      <xsl:message select="'matches: ' || $x, matches($x, $vp:relative-regex)"/>
      <xsl:message select="'matches: ', $vp:relative-regex"/>
      <xsl:message select="'matches: ' || $x,
                           matches($x, '^(\d+(\.\d+)?)\s*\*\s*(\+\s*(.*)$)')"/>
      -->

      <xsl:variable name="length" select="normalize-space($length)"/>

      <xsl:variable name="parsed"
                    select="if (matches($length, $vp:relative-regex))
                            then map { 'relative':
                                        xs:decimal(replace($length, $vp:relative-regex, '$1'))
                                     }
                            else map { 'relative': 0.0 }"/>

      <xsl:variable name="length"
                    select="if (matches($length, $vp:relative-regex))
                            then replace($length, $vp:relative-regex, '$4')
                            else $length"/>

      <xsl:choose>
        <xsl:when test="$parsed?relative and $length = ''">
          <xsl:sequence select="map:put($parsed, 'magnitude', 0)
                                => map:put('unit', 'px')"/>
        </xsl:when>
        <xsl:when test="matches($length, $vp:length-regex)">
          <xsl:variable name="mag"
                        select="xs:double(replace($length, $vp:length-regex, '$1'))"/>
          <xsl:variable name="unit"
                        select="replace($length, $vp:length-regex, '$3')"/>
          <xsl:variable name="unit"
                        select="if ($unit = '')
                                then 'px'
                                else $unit"/>

          <xsl:choose>
            <xsl:when test="map:contains($v:unit-scale, $unit) or $unit = '%'">
              <xsl:sequence select="map:put($parsed, 'magnitude', $mag)
                                    => map:put('unit', $unit)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:message expand-text="yes"
                           >Unrecognized unit {$unit}, using default length</xsl:message>
              <xsl:sequence select="map:put($parsed, 'magnitude', $default-length-magnitude)
                                    => map:put('unit', $default-length-unit)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message expand-text="yes"
                       >Unparsable length {$length}, using default length</xsl:message>
          <xsl:sequence select="map:put($parsed, 'magnitude', $default-length-magnitude)
                                => map:put('unit', $default-length-unit)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:length-units" as="xs:string?">
  <xsl:param name="length" as="xs:string?"/>
  <xsl:sequence select="if (exists($length))
                        then f:parse-length($length)?unit
                        else ()"/>
</xsl:function>

<xsl:function name="f:absolute-length" as="xs:double">
  <xsl:param name="length" as="map(*)"/>
  <xsl:choose>
    <xsl:when test="exists($length?magnitude) and exists($length?unit)
                    and map:contains($v:unit-scale, $length?unit)">
      <xsl:sequence select="round($length?magnitude * map:get($v:unit-scale, $length?unit))"/>
    </xsl:when>
    <xsl:otherwise>
      <!-- this should never happen, but ... -->
      <xsl:message>
        <xsl:text>Invalid length (</xsl:text>
        <xsl:value-of select="concat($length?magnitude, $length?unit)"/>
        <xsl:text>), returning 0 for absolute-length</xsl:text>
      </xsl:message>
      <xsl:sequence select="0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:relative-length" as="xs:double">
  <xsl:param name="length" as="map(*)"/>
  <xsl:sequence select="if ($length?relative)
                        then $length?relative
                        else 0.0"/>
</xsl:function>

<xsl:function name="f:empty-length" as="map(*)">
  <xsl:sequence select="map { }"/>
</xsl:function>

<xsl:function name="f:is-empty-length" as="xs:boolean">
  <xsl:param name="length" as="map(*)?"/>
  <xsl:sequence select="empty($length)
                        or (not($length?relative) and not($length?magnitude))"/>
</xsl:function>

<xsl:function name="f:equal-lengths" as="xs:boolean">
  <xsl:param name="a" as="map(*)?"/>
  <xsl:param name="b" as="map(*)?"/>
  <xsl:choose>
    <xsl:when test="f:is-empty-length($a) and f:is-empty-length($b)">
      <xsl:sequence select="true()"/>
    </xsl:when>
    <xsl:when test="f:is-empty-length($a) and not(f:is-empty-length($b))
                    or f:is-empty-length($b) and not(f:is-empty-length($a))">
      <xsl:sequence select="false()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$a?relative eq $b?relative
                            and $a?magnitude eq $b?magnitude
                            and $a?unit eq $b?unit"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:make-length" as="map(*)">
  <xsl:param name="relative" as="xs:double"/>
  <xsl:sequence select="map {
    'relative': $relative
    }"/>
</xsl:function>

<xsl:function name="f:make-length" as="map(*)">
  <xsl:param name="magnitude" as="xs:double"/>
  <xsl:param name="unit" as="xs:string"/>
  <xsl:sequence select="f:make-length(0.0, $magnitude, $unit)"/>
</xsl:function>

<xsl:function name="f:make-length" as="map(*)">
  <xsl:param name="relative" as="xs:double"/>
  <xsl:param name="magnitude" as="xs:double"/>
  <xsl:param name="unit" as="xs:string"/>
  <xsl:sequence select="map {
    'relative': 0.0,
    'magnitude': $magnitude,
    'unit': $unit
    }"/>
</xsl:function>

<xsl:function name="f:length-string" as="xs:string?">
  <xsl:param name="length" as="map(*)?"/>
  <xsl:if test="exists($length)">
    <xsl:variable name="rel"
                  select="if ($length?relative and $length?relative != 0.0)
                          then concat($length?relative, '*')
                          else ''"/>
    <xsl:sequence select="$rel || $length?magnitude || $length?unit"/>
  </xsl:if>
</xsl:function>

</xsl:stylesheet>

gentext.xsl

12 templates, 7 functions (2 unused, 5 used only in one other module)

Instructions
Function f:languages($context as document-node()) as xs:string+
Function f:in-scope-language($target as node()) as xs:string
Unused
Function fp:localization($language as xs:string) as element(l:l10n)
Function fp:localization-template($node as element(), $group as xs:string) as item()*
Function fp:lookup-localization-template($node as element(), $lang as xs:string, $group as xs:string) as element(l:template)?
Used in: main.xsl
Function fp:localization-list($node as element(), $name as xs:string) as item()*
Function fp:lookup-localization-list($node as element(), $lang as xs:string, $name as xs:string) as element(l:list)?
Used in: main.xsl
Template match ≅ l:l10n
Mode: mp:merge-custom
Matches: l:l10n
Template match ≅ l:gentext
Mode: mp:merge-custom
Matches: l:gentext
Template match ≅ l:gentext/l:token
Mode: mp:merge-custom
Matches: l:gentext/l:token
Template match ≅ l:properties
Mode: mp:merge-custom
Matches: l:properties
Template match ≅ l:properties/l:property
Mode: mp:merge-custom
Matches: l:properties/l:property
Template match ≅ l:group
Mode: mp:merge-custom
Matches: l:group
Template match ≅ l:group/l:template
Mode: mp:merge-custom
Matches: l:group/l:template
Template match ≅ l:l10n/l:list
Mode: mp:merge-custom
Matches: l:l10n/l:list
Template match ≅ l:letters/l:l
Mode: mp:merge-custom
Matches: l:letters/l:l
Template match ≅ *
Mode: mp:merge-custom
Matches: *
Template match ≅ *
Mode: m:gentext
Matches: *
Template match ≅ db:answer|db:question
Mode: m:gentext
Matches: db:answer, db:question
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:ls="http://docbook.org/ns/docbook/l10n/source"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:n="http://docbook.org/ns/docbook/l10n/number"
                xmlns:t="http://docbook.org/ns/docbook/l10n/title"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:function name="f:languages" as="xs:string+" cache="yes">
  <xsl:param name="context" as="document-node()"/>

  <xsl:variable name="always"
                select="('en', $default-language, $gentext-language)"/>

  <xsl:sequence
      select="if (exists($gentext-language))
              then distinct-values($always)
              else distinct-values(($always, $context//@xml:lang))"/>
</xsl:function>  

<xsl:function name="f:in-scope-language" as="xs:string" cache="yes">
  <xsl:param name="target" as="node()"/>
  <xsl:sequence select="($target/ancestor-or-self::*[@xml:lang][1]/@xml:lang,
                         $default-language)[1]"/>
</xsl:function>  

<!-- ============================================================ -->

<xsl:function name="fp:localization" as="element(l:l10n)" cache="yes">
  <xsl:param name="language" as="xs:string"/>

  <xsl:variable name="fn-region" select="lower-case($language) =&gt; replace('-', '_')"/>
  <xsl:variable name="fn" select="if (contains($fn-region, '_'))
                                  then substring-before($fn-region, '_')
                                  else ()"/>

  <xsl:variable name="base-locale" as="element(l:l10n)">
    <xsl:choose>
      <xsl:when test="doc-available('../locale/' || $fn-region || '.xml')">
        <xsl:sequence select="doc('../locale/' || $fn-region || '.xml')/l:l10n"/>
      </xsl:when>
      <xsl:when test="exists($fn) and doc-available('../locale/' || $fn || '.xml')">
        <xsl:sequence select="doc('../locale/' || $fn || '.xml')/l:l10n"/>
      </xsl:when>
      <xsl:when test="$language != $default-language">
        <xsl:sequence select="fp:localization($default-language)"/>
      </xsl:when>
      <xsl:when test="doc-available('../locale/en.xml')">
        <xsl:sequence select="doc('../locale/en.xml')/l:l10n"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes"
                     select="'Failed to load localization or fallback localization'"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$v:custom-localizations/ls:locale[@language = $base-locale/@language]">
      <xsl:variable name="custom" as="element(l:l10n)">
        <xsl:apply-templates
            select="$v:custom-localizations/ls:locale[@language = $base-locale/@language]"
            mode="mp:transform-locale"/>
      </xsl:variable>
      <xsl:apply-templates select="$base-locale" mode="mp:merge-custom">
        <xsl:with-param name="custom" as="element(l:l10n)" tunnel="yes"
                        select="$custom"/>
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$base-locale"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:localization-template" as="item()*">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="group" as="xs:string"/>

  <xsl:variable name="lang" select="f:l10n-language($node)"/>

  <xsl:choose>
    <xsl:when test="exists(fp:lookup-localization-template($node, $lang, $group))">
      <xsl:sequence select="fp:lookup-localization-template($node, $lang, $group)"/>
    </xsl:when>
    <xsl:when test="exists(fp:lookup-localization-template($node, $default-language, $group))">
      <xsl:if test="f:is-true($warn-about-missing-localizations)">
        <xsl:message expand-text="yes"
        >No localization for {$group}/{local-name($node)} in {$lang}, using {$default-language}</xsl:message>
      </xsl:if>
      <xsl:sequence select="fp:lookup-localization-template($node, $default-language, $group)"/>
    </xsl:when>
    <xsl:when test="exists(fp:lookup-localization-template($node, 'en', $group))">
      <xsl:if test="f:is-true($warn-about-missing-localizations)">
        <xsl:message expand-text="yes"
        >No localization for {$group}/{local-name($node)} in {$lang}, using en</xsl:message>
      </xsl:if>
      <xsl:sequence select="fp:lookup-localization-template($node, 'en', $group)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="f:is-true($warn-about-missing-localizations)">
        <xsl:message expand-text="yes"
        >No localization for {$group}/{local-name($node)} in {$lang}, using "MISSING"</xsl:message>
      </xsl:if>
      <lt:text>MISSING</lt:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:lookup-localization-template" as="element(l:template)?" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="lang" as="xs:string"/>
  <xsl:param name="group" as="xs:string"/>

  <xsl:variable name="l10n" select="fp:localization($lang)"/>

  <xsl:variable name="templates"
                select="$l10n/l:group[@name=$group]"/>

  <!--
  <xsl:message select="'lookup:', local-name($node), $lang, $group"/>
  -->

  <xsl:iterate select="$templates/l:template">
    <xsl:variable name="result" as="item()*">
      <xsl:evaluate xpath="@match" context-item="$node"/>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="boolean($result)">
        <!--<xsl:message select="'Y:', @match/string()"/>-->
        <xsl:break select="."/>
      </xsl:when>
      <xsl:otherwise>
        <!--<xsl:message select="'N:', @match/string()"/>-->
        <xsl:next-iteration/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:localization-list" as="item()*">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="name" as="xs:string"/>

  <xsl:variable name="lang" select="f:l10n-language($node)"/>

  <xsl:choose>
    <xsl:when test="exists(fp:lookup-localization-list($node, $lang, $name))">
      <xsl:sequence select="fp:lookup-localization-list($node, $lang, $name)"/>
    </xsl:when>
    <xsl:when test="exists(fp:lookup-localization-list($node, $default-language, $name))">
      <xsl:if test="f:is-true($warn-about-missing-localizations)">
        <xsl:message expand-text="yes"
        >No localization list for {$name} in {$lang}, using {$default-language}</xsl:message>
      </xsl:if>
      <xsl:sequence select="fp:lookup-localization-list($node, $default-language, $name)"/>
    </xsl:when>
    <xsl:when test="exists(fp:lookup-localization-list($node, 'en', $name))">
      <xsl:if test="f:is-true($warn-about-missing-localizations)">
        <xsl:message expand-text="yes"
        >No localization list for {$name}in {$lang}, using en</xsl:message>
      </xsl:if>
      <xsl:sequence select="fp:lookup-localization-list($node, 'en', $name)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="f:is-true($warn-about-missing-localizations)">
        <xsl:message expand-text="yes"
        >No localization list for {$name} in {$lang}, using "MISSING"</xsl:message>
      </xsl:if>
      <lt:text>MISSING</lt:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:lookup-localization-list" as="element(l:list)?" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="lang" as="xs:string"/>
  <xsl:param name="name" as="xs:string"/>

  <xsl:variable name="l10n" select="fp:localization($lang)"/>

  <xsl:choose>
    <xsl:when test="$l10n/l:list[@name=$name]">
      <xsl:sequence select="$l10n/l:list[@name=$name]"/>
    </xsl:when>
    <xsl:when test="$l10n/l:list[@name='_default']">
      <xsl:sequence select="$l10n/l:list[@name='_default']"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:mode name="mp:merge-custom" on-no-match="shallow-copy"/>

<xsl:template match="l:l10n" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="this" select="."/>
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="mp:merge-custom"/>

    <xsl:for-each select="$custom/l:group">
      <xsl:variable name="name" select="string(@name)"/>
      <xsl:if test="empty($this/l:group[@name = $name])">
        <xsl:message use-when="'localization' = $v:debug"
                     select="'Add localization group: ' || $name"/>
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>

    <xsl:for-each select="$custom/l:properties">
      <xsl:variable name="name" select="string(@name)"/>
      <xsl:if test="empty($this/l:properties[@name = $name])">
        <xsl:message use-when="'localization' = $v:debug"
                     select="'Add localization properties: ' || $name"/>
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>

    <xsl:for-each select="$custom/l:list">
      <xsl:variable name="name" select="string(@name)"/>
      <xsl:if test="empty($this/l:list[@name = $name])">
        <xsl:message use-when="'localization' = $v:debug"
                     select="'Add localization list: ' || $name"/>
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

<xsl:template match="l:gentext" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="this" select="."/>
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:for-each select="$custom/l:gentext/l:token">
      <xsl:variable name="key" select="string(@key)"/>
      <xsl:if test="empty($this/l:token[@key = $key])">
        <xsl:message use-when="'localization' = $v:debug"
                     select="'Add localization token: ' || $key"/>
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
    <xsl:apply-templates mode="mp:merge-custom"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="l:gentext/l:token" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="key" select="string(@key)"/>
  <xsl:variable name="override"
                select="$custom/l:gentext/l:token[@key=$key]"/>

  <xsl:choose>
    <xsl:when test="exists($override)">
      <xsl:message use-when="'localization' = $v:debug"
                   select="'Override localization token: ' || $key"/>
      <xsl:sequence select="$override"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates mode="mp:merge-custom"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="l:properties" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="this" select="."/>
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:for-each select="$custom/l:properties[@name = $this/@name]/l:property">
      <xsl:variable name="name" select="string(@name)"/>
      <xsl:if test="empty($this/l:property[@name = $name])">
        <xsl:message use-when="'localization' = $v:debug"
                     select="'Add localization property: ' || $this/@name || '/' || $name"/>
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
    <xsl:apply-templates mode="mp:merge-custom"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="l:properties/l:property" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="pname" select="string(../@name)"/>
  <xsl:variable name="name" select="string(@name)"/>
  <xsl:variable name="override"
                select="$custom/l:properties[@name=$pname]/l:property[@name=$name]"/>

  <xsl:choose>
    <xsl:when test="exists($override)">
      <xsl:message use-when="'localization' = $v:debug"
                   select="'Override localization property: ' || $pname || '/' || $name"/>
      <xsl:sequence select="$override"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates mode="mp:merge-custom"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="l:group" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="this" select="."/>
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:for-each select="$custom/l:group[@name = $this/@name]/l:template">
      <xsl:variable name="key" select="string(@key)"/>
      <xsl:if test="empty($this/l:template[@key = $key])">
        <xsl:message use-when="'localization' = $v:debug"
                     select="'Add localization template: ' || $this/@name || '/' || $key"/>
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
    <xsl:apply-templates mode="mp:merge-custom"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="l:group/l:template" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="pname" select="string(../@name)"/>
  <xsl:variable name="key" select="string(@key)"/>
  <xsl:variable name="override"
                select="$custom/l:group[@name=$pname]/l:template[@key=$key]"/>

  <xsl:choose>
    <xsl:when test="exists($override)">
      <xsl:message use-when="'localization' = $v:debug"
                   select="'Override localization template: ' || $pname || '/' || $key"/>
      <xsl:sequence select="$override"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates mode="mp:merge-custom"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="l:l10n/l:list" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="name" select="string(@name)"/>
  <xsl:variable name="override"
                select="$custom/l:list[@name=$name]"/>

  <xsl:choose>
    <xsl:when test="exists($override)">
      <xsl:message use-when="'localization' = $v:debug"
                   select="'Override localization list: ' || $name"/>
      <xsl:sequence select="$override"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates mode="mp:merge-custom"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="l:letters/l:l[exists(node())]" mode="mp:merge-custom">
  <xsl:param name="custom" as="element(l:l10n)" tunnel="yes"/>

  <xsl:variable name="symbol" select="string(.)"/>
  <xsl:variable name="override"
                select="$custom/l:letters/l:l[string(.) = $symbol]"/>

  <xsl:choose>
    <xsl:when test="exists($override)">
      <xsl:message use-when="'localization' = $v:debug"
                   select="'Override localization symbol: ' || $symbol"/>
      <xsl:sequence select="$override"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates mode="mp:merge-custom"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="*" mode="mp:merge-custom">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="mp:merge-custom"/>
  </xsl:copy>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:gentext">
  <xsl:param name="group" as="xs:string"/>
  <xsl:param name="content" as="item()*" select="()"/>

  <xsl:variable name="template"
                select="fp:localization-template(., $group)"/>

  <!--
  <xsl:message select="'GENTEXT:', local-name(.), $group, exists($content), $template"/>>
  -->

  <xsl:apply-templates select="$template" mode="mp:localization">
    <xsl:with-param name="context" select="."/>
    <xsl:with-param name="content" select="$content"/>
    <xsl:with-param name="group" select="$group"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:question|db:answer" mode="m:gentext">
  <xsl:param name="group" as="xs:string"/>
  <xsl:param name="content" as="item()*" select="()"/>

  <!-- This is a weird special case because the default label
       ends in a colon. If we're looking for a label separator,
       and we're using the 'qanda' label style, and the label
       text ends with punctuation, just output a space.
       Note: there are some extra conditionals in here to
       avoid doing extra work if the conditions don't apply. -->

  <xsl:variable name="label" as="xs:string?"
                select="if ($group = 'label-separator')
                        then ancestor::db:qandaset[@defaultlabel][1]/@defaultlabel/string()
                        else ()"/>
  <xsl:variable name="label" as="xs:string"
                select="if ($label)
                        then $label
                        else $qandaset-default-label"/>
  <xsl:variable name="text"
                select="if ($group = 'label-separator' and $label = 'qanda')
                        then f:l10n-token(., local-name(.))
                        else string(db:label)"/>

  <xsl:choose>
    <xsl:when test="$group = 'label-separator' and exists($text)
                    and matches($text, '\p{Po}$')">
      <xsl:sequence select="' '"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match>
        <xsl:with-param name="group" select="$group"/>
        <xsl:with-param name="content" select="$content"/>
      </xsl:next-match>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

l10n.xsl

13 templates (1 used only in one other module), 3 functions (1 used only in one other module)

Instructions
Template match ≅ l:template
Mode: mp:localization
Matches: l:template
Template match ≅ lt:text
Mode: mp:localization
Matches: lt:text
Template match ≅ lt:separator
Mode: mp:localization
Matches: lt:separator
Template match ≅ lt:percent
Mode: mp:localization
Matches: lt:percent
Template match ≅ lt:label
Mode: mp:localization
Matches: lt:label
Template match ≅ lt:content
Mode: mp:localization
Matches: lt:content
Template match ≅ lt:ref
Mode: mp:localization
Matches: lt:ref
Template match ≅ lt:token
Mode: mp:localization
Matches: lt:token
Function f:l10n-token#2($context as element(), $key as xs:string) as item()*
Function f:l10n-token#3($context as element(), $lang as xs:string, $key as xs:string) as item()*
Used in: main.xsl
Function fp:l10n-token($lang as xs:string, $key as xs:string) as item()*
Template match ≅ h:*
Mode: mp:localization
Matches: h:*
Template match ≅ lt:pagenum
Mode: mp:localization
Matches: lt:pagenum
Template match ≅ lt:*
Mode: mp:localization
Matches: lt:*
Template match ≅ *
Mode: m:gentext-list
Matches: *
Template tp:process-list match ≅
Used in: main.xsl
Mode: m:docbook
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="l:template" mode="mp:localization">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="label" as="item()*"/>
  <xsl:param name="content" as="item()*"/>

  <xsl:apply-templates select="*" mode="mp:localization">
    <xsl:with-param name="lang" select="f:l10n-language($context)" tunnel="yes"/>
    <xsl:with-param name="context" select="$context" tunnel="yes"/>
    <xsl:with-param name="label" select="$label" tunnel="yes"/>
    <xsl:with-param name="content" select="$content" tunnel="yes"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="lt:text" mode="mp:localization">
  <xsl:sequence select="node()"/>
</xsl:template>

<xsl:template match="lt:separator" mode="mp:localization">
  <xsl:param name="context" as="element()" tunnel="yes"/>

  <span class="sep">
    <xsl:apply-templates select="$context" mode="m:gentext">
      <xsl:with-param name="group" select="'label-separator'"/>
    </xsl:apply-templates>
  </span>
</xsl:template>

<xsl:template match="lt:percent" mode="mp:localization">
  <xsl:text>%</xsl:text>
</xsl:template>

<xsl:template match="lt:label" mode="mp:localization">
  <xsl:param name="label" as="item()*" tunnel="yes"/>
  <span class="label">
    <xsl:sequence select="$label"/>
  </span>
</xsl:template>

<xsl:template match="lt:content" mode="mp:localization">
  <xsl:param name="content" as="item()*" tunnel="yes"/>

  <xsl:sequence select="$content"/>
</xsl:template>

<xsl:template match="lt:ref" mode="mp:localization">
  <xsl:param name="context" as="element()" tunnel="yes"/>
  <xsl:variable name="ref" as="element()?">
    <xsl:evaluate xpath="@select" context-item="$context"/>
  </xsl:variable>

  <xsl:apply-templates select="$ref"
                       mode="m:crossref">
    <xsl:with-param name="context" select="@context"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="lt:token" mode="mp:localization">
  <xsl:param name="context" as="element()" tunnel="yes"/>
  <xsl:param name="lang" tunnel="yes"/>
  <xsl:sequence select="f:l10n-token($context, $lang, @key)"/>
</xsl:template>

<xsl:function name="f:l10n-token" as="item()*" cache="yes">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="key" as="xs:string"/>

  <xsl:variable name="lang" select="f:l10n-language($context)"/>
  <xsl:sequence select="fp:l10n-token($lang, $key)"/>
</xsl:function>

<xsl:function name="f:l10n-token" as="item()*" cache="yes">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="lang" as="xs:string"/>
  <xsl:param name="key" as="xs:string"/>
  <xsl:sequence select="fp:l10n-token($lang, $key)"/>
</xsl:function>

<xsl:function name="fp:l10n-token" as="item()*" cache="yes">
  <xsl:param name="lang" as="xs:string"/>
  <xsl:param name="key" as="xs:string"/>

  <xsl:variable name="l10n" select="fp:localization($lang)"/>
  <xsl:choose>
    <xsl:when test="exists($l10n/l:gentext/l:token[@key=$key])">
      <xsl:sequence select="($l10n/l:gentext/l:token[@key=$key])[1]/node()"/>
    </xsl:when>
    <xsl:when test="$lang != 'en'">
      <xsl:variable name="l10n" select="fp:localization('en')"/>
      <xsl:choose>
        <xsl:when test="exists($l10n/l:gentext/l:token[@key=$key])">
          <xsl:sequence select="($l10n/l:gentext/l:token[@key=$key])[1]/node()"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message select="'No gentext for ' || $key || ' in ' || $lang || ' or en'"/>
          <xsl:text>MISSING</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'No gentext for ' || $key || ' in ' || $lang"/>
      <xsl:text>MISSING</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="h:*" mode="mp:localization">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="mp:localization"/>
  </xsl:element>
</xsl:template>
  
<!-- page number is not content of html:a, but needs its own html:a element -->  
<xsl:template match="lt:pagenum" mode="mp:localization">
  <xsl:sequence select="."/>
</xsl:template>

<xsl:template match="lt:*" mode="mp:localization">
  <xsl:message terminate="yes"
               select="'Unexpected localization element: ' || node-name(.)"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:gentext-list">
  <xsl:param name="list" as="element()+"/>
  <xsl:param name="name" select="local-name(.)"/>

  <xsl:variable name="template"
                select="fp:localization-list(., $name)"/>

  <xsl:variable name="items" as="element(l:items)?">
    <xsl:iterate select="$template/l:items">
      <xsl:choose>
        <xsl:when test="count(.//lt:content) = count($list)">
          <xsl:break select="."/>
        </xsl:when>
        <xsl:when test="count(.//lt:content) le count($list) and .//l:repeat">
          <xsl:break select="."/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:next-iteration/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:iterate>
  </xsl:variable>

  <xsl:if test="empty($items)">
    <xsl:message expand-text="yes">
      <xsl:text>Failed to select items for {count($list)} element list </xsl:text>
      <xsl:text>in {local-name(.)}.</xsl:text>
    </xsl:message>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="empty($items/l:repeat)">
      <xsl:call-template name="tp:process-list">
        <xsl:with-param name="context" select="."/>
        <xsl:with-param name="lang" select="f:l10n-language(.)"/>
        <xsl:with-param name="list" select="$list"/>
        <xsl:with-param name="template" select="$items/*"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <!-- Assume the items contains (lt:content|lt:text)*, lt:repeat, (lt:content|lt:text)*
           where lt:repeat contains (lt:content|lt:text)* -->
      <xsl:variable name="before" select="$items/l:repeat/preceding-sibling::*"/>
      <xsl:variable name="after" select="$items/l:repeat/following-sibling::*"/>

      <xsl:variable name="bc" select="count($before/ancestor-or-self::lt:content)"/>
      <xsl:variable name="ac" select="count($after/ancestor-or-self::lt:content)"/>

      <xsl:variable name="rt" select="count($items/l:repeat//lt:content)"/>

      <xsl:variable name="ric" select="count($list) - ($ac + $bc)"/>
      <xsl:variable name="rc"
                    select="($ric + $rt - 1) idiv $rt"/>

      <xsl:variable name="expanded-list" as="element()+">
        <xsl:sequence select="$before"/>
        <xsl:for-each select="1 to $rc">
          <xsl:sequence select="$items/l:repeat/*"/>
        </xsl:for-each>
        <xsl:sequence select="$after"/>
      </xsl:variable>

      <xsl:call-template name="tp:process-list">
        <xsl:with-param name="context" select="."/>
        <xsl:with-param name="lang" select="f:l10n-language(.)"/>
        <xsl:with-param name="list" select="$list"/>
        <xsl:with-param name="template" select="$expanded-list"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="tp:process-list">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="lang" as="xs:string"/>
  <xsl:param name="list" as="element()*"/>
  <xsl:param name="template" as="element()*"/>

  <xsl:iterate select="$template">
    <xsl:param name="list" select="$list"/>

    <xsl:choose>
      <xsl:when test="self::lt:text">
        <xsl:sequence select="node()"/>
        <xsl:next-iteration>
          <xsl:with-param name="list" select="$list"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:when test="self::lt:content">
        <xsl:sequence select="$list[1]"/>
        <xsl:next-iteration>
          <xsl:with-param name="list" select="$list[position() gt 1]"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:when test="self::lt:token">
        <xsl:sequence select="f:l10n-token($context, $lang, @key)"/>
        <xsl:next-iteration>
          <xsl:with-param name="list" select="$list"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'Unexpected element in list template:', node-name(.)"/>
        <xsl:next-iteration>
          <xsl:with-param name="list" select="$list"/>
        </xsl:next-iteration>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:template>

</xsl:stylesheet>

functions.xsl

1 include

standalone-functions.xsl

1 template, 21 functions (6 unused, 14 used only in one other module), 2 variables (2 used only in one other module)

Instructions
Variable $v:pi-db-attributes-are-uris as xs:string*
Used in: main.xsl
Variable $vp:pi-match
Used in: main.xsl
Function f:pi#3($context as node()?, $property as xs:string, $default as xs:string*) as xs:string*
Function fp:pi-from-list($pis as processing-instruction()*, $property as xs:string, $default as xs:string*) as xs:string*
Function f:pi-attributes($pis as processing-instruction()*) as element()?
Function fp:pi-attributes($pis as processing-instruction()*, $pimap as map(*)) as map(*)?
Used in: main.xsl
Function fp:pi-pi-attributes($pimap as map(*), $pi as processing-instruction(), $text as xs:string?) as map(*)
Function fp:available-glossaries($root as element(), $collections as xs:string*) as document-node()
Used in: main.xsl
Function f:available-glossaries#1($term as element())
Unused
Function f:available-glossaries#2($term as element(), $collections as xs:string*)
Used in: main.xsl
Function f:glossentries#1($term as element()) as element(db:glossentry)*
Unused
Function f:glossentries#2($term as element(), $collections as xs:string*) as element(db:glossentry)*
Used in: main.xsl
Function f:glossrefs#1($term as element()) as element()*
Unused
Function f:glossrefs#2($term as element(), $root as document-node()) as element()*
Used in: main.xsl
Function fp:baseform($element as element()) as xs:string
Used in: main.xsl
Function fp:available-bibliographies($root as element(), $collections as xs:string*) as document-node()
Used in: main.xsl
Template match ≅ db:biblioentry|db:bibliomixed
Mode: mp:strip-empty-biblioentries
Matches: db:biblioentry, db:bibliomixed
Function f:available-bibliographies#1($term as element())
Unused
Function f:available-bibliographies#2($term as element(), $collections as xs:string*)
Used in: main.xsl
Function f:biblioentries#1($term as element()) as element()*
Unused
Function f:biblioentries#2($term as element(), $collections as xs:string*) as element()*
Function f:citations#1($term as element()) as element()*
Unused
Function f:citations#2($term as element(), $root as document-node()) as element()*
Used in: main.xsl
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:db="http://docbook.org/ns/docbook"
                xmlns:err="http://www.w3.org/2005/xqt-errors" 
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                exclude-result-prefixes="#all"
                version="3.0">
    
<!-- ===================================================================================
|    Functions designed for use independent from the xsTNG Stylesheets 
|    e. g. in Schematron rules  
|=================================================================================== -->

<xsl:key name="glossary-entry" match="db:glossary//db:glossentry"
         use="fp:baseform(db:glossterm)"/>

<xsl:key name="glossary-ref" match="db:glossterm[not(parent::db:glossentry)]|db:firstterm"
         use="fp:baseform(.)"/>

<xsl:key name="citation" match="db:citation" use="normalize-space(.)"/>

<xsl:key name="bibliography-entry"
         match="db:biblioentry[db:abbrev]|db:bibliomixed[db:abbrev]"
         use="db:abbrev ! normalize-space(.)"/>

<!-- ============================================================ -->

<xsl:variable name="v:pi-db-attributes-are-uris" as="xs:string*"
              select="('glossary-collection', 'bibliography-collection',
                       'annotation-collection')"/>
  
<!-- ====================================================================================
|    Functions for processing instructions and their pseudo attributes
|===================================================================================  -->  

<xsl:variable name="vp:pi-match"
              select="'^.*?(\c+)=[''&quot;](.*?)[''&quot;](.*)$'"/>  
  
<xsl:function name="f:pi" as="xs:string?" visibility="public">
  <xsl:param name="context" as="node()?"/>
  <xsl:param name="property" as="xs:string"/>
  <xsl:sequence select="f:pi($context, $property, ())"/>
</xsl:function>  

<xsl:function name="f:pi" as="xs:string*" visibility="public">
  <xsl:param name="context" as="node()?"/>
  <xsl:param name="property" as="xs:string"/>
  <xsl:param name="default" as="xs:string*"/>
  
  <xsl:choose>
    <xsl:when test="empty($context)">
      <xsl:sequence select="$default"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:pi-from-list($context/processing-instruction('db'),
                                            $property, $default)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:pi-from-list" as="xs:string*">
  <xsl:param name="pis" as="processing-instruction()*"/>
  <xsl:param name="property" as="xs:string"/>
  <xsl:param name="default" as="xs:string*"/>

  <xsl:variable name="value"
                select="f:pi-attributes($pis)/@*[local-name(.) = $property]/string()"/>

  <xsl:sequence select="if (empty($value))
                        then $default
                        else $value"/>
</xsl:function>

<xsl:function name="f:pi-attributes" as="element()?">
  <xsl:param name="pis" as="processing-instruction()*"/>

  <xsl:variable name="attributes"
    select="fp:pi-attributes($pis, map { })"/>
  
  <xsl:element name="pis" namespace="">
    <xsl:for-each select="map:keys($attributes)">
      <xsl:attribute name="{.}" select="map:get($attributes, .)"/>
    </xsl:for-each>
  </xsl:element>
</xsl:function>  

<xsl:function name="fp:pi-attributes" as="map(*)?">
  <xsl:param name="pis" as="processing-instruction()*"/>
  <xsl:param name="pimap" as="map(*)"/>
  
  <xsl:choose>
    <xsl:when test="empty($pis)">
      <xsl:sequence select="$pimap"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="map"
                    select="fp:pi-pi-attributes($pimap, $pis[1], normalize-space($pis[1]))"/>
      <xsl:sequence select="fp:pi-attributes(subsequence($pis, 2), $map)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>
  
<xsl:function name="fp:pi-pi-attributes" as="map(*)">
  <xsl:param name="pimap" as="map(*)"/>
  <xsl:param name="pi" as="processing-instruction()"/>
  <xsl:param name="text" as="xs:string?"/>

  <xsl:choose>
    <xsl:when test="matches($text, $vp:pi-match)">
      <xsl:variable name="aname" select="replace($text, $vp:pi-match, '$1')"/>
      <xsl:variable name="avalue" select="replace($text, $vp:pi-match, '$2')"/>
      <xsl:variable name="rest" select="replace($text, $vp:pi-match, '$3')"/>

      <xsl:variable name="avalue-resolved"
                    select="if ($aname = $v:pi-db-attributes-are-uris)
                            then tokenize(normalize-space($avalue), '\s+') ! resolve-uri(., base-uri($pi))
                            else $avalue"/>

      <xsl:variable name="list-value"
                    select="if (map:contains($pimap, $aname))
                            then (map:get($pimap, $aname), $avalue-resolved)
                            else $avalue-resolved"/>
      <xsl:sequence
          select="fp:pi-pi-attributes(map:put($pimap, $aname, $list-value), $pi, $rest)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$pimap"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ===================================================================================
|   Support for automatic glossary and glossary-collection
|=================================================================================== -->

<xsl:function name="fp:available-glossaries" as="document-node()" cache="yes">
  <xsl:param name="root" as="element()"/>
  <xsl:param name="collections" as="xs:string*"/>

  <xsl:variable name="gloss-uris" as="xs:string*">
    <xsl:sequence select="f:pi($root, 'glossary-collection')"/>
    <xsl:sequence select="$collections"/>
  </xsl:variable>

  <xsl:document>
    <!-- It doesn't *need* a single root element, but it feels cleaner -->
    <glossary-collection xmlns="http://docbook.org/ns/docbook">
      <xsl:sequence select="$root//db:glossary"/>
      <xsl:for-each select="tokenize(normalize-space(string-join($gloss-uris, ' ')), '\s+')">
        <xsl:try>
          <xsl:sequence select="doc(xs:anyURI(.))/db:glossary"/>
          <xsl:catch>
            <xsl:message select="'Failed to load glossary: ' || ."/>
            <xsl:message select="'    ' || $err:description"/>
          </xsl:catch>
        </xsl:try>
      </xsl:for-each>
    </glossary-collection>
  </xsl:document>
</xsl:function>

<xsl:function name="f:available-glossaries">
  <xsl:param name="term" as="element()"/>
  <xsl:sequence select="fp:available-glossaries(root($term)/*, ())"/>
</xsl:function>

<xsl:function name="f:available-glossaries">
  <xsl:param name="term" as="element()"/>
  <xsl:param name="collections" as="xs:string*"/>
  <xsl:sequence select="fp:available-glossaries(root($term)/*, $collections)"/>
</xsl:function>

<xsl:function name="f:glossentries" as="element(db:glossentry)*">
  <xsl:param name="term" as="element()"/>
  <xsl:sequence select="f:glossentries($term, ())"/>
</xsl:function>

<xsl:function name="f:glossentries" as="element(db:glossentry)*">
  <xsl:param name="term" as="element()"/>
  <xsl:param name="collections" as="xs:string*"/>

  <xsl:variable name="glossaries" select="f:available-glossaries($term, $collections)"/>

  <xsl:choose>
    <xsl:when test="$term/self::db:glossterm or $term/self::db:firstterm">
      <xsl:sequence select="key('glossary-entry', fp:baseform($term), $glossaries)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message
          select="'Warning: f:glossentries must not be called with '
                  || local-name($term) || ' as $term.'"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:glossrefs" as="element()*">
  <xsl:param name="term" as="element()"/>
  <xsl:sequence select="f:glossrefs($term, root($term))"/>
</xsl:function>

<xsl:function name="f:glossrefs" as="element()*">
  <xsl:param name="term" as="element()"/>
  <xsl:param name="root" as="document-node()"/>
  <xsl:sequence select="key('glossary-ref', fp:baseform($term), $root)"/>
</xsl:function>

<xsl:function name="fp:baseform" as="xs:string">
  <xsl:param name="element" as="element()"/>
  <xsl:sequence select="($element/@baseform, string($element))[1] ! normalize-space(.)"/>
</xsl:function>

<!-- ===================================================================================
|   Support for automatic bibliography and bibliography-collection
|=================================================================================== -->

<xsl:function name="fp:available-bibliographies" as="document-node()" cache="yes">
  <xsl:param name="root" as="element()"/>
  <xsl:param name="collections" as="xs:string*"/>

  <xsl:variable name="bibl-uris" as="xs:string*">
    <xsl:sequence select="f:pi($root, 'bibliography-collection')"/>
    <xsl:sequence select="$collections"/>
  </xsl:variable>

  <xsl:document>
    <!-- It doesn't *need* a single root element, but it feels cleaner -->
    <bibliography-collection xmlns="http://docbook.org/ns/docbook">
      <!-- for internal bibliographies, don't include the empty entries -->
      <xsl:apply-templates select="$root//db:bibliography" mode="mp:strip-empty-biblioentries"/>
      <xsl:for-each select="tokenize(normalize-space(string-join($bibl-uris, ' ')), '\s+')">
        <xsl:try>
          <xsl:sequence select="doc(xs:anyURI(.))/db:bibliography"/>
          <xsl:catch>
            <xsl:message select="'Failed to load bibliography: ' || ."/>
            <xsl:message select="'    ' || $err:description"/>
          </xsl:catch>
        </xsl:try>
      </xsl:for-each>
    </bibliography-collection>
  </xsl:document>
</xsl:function>

<xsl:mode name="mp:strip-empty-biblioentries" on-no-match="shallow-copy"/>
<xsl:template match="db:bibliomixed[empty(node() except db:abbrev)]
                     |db:biblioentry[empty(node() except db:abbrev)]"
              mode="mp:strip-empty-biblioentries">
  <!-- discard -->
</xsl:template>

<xsl:function name="f:available-bibliographies">
  <xsl:param name="term" as="element()"/>
  <xsl:sequence select="fp:available-bibliographies(root($term)/*, ())"/>
</xsl:function>

<xsl:function name="f:available-bibliographies">
  <xsl:param name="term" as="element()"/>
  <xsl:param name="collections" as="xs:string*"/>
  <xsl:sequence select="fp:available-bibliographies(root($term)/*, $collections)"/>
</xsl:function>

<xsl:function name="f:biblioentries" as="element()*">
  <xsl:param name="term" as="element()"/>
  <xsl:sequence select="f:biblioentries($term, ())"/>
</xsl:function>

<xsl:function name="f:biblioentries" as="element()*">
  <xsl:param name="term" as="element()"/>
  <xsl:param name="collections" as="xs:string*"/>

  <xsl:variable name="bibliographies" select="f:available-bibliographies($term, $collections)"/>

  <xsl:choose>
    <xsl:when test="$term/self::db:citation or $term/self::db:abbrev">
      <xsl:sequence select="key('bibliography-entry', normalize-space($term), $bibliographies)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message
          select="'Warning: f:biblioentries must not be called with '
                  || local-name($term) || ' as $term.'"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:citations" as="element()*">
  <xsl:param name="term" as="element()"/>
  <xsl:sequence select="f:citations($term, root($term))"/>
</xsl:function>

<xsl:function name="f:citations" as="element()*">
  <xsl:param name="term" as="element()"/>
  <xsl:param name="root" as="document-node()"/>
  <xsl:sequence select="key('citation', normalize-space($term), $root)"/>
</xsl:function>

</xsl:stylesheet>

38 functions (6 unused, 29 used only in one other module), 2 variables (2 used only in one other module)

Instructions
Variable $vp:translate-suppress-elements
Function f:translate-attribute($node as element()) as xs:boolean?
Function f:attributes#2($node as element(), $attributes as attribute()*) as attribute()*
Used in: main.xsl
Function f:attributes#4($node as element(), $attributes as attribute()*, $extra-classes as xs:string*, $exclude-classes as xs:string*) as attribute()*
Function f:orderedlist-startingnumber($list as element(db:orderedlist)) as xs:integer
Function f:gentext-letters($node as element()) as element(l:letters)
Unused
Function f:gentext-letters-for-language($node as element()) as element(l:letters)
Function fp:properties($context as element(), $properties as array(map(*))) as map(*)
Used in: main.xsl
Function f:date-format($context as element()) as xs:string
Function fp:replace-element#3($lines as array(*), $elemno as xs:integer, $new-elem as item()*) as array(*)
Used in: main.xsl
Function fp:replace-element#5($array as array(*), $elemno as xs:integer, $count as xs:integer, $new-elem as item()*, $newarray as array(*)) as array(*)
Used in: main.xsl
Function f:target($id as xs:string, $context as node()) as element()*
Function f:href($context as node(), $node as element()) as xs:string
Variable $vp:gidmap
Used in: main.xsl
Function f:generate-id#2($node as element(), $use-xml-id as xs:boolean) as xs:string
Function f:unique-id($node as element()) as xs:string
Unused
Function fp:css-properties($context as element()?) as attribute()?
Function f:spaces($length as item()*) as xs:string?
Function fp:lookup-string($context as element(), $lookup as element(), $table-name as xs:string) as node()*
Unused
Function fp:separator($node as element(), $key as xs:string) as node()*
Used in: main.xsl
Function f:label-separator($node as element()) as node()*
Unused
Function fp:parse-key-value-pairs#1($strings as xs:string*) as map(xs:string,xs:string)
Used in: main.xsl
Function fp:parse-key-value-pairs#2($strings as xs:string*, $map as map(xs:string,xs:string)) as map(xs:string,xs:string)
Used in: main.xsl
Function f:refsection($node as element()) as xs:boolean
Used in: main.xsl
Function f:section($node as element()) as xs:boolean
Unused
Function f:section-depth($node as element()?) as xs:integer
Used in: main.xsl
Function f:step-number($node as element(db:step)) as xs:integer+
Used in: main.xsl
Function f:step-numeration($node as element(db:step)) as xs:string
Function f:orderedlist-item-number($node as element(db:listitem)) as xs:integer+
Function f:orderedlist-item-numeration($node as element(db:listitem)) as xs:string
Function f:tokenize-on-char($string as xs:string, $char as xs:string) as xs:string*
Used in: main.xsl
Function f:uri-scheme($uri as xs:string) as xs:string?
Function f:relative-path($base as xs:string, $path as xs:string) as xs:string
Used in: main.xsl
Function f:orientation-class($node as element()) as xs:string?
Function f:conditional-orientation-class($node as element()) as xs:string?
Function f:global-syntax-highlighter($context as node()) as xs:string
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:array="http://www.w3.org/2005/xpath-functions/array"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:err="http://www.w3.org/2005/xqt-errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="array db f fp l m map mp v vp xs"
                version="3.0">

<xsl:include href="../standalone-functions.xsl"/>

<xsl:key name="id" match="*" use="@xml:id"/>
<xsl:key name="genid" match="*" use="generate-id(.)"/>

<xsl:variable name="vp:translate-suppress-elements"
              select="tokenize($translate-suppress-elements, '\s+')"/>

<xsl:function name="f:translate-attribute" as="xs:boolean?">
  <xsl:param name="node" as="element()"/>
  <xsl:if test="local-name($node) = $vp:translate-suppress-elements">
    <xsl:sequence select="false()"/>
  </xsl:if>
</xsl:function>

<xsl:function name="f:attributes" as="attribute()*">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="attributes" as="attribute()*"/>
  <xsl:sequence select="f:attributes($node, $attributes, local-name($node), ())"/>
</xsl:function>

<xsl:function name="f:attributes" as="attribute()*">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="attributes" as="attribute()*"/>
  <xsl:param name="extra-classes" as="xs:string*"/>
  <xsl:param name="exclude-classes" as="xs:string*"/>

  <!--
  <xsl:message>
    <xsl:text>f:attributes(</xsl:text>
    <xsl:value-of select="node-name($node)"/>
    <xsl:text>,</xsl:text>
    <xsl:sequence select="$attributes"/>
    <xsl:text>,</xsl:text>
    <xsl:sequence select="$extra-classes"/>
    <xsl:text>,</xsl:text>
    <xsl:sequence select="$exclude-classes"/>
    <xsl:text>)</xsl:text>
  </xsl:message>
  -->

  <!-- combine duplicates -->
  <xsl:variable name="names"
                select="distinct-values($attributes/node-name())"/>
  <xsl:for-each select="$names">
    <xsl:variable name="name" select="."/>
    <xsl:variable name="values" as="xs:string*"
                  select="$attributes[node-name()=$name]/string()"/>
    <xsl:if test="exists($values)">
      <xsl:attribute name="{$name}"
                     select="string-join($values, ' ')"/>
    </xsl:if>
  </xsl:for-each>

  <!-- if there isn't a class attribute, manufacture one -->
  <xsl:if test="not(QName('', 'class') = $attributes/node-name())">
    <xsl:variable name="roles"
                  select="(tokenize(normalize-space(string-join($extra-classes, ' '))),
                           tokenize(normalize-space($node/@role)),
                           if ($node/@revisionflag)
                           then 'rev'||$node/@revisionflag
                           else ())"/>
    <xsl:variable name="exclude" 
                  select="tokenize(normalize-space(string-join($exclude-classes, ' ')))"/>
    <!-- sort them and make them unique -->
    <xsl:variable name="classes" as="xs:string*">
      <xsl:for-each select="distinct-values($roles)">
        <xsl:sort select="."/>
        <xsl:if test="not(. = $exclude)">
          <xsl:sequence select="."/>
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>

    <xsl:if test="exists($classes)">
      <xsl:attribute name="class" select="string-join($classes, ' ')"/>
    </xsl:if>
  </xsl:if>

  <xsl:variable name="translate" select="f:translate-attribute($node)"/>
  <xsl:if test="exists($translate)">
    <xsl:attribute name="translate" select="if ($translate) then 'yes' else 'no'"/>
  </xsl:if>
</xsl:function>

<xsl:function name="f:is-true" as="xs:boolean" visibility="public">
  <xsl:param name="value"/>

  <xsl:choose>
    <xsl:when test="empty($value)">
      <xsl:value-of select="false()"/>
    </xsl:when>
    <xsl:when test="$value castable as xs:boolean">
      <xsl:value-of select="xs:boolean($value)"/>
    </xsl:when>
    <xsl:when test="$value castable as xs:integer">
      <xsl:value-of select="xs:integer($value) != 0"/>
    </xsl:when>
    <xsl:when test="string($value) = ('true', 'yes')">
      <xsl:value-of select="true()"/>
    </xsl:when>
    <xsl:when test="string($value) = ('false', 'no')">
      <xsl:value-of select="false()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message expand-text="yes" terminate="yes"
                   >Warning: interpreting ‘{$value}’ as true.</xsl:message>
      <xsl:value-of select="true()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:orderedlist-startingnumber" as="xs:integer">
  <xsl:param name="list" as="element(db:orderedlist)"/>

  <xsl:choose>
    <xsl:when test="not($list/@continuation = 'continues')">
      <xsl:sequence select="1"/>
    </xsl:when>
    <xsl:when test="empty($list/preceding::db:orderedlist)">
      <xsl:message>
        <xsl:text>Warning: orderedlist continuation=continues, </xsl:text>
        <xsl:text>but no preceding list</xsl:text>
      </xsl:message>
      <xsl:sequence select="1"/>
    </xsl:when>
    <xsl:otherwise>
      <!-- Hat tip to Gerrit for the fix here -->
      <xsl:variable name="plist"
                    select="$list/outermost(preceding::db:orderedlist)[last()]"/>

      <xsl:sequence select="f:orderedlist-startingnumber($plist)
                            + count($plist/db:listitem)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:l10n-language" as="xs:string" cache="yes">
  <xsl:param name="target" as="element()"/>

  <xsl:variable name="nearest-lang"
                select="$target/ancestor-or-self::*[@xml:lang][1]/@xml:lang"/>

  <xsl:variable name="mc-language" as="xs:string"
                select="if (exists($gentext-language))
                        then $gentext-language
                        else if (exists($nearest-lang) and $nearest-lang = '')
                             then $default-language
                             else ($nearest-lang, $default-language)[1]"/>

  <xsl:variable name="language" select="lower-case($mc-language)"/>

  <xsl:variable name="adjusted-language"
                select="if (contains($language, '-'))
                        then substring-before($language, '-')
                             || '_' || substring-after($language, '-')
                        else $language"/>

  <xsl:choose>
    <xsl:when test="doc-available(
                      resolve-uri($adjusted-language||'.xml', $v:localization-base-uri))">
      <xsl:sequence select="$adjusted-language"/>
    </xsl:when>
    <!-- try just the lang code without country -->
    <xsl:when test="doc-available(
                      resolve-uri(
                        substring-before($adjusted-language, '_')||'.xml', $v:localization-base-uri))">
      <xsl:sequence select="substring-before($adjusted-language,'_')"/>
    </xsl:when>
    <!-- or use the default -->
    <xsl:otherwise>
      <xsl:message>
        <xsl:text>No localization exists for "</xsl:text>
        <xsl:sequence select="$adjusted-language"/>
        <xsl:text>" or "</xsl:text>
        <xsl:sequence select="substring-before($adjusted-language,'_')"/>
        <xsl:text>". Using default "</xsl:text>
        <xsl:sequence select="$default-language"/>
        <xsl:text>".</xsl:text>
      </xsl:message>
      <xsl:sequence select="$default-language"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:gentext-letters" as="element(l:letters)">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="f:gentext-letters-for-language($node)"/>
</xsl:function>

<xsl:function name="f:gentext-letters-for-language" as="element(l:letters)">
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="lang" select="f:l10n-language($node)"/>
  <xsl:variable name="l10n" select="fp:localization($lang)"/>

  <xsl:variable name="letters"
                select="$l10n/l:letters"/>

  <xsl:if test="empty($letters)">
    <xsl:message select="'No letters for', $lang"/>
  </xsl:if>

  <xsl:if test="count($letters) gt 1">
    <xsl:message
        select="'Multiple letters for localization:', $lang"/>
  </xsl:if>

  <xsl:sequence select="$letters[1]"/>
</xsl:function>

<xsl:function name="fp:properties" as="map(*)">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="properties" as="array(map(*))"/>

  <xsl:variable name="props" as="map(*)*">
    <xsl:for-each select="1 to array:size($properties)">
      <xsl:variable name="map" select="array:get($properties, .)"/>
      <xsl:variable name="nodes" as="node()*">
        <xsl:evaluate context-item="$context" xpath="$map?xpath"/>
      </xsl:variable>
      <xsl:if test="exists($nodes)">
        <xsl:sequence select="$map"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="empty($props)">
      <xsl:message use-when="'properties' = $v:debug"
          select="'No properties for ' || local-name($context)"/>
      <xsl:sequence select="map { }"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$props[1]"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:date-format" as="xs:string">
  <xsl:param name="context" as="element()"/>

  <xsl:variable name="format"
                select="f:pi($context, 'date-format')"/>

  <xsl:choose>
    <xsl:when test="$context/*">
      <xsl:sequence select="'apply-templates'"/>
    </xsl:when>
    <xsl:when test="string($context) castable as xs:dateTime">
      <xsl:choose>
        <xsl:when test="$format">
          <xsl:sequence select="$format"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$date-dateTime-format"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:when test="string($context) castable as xs:date">
      <xsl:choose>
        <xsl:when test="$format">
          <xsl:sequence select="$format"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$date-date-format"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="'apply-templates'"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:replace-element" as="array(*)">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:param name="elemno" as="xs:integer"/>
  <xsl:param name="new-elem" as="item()*"/>
  <xsl:sequence
      select="fp:replace-element($lines, $elemno, 1, $new-elem, [])"/>
</xsl:function>

<xsl:function name="fp:replace-element" as="array(*)">
  <!-- See https://saxonica.plan.io/issues/4500 -->
  <!-- reimplement this with array:join when that but is fixed -->
  <xsl:param name="array" as="array(*)"/>
  <xsl:param name="elemno" as="xs:integer"/>
  <xsl:param name="count" as="xs:integer"/>
  <xsl:param name="new-elem" as="item()*"/>
  <xsl:param name="newarray" as="array(*)"/>

  <xsl:choose>
    <xsl:when test="$count gt array:size($array)">
      <xsl:sequence select="$newarray"/>
    </xsl:when>
    <xsl:when test="$count = $elemno">
      <xsl:sequence
          select="fp:replace-element($array, $elemno, $count+1, $new-elem,
                                     array:append($newarray, $new-elem))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence
          select="fp:replace-element($array, $elemno, $count+1, $new-elem,
                                     array:append($newarray, $array($count)))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:target" as="element()*" cache="yes">
  <xsl:param name="id" as="xs:string"/>
  <xsl:param name="context" as="node()"/>
  <xsl:sequence select="key('id', $id, root($context))"/>
</xsl:function>

<xsl:function name="f:href" as="xs:string" cache="yes">
  <xsl:param name="context" as="node()"/>
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="'#' || f:generate-id($node)"/>
</xsl:function>

<xsl:variable name="vp:gidmap" select="map {
  'acknowledgements': 'ack',
  'annotation': 'an',
  'appendix': 'ap',
  'article': 'art',
  'bibliodiv': 'bd',
  'bibliography': 'bi',
  'book': 'bo',
  'chapter': 'ch',
  'colophon': 'co',
  'dedication': 'ded',
  'equation': 'eq',
  'example': 'ex',
  'figure': 'fig',
  'glossary': 'g',
  'glossdiv': 'gd',
  'glossentry': 'ge',
  'glossterm': 'gt',
  'itemizedlist': 'il',
  'listitem': 'li',
  'orderedlist': 'ol',
  'part': 'part',
  'preface': 'p',
  'procedure': 'proc',
  'refentry': 're',
  'reference': 'ref',
  'refsect1': 'rs1_',
  'refsect2': 'rs2_',
  'refsect3': 'rs3_',
  'sect1': 's1_',
  'sect2': 's2_',
  'sect3': 's3_',
  'sect4': 's4_',
  'sect5': 's5_',
  'section': 's',
  'table': 'tab',
  'variablelist': 'vl',
  'varlistentry': 'vle'
  }"/>

<xsl:function name="f:generate-id" as="xs:string" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="f:generate-id($node, true())"/>
</xsl:function>

<xsl:function name="f:generate-id" as="xs:string" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="use-xml-id" as="xs:boolean"/>
  <xsl:choose>
    <xsl:when test="$use-xml-id and $node/@xml:id">
      <xsl:sequence select="$node/@xml:id/string()"/>
    </xsl:when>
    <xsl:when test="empty($node/parent::*)">
      <xsl:sequence select="$generated-id-root"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="aid" select="f:generate-id($node/parent::*, $use-xml-id)"/>
      <xsl:variable name="type" select="(map:get($vp:gidmap, local-name($node)),
                                         local-name($node))[1]"/>
      <xsl:variable name="prec"
                    select="$node/preceding-sibling::*[node-name(.)=node-name($node)]"/>
      <xsl:sequence
          select="$aid || $generated-id-sep || $type || string(count($prec)+1)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:id" as="xs:string" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="if ($node/@xml:id)
                        then $node/@xml:id/string()
                        else f:generate-id($node)"/>
</xsl:function>

<xsl:function name="f:unique-id" as="xs:string" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="f:generate-id($node, false())"/>
</xsl:function>

<xsl:function name="fp:css-properties" as="attribute()?">
  <xsl:param name="context" as="element()?"/>

  <xsl:variable name="generic-attributes" as="map(*)">
    <xsl:map>
      <xsl:for-each select="$context/@css:*" xmlns:css="https://xsltng.docbook.org/ns/css">
        <xsl:map-entry key="local-name(.)" select="string(.)"/>
      </xsl:for-each>
    </xsl:map>
  </xsl:variable>

  <xsl:variable name="media-attributes" as="map(*)">
    <xsl:map>
      <xsl:for-each select="$context/@*">
        <xsl:if test="namespace-uri(.) = 'https://xsltng.docbook.org/ns/css#' || $output-media">
          <xsl:map-entry key="local-name(.)" select="string(.)"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:map>
  </xsl:variable>

  <xsl:variable name="attributes" as="map(*)"
                select="map:merge(($generic-attributes, $media-attributes),
                                  map { 'duplicates': 'use-last' })"/>

  <xsl:if test="map:size($attributes) != 0">
    <xsl:iterate select="map:keys($attributes)">
      <xsl:param name="css" select="''"/>
      <xsl:on-completion>
        <xsl:attribute name="style" select="$css"/>
      </xsl:on-completion>
      <xsl:variable name="name" select="."/>
      <xsl:variable name="value" select="map:get($attributes, .)"/>
      <xsl:next-iteration>
        <xsl:with-param name="css" select="$css || $name || ':' || $value || ';'"/>
      </xsl:next-iteration>
    </xsl:iterate>
  </xsl:if>
</xsl:function>

<xsl:function name="f:spaces" as="xs:string?">
  <xsl:param name="length" as="item()*"/>

  <xsl:choose>
    <xsl:when test="empty($length)"/>
    <xsl:when test="count($length) gt 1">
      <xsl:sequence
          select="f:spaces(string-join($length ! string(.), ''))"/>
    </xsl:when>
    <xsl:when test="$length castable as xs:integer">
      <xsl:variable name="length" select="xs:integer($length)"/>
      <xsl:choose>
        <xsl:when test="$length lt 0"/>
        <xsl:when test="$length lt 10">
          <xsl:sequence select="substring('          ', 1, $length)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="'          ' || f:spaces($length - 10)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="f:spaces(string-length(string($length)))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:lookup-string" as="node()*">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="lookup" as="element()"/>
  <xsl:param name="table-name" as="xs:string"/>

  <xsl:variable name="value"
                select="$lookup/*[node-name(.)=node-name($context)]"/>

  <xsl:if test="count($value) gt 1">
    <xsl:message expand-text="yes"
                 >Duplicate {$table-name} for {node-name($context)}</xsl:message>
  </xsl:if>

  <xsl:sequence select="if (empty($value))
                        then $lookup/db:_default/node()
                        else $value[1]/node()"/>
</xsl:function>

<xsl:function name="fp:separator" as="node()*">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="key" as="xs:string"/>
  <xsl:sequence select="fp:localization-template($node, 'separator')"/>
</xsl:function>

<xsl:function name="f:label-separator" as="node()*">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="fp:separator($node, 'label-separator')"/>
</xsl:function>

<xsl:function name="fp:parse-key-value-pairs" as="map(xs:string,xs:string)">
  <xsl:param name="strings" as="xs:string*"/>
  <xsl:sequence select="fp:parse-key-value-pairs($strings, map { })"/>
</xsl:function>

<xsl:function name="fp:parse-key-value-pairs" as="map(xs:string,xs:string)">
  <xsl:param name="strings" as="xs:string*"/>
  <xsl:param name="map" as="map(xs:string,xs:string)"/>

  <xsl:variable name="car" select="$strings[1]"/>
  <xsl:variable name="cdr" select="subsequence($strings, 2)"/>

  <xsl:variable name="key" select="if (contains($car, ':'))
                                   then substring-before($car, ':')
                                   else '_default'"/>
  <xsl:variable name="value" select="if (contains($car, ':'))
                                     then substring-after($car, ':')
                                     else $car"/>

  <xsl:choose>
    <xsl:when test="empty($car)">
      <xsl:sequence select="$map"/>
    </xsl:when>
    <xsl:when test="map:contains($map, $key)">
      <xsl:message select="'Warning: ignoring duplicate key:', $key"/>
      <xsl:sequence select="fp:parse-key-value-pairs($cdr, $map)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:parse-key-value-pairs($cdr,
                               map:put($map, $key, $value))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:refsection" as="xs:boolean">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="$node/self::db:refsection
                        or $node/self::db:refsect1
                        or $node/self::db:refsect2
                        or $node/self::db:refsect3"/>
</xsl:function> 

<xsl:function name="f:section" as="xs:boolean" visibility="public">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="$node/self::db:section
                        or $node/self::db:sect1
                        or $node/self::db:sect2
                        or $node/self::db:sect3
                        or $node/self::db:sect4
                        or $node/self::db:sect5
                        or f:refsection($node)"/>
</xsl:function>

<xsl:function name="f:section-depth" as="xs:integer" visibility="public">
  <xsl:param name="node" as="element()?"/>
  <xsl:choose>
    <xsl:when test="empty($node)">
      <xsl:value-of select="0"/>
    </xsl:when>
    <xsl:when test="$node/self::db:section">
      <xsl:value-of select="count($node/ancestor::db:section) + 1"/>
    </xsl:when>
    <xsl:when test="$node/self::db:sect1 or $node/self::db:sect2
                    or $node/self::db:sect3 or $node/self::db:sect4
                    or $node/self::db:sect5">
      <xsl:value-of select="xs:integer(substring(local-name($node), 5))"/>
    </xsl:when>
    <xsl:when test="$node/self::db:refsection">
      <xsl:value-of select="count($node/ancestor::db:refsection)+1"/>
    </xsl:when>
    <xsl:when test="$node/self::db:refsect1 or $node/self::db:refsect2
                    or $node/self::db:refsect3">
      <xsl:value-of select="xs:integer(substring(local-name($node), 8))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="f:section-depth($node/parent::*)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:step-number" as="xs:integer+">
  <xsl:param name="node" as="element(db:step)"/>
  <xsl:iterate select="reverse($node/ancestor-or-self::*)">
    <xsl:param name="number" select="()"/>
    <xsl:choose>
      <xsl:when test="self::db:procedure">
        <xsl:sequence select="$number"/>
        <xsl:break/>
      </xsl:when>
      <xsl:when test="self::db:step">
        <xsl:next-iteration>
          <xsl:with-param name="number"
                          select="(count(preceding-sibling::db:step)+1, $number)"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:otherwise>
        <xsl:next-iteration>
          <xsl:with-param name="number" select="$number"/>
        </xsl:next-iteration>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:function>

<xsl:function name="f:step-numeration" as="xs:string">
  <xsl:param name="node" as="element(db:step)"/>
  <xsl:variable name="depth"
                select="count(f:step-number($node))"/>
  <xsl:variable name="depth"
                select="$depth
                        mod string-length($procedure-step-numeration)"/>
  <xsl:variable name="depth"
                select="if ($depth eq 0)
                        then string-length($procedure-step-numeration)
                        else $depth"/>
  <xsl:sequence select="substring($procedure-step-numeration, $depth, 1)"/>
</xsl:function>

<xsl:function name="f:orderedlist-item-number" as="xs:integer+">
  <xsl:param name="node" as="element(db:listitem)"/>
  <xsl:iterate select="reverse($node/ancestor-or-self::*)">
    <xsl:param name="number" select="()"/>
    <xsl:on-completion select="$number"/>
    <xsl:choose>
      <xsl:when test="self::db:listitem[parent::db:orderedlist]">
        <xsl:next-iteration>
          <xsl:with-param name="number"
                          select="(count(preceding-sibling::db:listitem)
                                   + f:orderedlist-startingnumber(parent::db:orderedlist),
                                   $number)"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:otherwise>
        <xsl:next-iteration>
          <xsl:with-param name="number" select="$number"/>
        </xsl:next-iteration>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:function>

<xsl:function name="f:orderedlist-item-numeration" as="xs:string">
  <xsl:param name="node" as="element(db:listitem)"/>

  <xsl:variable name="numeration" select="$node/parent::db:orderedlist/@numeration"/>

  <xsl:choose>
    <xsl:when test="exists($numeration)">
      <xsl:choose>
        <xsl:when test="$numeration = 'upperalpha'">
          <xsl:sequence select="'A'"/>
        </xsl:when>
        <xsl:when test="$numeration = 'loweralpha'">
          <xsl:sequence select="'a'"/>
        </xsl:when>
        <xsl:when test="$numeration = 'upperroman'">
          <xsl:sequence select="'I'"/>
        </xsl:when>
        <xsl:when test="$numeration = 'lowerroman'">
          <xsl:sequence select="'i'"/>
        </xsl:when>
        <xsl:otherwise> <!-- arabic -->
          <xsl:sequence select="'1'"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="depth"
                    select="count(f:orderedlist-item-number($node))"/>
      <xsl:variable name="depth"
                    select="$depth
                            mod string-length($orderedlist-item-numeration)"/>
      <xsl:variable name="depth"
                    select="if ($depth eq 0)
                            then string-length($orderedlist-item-numeration)
                            else $depth"/>
      <xsl:sequence select="substring($orderedlist-item-numeration, $depth, 1)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:tokenize-on-char" as="xs:string*">
  <xsl:param name="string" as="xs:string"/>
  <xsl:param name="char" as="xs:string"/>

  <xsl:variable name="ch" select="substring($char||' ', 1, 1)"/>

  <xsl:variable name="tchar"
                select="if ($ch = ('.', '?', '*', '{', '}', '\', '\[', '\]'))
                        then '\' || $ch
                        else $ch"/>

  <xsl:sequence select="tokenize($string, $tchar)"/>
</xsl:function>

<xsl:function name="f:uri-scheme" as="xs:string?">
  <xsl:param name="uri" as="xs:string"/>

  <xsl:if test="matches($uri, '^[-a-zA-Z0-9]+:')">
    <xsl:sequence select="replace($uri, '^([-a-zA-Z0-9]+):.*$', '$1')"/>
  </xsl:if>
</xsl:function>

<xsl:function name="f:relative-path" as="xs:string">
  <xsl:param name="base" as="xs:string"/>
  <xsl:param name="path" as="xs:string"/>

  <xsl:choose>
    <xsl:when test="exists(f:uri-scheme($path)) and f:uri-scheme($path) ne 'file'">
      <!-- It starts with a non-file: scheme, just assume it's absolute. -->
      <xsl:sequence select="$path"/>
    </xsl:when>
    <xsl:otherwise>
      <!-- Strip away variant forms of file: (file:/, file://, file:///, ...) -->
      <!-- In particular, file:/C:/path vs. file:///C:/path on Windows -->
      <xsl:variable name="bpath" select="replace($base, '^file:/+', '')"/>
      <xsl:variable name="ppath" select="replace($path, '^file:/+', '')"/>

      <xsl:variable name="base-parts" select="tokenize($bpath, '/')[position() lt last()]"/>
      <xsl:variable name="path-parts" select="tokenize($ppath, '/')"/>

      <!--
          <xsl:message select="'BP:', $base-parts"/>
          <xsl:message select="'PP:', $path-parts"/>
      -->

      <xsl:variable name="common-prefix" as="xs:string*">
        <xsl:iterate select="$base-parts">
          <xsl:param name="pos" select="1"/>
          <xsl:param name="common" select="()"/>
          <xsl:on-completion select="$common"/>
          <xsl:if test="$base-parts[$pos] = $path-parts[$pos]">
            <xsl:next-iteration>
              <xsl:with-param name="pos" select="$pos + 1"/>
              <xsl:with-param name="common" select="($common, $base-parts[$pos])"/>
            </xsl:next-iteration>
          </xsl:if>
        </xsl:iterate>
      </xsl:variable>

      <!--
          <xsl:message select="'CP:', $common-prefix"/>
      -->

      <xsl:variable name="base-tail"
                    select="$base-parts[position() gt count($common-prefix)]"/>
      <xsl:variable name="path-tail"
                    select="$path-parts[position() gt count($common-prefix)]"/>

      <!--
          <xsl:message select="'BT:', $base-tail"/>
          <xsl:message select="'PT:', $path-tail"/>
      -->

      <xsl:variable name="final-parts" as="xs:string*">
        <xsl:for-each select="1 to count($base-tail)">
          <xsl:sequence select="'..'"/>
        </xsl:for-each>
        <xsl:sequence select="$path-tail"/>
      </xsl:variable>

      <xsl:sequence select="string-join($final-parts, '/')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>  

<xsl:function name="f:orientation-class" as="xs:string?">
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="nearest"
                select="($node/ancestor-or-self::*[contains-token(@role,'landscape')
                                                   or contains-token(@role,'portrait')]
                         | $node/ancestor-or-self::db:table[@orient]
                         | $node/ancestor-or-self::db:informaltable[@orient])[last()]"/>

  <xsl:choose>
    <xsl:when test="$output-media != 'print'"/>
    <xsl:when test="$nearest/@orient and $nearest/@orient = 'land'">
      <xsl:sequence select="'landscape'"/>
    </xsl:when>
    <xsl:when test="$nearest/@orient">
      <xsl:sequence select="'portrait'"/>
    </xsl:when>
    <xsl:when test="contains-token($nearest/@role, 'landscape')">
      <xsl:sequence select="'landscape'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="'portrait'"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:conditional-orientation-class" as="xs:string?">
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="parent" select="$node/parent::element() ! f:orientation-class(.)"/>
  <xsl:variable name="orient" select="f:orientation-class($node)"/>

<!--
  <xsl:message select="node-name($node), 'P:', $parent, ' O:', $orient"/>

      <xsl:sequence select="$orient"/>
-->
  <xsl:choose>
    <xsl:when test="exists($parent) and $parent eq $orient"/>
    <xsl:otherwise>
      <xsl:sequence select="$orient"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:global-syntax-highlighter" as="xs:string" cache="yes">
  <xsl:param name="context" as="node()"/>
  <xsl:choose>
    <xsl:when test="f:pi($context/root()/*, 'syntax-highlighter')">
      <xsl:sequence select="f:pi($context/root()/*, 'syntax-highlighter')"/>
    </xsl:when>
    <xsl:when test="f:pi($context/root()/*/db:info, 'syntax-highlighter')">
      <xsl:sequence select="f:pi($context/root()/*/db:info, 'syntax-highlighter')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$verbatim-syntax-highlighter"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>
  
</xsl:stylesheet>

toc.xsl

33 templates (3 used only in one other module)

Instructions
Template match ≅ *
Calls: tp:toc
Mode: m:toc
Matches: *
Template match ≅ /db:article|db:book|db:part|db…
Uses: $auto-toc
Calls: tp:toc
Mode: m:toc
Matches: /db:article, db:book, db:part, db:reference, db:set
Template match ≅ *
Mode: m:persistent-toc
Matches: *
Template match ≅ comment()|processing-instructi…
Mode: m:persistent-toc
Matches: comment(), processing-instruction(), text()
Template match ≅ /db:article|db:book|db:part|db…
Calls: tp:toc
Mode: m:persistent-toc
Matches: /db:article, db:book, db:part, db:reference, db:set
Template tp:toc match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ *
Mode: m:toc-entry
Matches: *
Template match ≅ comment()|processing-instructi…
Mode: m:toc-entry
Matches: comment(), processing-instruction(), text()
Template match ≅ db:acknowledgements|db:appendi…
Calls: f:id()
Mode: m:toc-entry
Matches: db:acknowledgements, db:appendix, db:article, db:bibliography, db:book, db:chapter, db:dedication, db:glossary, db:index, db:part, db:preface, db:reference, db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section, db:set, db:topic
Template match ≅ db:refentry
Mode: m:toc-entry
Priority: 100
Matches: db:refentry
Template match ≅ *
Mode: m:toc-entry
Priority: 10
Matches: *
Template match ≅ db:bibliodiv|db:colophon|db:gl…
Mode: m:toc-entry
Matches: db:bibliodiv, db:colophon, db:glossdiv, db:indexdiv
Template match ≅ *
Mode: m:toc-nested
Matches: *
Template match ≅ comment()|processing-instructi…
Mode: m:toc-nested
Matches: comment(), processing-instruction(), text()
Template match ≅ db:equation|db:example|db:figu…
Mode: m:toc-nested
Matches: db:equation, db:example, db:figure, db:formalgroup, db:procedure, db:table
Template match ≅ db:refsect1|db:refsect2|db:ref…
Mode: m:toc-nested
Matches: db:refsect1, db:refsect2, db:refsect3, db:refsection, db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section
Template match ≅ *
Mode: m:list-of-figures
Matches: *
Template match ≅ db:book|db:set
Mode: m:list-of-figures
Matches: db:book, db:set
Template match ≅ *
Mode: m:list-of-tables
Matches: *
Template match ≅ db:book|db:set
Mode: m:list-of-tables
Matches: db:book, db:set
Template match ≅ *
Mode: m:list-of-examples
Matches: *
Template match ≅ db:book|db:set
Mode: m:list-of-examples
Matches: db:book, db:set
Template match ≅ *
Mode: m:list-of-equations
Matches: *
Template match ≅ db:book|db:set
Mode: m:list-of-equations
Matches: db:book, db:set
Template match ≅ *
Mode: m:list-of-procedures
Matches: *
Template match ≅ db:book|db:set
Mode: m:list-of-procedures
Matches: db:book, db:set
Template tp:list-of-titles match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ *
Calls: f:id()
Mode: m:list-of-titles
Matches: *
Template match ≅ db:toc|processing-instruction(…
Mode: m:docbook
Matches: db:toc, processing-instruction('db-toc')
Template match ≅ db:toc
Mode: m:docbook
Matches: db:toc
Template match ≅ db:tocdiv
Mode: m:docbook
Matches: db:tocdiv
Template match ≅ db:tocentry
Mode: m:docbook
Matches: db:tocentry
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:mode name="m:toc" on-no-match="shallow-skip"/>

<xsl:template match="*" mode="m:toc">
  <xsl:param name="placed-by-author" select="false()"/>
  <xsl:if test="$placed-by-author">
    <xsl:call-template name="tp:toc">
      <xsl:with-param name="persistent" select="false()" tunnel="yes"/>
      <xsl:with-param name="root-element" select="." tunnel="yes"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template match="/db:article|db:set|db:book|db:part|db:reference"
              mode="m:toc">
  <xsl:param name="placed-by-author" select="false()"/>
  <xsl:if test="$placed-by-author
                or (f:is-true($auto-toc)
                    and not(db:toc|processing-instruction('db-toc')))">
    <xsl:call-template name="tp:toc">
      <xsl:with-param name="persistent" select="false()" tunnel="yes"/>
      <xsl:with-param name="root-element" select="." tunnel="yes"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<!-- By default, the persistent ToC is the same as the regular ToC.
     Customize the m:persistent-toc templates to change this. -->
<xsl:template match="*" mode="m:persistent-toc"/>
<xsl:template match="text()|processing-instruction()|comment()" mode="m:persistent-toc"/>
<xsl:template match="/db:article|db:set|db:book|db:part|db:reference"
              mode="m:persistent-toc">
  <xsl:call-template name="tp:toc">
    <xsl:with-param name="persistent" select="true()" tunnel="yes"/>
    <xsl:with-param name="root-element" select="." tunnel="yes"/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="tp:toc">
  <xsl:param name="persistent" as="xs:boolean" tunnel="yes"/>
  <xsl:param name="root-element" as="element()" tunnel="yes"/>

  <xsl:variable name="entries" as="element()*">
    <xsl:apply-templates mode="m:toc-entry">
      <xsl:with-param name="persistent" select="$persistent" tunnel="yes"/>
      <xsl:with-param name="root-element" select="$root-element" tunnel="yes"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:variable name="lists-of-titles" as="element()*">
    <xsl:apply-templates select="." mode="m:list-of-figures"/>
    <xsl:apply-templates select="." mode="m:list-of-tables"/>
    <xsl:apply-templates select="." mode="m:list-of-examples"/>
    <xsl:apply-templates select="." mode="m:list-of-equations"/>
    <xsl:apply-templates select="." mode="m:list-of-procedures"/>
  </xsl:variable>

  <xsl:variable name="size" select="count($entries/descendant-or-self::h:li)
                                    + count($lists-of-titles//h:li)"/>
  
  <xsl:where-populated>
    <div class="list-of-titles">
      <xsl:if test="$size gt 1 or (f:is-true($generate-trivial-toc) and $size eq 1)">
        <div class="lot toc">
          <div class="title">
            <xsl:apply-templates select="." mode="m:gentext">
              <xsl:with-param name="group" select="'table-of-contents'"/>
            </xsl:apply-templates>
          </div>
          <ul class="toc">
            <xsl:sequence select="$entries"/>
          </ul>
        </div>
        <xsl:sequence select="$lists-of-titles"/>
      </xsl:if>
    </div>
  </xsl:where-populated>
</xsl:template>  

<xsl:template match="*" mode="m:toc-entry"/>
<xsl:template match="text()|processing-instruction()|comment()" mode="m:toc-entry"/>
<xsl:template mode="m:toc-entry"
              match="db:set|db:book|db:part|db:reference

                     |db:preface|db:chapter|db:appendix|db:article
                     |db:topic|db:part|db:reference|db:dedication
                     |db:bibliography|db:index|db:glossary
                     |db:acknowledgements

                     |db:article
                     |db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5
                     |db:topic
                     ">
  <xsl:param name="persistent" as="xs:boolean" tunnel="yes"/>
  <xsl:param name="root-element" as="element()" tunnel="yes"/>
  <li>
    <a href="#{f:id(.)}">
      <xsl:apply-templates select="." mode="m:headline">
        <xsl:with-param name="purpose" select="'lot'"/>
      </xsl:apply-templates>
    </a>
    <xsl:where-populated>
      <ul class="toc">
        <xsl:apply-templates mode="m:toc-nested">
          <xsl:with-param name="persistent" select="$persistent" tunnel="yes"/>
          <xsl:with-param name="root-element" select="$root-element" tunnel="yes"/>
        </xsl:apply-templates>
      </ul>
    </xsl:where-populated>
  </li>
</xsl:template>

<xsl:template match="db:refentry" mode="m:toc-entry" priority="100">
  <xsl:variable name="refmeta" select=".//db:refmeta"/>
  <xsl:variable name="refentrytitle" select="$refmeta//db:refentrytitle"/>
  <xsl:variable name="refnamediv" select=".//db:refnamediv"/>
  <xsl:variable name="refname" select="$refnamediv//db:refname"/>

  <xsl:variable name="title">
    <xsl:choose>
      <xsl:when test="$refentrytitle">
        <xsl:apply-templates select="$refentrytitle[1]">
          <xsl:with-param name="purpose" select="'lot'"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="$refnamediv/db:refdescriptor">
        <xsl:apply-templates select="($refnamediv/db:refdescriptor)[1]">
          <xsl:with-param name="purpose" select="'lot'"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="$refname">
        <xsl:apply-templates select="$refname[1]">
          <xsl:with-param name="purpose" select="'lot'"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise></xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <li>
    <span class='refentrytitle'>
      <a href="#{f:id(.)}">
        <xsl:sequence select="$title"/>
      </a>
    </span>
    <xsl:if test="f:is-true($annotate-toc)">
      <xsl:apply-templates select="(db:refnamediv/db:refpurpose)[1]">
        <xsl:with-param name="purpose" select="'lot'"/>
      </xsl:apply-templates>
    </xsl:if>
  </li>
</xsl:template>

<xsl:template match="*[not(db:info/db:title)]" mode="m:toc-entry"
              priority="10">
  <!-- things without titles don't appear in the, uh, lists of titles -->
  <!-- preface, dedication, acknowledgements, colophon, equation,
       and procedure spring to mind... -->
</xsl:template>

<xsl:template match="db:colophon|db:bibliodiv|db:glossdiv|db:indexdiv"
              mode="m:toc-entry">
  <!-- by default, these don't appear in the ToC -->
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:toc-nested">
  <xsl:apply-templates select="." mode="m:toc-entry"/>
</xsl:template>
<xsl:template match="text()|processing-instruction()|comment()" mode="m:toc-nested"/>

<xsl:template match="db:formalgroup
                     |db:figure|db:table|db:example|db:equation|db:procedure"
              mode="m:toc-nested">
  <!-- these don't nest -->
</xsl:template>

<xsl:template match="db:section|db:sect1|db:sect2|db:sect3|db:sect4|db:sect5
                     |db:refsection|db:refsect1|db:refsect2|db:refsect3"
              mode="m:toc-nested">
  <xsl:param name="persistent" as="xs:boolean" tunnel="yes"/>
  <xsl:param name="root-element" as="element()" tunnel="yes"/>

  <xsl:variable name="depth" as="xs:integer">
    <xsl:choose>
      <xsl:when test="self::db:section">
        <xsl:sequence select="count(ancestor::db:section)+1"/>
      </xsl:when>
      <xsl:when test="self::db:refsection">
        <xsl:sequence select="count(ancestor::db:refsection)+1"/>
      </xsl:when>
      <xsl:when test="self::db:sect5">
        <xsl:sequence select="5"/>
      </xsl:when>
      <xsl:when test="self::db:sect4">
        <xsl:sequence select="4"/>
      </xsl:when>
      <xsl:when test="self::db:sect3 or self::db:refsect3">
        <xsl:sequence select="3"/>
      </xsl:when>
      <xsl:when test="self::db:sect2 or self::db:refsect2">
        <xsl:sequence select="2"/>
      </xsl:when>
      <xsl:when test="self::db:sect1 or self::db:refsect1">
        <xsl:sequence select="1"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'ERROR: no section matched in m:toc-nested: ' || node-name(.)"/>
        <xsl:sequence select="0"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:if test="$vp:section-toc-depth ge $depth">
    <xsl:apply-templates select="." mode="m:toc-entry"/>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:list-of-figures"/>
<xsl:template match="db:set|db:book" mode="m:list-of-figures">
  <xsl:if test="f:is-true($lists-of-figures)">
    <xsl:call-template name="tp:list-of-titles">
      <xsl:with-param name="elements"
                      select=".//db:figure[not(ancestor::db:formalgroup)]
                              |.//db:formalgroup[db:figure]"/>
      <xsl:with-param name="class" select="'list-of-figures'"/>
      <xsl:with-param name="group" select="'list-of-figures'"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="m:list-of-tables"/>
<xsl:template match="db:set|db:book" mode="m:list-of-tables">
  <xsl:if test="f:is-true($lists-of-tables)">
    <xsl:call-template name="tp:list-of-titles">
      <xsl:with-param name="elements"
                      select=".//db:table[not(ancestor::db:formalgroup)]
                              |.//db:formalgroup[db:table]"/>
      <xsl:with-param name="class" select="'list-of-tables'"/>
      <xsl:with-param name="group" select="'list-of-tables'"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="m:list-of-examples"/>
<xsl:template match="db:set|db:book" mode="m:list-of-examples">
  <xsl:if test="f:is-true($lists-of-examples)">
    <xsl:call-template name="tp:list-of-titles">
      <xsl:with-param name="elements"
                      select=".//db:example[not(ancestor::db:formalgroup)]
                              |.//db:formalgroup[db:example]"/>
      <xsl:with-param name="class" select="'list-of-examples'"/>
      <xsl:with-param name="group" select="'list-of-examples'"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="m:list-of-equations"/>
<xsl:template match="db:set|db:book" mode="m:list-of-equations">
  <xsl:if test="f:is-true($lists-of-equations)">
    <xsl:call-template name="tp:list-of-titles">
      <xsl:with-param
          name="elements"
          select=".//db:equation[db:info/db:title and not(ancestor::db:formalgroup)]
                  |.//db:formalgroup[db:equation]"/>
      <xsl:with-param name="class" select="'list-of-equations'"/>
      <xsl:with-param name="group" select="'list-of-equations'"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="m:list-of-procedures"/>
<xsl:template match="db:set|db:book" mode="m:list-of-procedures">
  <xsl:if test="f:is-true($lists-of-procedures)">
    <xsl:call-template name="tp:list-of-titles">
      <xsl:with-param name="elements" select=".//db:procedure[db:info/db:title]"/>
      <xsl:with-param name="class" select="'list-of-procedures'"/>
      <xsl:with-param name="group" select="'list-of-procedures'"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template name="tp:list-of-titles">
  <xsl:param name="elements" as="element()*" required="yes"/>
  <xsl:param name="class" as="xs:string" required="yes"/>
  <xsl:param name="group" as="xs:string" required="yes"/>

  <xsl:if test="$elements">
    <div class="{$class} lot">
      <div class="title">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="$group"/>
        </xsl:apply-templates>
      </div>
      <ul class="toc">
        <xsl:apply-templates select="$elements" mode="m:list-of-titles"/>
      </ul>
    </div>
  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="m:list-of-titles">
  <li>
    <a href="#{f:id(.)}">
      <xsl:apply-templates select="." mode="m:headline">
        <xsl:with-param name="purpose" select="'lot'"/>
      </xsl:apply-templates>
    </a>
  </li>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:toc[not(*)] | processing-instruction('db-toc')">
  <xsl:apply-templates select=".." mode="m:toc">
    <xsl:with-param name="placed-by-author" select="true()"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:toc[*]">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <div class="lot toc">
      <xsl:apply-templates select="." mode="m:generate-titlepage"/>
      <xsl:where-populated>
        <ul>
          <xsl:apply-templates select="db:tocdiv|db:tocentry"/>
        </ul>
      </xsl:where-populated>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:tocdiv">
  <li>
    <xsl:call-template name="tp:tocentry-link">
      <xsl:with-param name="content" as="node()">
        <xsl:apply-templates select="db:info/db:title/node()"/>
      </xsl:with-param>
    </xsl:call-template>
    <xsl:where-populated>
      <ul>
        <xsl:apply-templates select="db:tocdiv|db:tocentry"/>
      </ul>
    </xsl:where-populated>
  </li>
</xsl:template>

<xsl:template match="db:tocentry">
  <li>
    <xsl:call-template name="tp:tocentry-link">
      <xsl:with-param name="content" as="node()">
        <xsl:apply-templates/>
      </xsl:with-param>
    </xsl:call-template>
  </li>
</xsl:template>

<xsl:template name="tp:tocentry-link">
  <xsl:param name="content" as="node()*"/>

  <xsl:variable name="target" select="if (@linkend)
                                      then key('id', @linkend)
                                      else ()"/>
  <xsl:choose>
    <xsl:when test="not(@linkend)">
      <xsl:sequence select="$content"/>
    </xsl:when>
    <xsl:when test="empty($target)">
      <xsl:message select="'Link to non-existent ID: ' || @linkend"/>
      <xsl:sequence select="$content"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="tp:link">
        <xsl:with-param name="href" select="f:href(., $target)"/>
        <xsl:with-param name="content" select="$content"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

divisions.xsl

2 templates

Instructions
Template match ≅ db:book|db:part|db:partintro|d…
Mode: m:docbook
Matches: db:book, db:part, db:partintro, db:reference, db:set
Template match ≅ *
Mode: m:back-cover
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m t xs"
                version="3.0">

<xsl:template match="db:set|db:book|db:part|db:reference|db:partintro">
  <article>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="db:partintro"/>
    <xsl:apply-templates select="." mode="m:toc"/>
    <xsl:apply-templates select="node() except db:partintro"/>
    <xsl:if test="self::db:set|self::db:book">
      <xsl:apply-templates select="." mode="m:back-cover"/>
    </xsl:if>
  </article>
</xsl:template>

<xsl:template match="*" mode="m:back-cover">
  <!--
  <section class="back-cover">
    <xsl:if test="not($output-media = 'print')">
      <xsl:attribute name="db-chunk" select="'back-cover' || $html-extension"/>
      <xsl:attribute name="db-navigable" select="'true'"/>
    </xsl:if>
    <p>Contents of back cover goes here</p>
  </section>
  -->
</xsl:template>

</xsl:stylesheet>

components.xsl

1 template

Instructions
Template match ≅ db:acknowledgements|db:appendi…
Mode: m:docbook
Matches: db:acknowledgements, db:appendix, db:article, db:chapter, db:colophon, db:dedication, db:preface, db:topic
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m t xs"
                version="3.0">

<xsl:template match="db:preface|db:chapter|db:appendix|db:article
                     |db:topic|db:acknowledgements|db:dedication|db:colophon">
  <xsl:variable name="gi" select="if (parent::*)
                                  then 'section'
                                  else 'article'"/>
  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="." mode="m:toc"/>

    <xsl:variable name="section" select="db:section[1] | db:sect1[1]"/>
    <xsl:choose>
      <xsl:when test="empty($section)">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="nodes" select="$section/preceding-sibling::node()"/>
        <xsl:apply-templates select="$nodes/self::db:toc"/>
        <xsl:where-populated>
          <div class="db-bfs{f:conditional-orientation-class(.) ! concat(' ', .)}">
            <xsl:apply-templates select="$nodes except $nodes/self::db:toc"/>
          </div>
        </xsl:where-populated>
        <xsl:apply-templates select="$section, $section/following-sibling::node()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:element>
</xsl:template>

</xsl:stylesheet>

refentry.xsl

11 templates

Instructions
Template match ≅ db:refentry
Mode: m:docbook
Matches: db:refentry
Template match ≅ db:refnamediv
Mode: m:docbook
Matches: db:refnamediv
Template match ≅ db:refnamediv
Mode: m:docbook
Matches: db:refnamediv
Template match ≅ db:refclass|db:refmeta
Mode: m:docbook
Matches: db:refclass, db:refmeta
Template match ≅ db:refdescriptor
Mode: m:docbook
Matches: db:refdescriptor
Template match ≅ db:manvolnum
Mode: m:docbook
Matches: db:manvolnum
Template match ≅ db:refentrytitle
Mode: m:docbook
Matches: db:refentrytitle
Template match ≅ db:refname
Mode: m:docbook
Matches: db:refname
Template match ≅ db:refpurpose
Mode: m:docbook
Matches: db:refpurpose
Template match ≅ db:refsynopsisdiv
Mode: m:docbook
Matches: db:refsynopsisdiv
Template match ≅ db:refsect1|db:refsect2|db:ref…
Mode: m:docbook
Matches: db:refsect1, db:refsect2, db:refsect3, db:refsection
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp m t vp xs"
                version="3.0">

<xsl:template match="db:refentry">
  <xsl:variable name="gi" select="if (parent::*)
                                  then 'section'
                                  else 'article'"/>

  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="." mode="m:toc"/>
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:template match="db:refnamediv[preceding-sibling::db:refnamediv]"/>
<xsl:template match="db:refnamediv[not(preceding-sibling::db:refnamediv)]">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>

    <xsl:choose>
      <xsl:when test="$refentry-generate-name">
        <header>
          <h2>
            <xsl:apply-templates select="." mode="m:gentext">
              <xsl:with-param name="group" select="'label'"/>
              <xsl:with-param name="content" select="()"/>
            </xsl:apply-templates>
          </h2>
        </header>
      </xsl:when>

      <xsl:when test="$refentry-generate-title">
        <header>
          <h2>
            <xsl:choose>
              <xsl:when test="../db:refmeta/db:refentrytitle">
                <xsl:apply-templates select="../db:refmeta/db:refentrytitle"/>
              </xsl:when>
              <xsl:when test="db:refdescriptor">
                <xsl:apply-templates select="db:refdescriptor"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="db:refname[1]"/>
              </xsl:otherwise>
            </xsl:choose>
          </h2>
        </header>
      </xsl:when>
    </xsl:choose>

    <p>
      <xsl:choose>
        <xsl:when test="db:refdescriptor">
          <xsl:apply-templates select="db:refdescriptor"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="db:refname"/>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:apply-templates select="* except (db:refdescriptor|db:refname)"/>
    </p>
    <xsl:for-each select="following-sibling::db:refnamediv">
      <p>
        <xsl:choose>
          <xsl:when test="db:refdescriptor">
            <xsl:apply-templates select="db:refdescriptor"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="db:refname"/>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:apply-templates select="* except (db:refdescriptor|db:refname)"/>
      </p>
    </xsl:for-each>
  </div>
</xsl:template>

<xsl:template match="db:refmeta|db:refclass"/>

<xsl:template match="db:refdescriptor">
  <xsl:param name="purpose" select="''"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:manvolnum">
  <span class="manvolnum">
    <span class="sep">(</span>
    <xsl:apply-templates/>
    <span class="sep">)</span>
  </span>
</xsl:template>

<xsl:template match="db:refentrytitle">
  <xsl:param name="purpose" select="''"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:refname">
  <xsl:param name="purpose" select="''"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:if test="not($purpose = 'lot') and following-sibling::db:refname">
    <span class="refname-sep">, </span>
  </xsl:if>
</xsl:template>

<xsl:template match="db:refpurpose">
  <xsl:param name="purpose" select="''"/>
  <span>
    <xsl:choose>
      <xsl:when test="$purpose = 'lot'">
        <xsl:attribute name="class" select="local-name(.)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="m:attributes"/>
      </xsl:otherwise>
    </xsl:choose>
    <span class="refpurpose-sep"> — </span>
    <span class="refpurpose-text">
      <xsl:apply-templates/>
    </span>
    <xsl:if test="not($purpose = 'lot')
                  and not(matches(normalize-space(.), '\p{P}$'))">
      <span class="refpurpose-punc">
        <xsl:text>.</xsl:text>
      </span>
    </xsl:if>
  </span>
</xsl:template>

<xsl:template match="db:refsynopsisdiv">
  <section>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <header>
      <h2>
        <xsl:choose>
          <xsl:when test="db:info/db:title">
            <xsl:apply-templates select="db:info/db:title"
                                 mode="m:titlepage-mode"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>Synopsis</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </h2>
    </header>
    <xsl:apply-templates/>
  </section>
</xsl:template>

<xsl:template match="db:refsection|db:refsect1|db:refsect2|db:refsect3">
  <section>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </section>
</xsl:template>

</xsl:stylesheet>

bibliography.xsl

24 templates

Instructions
Template match ≅ db:bibliodiv|db:bibliography|d…
Mode: m:docbook
Matches: db:bibliodiv, db:bibliography, db:bibliolist
Template match ≅ db:biblioentry
Calls: f:pi()
Mode: m:docbook
Matches: db:biblioentry
Template match ≅ db:bibliomixed
Mode: m:docbook
Matches: db:bibliomixed
Template match ≅ db:bibliography//db:titleabbre…
Mode: m:docbook
Matches: db:bibliography//db:titleabbrev
Template match ≅ db:bibliomset
Mode: m:bibliomixed
Matches: db:bibliomset
Template match ≅ db:abbrev
Mode: m:bibliomixed
Matches: db:abbrev
Template match ≅ db:title
Mode: m:bibliomixed
Matches: db:title
Template match ≅ db:address|db:publisher
Mode: m:bibliomixed
Matches: db:address, db:publisher
Template match ≅ *
Mode: m:bibliomixed
Matches: *
Template match ≅ text()
Mode: m:bibliomixed
Matches: text()
Template match ≅ db:releaseinfo
Mode: m:bibliomixed
Matches: db:releaseinfo
Template match ≅ db:biblioset
Mode: m:biblioentry
Matches: db:biblioset
Template match ≅ db:title
Mode: m:biblioentry
Matches: db:title
Template match ≅ db:subtitle
Calls: t:inline
Mode: m:biblioentry
Matches: db:subtitle
Template match ≅ db:address|db:publisher
Mode: m:biblioentry
Matches: db:address, db:publisher
Template match ≅ db:collab|db:othercredit
Mode: m:biblioentry
Matches: db:collab, db:othercredit
Template match ≅ db:confgroup
Mode: m:biblioentry
Matches: db:confgroup
Template match ≅ db:confdates|db:confnum|db:con…
Mode: m:biblioentry
Matches: db:confdates, db:confnum, db:confsponsor, db:conftitle
Template match ≅ db:contractnum|db:contractspon…
Mode: m:biblioentry
Matches: db:contractnum, db:contractsponsor, db:edition, db:volumenum
Template match ≅ db:releaseinfo
Mode: m:biblioentry
Matches: db:releaseinfo
Template match ≅ *
Mode: m:biblioentry
Matches: *
Template match ≅ text()
Mode: m:biblioentry
Matches: text()
Template match ≅ db:authorgroup
Mode: m:docbook
Matches: db:authorgroup
Template match ≅ db:biblioid|db:bibliomisc|db:b…
Calls: t:inline
Mode: m:docbook
Matches: db:biblioid, db:bibliomisc, db:bibliosource, db:orgdiv, db:subtitle
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp m t vp xs"
                version="3.0">

<xsl:template match="db:bibliography|db:bibliodiv|db:bibliolist">
  <xsl:variable name="gi" select="if (parent::*)
                                  then 'div'
                                  else 'article'"/>
  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:template match="db:biblioentry">
  <xsl:variable name="style"
                select="f:pi(.., 'bibliography-style', $bibliography-style)"/>
  <xsl:choose>
    <xsl:when test="$style = 'iso690'">
      <xsl:apply-templates select="." mode="m:biblio690"/>
    </xsl:when>
    <xsl:otherwise>
      <p>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:call-template name="t:biblioentry"/>
      </p>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:bibliomixed">
  <p>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:choose>
      <xsl:when test="@xml:id and not(child::*[1]/self::db:abbrev)">
        <xsl:sequence select="'[' || @xml:id || '] '"/>
        <xsl:apply-templates mode="m:bibliomixed"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates mode="m:bibliomixed"/>
      </xsl:otherwise>
    </xsl:choose>
  </p>
</xsl:template>
  
<xsl:template match="db:bibliography//db:titleabbrev">
  <span class="titleabbrev">
    <xsl:apply-templates select="node()"/>
  </span>
</xsl:template>  

<!-- ============================================================ -->

<xsl:template match="db:bibliomset" mode="m:bibliomixed">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:bibliomixed"/>
  </span>
</xsl:template>

<xsl:template match="db:abbrev" mode="m:bibliomixed">
  <xsl:choose>
    <xsl:when test="empty(preceding-sibling::*)
                    and (normalize-space(preceding-sibling::text()) = '')">
      <!-- this is the citation -->
      <xsl:text>[</xsl:text>
      <span>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates mode="m:bibliomixed"/>
      </span>
      <xsl:text>] </xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <span>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates mode="m:bibliomixed"/>
      </span>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:title" mode="m:bibliomixed">
  <xsl:choose>
    <xsl:when test="parent::db:bibliomset[@relation='article']">
      <q>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </q>
    </xsl:when>
    <xsl:otherwise>
      <cite>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </cite>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:address|db:publisher" mode="m:bibliomixed">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:bibliomixed"/>
  </span>
</xsl:template>

<xsl:template match="*" mode="m:bibliomixed">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="text()" mode="m:bibliomixed">
  <xsl:choose>
    <xsl:when test="parent::db:address">
      <!-- explicitly xsl:value-of because we need a text node -->
      <xsl:value-of select="replace(string(.), '&#10;', ' / ')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:releaseinfo" mode="m:bibliomixed">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:biblioentry"/>
  </span>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:biblioset" mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:title" mode="m:biblioentry">
  <xsl:choose>
    <xsl:when test="parent::db:biblioset[@relation='article']">
      <q>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </q>
    </xsl:when>
    <xsl:otherwise>
      <cite>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </cite>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:subtitle" mode="m:biblioentry">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:address|db:publisher" mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:biblioentry"/>
  </span>
</xsl:template>

<xsl:template match="db:collab|db:othercredit" mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:personname|db:orgname"
                         mode="m:biblioentry"/>
  </span>
</xsl:template>

<xsl:template match="db:confgroup" mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:biblioentry"/>
  </span>
</xsl:template>

<xsl:template match="db:confdates|db:conftitle|db:confsponsor|db:confnum"
              mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:biblioentry"/>
  </span>
  <xsl:text>. </xsl:text>
</xsl:template>

<xsl:template match="db:contractnum|db:contractsponsor
                     |db:edition|db:volumenum" mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:biblioentry"/>
  </span>
</xsl:template>

<xsl:template match="db:releaseinfo" mode="m:biblioentry">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:biblioentry"/>
  </span>
</xsl:template>

<xsl:template match="*" mode="m:biblioentry">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="text()" mode="m:biblioentry">
  <xsl:choose>
    <xsl:when test="parent::db:address">
      <!-- explicitly xsl:value-of because we need a text node -->
      <xsl:value-of select="replace(string(.), '&#10;', ' / ')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
  

<!-- ============================================================ -->

<xsl:template match="db:authorgroup[not(parent::db:info)]">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:call-template name="t:person-name-list"/>
  </span>
</xsl:template>

<xsl:template match="db:biblioid|db:orgdiv|db:bibliosource
                     |db:bibliomisc|db:subtitle">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<!-- ============================================================ -->

</xsl:stylesheet>

biblio690.xsl

34 templates, 2 functions (2 used only in one other module)

Instructions
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:biblioset
Mode: m:biblio690
Matches: db:biblioset
Template match ≅ db:authorgroup
Mode: m:biblio690
Matches: db:authorgroup
Template match ≅ db:author
Mode: m:biblio690
Matches: db:author
Template match ≅ db:personname
Mode: m:biblio690
Matches: db:personname
Template match ≅ db:title
Mode: m:biblio690
Matches: db:title
Template match ≅ db:biblioset
Mode: m:biblio690
Matches: db:biblioset
Template match ≅ db:title
Mode: mp:biblio690-punct
Matches: db:title
Template match ≅ db:subtitle
Mode: m:biblio690
Matches: db:subtitle
Template match ≅ db:bibliomisc
Mode: m:biblio690
Matches: db:bibliomisc
Template match ≅ db:bibliomisc
Mode: m:biblio690
Matches: db:bibliomisc
Template match ≅ db:bibliomisc
Mode: m:biblio690
Matches: db:bibliomisc
Template match ≅ db:bibliomisc
Mode: m:biblio690
Matches: db:bibliomisc
Template match ≅ db:bibliomisc
Mode: m:biblio690
Matches: db:bibliomisc
Template match ≅ db:edition
Mode: m:biblio690
Matches: db:edition
Template match ≅ db:volumenum
Mode: m:biblio690
Matches: db:volumenum
Template match ≅ db:issuenum
Mode: m:biblio690
Matches: db:issuenum
Template match ≅ db:publisher
Mode: m:biblio690
Matches: db:publisher
Template match ≅ db:pubdate
Mode: m:biblio690
Matches: db:pubdate
Template match ≅ db:date
Mode: m:biblio690
Matches: db:date
Template match ≅ db:pagenums
Mode: m:biblio690
Matches: db:pagenums
Template match ≅ db:biblioid
Mode: m:biblio690
Matches: db:biblioid
Template match ≅ db:biblioid
Mode: m:biblio690
Matches: db:biblioid
Template match ≅ db:city|db:firstname|db:givenn…
Mode: m:biblio690
Matches: db:city, db:firstname, db:givenname, db:publishername, db:surname
Template match ≅ db:biblioentry
Mode: m:biblio690
Matches: db:biblioentry
Template match ≅ db:*
Mode: m:biblio690
Matches: db:*
Function fp:iso690($context as element(), $key as xs:string) as item()*
Used in: main.xsl
Function fp:optional-sep($context as element(), $key as xs:string) as item()*
Used in: main.xsl
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xlink='http://www.w3.org/1999/xlink'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:biblio690"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="db:biblioentry[contains-token(@role, 'monograph')]">
  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <xsl:sequence select="db:authorgroup|db:author"/>
      <xsl:sequence select="db:title"/>
      <xsl:sequence select="db:subtitle"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'medium')]"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'secondary')]"/>
      <xsl:sequence select="db:edition"/>
      <xsl:sequence select="db:publisher"/>
      <xsl:sequence select="db:pubdate"/>
      <xsl:sequence select="db:pagenums"/>
      <xsl:sequence select="db:date[contains-token(@role, 'cit')]"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'serie')]"/>
      <xsl:sequence select="db:bibliomisc[not(@role)]"/>
      <xsl:sequence select="db:biblioid"/>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'serial')]">
  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <xsl:sequence select="db:title"/>
      <xsl:sequence select="db:subtitle"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'medium')]"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'secondary')]"/>
      <xsl:sequence select="db:edition"/>
      <xsl:sequence select="db:pubdate[contains-token(@role, 'issuing')]"/>
      <xsl:sequence select="db:issuenum"/>
      <xsl:sequence select="db:publisher"/>
      <xsl:sequence select="db:pubdate[not(@role)]"/>
      <xsl:sequence select="db:date[contains-token(@role, 'cit')]"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'serie')]"/>
      <xsl:sequence select="db:bibliomisc[not(@role)]"/>
      <xsl:sequence select="db:biblioid"/>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'part')]">
  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <xsl:sequence select="db:authorgroup|db:author"/>
      <xsl:sequence select="db:title"/>
      <xsl:sequence select="db:subtitle"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'medium')]"/>
      <xsl:sequence select="db:edition"/>
      <xsl:sequence select="db:volumenum"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'secondary')]"/>
      <xsl:sequence select="db:publisher"/>
      <xsl:sequence select="db:pubdate"/>
      <xsl:sequence select="db:date[contains-token(@role, 'cit')]"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'secnum')]"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'sectitle')]"/>
      <xsl:sequence select="db:pagenums"/>
      <xsl:sequence select="db:biblioid"/>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'contribution')]">
  <xsl:variable name="part" select="db:biblioset[@relation='part']"/>
  <xsl:variable name="book" select="db:biblioset[@relation='book']"/>

  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <db:biblioset relation="part">
        <xsl:sequence select="$part/db:authorgroup|$part/db:author"/>
        <xsl:sequence select="$part/db:title"/>
        <xsl:sequence select="$part/db:subtitle"/>
      </db:biblioset>
      <db:biblioset relation="book">
        <xsl:sequence select="$book/db:authorgroup|$book/db:author"/>
        <xsl:sequence select="$book/db:title"/>
        <xsl:sequence select="$book/db:subtitle"/>
        <xsl:sequence select="$book/db:bibliomisc[contains-token(@role, 'medium')]"/>
        <xsl:sequence select="$book/db:edition"/>
        <xsl:sequence select="$book/db:publisher"/>
        <xsl:sequence select="$book/db:pubdate"/>
        <xsl:sequence select="$book/db:volumenum"/>
        <xsl:sequence select="$book/db:pagenums"/>
        <xsl:sequence select="$book/db:biblioid"/>
      </db:biblioset>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'article')]">
  <xsl:variable name="art" select="db:biblioset[@relation='article']"/>
  <xsl:variable name="jour" select="db:biblioset[@relation='journal']"/>

  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <db:biblioset relation="part">
        <xsl:sequence select="$art/db:authorgroup|$art/db:author"/>
        <xsl:sequence select="$art/db:title"/>
        <xsl:sequence select="$art/db:subtitle"/>
        <xsl:sequence select="$art/db:bibliomisc[contains-token(@role, 'secondary')]"/>
      </db:biblioset>
      <db:biblioset relation="journal">
        <xsl:sequence select="$jour/db:title"/>
        <xsl:sequence select="$jour/db:subtitle"/>
        <xsl:sequence select="$jour/db:bibliomisc[contains-token(@role, 'medium')]"/>
        <xsl:sequence select="$jour/db:pubdate"/>
        <xsl:sequence select="$jour/db:volumenum"/>
        <xsl:sequence select="$jour/db:issuenum"/>
        <xsl:sequence select="$jour/db:date[contains-token(@role, 'cit')]"/>
        <xsl:sequence select="$jour/db:pagenums"/>
        <xsl:sequence select="$jour/db:biblioid"/>
      </db:biblioset>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'patent')]">
  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <xsl:sequence select="db:authorgroup|db:author"/>
      <xsl:sequence select="db:title"/>
      <xsl:sequence select="db:subtitle"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'secondary')]"/>
      <xsl:sequence select="db:bibliomisc[not(@role)]"/>
      <xsl:sequence select="db:address"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'patenttype')]"/>
      <xsl:sequence select="db:biblioid[@otherclass='patentnum']"/>
      <xsl:sequence select="db:pubdate"/>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'messagesystem')]">
  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <xsl:sequence select="db:title"/>
      <xsl:sequence select="db:subtitle"/>
      <xsl:sequence select="db:bibliomisc[contains-token(@role, 'medium')]"/>
      <xsl:sequence select="db:publisher"/>
      <xsl:sequence select="db:pubdate"/>
      <xsl:sequence select="db:date[contains-token(@role, 'cit')]"/>
      <xsl:sequence select="db:biblioid"/>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'message')]">
  <xsl:variable name="part" select="db:biblioset[@relation='part']"/>
  <xsl:variable name="book" select="db:biblioset[@relation='book']"/>

  <xsl:variable name="entry">
    <db:biblioentry role="{@role}">
      <db:biblioset relation="part">
        <xsl:sequence select="$part/db:authorgroup|$part/db:author"/>
        <xsl:sequence select="$part/db:title"/>
        <xsl:sequence select="$part/db:subtitle"/>
      </db:biblioset>
      <db:biblioset relation="book">
        <xsl:sequence select="$book/db:title"/>
        <xsl:sequence select="$book/db:subtitle"/>
        <xsl:sequence select="$book/db:bibliomisc[contains-token(@role, 'medium')]"/>
        <xsl:sequence select="$book/db:publisher"/>
        <xsl:sequence select="$book/db:pubdate"/>
        <xsl:sequence select="$book/db:date[contains-token(@role, 'cit')]"/>
        <xsl:sequence select="$book/db:biblioid"/>
      </db:biblioset>
    </db:biblioentry>
  </xsl:variable>

  <p>
    <xsl:apply-templates select="$entry/db:biblioentry" mode="m:attributes">
      <xsl:with-param name="style" select="'iso690'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="$entry/db:biblioentry/*"/>
  </p>
</xsl:template>

<xsl:template match="db:biblioentry">
  <p>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </p>
</xsl:template>

<xsl:template match="db:biblioset">
  <xsl:if test="preceding-sibling::db:biblioset and @relation = 'book'">
    <xsl:sequence select="f:l10n-token(., 'In')"/>
    <xsl:text> </xsl:text>
  </xsl:if>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:authorgroup">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:gentext-list">
      <xsl:with-param name="list" as="element()*">
        <xsl:apply-templates select="*"/>
      </xsl:with-param>
    </xsl:apply-templates>
  </span>
  <xsl:sequence select="fp:optional-sep(*[last()], 'primary.sep')"/>
</xsl:template>

<xsl:template match="db:author">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="*"/>
  </span>
  <xsl:if test="not(parent::db:authorgroup)">
    <xsl:sequence select="fp:optional-sep(., 'primary.sep')"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:personname">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:surname"/>
    <xsl:sequence select="fp:iso690(., 'lastfirst.sep')"/>
    <xsl:apply-templates select="db:firstname|db:givenname"/>
  </span>
</xsl:template>

<xsl:template match="db:title">
  <em>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </em>
  <xsl:apply-templates select="." mode="mp:biblio690-punct"/>
</xsl:template>

<xsl:template match="db:biblioset[@relation='article']/db:title
                     |db:biblioset[@relation='part']/db:title">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:apply-templates select="." mode="mp:biblio690-punct"/>
</xsl:template>

<xsl:template match="db:title" mode="mp:biblio690-punct">
  <xsl:choose>
    <xsl:when test="following-sibling::db:bibliomisc[contains-token(@role, 'medium')]">
      <!-- nop -->
    </xsl:when>
    <xsl:when test="following-sibling::db:subtitle">
      <xsl:sequence select="fp:optional-sep(., 'submaintitle.sep')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:optional-sep(., 'title.sep')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:subtitle">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:optional-sep(., 'title.sep')"/>
</xsl:template>

<xsl:template match="db:bibliomisc[contains-token(@role, 'secondary')]">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:variable name="fs" select="following-sibling::*[1]"/>
  <xsl:choose>
    <xsl:when test="$fs/self::db:bibliomisc and contains-token($fs/@role, 'secondary')">
      <xsl:sequence select="fp:iso690(., 'secondary.person.sep')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:optional-sep(., 'secondary.sep')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:bibliomisc[contains-token(@role, 'serie')]">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:optional-sep(., 'serie.sep')"/>
</xsl:template>

<xsl:template match="db:bibliomisc[contains-token(@role, 'patenttype')]">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:optional-sep(., 'pattype.sep')"/>
</xsl:template>

<xsl:template match="db:bibliomisc[contains-token(@role, 'medium')]">
  <xsl:sequence select="fp:iso690(., 'medium1')"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:iso690(., 'medium2')"/>
  <xsl:sequence select="fp:iso690(., 'title.sep')"/>
</xsl:template>

<xsl:template match="db:bibliomisc">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:optional-sep(., 'primary.sep')"/> <!-- ??? -->
</xsl:template>

<xsl:template match="db:edition">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:optional-sep(., 'edition.sep')"/>
</xsl:template>

<xsl:template match="db:volumenum">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="if ((following-sibling::db:issuenum)
                            or (following-sibling::db:pagenums))
                        then fp:iso690(., 'edition.serial.sep')
                        else fp:optional-sep(., 'edition.sep')"/>
</xsl:template>

<xsl:template match="db:issuenum">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:choose>
    <xsl:when test="following-sibling::*[1]/self::db:date[contains-token(@role, 'cit')]">
      <!-- nop -->
    </xsl:when>
    <xsl:when test="following-sibling::db:pagenums">
      <xsl:sequence select="fp:iso690(., 'edition.serial.sep')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:optional-sep(., 'edition.sep')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:publisher">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:if test="db:address/db:city">
      <xsl:apply-templates select="db:address/db:city"/>
      <xsl:sequence select="fp:iso690(., 'placepubl.sep')"/>
    </xsl:if>
    <xsl:apply-templates select="db:publishername"/>
  </span>
  <xsl:choose>
    <xsl:when test="following-sibling::*[1]/self::db:pubdate">
      <xsl:sequence select="fp:iso690(., 'publyear.sep')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:optional-sep(., 'pubinfo.sep')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:pubdate">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
    <xsl:if test="ends-with(normalize-space(.), '-')
                  or ends-with(normalize-space(.), '—')">
      <xsl:text> </xsl:text>
    </xsl:if>
  </span>
  <xsl:choose>
    <xsl:when test="following-sibling::*[1]/self::db:date[contains-token(@role, 'cit')]">
      <!-- nop -->
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="if (following-sibling::db:volumenum)
                            then fp:iso690(., 'edition.serial.sep')
                            else fp:optional-sep(., 'pubinfo.sep')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:date[contains-token(@role, 'cit')]">
  <xsl:sequence select="fp:iso690(., 'datecit1')"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:iso690(., 'datecit2')"/>
  <xsl:sequence select="if (following-sibling::db:pagenums)
                        then fp:iso690(., 'edition.serial.sep')
                        else fp:optional-sep(., 'pubinfo.sep')"/>
</xsl:template>

<xsl:template match="db:pagenums">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="fp:optional-sep(., 'primary.sep')"/> <!-- ??? -->
</xsl:template>

<xsl:template match="db:biblioid[contains-token(@class, 'uri')]">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:choose>
      <xsl:when test="preceding-sibling::db:biblioid[contains-token(@class, 'uri')]">
        <xsl:sequence select="fp:iso690(., 'acctoo')"/>
        <xsl:apply-templates/>
        <xsl:if test="empty(node())">
          <xsl:sequence select="if (starts-with(@xlink:href, 'http'))
                                then fp:iso690(., 'onwww')
                                else fp:iso690(., 'oninet')"/>
        </xsl:if>
        <xsl:text>: </xsl:text>
        <xsl:sequence select="fp:iso690(., 'link1')"/>
        <xsl:sequence select="string(@xlink:href)"/>
        <xsl:sequence select="fp:iso690(., 'link2')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="fp:iso690(., 'access')"/>
        <xsl:apply-templates/>
        <xsl:if test="empty(node())">
          <xsl:sequence select="if (starts-with(@xlink:href, 'http'))
                                then fp:iso690(., 'onwww')
                                else fp:iso690(., 'oninet')"/>
        </xsl:if>
        <xsl:text>: </xsl:text>
        <xsl:sequence select="fp:iso690(., 'link1')"/>
        <xsl:sequence select="string(@xlink:href)"/>
        <xsl:sequence select="fp:iso690(., 'link2')"/>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:sequence select="fp:optional-sep(., 'primary.sep')"/> <!-- ??? -->
  </span>
</xsl:template>

<xsl:template match="db:biblioid">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:choose>
      <xsl:when test="@class = 'isbn'">
        <xsl:sequence select="fp:iso690(., 'isbn')"/>
      </xsl:when>
      <xsl:when test="@class = 'issn'">
        <xsl:sequence select="fp:iso690(., 'issn')"/>
      </xsl:when>
      <xsl:otherwise/>
    </xsl:choose>
    <xsl:apply-templates/>
  </span>
  <xsl:sequence select="if (contains-token(@otherclass, 'patentnum'))
                        then fp:optional-sep(., 'patnum.sep')
                        else fp:optional-sep(., 'primary.sep')"/> <!-- ??? -->
</xsl:template>

<xsl:template match="db:surname|db:firstname|db:givenname
                     |db:city|db:publishername">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:biblioentry[contains-token(@role, 'patent')]/db:address">
  <xsl:if test="db:country">
    <span>
      <xsl:apply-templates select="db:country" mode="m:attributes"/>
      <xsl:apply-templates select="db:country/node()"/>
    </span>
    <xsl:text> </xsl:text>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:*">
  <xsl:apply-templates select="." mode="m:docbook"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="fp:iso690" as="item()*">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="key" as="xs:string"/>

  <xsl:sequence select="f:l10n-token($context, 'iso690.'||$key)"/>
</xsl:function>

<xsl:function name="fp:optional-sep" as="item()*">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="key" as="xs:string"/>

  <xsl:choose>
    <xsl:when test="ends-with(normalize-space($context), '.')">
      <xsl:text> </xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="f:l10n-token($context, 'iso690.'||$key)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

</xsl:stylesheet>

glossary.xsl

11 templates (1 used only in one other module)

Instructions
Template match ≅ db:glossary|db:glossdiv|db:glo…
Mode: m:docbook
Matches: db:glossary, db:glossdiv, db:glosslist
Template t:glossary-divisions match ≅
Calls: f:id()
Used in: main.xsl
Mode: m:docbook
Template match ≅ db:glossentry
Mode: m:docbook
Matches: db:glossentry
Template match ≅ db:glossterm
Calls: t:inline
Mode: m:docbook
Matches: db:glossterm
Template match ≅ db:glossentry/db:glossterm
Mode: m:docbook
Matches: db:glossentry/db:glossterm
Template match ≅ db:glossentry/db:acronym
Mode: m:docbook
Matches: db:glossentry/db:acronym
Template match ≅ db:glossentry/db:abbrev
Mode: m:docbook
Matches: db:glossentry/db:abbrev
Template match ≅ db:glosssee
Calls: f:href()
Mode: m:docbook
Matches: db:glosssee
Template match ≅ db:glossentry/db:glossdef
Mode: m:docbook
Matches: db:glossentry/db:glossdef
Template match ≅ db:glossseealso
Mode: m:docbook
Matches: db:glossseealso
Template match ≅ db:glossseealso
Calls: f:href()
Mode: m:docbook
Matches: db:glossseealso
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp m t vp xs"
                version="3.0">

<xsl:key name="glossterm" match="db:glossentry"
         use="(db:glossterm/@baseform, db:glossterm/normalize-space(.))[1]"/>

<xsl:template match="db:glossary|db:glossdiv|db:glosslist">
  <xsl:variable name="gi" select="if (parent::*)
                                  then 'div'
                                  else 'article'"/>

  <xsl:variable name="make-divisions"
                select="if (empty(f:pi(., 'glossary-divisions', ())))
                        then f:is-true($glossary-automatic-divisions)
                        else f:is-true(f:pi(., 'glossary-divisions', 'false'))"/>

  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="* except (db:glossentry|db:bibliography)"/>
    <xsl:choose>
      <xsl:when test="self::db:glossary and db:glossentry
                      and f:is-true($glossary-sort-entries) and $make-divisions">
        <xsl:call-template name="t:glossary-divisions">
          <xsl:with-param name="terms" as="element(db:glossentry)+">
            <xsl:for-each select="db:glossentry">
              <xsl:sort select="(@sortas, normalize-space(db:glossterm[1]))[1]"
                        collation="{$sort-collation}"/>
              <xsl:sequence select="."/>
            </xsl:for-each>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="db:glossentry and f:is-true($glossary-sort-entries)">
        <dl class="{local-name(.)}">
          <xsl:apply-templates select="db:glossentry">
            <xsl:sort select="(@sortas, normalize-space(db:glossterm[1]))[1]"
                      collation="{$sort-collation}"/>
          </xsl:apply-templates>
        </dl>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="db:glossentry"/>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:apply-templates select="db:bibliography"/>
  </xsl:element>
</xsl:template>

<xsl:template name="t:glossary-divisions">
  <xsl:param name="terms" as="element(db:glossentry)+"/>

  <xsl:variable name="gid" select="f:id(.)"/>

  <xsl:for-each-group
      select="$terms"
      group-by="upper-case(substring((db:glossterm[1]/@baseform, string(db:glossterm[1]))[1], 1, 1))">
    <xsl:choose>
      <!-- Don't bother with the group in the unlikely event that there's only one. -->
      <xsl:when test="last() gt 1">
        <div id="{$gid}_{current-grouping-key()}" class="glossdiv">
          <header>
            <div class="title">
              <xsl:sequence select="current-grouping-key()"/>
            </div>
          </header>
          <dl>
            <xsl:apply-templates select="current-group()"/>
          </dl>
        </div>
      </xsl:when>
      <xsl:otherwise>
        <dl>
          <xsl:apply-templates select="current-group()"/>
        </dl>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each-group>
</xsl:template>

<xsl:template match="db:glossentry">
  <dt>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:glossterm|db:indexterm"/>
  </dt>
  <xsl:apply-templates select="db:glosssee|db:glossdef"/>
</xsl:template>

<xsl:template match="db:glossterm">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:glossentry/db:glossterm">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:if test="following-sibling::db:glossterm">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'separator'"/>
    </xsl:apply-templates>
  </xsl:if>
</xsl:template>

<xsl:template match="db:glossentry/db:acronym">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:if test="following-sibling::db:acronym
                |following-sibling::db:abbrev">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'separator'"/>
    </xsl:apply-templates>
  </xsl:if>
</xsl:template>

<xsl:template match="db:glossentry/db:abbrev">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <xsl:if test="following-sibling::db:acronym
                |following-sibling::db:abbrev">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'separator'"/>
    </xsl:apply-templates>
  </xsl:if>
</xsl:template>

<xsl:template match="db:glosssee">
  <xsl:variable name="target"
                select="(key('id', @otherterm),
                         key('glossterm', normalize-space(.)))[1]"/>

  <dd>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <p>
      <xsl:apply-templates select="." mode="m:gentext">
        <xsl:with-param name="group" select="'see'"/>
        <xsl:with-param name="content">
          <xsl:choose>
            <xsl:when test="$target">
              <a href="{f:href(., $target)}">
                <xsl:apply-templates/>
              </a>
            </xsl:when>
            <xsl:when test="@otherterm and not($target)">
              <xsl:message>
                <xsl:text>Warning: </xsl:text>
                <xsl:text>glosssee @otherterm reference not found: </xsl:text>
                <xsl:value-of select="@otherterm"/>
              </xsl:message>
              <xsl:apply-templates/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:apply-templates/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
      </xsl:apply-templates>
      <xsl:text>.</xsl:text>
    </p>
  </dd>
</xsl:template>

<xsl:template match="db:glossentry/db:glossdef">
  <dd>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="*[not(self::db:glossseealso)]"/>
  </dd>
  <xsl:apply-templates select="db:glossseealso"/>
</xsl:template>

<xsl:template match="db:glossseealso[preceding-sibling::db:glossseealso]"/>
<xsl:template match="db:glossseealso">
  <dd>
    <p>
      <xsl:variable name="targets" as="element()+">
        <xsl:for-each select="(., following-sibling::db:glossseealso)">
          <xsl:variable name="target"
                        select="if (key('id', @otherterm))
                                then key('id', @otherterm)[1]
                                else key('glossterm', string(.))"/>
          <xsl:choose>
            <xsl:when test="$target">
              <a href="{f:href(/,$target)}">
                <xsl:apply-templates select="$target" mode="m:crossref"/>
              </a>
            </xsl:when>
            <xsl:when test="@otherterm and not($target)">
              <xsl:message>
                <xsl:text>Warning: </xsl:text>
                <xsl:text>glossseealso @otherterm reference not found: </xsl:text>
                <xsl:value-of select="@otherterm"/>
              </xsl:message>
              <span>
                <xsl:apply-templates/>
              </span>
            </xsl:when>
            <xsl:otherwise>
              <span>
                <xsl:apply-templates/>
              </span>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each>
      </xsl:variable>

      <xsl:apply-templates select="." mode="m:gentext-list">
        <xsl:with-param name="list" select="$targets"/>
        <xsl:with-param name="name" select="'glossary-seealso'"/>
      </xsl:apply-templates>
    </p>
  </dd>
</xsl:template>

<!-- ============================================================ -->

</xsl:stylesheet>

index.xsl

21 templates (3 used only in one other module), 8 functions (1 unused, 7 used only in one other module)

Instructions
Template match ≅ db:indexterm
Mode: m:docbook
Matches: db:indexterm
Template match ≅ db:primary|db:secondary|db:see…
Mode: m:docbook
Matches: db:primary, db:secondary, db:see, db:seealso, db:tertiary
Template match ≅ db:index|db:indexdiv|db:setind…
Mode: m:docbook
Matches: db:index, db:indexdiv, db:setindex
Function fp:primary($indexterm as element(db:indexterm)) as xs:string
Used in: main.xsl
Function fp:secondary($indexterm as element(db:indexterm)) as xs:string
Used in: main.xsl
Function fp:tertiary($indexterm as element(db:indexterm)) as xs:string
Used in: main.xsl
Function fp:scope($node as element(), $scope as element(), $role as xs:string?, $type as xs:string?) as xs:boolean
Used in: main.xsl
Function fp:nearest-section($node as element()) as element()
Used in: main.xsl
Function fp:nearest-section-id($indexterm as element(db:indexterm)) as xs:string
Unused
Function fp:group-index($term as xs:string, $node as element())
Used in: main.xsl
Function fp:group-label($index as xs:integer, $node as element())
Used in: main.xsl
Template t:generate-index match ≅
Template match ≅ db:indexterm
Mode: m:index-div
Matches: db:indexterm
Template match ≅ db:indexterm
Mode: m:index-primary
Matches: db:indexterm
Template match ≅ db:indexterm
Mode: m:index-secondary
Matches: db:indexterm
Template match ≅ db:indexterm
Mode: m:index-tertiary
Matches: db:indexterm
Template tp:indexed-section match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ db:indexterm
Mode: mp:reference
Matches: db:indexterm
Template t:index-zone-reference match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ db:indexterm
Mode: m:index-see
Matches: db:indexterm
Template match ≅ db:indexterm
Mode: m:index-seealso
Matches: db:indexterm
Template match ≅ db:indexentry
Mode: m:docbook
Matches: db:indexentry
Template match ≅ db:primaryie
Mode: m:docbook
Matches: db:primaryie
Template match ≅ db:secondaryie
Mode: m:docbook
Matches: db:secondaryie
Template match ≅ db:tertiaryie
Mode: m:docbook
Matches: db:tertiaryie
Template match ≅ db:seeie
Mode: m:docbook
Priority: 10
Matches: db:seeie
Template match ≅ db:seeie
Mode: m:docbook
Matches: db:seeie
Template match ≅ db:seealsoie
Mode: m:docbook
Priority: 10
Matches: db:seealsoie
Template match ≅ db:seealsoie
Mode: m:docbook
Matches: db:seealsoie
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="db:indexterm">
  <span class="indexterm" id="{f:generate-id(.)}">
    <xsl:if test="not(empty($index-show-entries))">
      <xsl:attribute name="title" select="string-join(.//*, ', ')"/>
      <xsl:sequence select="$index-show-entries"/>
    </xsl:if>
  </span>
</xsl:template>

<xsl:template match="db:primary|db:secondary|db:tertiary|db:see|db:seealso"/>

<xsl:template match="db:setindex|db:index|db:indexdiv">
  <xsl:variable name="gi" select="if (parent::*)
                                  then 'div'
                                  else 'article'"/>
  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates
        select="node() except (db:indexdiv|db:indexentry|db:segmentedlist)"/>

    <xsl:variable name="autoindex"
                  select="f:pi(., 'autoindex', $generate-index)"/>
    <xsl:if test="f:is-true($autoindex)">
      <div class="index-list">
        <xsl:choose>
          <xsl:when test="not(db:indexdiv|db:indexentry|db:segmentedlist)
                          and f:is-true(f:pi(., 'autoindex', 'true'))
                          and f:is-true($generate-index)">
            <xsl:call-template name="t:generate-index">
              <xsl:with-param name="scope" select="parent::*"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <!-- There will be exactly one of
                 db:indexdiv, db:indexentry, or db:segmentedlist -->
            <xsl:apply-templates select="db:indexdiv|db:segmentedlist"/>
            <xsl:if test="db:indexentry">
              <ul>
                <xsl:apply-templates select="db:indexentry"/>
              </ul>
            </xsl:if>
          </xsl:otherwise>
        </xsl:choose>
      </div>
    </xsl:if>
  </xsl:element>
</xsl:template>

<!-- ============================================================ -->

<xsl:key name="primary"
         match="db:indexterm"
         use="normalize-space(concat(db:primary/@sortas, db:primary[not(@sortas)]))"/>

<xsl:key name="endofrange"
         match="db:indexterm[@class='endofrange']"
         use="@startref"/>

<!-- ============================================================ -->

<xsl:function name="fp:primary" as="xs:string">
  <xsl:param name="indexterm" as="element(db:indexterm)"/>
  <xsl:sequence
      select="normalize-space(concat($indexterm/db:primary/@sortas,
                                     $indexterm/db:primary[not(@sortas)]))"/>
</xsl:function>

<xsl:function name="fp:secondary" as="xs:string">
  <xsl:param name="indexterm" as="element(db:indexterm)"/>
  <xsl:sequence
      select="normalize-space(concat($indexterm/db:secondary/@sortas,
                                     $indexterm/db:secondary[not(@sortas)]))"/>
</xsl:function>

<xsl:function name="fp:tertiary" as="xs:string">
  <xsl:param name="indexterm" as="element(db:indexterm)"/>
  <xsl:sequence
      select="normalize-space(concat($indexterm/db:tertiary/@sortas,
                                     $indexterm/db:tertiary[not(@sortas)]))"/>
</xsl:function>

<xsl:function name="fp:scope" as="xs:boolean">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="scope" as="element()"/>
  <xsl:param name="role" as="xs:string?"/>
  <xsl:param name="type" as="xs:string?"/>
  <xsl:sequence
      select="count($node/ancestor::node()|$scope) = count($node/ancestor::node())
                and ($role = $node/@role or $type = $node/@type or
                (string-length($role) = 0 and string-length($type) = 0))"/>
</xsl:function>

<xsl:function name="fp:nearest-section" as="element()">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="($node/ancestor-or-self::db:set
                         |$node/ancestor-or-self::db:book
                         |$node/ancestor-or-self::db:part
                         |$node/ancestor-or-self::db:reference
                         |$node/ancestor-or-self::db:partintro
                         |$node/ancestor-or-self::db:chapter
                         |$node/ancestor-or-self::db:appendix
                         |$node/ancestor-or-self::db:preface
                         |$node/ancestor-or-self::db:article
                         |$node/ancestor-or-self::db:section
                         |$node/ancestor-or-self::db:sect1
                         |$node/ancestor-or-self::db:sect2
                         |$node/ancestor-or-self::db:sect3
                         |$node/ancestor-or-self::db:sect4
                         |$node/ancestor-or-self::db:sect5
                         |$node/ancestor-or-self::db:refentry
                         |$node/ancestor-or-self::db:refsect1
                         |$node/ancestor-or-self::db:refsect2
                         |$node/ancestor-or-self::db:refsect3
                         |$node/ancestor-or-self::db:simplesect
                         |$node/ancestor-or-self::db:bibliography
                         |$node/ancestor-or-self::db:glossary
                         |$node/ancestor-or-self::db:index)[last()]"/>
</xsl:function>

<xsl:function name="fp:nearest-section-id" as="xs:string">
  <xsl:param name="indexterm" as="element(db:indexterm)"/>
  <xsl:sequence select="f:generate-id(fp:nearest-section($indexterm))"/>
</xsl:function>

<xsl:function name="fp:group-index">
  <xsl:param name="term" as="xs:string"/>
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="letters"
                select="f:gentext-letters-for-language($node)"/>

  <xsl:variable name="long-letter-index"
                select="$letters/l:l[. = substring($term,1,2)]/@i"/>

  <xsl:variable name="short-letter-index"
                select="$letters/l:l[. = substring($term,1,1)]/@i"/>

  <xsl:sequence select="($long-letter-index, $short-letter-index, 0)[1]"/>
</xsl:function>

<xsl:function name="fp:group-label">
  <xsl:param name="index" as="xs:integer"/>
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="letters"
                select="f:gentext-letters-for-language($node)"/>

  <xsl:value-of select="$letters/l:l[@i=$index][1]"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:template name="t:generate-index">
  <xsl:param name="scope" select="(ancestor::db:book|/)[last()]"/>

  <xsl:variable name="role"
                select="if (f:is-true($index-on-role))
                        then @role
                        else ()"/>

  <xsl:variable name="type"
                select="if (f:is-true($index-on-type))
                        then @type
                        else ()"/>

  <div class="generated-index">
    <xsl:for-each-group select="//db:indexterm[fp:scope(., $scope, $role, $type)]
                                   [not(@class = 'endofrange')]"
                        group-by="fp:group-index(fp:primary(.), $scope)">
      <xsl:sort select="fp:group-index(fp:primary(.), $scope)" data-type="number"/>
      <xsl:apply-templates select="." mode="m:index-div">
        <xsl:with-param name="scope" select="$scope"/>
        <xsl:with-param name="role" select="$role"/>
        <xsl:with-param name="type" select="$type"/>
        <xsl:with-param name="lang" select="f:l10n-language($scope)"/>
        <xsl:with-param name="nodes" select="current-group()"/>
        <xsl:with-param name="group-index" select="current-grouping-key()"/>
      </xsl:apply-templates>
    </xsl:for-each-group>
  </div>
</xsl:template>

<xsl:template match="db:indexterm" mode="m:index-div">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="nodes" as="element()*"/>
  <xsl:param name="group-index"/>

  <xsl:if test="$nodes">
    <div class="generated-indexdiv">
      <header>
        <h3>
          <xsl:value-of select="fp:group-label($group-index, $scope)"/>
        </h3>
      </header>
      <ul>
        <xsl:for-each-group select="$nodes" group-by="fp:primary(.)">
          <xsl:sort select="fp:primary(.)" lang="{$lang}"/>
          <xsl:apply-templates select="current-group()[1]" mode="m:index-primary">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
          </xsl:apply-templates>
        </xsl:for-each-group>
      </ul>
    </div>
  </xsl:if>
</xsl:template>

<xsl:template match="db:indexterm" mode="m:index-primary">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>

  <xsl:variable name="key" select="fp:primary(.)"/>
  <xsl:variable name="refs"
                select="key('primary', $key)[fp:scope(., $scope, $role, $type)]"/>
  <li>
    <xsl:value-of select="db:primary"/>
    <xsl:for-each-group select="$refs[not(db:secondary) and not(db:see)]"
                        group-by="concat(fp:primary(.), ' ', fp:nearest-section-id(.))">
      <xsl:call-template name="tp:indexed-section">
        <xsl:with-param name="nodes" select="current-group()"/>
        <xsl:with-param name="scope" select="$scope"/>
        <xsl:with-param name="role" select="$role"/>
        <xsl:with-param name="type" select="$type"/>
        <xsl:with-param name="lang" select="$lang"/>
      </xsl:call-template>
    </xsl:for-each-group>

    <xsl:if test="$refs[not(db:secondary)]/*[self::db:see]">
      <xsl:for-each-group select="$refs[db:see]"
                          group-by="concat(fp:primary(.), ' ', ' ', ' ', db:see)">
        <xsl:apply-templates select="." mode="m:index-see">
          <xsl:with-param name="scope" select="$scope"/>
          <xsl:with-param name="role" select="$role"/>
          <xsl:with-param name="type" select="$type"/>
          <xsl:with-param name="lang" select="$lang"/>
          <xsl:sort select="upper-case(db:see)" lang="{$lang}"/>
        </xsl:apply-templates>
      </xsl:for-each-group>
    </xsl:if>
    <xsl:if test="$refs/db:secondary or $refs[not(db:secondary)]/*[self::db:seealso]">
      <ul>
        <xsl:for-each-group select="$refs[db:seealso]"
                            group-by="concat(fp:primary(.), ' ', ' ', ' ', db:seealso[1])">
          <xsl:apply-templates select="." mode="m:index-seealso">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
            <xsl:sort select="upper-case(db:seealso[1])" lang="{$lang}"/>
          </xsl:apply-templates>
        </xsl:for-each-group>
        <xsl:for-each-group select="$refs[db:secondary]"
                            group-by="concat(fp:primary(.), ' ', fp:secondary(.))">
          <xsl:apply-templates select="." mode="m:index-secondary">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
            <xsl:with-param name="refs" select="current-group()"/>
            <xsl:sort select="upper-case(fp:secondary(.))" lang="{$lang}"/>
          </xsl:apply-templates>
        </xsl:for-each-group>
      </ul>
    </xsl:if>
  </li>
</xsl:template>

<xsl:template match="db:indexterm" mode="m:index-secondary">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="refs" as="element()*"/>
  <xsl:param name="lang" select="'en'"/>

  <xsl:variable name="key" select="concat(fp:primary(.), ' ', fp:secondary(.))"/>
  <li>
    <xsl:value-of select="db:secondary"/>
    <xsl:for-each-group select="$refs[not(db:tertiary) and not(db:see)]"
                        group-by="concat($key, ' ', fp:nearest-section-id(.))">
      <xsl:call-template name="tp:indexed-section">
        <xsl:with-param name="nodes" select="current-group()"/>
        <xsl:with-param name="scope" select="$scope"/>
        <xsl:with-param name="role" select="$role"/>
        <xsl:with-param name="type" select="$type"/>
        <xsl:with-param name="lang" select="$lang"/>
      </xsl:call-template>
    </xsl:for-each-group>

    <xsl:if test="$refs[not(db:tertiary)]/*[self::db:see]">
      <xsl:for-each-group select="$refs[db:see]"
                          group-by="concat(fp:primary(.), ' ', fp:secondary(.), ' ', ' ', db:see)">
        <xsl:apply-templates select="." mode="m:index-see">
          <xsl:with-param name="scope" select="$scope"/>
          <xsl:with-param name="role" select="$role"/>
          <xsl:with-param name="type" select="$type"/>
          <xsl:with-param name="lang" select="$lang"/>
          <xsl:sort select="upper-case(db:see)" lang="{$lang}"/>
        </xsl:apply-templates>
      </xsl:for-each-group>
    </xsl:if>
    <xsl:if test="$refs/db:tertiary or $refs[not(db:tertiary)]/*[self::db:seealso]">
      <ul>
        <xsl:if test="count(db:seealso) &gt; 1">
          <xsl:message>Multiple see also's not supported: only using first</xsl:message>
        </xsl:if>

        <xsl:for-each-group select="$refs[db:seealso]"
                            group-by="concat(fp:primary(.), ' ', fp:secondary(.), ' ', ' ', db:seealso[1])">
          <xsl:apply-templates select="." mode="m:index-seealso">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
            <xsl:sort select="upper-case(db:seealso[1])" lang="{$lang}"/>
          </xsl:apply-templates>
        </xsl:for-each-group>

        <xsl:for-each-group select="$refs[db:tertiary]"
                            group-by="concat($key, ' ', fp:tertiary(.))">
          <xsl:apply-templates select="." mode="m:index-tertiary">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
            <xsl:with-param name="refs" select="current-group()"/>
            <xsl:sort select="upper-case(fp:tertiary(.))" lang="{$lang}"/>
          </xsl:apply-templates>
        </xsl:for-each-group>
      </ul>
    </xsl:if>
  </li>
</xsl:template>

<xsl:template match="db:indexterm" mode="m:index-tertiary">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="refs" as="element()*"/>

  <xsl:variable name="key" select="concat(fp:primary(.), ' ', fp:secondary(.), ' ', fp:tertiary(.))"/>
  <li>
    <xsl:value-of select="db:tertiary"/>
    <xsl:for-each-group select="$refs[not(db:see)]"
                        group-by="concat($key, ' ', fp:nearest-section-id(.))">
      <xsl:call-template name="tp:indexed-section">
        <xsl:with-param name="nodes" select="current-group()"/>
        <xsl:with-param name="scope" select="$scope"/>
        <xsl:with-param name="role" select="$role"/>
        <xsl:with-param name="type" select="$type"/>
        <xsl:with-param name="lang" select="$lang"/>
      </xsl:call-template>
    </xsl:for-each-group>

    <xsl:if test="$refs/db:see">
      <xsl:for-each-group select="$refs[db:see]"
                          group-by="concat(fp:primary(.), ' ', fp:secondary(.), ' ', fp:tertiary(.), ' ', db:see)">
        <xsl:apply-templates select="." mode="m:index-see">
          <xsl:with-param name="scope" select="$scope"/>
          <xsl:with-param name="role" select="$role"/>
          <xsl:with-param name="type" select="$type"/>
          <xsl:with-param name="lang" select="$lang"/>
          <xsl:sort select="upper-case(db:see)" lang="{$lang}"/>
        </xsl:apply-templates>
      </xsl:for-each-group>
    </xsl:if>
    <xsl:if test="$refs/db:seealso">
      <ul>
        <xsl:if test="count(db:seealso) &gt; 1">
          <xsl:message>Multiple see also's not supported: only using first</xsl:message>
        </xsl:if>

        <xsl:for-each-group select="$refs[db:seealso]"
                            group-by="concat(fp:primary(.), ' ', fp:secondary(.), ' ', fp:tertiary(.), ' ', db:seealso[1])">
          <xsl:apply-templates select="." mode="m:index-seealso">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
            <xsl:sort select="upper-case(db:seealso[1])" lang="{$lang}"/>
          </xsl:apply-templates>
        </xsl:for-each-group>
      </ul>
    </xsl:if>
  </li>
</xsl:template>

<xsl:template name="tp:indexed-section">
  <xsl:param name="nodes" as="element()+" required="yes"/>
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>

  <xsl:choose>
    <xsl:when test="f:is-true($indexed-section-groups)">
      <xsl:text>, </xsl:text>
      <xsl:variable name="tobject"
                    select="$nodes[1]/ancestor::*[db:title or db:info/db:title][1]"/>
      <span class="indexed-section">
        <xsl:attribute name="title">
          <xsl:apply-templates select="$tobject" mode="m:headline">
            <xsl:with-param name="purpose" select="'index-tooltip'"/>
          </xsl:apply-templates>
        </xsl:attribute>

        <xsl:for-each select="$nodes">
          <xsl:variable name="pos" select="position()"/>
          <xsl:variable name="last" select="count(current-group())"/>

          <xsl:apply-templates select="." mode="mp:reference">
            <xsl:with-param name="scope" select="$scope"/>
            <xsl:with-param name="role" select="$role"/>
            <xsl:with-param name="type" select="$type"/>
            <xsl:with-param name="lang" select="$lang"/>
            <xsl:with-param name="separator" select="if ($pos = 1) then '' else ', '"/>
            <xsl:with-param name="position" select="$pos"/>
            <xsl:with-param name="last" select="$last"/>
          </xsl:apply-templates>
        </xsl:for-each>
      </span>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="$nodes[1]" mode="mp:reference">
        <xsl:with-param name="scope" select="$scope"/>
        <xsl:with-param name="role" select="$role"/>
        <xsl:with-param name="type" select="$type"/>
        <xsl:with-param name="lang" select="$lang"/>
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:indexterm" mode="mp:reference">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="separator" select="', '"/>
  <xsl:param name="position" as="xs:integer?"/>
  <xsl:param name="last" as="xs:integer?"/>

  <xsl:value-of select="$separator"/>
  <xsl:choose>
    <xsl:when test="@zone">
      <xsl:call-template name="t:index-zone-reference">
        <xsl:with-param name="zones" select="tokenize(@zone, '\s+')"/>
        <xsl:with-param name="scope" select="$scope"/>
        <xsl:with-param name="role" select="$role"/>
        <xsl:with-param name="type" select="$type"/>
        <xsl:with-param name="lang" select="$lang"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="tobject"
                    select="ancestor::*[db:title or db:info/db:title][1]"/>
      <a class="indexref" href="{f:href(/,.)}">
        <xsl:if test="not(f:is-true($indexed-section-groups))">
          <xsl:attribute name="title">
            <xsl:apply-templates select="$tobject" mode="m:headline">
              <xsl:with-param name="purpose" select="'index-tooltip'"/>
            </xsl:apply-templates>
          </xsl:attribute>
        </xsl:if>
        <xsl:sequence select="($position, position())[1]"/>
      </a>

      <xsl:if test="key('endofrange', @xml:id)[fp:scope(., $scope, $role, $type)]">
        <xsl:apply-templates select="key('endofrange', @xml:id)[fp:scope(., $scope, $role, $type)][last()]"
                             mode="mp:reference">
          <xsl:with-param name="scope" select="$scope"/>
          <xsl:with-param name="role" select="$role"/>
          <xsl:with-param name="type" select="$type"/>
          <xsl:with-param name="lang" select="$lang"/>
          <xsl:with-param name="separator" select="'-'"/>
        </xsl:apply-templates>
      </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="t:index-zone-reference">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="zones" as="xs:string*"/>

  <xsl:choose>
    <xsl:when test="empty($zones)"/>
    <xsl:otherwise>
      <xsl:variable name="zone" select="$zones[1]"/>
      <xsl:variable name="target" select="key('id', $zone)
                                             [fp:scope(., $scope, $role, $type)]"/>
      <xsl:choose>
        <xsl:when test="$target">
          <a class="indexref" href="{f:href(/,$target[1])}">
            <xsl:apply-templates select="$target[1]" mode="m:headline">
              <xsl:with-param name="purpose" select="'index'"/>
            </xsl:apply-templates>
          </a>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message select="'Warning: missing zone:', $zone"/>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:if test="count($zones) gt 1">
        <xsl:text>, </xsl:text>
        <xsl:call-template name="t:index-zone-reference">
          <xsl:with-param name="zones" select="substring-after($zones, ' ')"/>
          <xsl:with-param name="scope" select="$scope"/>
          <xsl:with-param name="role" select="$role"/>
          <xsl:with-param name="type" select="$type"/>
          <xsl:with-param name="lang" select="$lang"/>
        </xsl:call-template>
      </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:indexterm" mode="m:index-see">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>

  <xsl:apply-templates select="db:see" mode="m:gentext">
    <xsl:with-param name="group" select="'see'"/>
    <xsl:with-param name="content">
      <span class="see">
        <xsl:apply-templates select="db:see/node()"/>
      </span>
    </xsl:with-param>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:indexterm" mode="m:index-seealso">
  <xsl:param name="scope" select="."/>
  <xsl:param name="role" select="''"/>
  <xsl:param name="type" select="''"/>
  <xsl:param name="lang" select="'en'"/>

  <xsl:variable name="list" as="element()+">
    <xsl:for-each select="db:seealso">
      <xsl:sort select="upper-case(.)" lang="{$lang}"/>
      <span class="seealso">
        <xsl:apply-templates select="node()"/>
      </span>
    </xsl:for-each>
  </xsl:variable>

  <li>
    <xsl:apply-templates select="." mode="m:gentext-list">
      <xsl:with-param name="list" select="$list"/>
      <xsl:with-param name="name" select="'index-seealso'"/>
    </xsl:apply-templates>
  </li>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:indexentry">
  <xsl:apply-templates select="db:primaryie"/>
</xsl:template>

<xsl:template match="db:primaryie">
  <li>
    <xsl:apply-templates/>
    <xsl:choose>
      <xsl:when test="following-sibling::db:secondaryie">
        <ul>
          <xsl:apply-templates select="following-sibling::db:secondaryie"/>
        </ul>
      </xsl:when>
      <xsl:when test="following-sibling::db:seeie
                      |following-sibling::db:seealsoie">
        <ul>
          <xsl:apply-templates select="following-sibling::db:seeie
                                       |following-sibling::db:seealsoie"/>
        </ul>
      </xsl:when>
    </xsl:choose>
  </li>
</xsl:template>

<xsl:template match="db:secondaryie">
  <li>
    <xsl:apply-templates/>
    <xsl:choose>
      <xsl:when test="following-sibling::db:tertiaryie">
        <ul>
          <xsl:apply-templates select="following-sibling::db:tertiaryie"/>
        </ul>
      </xsl:when>
      <xsl:when test="following-sibling::db:seeie
                      |following-sibling::db:seealsoie">
        <ul>
          <xsl:apply-templates select="following-sibling::db:seeie
                                       |following-sibling::db:seealsoie"/>
        </ul>
      </xsl:when>
    </xsl:choose>
  </li>
</xsl:template>

<xsl:template match="db:tertiaryie">
  <li>
    <xsl:apply-templates/>
    <xsl:if test="following-sibling::db:seeie
                  |following-sibling::db:seealsoie">
      <ul>
        <xsl:apply-templates select="following-sibling::db:seeie
                                     |following-sibling::db:seealsoie"/>
      </ul>
    </xsl:if>
  </li>
</xsl:template>

<xsl:template match="db:seeie[preceding-sibling::db:seeie]" priority="10"/>
<xsl:template match="db:seeie">
  <xsl:variable name="targets" as="element()+">
    <xsl:for-each select="(., following-sibling::db:seeie)">
      <xsl:sort select="lower-case(.)"/>
      <span>
        <xsl:apply-templates/>
      </span>
    </xsl:for-each>
  </xsl:variable>
  
  <li>
    <xsl:apply-templates select="." mode="m:gentext-list">
      <xsl:with-param name="list" select="$targets"/>
      <xsl:with-param name="name" select="'index-seeie'"/>
    </xsl:apply-templates>
  </li>
</xsl:template>

<xsl:template match="db:seealsoie[preceding-sibling::db:seealsoie]" priority="10"/>
<xsl:template match="db:seealsoie">
  <xsl:variable name="targets" as="element()+">
    <xsl:for-each select="(., following-sibling::db:seealsoie)">
      <xsl:sort select="lower-case(.)"/>
      <span>
        <xsl:apply-templates/>
      </span>
    </xsl:for-each>
  </xsl:variable>
  
  <li>
    <xsl:apply-templates select="." mode="m:gentext-list">
      <xsl:with-param name="list" select="$targets"/>
      <xsl:with-param name="name" select="'index-seealsoie'"/>
    </xsl:apply-templates>
  </li>
</xsl:template>

</xsl:stylesheet>

sections.xsl

2 templates, 1 variable (1 used only in one other module)

Instructions
Template match ≅ db:sect1|db:sect2|db:sect3|db:…
Mode: m:docbook
Matches: db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section, db:simplesect
Variable $v:bridgehead-map as map(*)
Used in: main.xsl
Template match ≅ db:bridgehead
Mode: m:docbook
Matches: db:bridgehead
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m map t v xs"
                version="3.0">

<xsl:template match="db:sect1|db:sect2|db:sect3|db:sect4|db:sect5
                     |db:section|db:simplesect">
  <section>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </section>
</xsl:template>

<xsl:variable name="v:bridgehead-map" as="map(*)">
  <xsl:map>
    <xsl:map-entry key="'sect1'" select="'h2'"/>
    <xsl:map-entry key="'sect2'" select="'h3'"/>
    <xsl:map-entry key="'sect3'" select="'h4'"/>
    <xsl:map-entry key="'sect4'" select="'h5'"/>
    <xsl:map-entry key="'sect5'" select="'h5'"/>
    <xsl:map-entry key="'sect6'" select="'h5'"/>
    <xsl:map-entry key="'block'" select="'div'"/>
  </xsl:map>
</xsl:variable>

<xsl:template match="db:bridgehead">
  <xsl:variable name="renderas" as="xs:string">
    <xsl:choose>
      <xsl:when test="@renderas">
        <xsl:sequence select="@renderas/string()"/>
      </xsl:when>
      <xsl:when test="parent::db:section">
        <xsl:sequence select="'sect' || (count(ancestor::db:section)+1)"/>
      </xsl:when>
      <xsl:when test="parent::db:refsection">
        <xsl:sequence select="'sect' || (count(ancestor::db:refsection)+1)"/>
      </xsl:when>
      <xsl:when test="parent::db:sect5">
        <xsl:sequence select="'sect5'"/>
      </xsl:when>
      <xsl:when test="parent::db:sect1|parent::db:sect2|parent::db:sect3|parent::db:sect4">
        <xsl:sequence select="'sect' ||
                               (xs:integer(substring(local-name(parent::*), 5, 1)) + 1)"/>
      </xsl:when>
      <xsl:when test="parent::db:refsect1|parent::db:refsect2|parent::db:refsect3">
        <xsl:sequence select="'sect' ||
                               (xs:integer(substring(local-name(parent::*), 8, 1)) + 1)"/>
      </xsl:when>
      <xsl:when test="parent::db:article|parent::db:chapter|parent::db:appendix
                      |parent::db:preface|parent::db:partintro
                      |parent::db:part|parent::db:reference">
        <xsl:sequence select="'sect1'"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="'block'"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="empty(map:get($v:bridgehead-map, $renderas))">
      <xsl:message select="'Unknown bridgehead renderas:', $renderas"/>
      <div>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="extra-classes" select="('title')"/>
        </xsl:apply-templates>
        <xsl:apply-templates/>
      </div>
    </xsl:when>
    <xsl:when test="map:get($v:bridgehead-map, $renderas) = 'div'">
      <div>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="extra-classes" select="('title')"/>
        </xsl:apply-templates>
        <xsl:apply-templates/>
      </div>
    </xsl:when>
    <xsl:otherwise>
      <xsl:element name="{map:get($v:bridgehead-map, $renderas)}"
                   namespace="http://www.w3.org/1999/xhtml">
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </xsl:element>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

templates.xsl

15 templates (1 used only in one other module), 3 functions (3 used only in one other module), 3 variables (3 used only in one other module)

Instructions
Variable $v:templates as document-node()
Used in: main.xsl
Variable $vp:templates as document-node()
Function fp:construct-templates($templates as element()*, $list as element()*)
Used in: main.xsl
Template match ≅ element()
Mode: mp:expand-template
Matches: element()
Template match ≅ tmp:insert
Mode: mp:expand-template
Matches: tmp:insert
Template match ≅ attribute()|comment()|processi…
Mode: mp:expand-template
Matches: attribute(), comment(), processing-instruction(), text()
Function f:template($context as element(), $default as element()) as element()
Function fp:pick-template($context as element(), $templates as element()+) as element()
Used in: main.xsl
Variable $v:titlepage-default as element()
Used in: main.xsl
Template match ≅ *
Mode: m:generate-titlepage
Matches: *
Template match ≅ tmp:apply-templates
Mode: mp:construct-titlepage
Matches: tmp:apply-templates
Template match ≅ tmp:content
Mode: mp:construct-titlepage
Matches: tmp:content
Template match ≅ element()
Mode: mp:construct-titlepage
Matches: element()
Template match ≅ text()
Mode: mp:construct-titlepage
Matches: text()
Template match ≅ attribute()|comment()|processi…
Mode: mp:construct-titlepage
Matches: attribute(), comment(), processing-instruction()
Template t:biblioentry match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ tmp:apply-templates
Mode: mp:construct-biblioentry
Matches: tmp:apply-templates
Template match ≅ tmp:content
Mode: mp:construct-biblioentry
Matches: tmp:content
Template match ≅ element()
Mode: mp:construct-biblioentry
Matches: element()
Template match ≅ text()
Mode: mp:construct-biblioentry
Matches: text()
Template match ≅ attribute()|comment()|processi…
Mode: mp:construct-biblioentry
Matches: attribute(), comment(), processing-instruction()
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tmp="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db dbe f fp h m mp t tmp v vp xs"
                version="3.0">

<!-- It's a real shame that you can't have mode variables ... -->

<xsl:strip-space elements="tmp:*"/>

<!-- The default value is an empty document, it's for customizations to override -->
<xsl:variable name="v:templates" as="document-node()">
  <xsl:document/>
</xsl:variable>

<xsl:variable name="vp:templates" as="document-node()">
  <!-- Yes, this is basically fold-left done the hard way,
       but it avoids an EE feature in Saxon 9. -->
  <xsl:document>
    <xsl:sequence
        select="fp:construct-templates(($v:templates/*, doc('templates.xml')/*/*),
                                        ())"/>
  </xsl:document>
</xsl:variable>

<xsl:function name="fp:construct-templates">
  <xsl:param name="templates" as="element()*"/>
  <xsl:param name="list" as="element()*"/>

  <xsl:variable name="car" select="subsequence($templates, 1, 1)"/>
  <xsl:variable name="cdr" select="subsequence($templates, 2)"/>

  <xsl:choose>
    <xsl:when test="empty($templates)">
      <xsl:sequence select="$list"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="template" as="element()">
        <xsl:apply-templates select="$car" mode="mp:expand-template">
          <xsl:with-param name="list" select="$list"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:sequence select="fp:construct-templates($cdr, ($list, $template))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="element()" mode="mp:expand-template">
  <xsl:param name="list" as="element()*" required="yes"/>
  <xsl:copy>
    <xsl:apply-templates select="@*, node()"
                         mode="mp:expand-template">
      <xsl:with-param name="list" select="$list"/>
    </xsl:apply-templates>
  </xsl:copy>
</xsl:template>

<xsl:template match="tmp:insert" mode="mp:expand-template">
  <xsl:param name="list" as="element()*" required="yes"/>

  <xsl:variable name="name"
                select="QName('http://docbook.org/ns/docbook/templates', @ref)"/>
  <xsl:variable name="template"
                select="($list[node-name(.)=$name])[1]"/>

  <xsl:sequence select="if (empty($template))
                        then error($dbe:INVALID-TEMPLATE,
                                   'No such template: ' || @ref)
                        else $template/*"/>
</xsl:template>

<xsl:template match="attribute()|text()|comment()|processing-instruction()"
              mode="mp:expand-template">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="f:template" as="element()">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="default" as="element()"/>

  <xsl:variable name="xpath" select="'/db:' || local-name($context)"/>
  <xsl:variable name="template" as="element()*">
    <xsl:evaluate context-item="$vp:templates" xpath="$xpath"/>
  </xsl:variable>

  <xsl:variable name="template" as="element()*">
    <xsl:sequence select="if (empty($template))
                          then $default
                          else $template"/>
  </xsl:variable>

  <xsl:sequence select="if (count($template) gt 1)
                        then fp:pick-template($context, $template)
                        else $template"/>
</xsl:function>

<xsl:function name="fp:pick-template" as="element()">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="templates" as="element()+"/>

  <xsl:if test="not($templates[1]/@context) and (count($templates) gt 1)"
          use-when="'templates' = $v:debug">
    <xsl:message select="'A template without context should be last.'"/>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="count($templates) eq 1 or not($templates[1]/@context)">
      <xsl:sequence select="$templates[1]"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="items" as="item()*">
        <xsl:evaluate context-item="$context"
                      xpath="$templates[1]/@context"/>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="count($items) = 1 and $items instance of xs:boolean">
          <xsl:sequence
              select="if ($items)
                      then $templates[1]
                      else fp:pick-template($context, subsequence($templates, 2))"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="if (empty($items))
                                then fp:pick-template($context, subsequence($templates, 2))
                                else $templates[1]"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:variable name="v:titlepage-default" as="element()">
  <tmp:titlepage-default>
    <header>
      <tmp:apply-templates select="db:title">
        <div class="title"><tmp:content/></div>
      </tmp:apply-templates>
    </header>
  </tmp:titlepage-default>
</xsl:variable>

<xsl:template match="*" mode="m:generate-titlepage">
  <xsl:if test="fp:pmuj-enabled(/)">
    <xsl:sequence select="@xml:id ! fp:pmuj(./parent::*, ./string())"/>
  </xsl:if>

  <xsl:variable name="template"
                select="if (db:info/tmp:titlepage-template)
                        then db:info/tmp:titlepage-template
                        else f:template(., $v:titlepage-default)"/>

  <xsl:if test="empty(db:info)" use-when="'templates' = $v:debug">
    <xsl:message terminate="yes" select="'No db:info in', local-name(.)"/>
  </xsl:if>

  <xsl:variable name="empty-info" as="element(db:info)">
    <info xmlns="http://docbook.org/ns/docbook"/>
  </xsl:variable>

  <xsl:variable name="info"
                select="if (empty(db:info)) then $empty-info else db:info"/>

  <xsl:variable name="titlepage" as="node()*">
    <xsl:apply-templates select="$template/node()" mode="mp:construct-titlepage">
      <xsl:with-param name="info" select="$info"/>
      <xsl:with-param name="context" select="$info"/>
      <xsl:with-param name="template" select="$template/*"/>
      <xsl:with-param name="content" select="()"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:sequence select="if (count($titlepage) = 1
                            and $titlepage/self::*
                            and empty($titlepage/node())
                            and empty($titlepage/@*))
                        then ()
                        else $titlepage"/>
</xsl:template>

<xsl:template match="tmp:apply-templates" mode="mp:construct-titlepage">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>

  <xsl:variable name="content" select="node()"/>

  <xsl:variable use-when="'templates' = $v:debug"
                name="select" select="@select/string()"/>

  <xsl:variable name="elements" as="element()*">
    <xsl:evaluate xpath="@select" context-item="$context"/>
  </xsl:variable>

  <xsl:for-each select="$elements">
    <xsl:message use-when="'templates-matches' = $v:debug"
                 select="'Template:', $select, 'matched', node-name(.)"/>

    <xsl:choose>
      <xsl:when test="empty($content)">
        <xsl:apply-templates select="." mode="m:titlepage"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="$content" mode="mp:construct-titlepage">
          <xsl:with-param name="info" select="$info"/>
          <xsl:with-param name="context" select="."/>
          <xsl:with-param name="template" select="$template"/>
          <xsl:with-param name="content" select="."/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:template>

<xsl:template match="tmp:content" mode="mp:construct-titlepage">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>

  <xsl:apply-templates select="$content" mode="m:titlepage"/>
</xsl:template>

<xsl:template match="element()" mode="mp:construct-titlepage">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>

  <!-- For some reason, exclude-result-prefixes isn't
       excluding them, so just don't copy them. -->
  <xsl:element name="{node-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="@*,node()"
                         mode="mp:construct-titlepage">
      <xsl:with-param name="info" select="$info"/>
      <xsl:with-param name="context" select="$context"/>
      <xsl:with-param name="template" select="$template"/>
      <xsl:with-param name="content" select="$content"/>
    </xsl:apply-templates>
  </xsl:element>
</xsl:template>

<xsl:template match="text()" mode="mp:construct-titlepage">
  <!-- whitespace in templates is ignored -->
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>
  <xsl:if test="normalize-space(.) != ''">
    <xsl:copy/>
  </xsl:if>
</xsl:template>

<xsl:template match="attribute()|comment()|processing-instruction()"
              mode="mp:construct-titlepage">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="t:biblioentry">
  <!-- For many elements, there's a default template if the user fails
       to provide one. There's so much variation in biblioentry, it's
       not even worth trying. -->
  <xsl:variable name="biblioentry-default" as="element()">
    <tmp:biblioentry-default>
      <div>ERROR: No template for biblioentry</div>
    </tmp:biblioentry-default>
  </xsl:variable>
  <xsl:variable name="template" select="f:template(., $biblioentry-default)"/>
  <xsl:variable name="entry" as="node()*">
    <xsl:apply-templates select="$template/node()" mode="mp:construct-biblioentry">
      <xsl:with-param name="info" select="."/>
      <xsl:with-param name="context" select="."/>
      <xsl:with-param name="template" select="$template/*"/>
      <xsl:with-param name="content" select="()"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:sequence select="if (count($entry) = 1
                            and $entry/self::*
                            and empty($entry/node())
                            and empty($entry/@*))
                        then ()
                        else $entry"/>
</xsl:template>

<xsl:template match="tmp:apply-templates" mode="mp:construct-biblioentry">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>

  <xsl:variable name="content" select="node()"/>

  <xsl:variable use-when="'templates' = $v:debug"
                name="select" select="@select/string()"/>

  <xsl:variable name="elements" as="element()*">
    <xsl:evaluate xpath="@select" context-item="$context"/>
  </xsl:variable>

  <xsl:for-each select="$elements">
    <xsl:message use-when="'templates-matches' = $v:debug"
                 select="'Template:', $select, 'matched', node-name(.)"/>

    <xsl:choose>
      <xsl:when test="empty($content)">
        <xsl:apply-templates select="." mode="m:biblioentry"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="$content" mode="mp:construct-biblioentry">
          <xsl:with-param name="info" select="$info"/>
          <xsl:with-param name="context" select="."/>
          <xsl:with-param name="template" select="$template"/>
          <xsl:with-param name="content" select="."/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:template>

<xsl:template match="tmp:content" mode="mp:construct-biblioentry">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>

  <xsl:apply-templates select="$content" mode="m:biblioentry"/>
</xsl:template>

<xsl:template match="element()" mode="mp:construct-biblioentry">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>

  <!-- For some reason, exclude-result-prefixes isn't
       excluding them, so just don't copy them. -->
  <xsl:element name="{node-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="@*,node()"
                         mode="mp:construct-biblioentry">
      <xsl:with-param name="info" select="$info"/>
      <xsl:with-param name="context" select="$context"/>
      <xsl:with-param name="template" select="$template"/>
      <xsl:with-param name="content" select="$content"/>
    </xsl:apply-templates>
  </xsl:element>
</xsl:template>

<xsl:template match="text()" mode="mp:construct-biblioentry">
  <!-- whitespace in templates is ignored -->
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>
  <xsl:if test="normalize-space(.) != ''">
    <xsl:copy/>
  </xsl:if>
</xsl:template>

<xsl:template match="attribute()|comment()|processing-instruction()"
              mode="mp:construct-biblioentry">
  <xsl:param name="info" required="yes"/>
  <xsl:param name="context" required="yes"/>
  <xsl:param name="template" required="yes"/>
  <xsl:param name="content" required="yes"/>
  <xsl:copy/>
</xsl:template>


</xsl:stylesheet>

titlepage.xsl

9 templates

Instructions
Template match ≅ db:title
Mode: m:titlepage
Matches: db:title
Template match ≅ db:formalpara/db:info/db:title
Mode: m:titlepage
Priority: 10
Matches: db:formalpara/db:info/db:title
Template match ≅ db:subtitle
Mode: m:titlepage
Matches: db:subtitle
Template match ≅ db:abstract|db:authorgroup|db:…
Mode: m:titlepage
Matches: db:abstract, db:authorgroup, db:copyright, db:legalnotice, db:pubdate, db:revhistory
Template match ≅ db:releaseinfo
Mode: m:titlepage
Matches: db:releaseinfo
Template match ≅ db:author
Mode: m:titlepage
Matches: db:author
Template match ≅ db:editor
Mode: m:titlepage
Matches: db:editor
Template match ≅ db:jobtitle|db:orgdiv|db:orgna…
Mode: m:titlepage
Matches: db:jobtitle, db:orgdiv, db:orgname
Template match ≅ *
Uses: $v:debug
Mode: m:titlepage
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/l10n/title"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="db:title" mode="m:titlepage">
  <xsl:apply-templates select="../.." mode="m:headline">
    <xsl:with-param name="purpose" select="'title'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:formalpara/db:info/db:title" mode="m:titlepage" priority="10">
  <span>
    <xsl:choose>
      <xsl:when test="matches(normalize-space(string(.)), '^.*\p{P}$')">
        <xsl:attribute name="class" select="'title titlepunct'"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:attribute name="class" select="'title'"/>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:apply-templates mode="m:titlepage"/>
  </span>
</xsl:template>

<xsl:template match="db:subtitle" mode="m:titlepage">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:copyright|db:abstract|db:legalnotice
                     |db:authorgroup|db:revhistory
                     |db:pubdate"
              mode="m:titlepage">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="db:releaseinfo" mode="m:titlepage">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:author" mode="m:titlepage">
  <xsl:apply-templates select="db:personname|db:orgname"/>
</xsl:template>

<xsl:template match="db:editor" mode="m:titlepage">
  <span class="editedby">
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'label'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'label-separator'"/>
    </xsl:apply-templates>
  </span>
  <xsl:apply-templates select="db:personname|db:orgname"/>
</xsl:template>

<xsl:template match="db:jobtitle|db:orgname|db:orgdiv" mode="m:titlepage">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="*" mode="m:titlepage">
  <xsl:if test="'templates' = $v:debug">
    <xsl:message select="'No titlepage template for: ' || node-name(.)"/>
  </xsl:if>
  <xsl:apply-templates select="."/>
</xsl:template>

</xsl:stylesheet>

info.xsl

20 templates (5 used only in one other module), 2 functions (2 used only in one other module)

Instructions
Template match ≅ db:info
Mode: m:docbook
Matches: db:info
Template match ≅ db:copyright
Mode: m:docbook
Matches: db:copyright
Template match ≅ db:year
Mode: m:copyright-years
Matches: db:year
Function fp:collapse-years#1($years as element(db:year)*)
Function fp:collapse-years#4($years as element(db:year)*, $first as xs:boolean, $prevyear as element(db:year)?, $constructed as item()*)
Template match ≅ db:holder|db:year
Mode: m:docbook
Matches: db:holder, db:year
Template match ≅ db:abstract|db:authorgroup|db:…
Mode: m:docbook
Matches: db:abstract, db:authorgroup, db:legalnotice
Template match ≅ db:firstname|db:givenname|db:h…
Mode: m:docbook
Matches: db:firstname, db:givenname, db:honorific, db:lineage, db:othername, db:surname
Template match ≅ db:personname
Mode: m:docbook
Matches: db:personname
Template t:person-name match ≅
Template t:person-name-family-given match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ /
Mode: m:to-uppercase
Matches: /
Template match ≅ *
Mode: m:to-uppercase
Matches: *
Template match ≅ comment()|processing-instructi…
Mode: m:to-uppercase
Matches: comment(), processing-instruction()
Template match ≅ text()
Mode: m:to-uppercase
Matches: text()
Template t:person-name-last-first match ≅
Used in: main.xsl
Mode: m:docbook
Template t:person-name-first-last match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ db:city|db:country|db:fax|db:o…
Calls: t:inline
Mode: m:docbook
Matches: db:city, db:country, db:fax, db:otheraddr, db:phone, db:pob, db:postcode, db:state, db:street
Template t:person-name-list match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ db:artpagenums|db:authorinitia…
Calls: t:inline
Mode: m:docbook
Matches: db:artpagenums, db:authorinitials, db:contractnum, db:contractsponsor, db:edition, db:issuenum, db:pagenums, db:publishername, db:seriesvolnums, db:volumenum
Template match ≅ db:pubdate
Calls: f:pi()
Calls: t:inline
Mode: m:docbook
Matches: db:pubdate
Template match ≅ db:info/db:pubdate
Calls: f:pi()
Calls: t:inline
Mode: m:docbook
Matches: db:info/db:pubdate
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db dbe f fp m t v vp xs"
                version="3.0">

<xsl:template match="db:info">
  <xsl:apply-templates select="db:indexterm"/>
  <xsl:where-populated>
    <head>
      <xsl:apply-templates select=".." mode="m:head-additions"/>
    </head>
  </xsl:where-populated>
</xsl:template>

<xsl:template match="db:copyright">
  <xsl:variable name="gi" select="if (parent::db:info)
                                  then 'div'
                                  else 'span'"/>
  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:text>Copyright © </xsl:text>
    <xsl:apply-templates select="db:year[1]" mode="m:copyright-years"/>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="db:holder"/>
  </xsl:element>
</xsl:template>

<xsl:template match="db:year" mode="m:copyright-years">
  <xsl:param name="prevyear" select="()"/>
  <xsl:param name="range" select="()"/>

  <xsl:choose>
    <xsl:when test="not($copyright-collapse-years)">
      <span>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </span>
      <xsl:if test="following-sibling::db:year">
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="(following-sibling::db:year)[1]"/>
      </xsl:if>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:collapse-years((., following-sibling::db:year))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:function name="fp:collapse-years">
  <xsl:param name="years" as="element(db:year)*"/>

  <span class="copyright-years">
    <xsl:choose>
      <xsl:when test="count($years) = 1">
        <xsl:apply-templates select="$years"/>
      </xsl:when>
      <xsl:when test="count($years) = 2">
        <xsl:choose>
          <xsl:when test="$years[1] castable as xs:integer
                          and $years[2] castable as xs:integer
                          and xs:integer($years[2]) = xs:integer($years[1]) + 1">
            <xsl:apply-templates select="$years[1]"/>
            <xsl:sequence select="$copyright-year-range-separator"/>
            <xsl:apply-templates select="$years[2]"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="$years[1]"/>
            <xsl:sequence select="$copyright-year-separator"/>
            <xsl:apply-templates select="$years[2]"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="fp:collapse-years($years, true(), (), ())"/>
      </xsl:otherwise>
    </xsl:choose>
  </span>
</xsl:function>

<xsl:function name="fp:collapse-years">
  <xsl:param name="years" as="element(db:year)*"/>
  <xsl:param name="first" as="xs:boolean"/>
  <xsl:param name="prevyear" as="element(db:year)?"/>
  <xsl:param name="constructed" as="item()*"/>

  <!--
  <xsl:message select="'[', $prevyear/string(), ']:', $years ! string(.)"/>
  <xsl:message select="'X:', $first, $constructed"/>
  -->

  <xsl:choose>
    <xsl:when test="empty($prevyear)">
      <xsl:sequence
          select="fp:collapse-years(subsequence($years, 2), $first, $years[1], ())"/>
    </xsl:when>
    <xsl:when test="empty($years)">
      <xsl:choose>
        <xsl:when test="empty($constructed)">
          <xsl:if test="not($first)">
            <xsl:sequence select="$copyright-year-separator"/>
          </xsl:if>
          <xsl:apply-templates select="$prevyear"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="not($first)">
            <xsl:sequence select="$copyright-year-separator"/>
          </xsl:if>
          <xsl:sequence select="$constructed"/>
          <xsl:sequence select="$copyright-year-range-separator"/>
          <xsl:apply-templates select="$prevyear"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:when test="$prevyear castable as xs:integer
                    and $years[1] castable as xs:integer
                    and xs:integer($years[1]) = xs:integer($prevyear) + 1">
      <xsl:choose>
        <xsl:when test="empty($constructed)">
          <xsl:variable name="firstitem" as="item()*">
            <xsl:apply-templates select="$prevyear"/>
          </xsl:variable>
          <xsl:sequence
              select="fp:collapse-years(subsequence($years, 2),
                         $first, $years[1], $firstitem)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence
              select="fp:collapse-years(subsequence($years, 2),
                         $first, $years[1], $constructed)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:choose>
        <xsl:when test="empty($constructed)">
          <xsl:if test="not($first)">
            <xsl:sequence select="$copyright-year-separator"/>
          </xsl:if>
          <xsl:apply-templates select="$prevyear"/>
          <xsl:sequence
              select="fp:collapse-years(subsequence($years, 2), false(), $years[1], ())"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="not($first)">
            <xsl:sequence select="$copyright-year-separator"/>
          </xsl:if>
          <xsl:sequence select="$constructed"/>
          <xsl:sequence select="$copyright-year-range-separator"/>
          <xsl:apply-templates select="$prevyear"/>
          <xsl:sequence
              select="fp:collapse-years(subsequence($years, 2), false(), $years[1], ())"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="db:holder|db:year">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:abstract|db:legalnotice
                     |db:authorgroup[parent::db:info]">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:honorific|db:firstname|db:othername
                     |db:surname|db:givenname|db:lineage">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:personname">
  <xsl:variable name="node" select="."/>

  <xsl:variable name="style" as="xs:string?">
    <xsl:for-each select="$v:personal-name-styles">
      <xsl:if test="contains-token($node/@role, .)">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="style" as="xs:string?">
    <xsl:choose>
      <xsl:when test="empty($style)
                      and (parent::db:author
                           or parent::db:editor
                           or parent::db:othercredit)
                      and parent::*/@role">
        <xsl:for-each select="$v:personal-name-styles">
          <xsl:if test="contains-token($node/parent::*/@role, .)">
            <xsl:sequence select="."/>
          </xsl:if>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$style"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="style">
    <xsl:choose>
      <xsl:when test="empty($style)">
        <xsl:choose>
          <xsl:when test="exists($personal-name-style)">
            <xsl:sequence select="$personal-name-style"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="." mode="m:gentext">
              <xsl:with-param name="group" select="'name-style'"/>
            </xsl:apply-templates>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$style"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <span>
    <xsl:apply-templates select="." mode="m:attributes">
      <xsl:with-param name="style" select="string($style)"/>
    </xsl:apply-templates>
    <xsl:call-template name="t:person-name">
      <xsl:with-param name="style" select="$style"/>
    </xsl:call-template>
  </span>
</xsl:template>

<xsl:template name="t:person-name">
  <xsl:param name="style" as="xs:string" required="yes"/>

  <xsl:variable name="node" select="."/>

  <xsl:choose>
    <xsl:when test="not(*)">
      <xsl:value-of select="normalize-space(.)"/>
    </xsl:when>
    <xsl:when test="$style = 'FAMILY-given'">
      <xsl:call-template name="t:person-name-family-given"/>
    </xsl:when>
    <xsl:when test="$style = 'last-first'">
      <xsl:call-template name="t:person-name-last-first"/>
    </xsl:when>
    <xsl:when test="$style = 'first-last'">
      <xsl:call-template name="t:person-name-first-last"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="error($dbe:INVALID-NAME-STYLE,
        'Invalid name style: ' || $style)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="t:person-name-family-given">
  <!-- The family-given style applies a convention for identifying given -->
  <!-- and family names in locales where it may be ambiguous -->
  <xsl:variable name="surname">
    <xsl:apply-templates select="db:surname[1]"/>
  </xsl:variable>

  <xsl:apply-templates select="$surname/node()" mode="m:to-uppercase"/>

  <xsl:if test="db:surname and (db:firstname or db:givenname)">
    <xsl:text> </xsl:text>
  </xsl:if>

  <xsl:apply-templates select="(db:firstname|db:givenname)[1]"/>

  <xsl:text> [FAMILY Given]</xsl:text>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="/" mode="m:to-uppercase">
  <xsl:apply-templates mode="m:to-uppercase"/>
</xsl:template>

<xsl:template match="*" mode="m:to-uppercase">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="m:to-uppercase"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="processing-instruction()|comment()" mode="m:to-uppercase">
  <xsl:copy/>
</xsl:template>

<xsl:template match="text()" mode="m:to-uppercase">
  <xsl:value-of select="upper-case(.)"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="t:person-name-last-first">
  <xsl:apply-templates select="db:surname[1]"/>

  <xsl:if test="db:surname and (db:firstname or db:givenname)">
    <xsl:text>, </xsl:text>
  </xsl:if>

  <xsl:apply-templates select="(db:firstname|db:givenname)[1]"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="t:person-name-first-last">
  <xsl:if test="db:honorific">
    <xsl:apply-templates select="db:honorific[1]"/>
  </xsl:if>

  <xsl:if test="db:firstname or db:givenname">
    <xsl:if test="db:honorific">
      <xsl:text> </xsl:text>
    </xsl:if>
    <xsl:apply-templates select="(db:firstname|db:givenname)[1]"/>
  </xsl:if>

  <xsl:if test="db:othername and f:is-true($othername-in-middle)">
    <xsl:if test="db:honorific or db:firstname or db:givenname">
      <xsl:text> </xsl:text>
    </xsl:if>
    <xsl:apply-templates select="db:othername[1]"/>
  </xsl:if>

  <xsl:if test="db:surname">
    <xsl:if test="db:honorific or db:firstname or db:givenname
                  or (db:othername and f:is-true($othername-in-middle))">
      <xsl:text> </xsl:text>
    </xsl:if>
    <xsl:apply-templates select="db:surname[1]"/>
  </xsl:if>

  <xsl:if test="db:lineage">
    <xsl:text>, </xsl:text>
    <xsl:apply-templates select="db:lineage[1]"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:city|db:country|db:fax|db:phone
                     |db:pob|db:postcode|db:state|db:street|db:otheraddr">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="t:person-name-list">
  <!-- Return a formatted string representation of the contents of
       the current element. The current element must contain one or
       more AUTHORs, CORPAUTHORs, OTHERCREDITs, and/or EDITORs.

       John Doe
     or
       John Doe and Jane Doe
     or
       John Doe, Jane Doe, and A. Nonymous
  -->

  <xsl:apply-templates select="." mode="m:gentext-list">
    <xsl:with-param name="list" as="element()*">
      <xsl:apply-templates
        select="db:author|db:corpauthor|db:othercredit|db:editor"/>
    </xsl:with-param>
  </xsl:apply-templates>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:publishername
                     |db:seriesvolnums|db:volumenum|db:issuenum
                     |db:artpagenums|db:authorinitials|db:edition|db:pagenums
                     |db:contractnum|db:contractsponsor">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:pubdate">
  <xsl:variable name="format" select="f:pi(., 'format')"/>

  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'time'"/>
    <xsl:with-param name="content">
      <xsl:apply-templates/>
    </xsl:with-param>
    <xsl:with-param name="extra-attributes" as="attribute()*">
      <xsl:choose>
        <xsl:when test="*"/>
        <xsl:when test="string(.) castable as xs:dateTime">
          <xsl:attribute name="datetime" select="."/>
        </xsl:when>
        <xsl:when test="string(.) castable as xs:date">
          <xsl:attribute name="datetime" select="."/>
        </xsl:when>
        <xsl:when test="string(.) castable as xs:integer">
          <xsl:attribute name="datetime" select="."/>
        </xsl:when>
        <xsl:otherwise/>
      </xsl:choose>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:info/db:pubdate">
  <xsl:variable name="format" select="f:pi(., 'format')"/>

  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'time'"/>
    <xsl:with-param name="content">
      <xsl:choose>
        <xsl:when test="*">
          <xsl:apply-templates/>
        </xsl:when>
        <xsl:when test="string(.) castable as xs:dateTime">
          <xsl:variable name="date" select="xs:dateTime(string(.))"/>
          <xsl:choose>
            <xsl:when test="$format">
              <xsl:sequence select="format-dateTime($date, $format)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:sequence select="format-dateTime($date, $date-dateTime-format)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="string(.) castable as xs:date">
          <xsl:variable name="date" select="xs:date(string(.))"/>
          <xsl:choose>
            <xsl:when test="$format">
              <xsl:sequence select="format-date($date, $format)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:sequence select="format-date($date, $date-date-format)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:with-param>
    <xsl:with-param name="extra-attributes" as="attribute()*">
      <xsl:choose>
        <xsl:when test="*"/>
        <xsl:when test="string(.) castable as xs:dateTime">
          <xsl:attribute name="datetime" select="."/>
        </xsl:when>
        <xsl:when test="string(.) castable as xs:date">
          <xsl:attribute name="datetime" select="."/>
        </xsl:when>
        <xsl:when test="string(.) castable as xs:integer">
          <xsl:attribute name="datetime" select="."/>
        </xsl:when>
        <xsl:otherwise/>
      </xsl:choose>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>
</xsl:stylesheet>

lists.xsl

33 templates (1 used only in one other module), 2 functions (2 used only in one other module), 1 variable (1 unused)

Instructions
Template match ≅ db:itemizedlist
Mode: m:docbook
Matches: db:itemizedlist
Template match ≅ db:orderedlist
Mode: m:docbook
Matches: db:orderedlist
Template tp:orderedlist-properties match ≅
Template match ≅ db:listitem
Mode: m:docbook
Matches: db:listitem
Template match ≅ db:simplelist
Mode: m:docbook
Matches: db:simplelist
Template match ≅ db:simplelist
Mode: m:docbook
Matches: db:simplelist
Function fp:select-vert-members($member as element(db:member)?, $rows as xs:integer) as element(db:member)*
Used in: main.xsl
Template match ≅ db:member
Mode: m:docbook
Matches: db:member
Template match ≅ db:variablelist
Mode: m:docbook
Matches: db:variablelist
Variable $vp:term
Unused
Template match ≅ db:varlistentry
Mode: m:docbook
Matches: db:varlistentry
Template match ≅ db:term
Mode: m:docbook
Matches: db:term
Function fp:estimated-term-length($entry as element(db:varlistentry))
Used in: main.xsl
Template match ≅ db:variablelist
Mode: m:docbook
Matches: db:variablelist
Template match ≅ db:term
Mode: m:panelset
Matches: db:term
Template match ≅ db:varlistentry
Mode: m:panelset
Matches: db:varlistentry
Template match ≅ db:listitem
Mode: m:panelset
Matches: db:listitem
Template match ≅ db:segmentedlist
Calls: f:pi()
Mode: m:docbook
Matches: db:segmentedlist
Template match ≅ db:segtitle
Mode: m:docbook
Matches: db:segtitle
Template match ≅ db:segtitle
Mode: m:segtitle-in-seg
Matches: db:segtitle
Template match ≅ db:seglistitem
Mode: m:docbook
Matches: db:seglistitem
Template match ≅ db:seg
Mode: m:docbook
Matches: db:seg
Template match ≅ db:segmentedlist
Calls: f:pi()
Mode: m:seglist-table
Matches: db:segmentedlist
Template match ≅ db:segtitle
Mode: m:seglist-table
Matches: db:segtitle
Template match ≅ db:seglistitem
Mode: m:seglist-table
Matches: db:seglistitem
Template match ≅ db:seg
Mode: m:seglist-table
Matches: db:seg
Template match ≅ db:calloutlist
Mode: m:docbook
Matches: db:calloutlist
Template match ≅ db:callout
Mode: m:docbook
Matches: db:callout
Template match ≅ *
Mode: m:callout-link
Matches: *
Template match ≅ db:area|db:co
Mode: m:callout-link
Matches: db:area, db:co
Template match ≅ db:procedure
Mode: m:docbook
Matches: db:procedure
Template match ≅ db:step
Mode: m:docbook
Matches: db:step
Template match ≅ db:substeps
Mode: m:docbook
Matches: db:substeps
Template match ≅ db:stepalternatives
Mode: m:docbook
Matches: db:stepalternatives
Template match ≅ db:result
Mode: m:docbook
Matches: db:result
Template match ≅ db:task|db:taskprerequisites|d…
Mode: m:docbook
Matches: db:task, db:taskprerequisites, db:taskrelated, db:tasksummary
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db dbe f fp m t tp vp xs"
                version="3.0">

<xsl:template match="db:itemizedlist">
  <xsl:variable name="compact" as="xs:boolean"
                select="@spacing = 'compact'
                        and count(db:listitem/db:para) = count(db:listitem)
                        and empty(db:listitem/*[not(self::db:para)])"/>

  <xsl:choose>
    <xsl:when test="empty(db:info/*)
                    and empty(* except (db:info|db:listitem|db:annotation))">
      <ul>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:if test="@mark">
          <xsl:attribute name="db-mark" select="@mark"/>
        </xsl:if>
        <xsl:apply-templates select="node()">
          <xsl:with-param name="compact" select="$compact"/>
        </xsl:apply-templates>
      </ul>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <xsl:apply-templates select="* except db:listitem"/>
        <ul>
          <xsl:if test="@mark">
            <xsl:attribute name="db-mark" select="@mark"/>
          </xsl:if>
          <xsl:apply-templates select="db:listitem">
            <xsl:with-param name="compact" select="$compact"/>
          </xsl:apply-templates>
        </ul>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:orderedlist">
  <xsl:variable name="compact" as="xs:boolean"
                select="@spacing = 'compact'
                        and count(db:listitem/db:para) = count(db:listitem)
                        and empty(db:listitem/*[not(self::db:para)])"/>

  <xsl:choose>
    <xsl:when test="empty(db:info/*)
                    and empty(* except (db:info|db:listitem|db:annotation))">
      <ol>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:call-template name="tp:orderedlist-properties"/>
        <xsl:apply-templates select="node()">
          <xsl:with-param name="compact" select="$compact"/>
        </xsl:apply-templates>
      </ol>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="exclude-classes"
                          select="if (@inheritnum = 'inherit')
                                  then 'inheritnum'
                                  else ()"/>
        </xsl:apply-templates>
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <xsl:apply-templates select="* except db:listitem"/>
        <ol>
          <xsl:if test="@inheritnum = 'inherit'">
            <xsl:attribute name="class" select="'inheritnum'"/>
          </xsl:if>
          <xsl:call-template name="tp:orderedlist-properties"/>
          <xsl:apply-templates select="db:listitem">
            <xsl:with-param name="compact" select="$compact"/>
          </xsl:apply-templates>
        </ol>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="tp:orderedlist-properties">
  <xsl:if test="@startingnumber">
    <xsl:attribute name="start" select="@startingnumber"/>
  </xsl:if>
  <xsl:if test="@continuation='continues'">
    <xsl:attribute name="start" select="f:orderedlist-startingnumber(.)"/>
  </xsl:if>
  <xsl:choose>
    <xsl:when test="empty(@numeration)">
      <xsl:attribute name="type"
                     select="f:orderedlist-item-numeration(db:listitem[1])"/>
    </xsl:when>
    <xsl:when test="@numeration='arabic'">
      <xsl:attribute name="type" select="'1'"/>
    </xsl:when>
    <xsl:when test="@numeration='upperalpha'">
      <xsl:attribute name="type" select="'A'"/>
    </xsl:when>
    <xsl:when test="@numeration='loweralpha'">
      <xsl:attribute name="type" select="'a'"/>
    </xsl:when>
    <xsl:when test="@numeration='upperroman'">
      <xsl:attribute name="type" select="'I'"/>
    </xsl:when>
    <xsl:when test="@numeration='lowerroman'">
      <xsl:attribute name="type" select="'i'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message>Ignoring unexpected numeration: {@numeration}</xsl:message>
    </xsl:otherwise>
  </xsl:choose>

  <xsl:if test="@inheritnum and not(@inheritnum = ('ignore', 'inherit'))">
    <xsl:message>Ignoring unexpected inheritnum: {@inheritnum}</xsl:message>
  </xsl:if>
</xsl:template>

<xsl:template match="db:listitem">
  <xsl:param name="compact" as="xs:boolean" select="false()"/>

  <xsl:variable name="gi" select="if (parent::db:varlistentry)
                                  then 'dd'
                                  else 'li'"/>

  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:if test="@override">
      <xsl:choose>
        <xsl:when test="parent::db:orderedlist">
          <xsl:attribute name="value" select="@override"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:attribute name="db-mark" select="@override"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
    <xsl:choose>
      <xsl:when test="$compact and count(*) = 1 and db:para">
        <xsl:apply-templates select="db:para/node()"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:element>
</xsl:template>

<xsl:template match="db:simplelist[@type='inline']">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:for-each select="db:member">
      <xsl:if test="position() gt 1">, </xsl:if>
      <xsl:apply-templates select="."/>
    </xsl:for-each>
  </span>
</xsl:template>

<xsl:template match="db:simplelist">
  <xsl:variable name="cols" as="xs:integer">
    <xsl:choose>
      <xsl:when test="not(@columns)">
        <xsl:sequence select="1"/>
      </xsl:when>
      <xsl:when test="@columns castable as xs:integer">
        <xsl:sequence select="@columns cast as xs:integer"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>Treating non-numeric columns ({@columns}) as 1</xsl:message>
        <xsl:sequence select="1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="@type = 'horiz'">
      <div>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="extra-classes" select="'simplelisthoriz'"/>
        </xsl:apply-templates>
        <xsl:for-each select="db:member">
          <xsl:if test="$cols eq 1 or position() mod $cols = 1">
            <xsl:variable name="member" select="."/>
            <xsl:variable name="row" as="element(db:member)+">
              <xsl:sequence select="$member"/>
              <xsl:if test="$cols gt 1">
                <xsl:sequence select="$member/following-sibling::db:member
                                      [position() lt $cols]"/>
              </xsl:if>
            </xsl:variable>
            <div class="row">
              <xsl:apply-templates select="$row"/>
            </div>
          </xsl:if>
        </xsl:for-each>
      </div>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="extra-classes" select="'simplelistvert'"/>
        </xsl:apply-templates>

        <xsl:choose>
          <xsl:when test="$cols = 1">
            <!-- special case a single column... -->
            <xsl:for-each select="db:member">
              <div class="row">
                <xsl:apply-templates select="."/>
              </div>
            </xsl:for-each>
          </xsl:when>
          <xsl:otherwise>
            <xsl:variable name="rows" select="(count(db:member) + $cols - 1) idiv $cols"/>
            <xsl:for-each select="subsequence(db:member, 1, $rows)">
              <div class="row">
                <xsl:apply-templates select="fp:select-vert-members(., $rows)"/>
              </div>
            </xsl:for-each>
          </xsl:otherwise>
        </xsl:choose>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- 
  A B C D E => A D
               B E
               C
-->

<xsl:function name="fp:select-vert-members" as="element(db:member)*">
  <xsl:param name="member" as="element(db:member)?"/>
  <xsl:param name="rows" as="xs:integer"/>

  <xsl:variable name="next"
                select="$member/following-sibling::*[position() eq $rows]"/>

  <xsl:sequence select="$member"/>
  <xsl:if test="$next">
    <xsl:sequence select="fp:select-vert-members($next, $rows)"/>
  </xsl:if>
</xsl:function>

<xsl:template match="db:member">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:variablelist">
  <xsl:variable name="compact" as="xs:boolean"
                select="@spacing = 'compact'
                        and count(db:varlistentry/db:listitem/db:para)
                            = count(db:varlistentry/db:listitem)
                        and empty(db:varlistentry/db:listitem/*[not(self::db:para)])"/>
  <xsl:variable name="term-length"
                select="if (@termlength)
                        then @termlength/string()
                        else max((db:varlistentry ! fp:estimated-term-length(.)))"/>

  <xsl:choose>
    <xsl:when test="empty(db:info/*)
                    and empty(* except (db:info|db:varlistentry|db:annotation))">
      <dl>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="term-length" select="$term-length"/>
        </xsl:apply-templates>
        <xsl:apply-templates select="node()">
          <xsl:with-param name="compact" select="$compact"/>
        </xsl:apply-templates>
      </dl>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes">
          <xsl:with-param name="term-length" select="$term-length"/>
        </xsl:apply-templates>
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <xsl:apply-templates select="* except db:varlistentry"/>
        <dl>
          <!-- repeat the class and other attributes for convenience -->
          <xsl:apply-templates select="." mode="m:attributes">
            <xsl:with-param name="term-length" select="$term-length"/>
            <xsl:with-param name="exclude-id" select="true()"/>
          </xsl:apply-templates>
          <xsl:apply-templates select="db:varlistentry">
            <xsl:with-param name="compact" select="$compact"/>
          </xsl:apply-templates>
        </dl>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:variable name="vp:term"
              select="QName('http://docbook.org/ns/docbook', 'term')"/>

<xsl:template match="db:varlistentry">
  <xsl:param name="compact" as="xs:boolean" select="false()"/>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:choose>
      <xsl:when test="f:is-true($varlistentry-separate-terms)">
        <xsl:apply-templates select="db:term"/>
      </xsl:when>
      <xsl:otherwise>
        <dt>
          <xsl:apply-templates select="." mode="m:gentext-list">
            <xsl:with-param name="list" as="element()*">
              <xsl:apply-templates select="db:term"/>
            </xsl:with-param>
          </xsl:apply-templates>
        </dt>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:apply-templates select="db:listitem">
      <xsl:with-param name="compact" select="$compact"/>
    </xsl:apply-templates>
  </div>
</xsl:template>

<xsl:template match="db:term">
  <xsl:variable name="gi" select="if (f:is-true($varlistentry-separate-terms))
                                  then 'dt' else 'span'"/>
  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:function name="fp:estimated-term-length">
  <xsl:param name="entry" as="element(db:varlistentry)"/>
  <xsl:value-of select="sum(($entry/db:term ! string-length(string(.))))"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="db:variablelist[f:is-true($variablelist-panelset)
                                     and contains-token(@role, 'panelset')]">
  <div class="panelset variablelist">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="* except db:varlistentry"/>

    <xsl:variable name="button-set" select="'tabs_' || f:id(.)"/>

    <div class="panelset">
      <xsl:for-each select="db:varlistentry">
        <!-- inline style so that the radio button is not displayed -->
        <!-- even if the CSS is missing -->
        <input type="radio" name="{$button-set}" id="{f:id(.)}" style="display:none">
          <xsl:if test="position() = 1">
            <xsl:attribute name="checked" select="'checked'"/>
          </xsl:if>
        </input>
        <label for="{f:id(.)}">
          <xsl:apply-templates select="db:term" mode="m:panelset"/>
        </label>
        <div class="paneltab">
          <xsl:apply-templates select="." mode="m:panelset"/>
        </div>
      </xsl:for-each>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:term" mode="m:panelset">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:varlistentry" mode="m:panelset">
  <xsl:apply-templates select="db:listitem" mode="m:panelset"/>
</xsl:template>

<xsl:template match="db:listitem" mode="m:panelset">
  <xsl:apply-templates/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:segmentedlist">
  <xsl:variable name="presentation"
                select="f:pi(., 'segmentedlist-style', $segmentedlist-style)"/>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>

    <xsl:choose>
      <xsl:when test="$presentation = 'table'">
        <xsl:apply-templates select="." mode="m:seglist-table"/>
      </xsl:when>
      <xsl:when test="$presentation = 'list'">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'Unrecognized segementedlist-style:', $presentation"/>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<xsl:template match="db:segtitle"/>

<xsl:template match="db:segtitle" mode="m:segtitle-in-seg">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:seglistitem">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:seg">
  <xsl:variable name="segnum" select="count(preceding-sibling::db:seg)+1"/>
  <xsl:variable name="seglist" select="ancestor::db:segmentedlist"/>
  <xsl:variable name="segtitles" select="$seglist/db:segtitle"/>

  <!--
     Note: segtitle is only going to be the right thing in a well formed
     SegmentedList.  If there are too many Segs or too few SegTitles,
     you'll get something odd...maybe an error
  -->

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <strong>
      <span class="segtitle">
        <xsl:apply-templates select="$segtitles[$segnum=position()]"
                             mode="m:segtitle-in-seg"/>
        <xsl:text>: </xsl:text>
      </span>
    </strong>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:segmentedlist" mode="m:seglist-table">
  <xsl:variable name="table-summary"
                select="f:pi(., 'table-summary')"/>

  <table>
    <xsl:if test="$table-summary != '' and 'summary' = $vp:table-accessibility">
      <xsl:attribute name="summary">
        <xsl:value-of select="$table-summary"/>
      </xsl:attribute>
    </xsl:if>
    <thead class="segtitles">
      <tr>
        <xsl:apply-templates select="db:segtitle" mode="m:seglist-table"/>
      </tr>
    </thead>
    <tbody>
      <xsl:apply-templates select="db:seglistitem" mode="m:seglist-table"/>
    </tbody>
  </table>
</xsl:template>

<xsl:template match="db:segtitle" mode="m:seglist-table">
  <th>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </th>
</xsl:template>

<xsl:template match="db:seglistitem" mode="m:seglist-table">
  <xsl:variable name="seglinum">
    <xsl:number from="db:segmentedlist" count="db:seglistitem"/>
  </xsl:variable>

  <tr>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:seglist-table"/>
  </tr>
</xsl:template>

<xsl:template match="db:seg" mode="m:seglist-table">
  <td>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </td>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:calloutlist">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="* except db:callout"/>
    <dl>
      <xsl:apply-templates select="db:callout"/>
    </dl>
  </div>
</xsl:template>

<xsl:template match="db:callout">
  <xsl:variable name="context" select="."/>
  <dt>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:for-each select="tokenize(normalize-space(@arearefs), '\s+')">
      <xsl:variable name="id" select="."/>
      <xsl:variable name="area" select="key('id', $id, root($context))"/>
      <xsl:if test="count($area) gt 1">
        <xsl:message select="'Multiple areas with the same id: ' || $id"/>
      </xsl:if>

      <xsl:choose>
        <xsl:when test="$area/parent::db:areaset
                        and $area/preceding-sibling::db:area"/>
        <xsl:when test="$area/self::db:areaset">
          <xsl:apply-templates select="$area/db:area[1]"
                               mode="m:callout-link">
            <xsl:with-param name="id" select="$id"/>
            <xsl:with-param name="target" select="$area"/>
          </xsl:apply-templates>
        </xsl:when>
        <xsl:when test="$area/self::db:area">
          <xsl:apply-templates select="$area"
                               mode="m:callout-link">
            <xsl:with-param name="id" select="$id"/>
            <xsl:with-param name="target" select="$area"/>
          </xsl:apply-templates>
        </xsl:when>
        <xsl:when test="$area/self::db:co">
          <xsl:apply-templates select="$area"
                               mode="m:callout-link">
            <xsl:with-param name="id" select="$id"/>
            <xsl:with-param name="target" select="$area"/>
          </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="error($dbe:INVALID-AREAREFS,
                                      'Invalid area in arearefs: ' || $id)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </dt>
  <dd>
    <xsl:apply-templates/>
  </dd>
</xsl:template>

<xsl:template match="*" mode="m:callout-link">
  <xsl:message select="'Error: processing ', node-name(.), ' in m:callout-link mode'"/>
  <span class="error">???</span>
</xsl:template>

<xsl:template match="db:area|db:co" mode="m:callout-link">
  <xsl:param name="id" as="xs:string"/>
  <xsl:param name="target" as="element()?"/>
  <a class="callout-bug" href="#{$id}">
    <xsl:apply-templates select="." mode="m:callout-bug">
      <xsl:with-param name="id" select="$id"/>
    </xsl:apply-templates>
  </a>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:procedure">
  <xsl:choose>
    <xsl:when test="empty(db:info/*)
                    and empty(* except (db:step|db:annotation))">
      <ol type="1">
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates select="db:step"/>
      </ol>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <xsl:apply-templates select="child::* except (db:step|db:result)"/>
        <ol class="{local-name(.)}" type="1">
          <xsl:apply-templates select="db:step"/>
        </ol>
        <xsl:apply-templates select="db:result"/>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:step">
  <li>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </li>
</xsl:template>

<xsl:template match="db:substeps">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <ol class="procedure {local-name(.)}"
        type="{f:step-numeration(db:step[1])}">
      <xsl:apply-templates/>
    </ol>
  </div>
</xsl:template>

<xsl:template match="db:stepalternatives">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <ul class="procedure {local-name(.)}">
      <xsl:apply-templates/>
    </ul>
  </div>
</xsl:template>

<xsl:template match="db:result">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:task|db:tasksummary|db:taskprerequisites|db:taskrelated">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

</xsl:stylesheet>

blocks.xsl

25 templates

Instructions
Template match ≅ db:informalequation|db:informa…
Mode: m:docbook
Matches: db:informalequation, db:informalexample, db:informalfigure
Template match ≅ db:formalgroup
Mode: m:docbook
Matches: db:formalgroup
Template match ≅ db:equation|db:example|db:figu…
Mode: m:docbook
Matches: db:equation, db:example, db:figure, db:screenshot
Template match ≅ math:*
Mode: m:docbook
Matches: math:*
Template match ≅ db:para
Mode: m:docbook
Matches: db:para
Template match ≅ db:simpara
Mode: m:docbook
Matches: db:simpara
Template match ≅ db:formalpara
Mode: m:docbook
Matches: db:formalpara
Template match ≅ db:blockquote|db:epigraph
Mode: m:docbook
Matches: db:blockquote, db:epigraph
Template match ≅ db:attribution
Mode: m:docbook
Matches: db:attribution
Template match ≅ db:revhistory
Calls: f:pi()
Mode: m:docbook
Matches: db:revhistory
Template match ≅ db:revision
Mode: m:revhistory-list
Matches: db:revision
Template match ≅ db:revision
Mode: m:revhistory-table
Matches: db:revision
Template match ≅ db:revdescription
Mode: m:docbook
Matches: db:revdescription
Template match ≅ db:revremark
Mode: m:docbook
Matches: db:revremark
Template match ≅ db:sidebar
Mode: m:docbook
Matches: db:sidebar
Template match ≅ db:qandaset
Mode: m:docbook
Matches: db:qandaset
Template match ≅ db:qandadiv
Mode: m:docbook
Matches: db:qandadiv
Template match ≅ db:qandaentry
Mode: m:docbook
Matches: db:qandaentry
Template match ≅ db:answer|db:question
Mode: m:docbook
Matches: db:answer, db:question
Template match ≅ db:qandaset
Mode: m:toc
Matches: db:qandaset
Template match ≅ db:qandadiv
Mode: m:toc
Matches: db:qandadiv
Template match ≅ db:qandaentry
Calls: f:id()
Mode: m:toc
Matches: db:qandaentry
Template match ≅ db:question
Mode: m:toc
Matches: db:question
Template match ≅ db:label
Calls: t:inline
Mode: m:docbook
Matches: db:label
Template match ≅ db:remark
Mode: m:docbook
Matches: db:remark
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:math="http://www.w3.org/1998/Math/MathML"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f h m map math t v xs"
                version="3.0">

<xsl:template match="db:informalfigure|db:informalequation|db:informalexample">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:formalgroup">
  <xsl:variable name="placement"
                select="if (map:get($v:formal-object-title-placement, local-name(.)))
                        then map:get($v:formal-object-title-placement, local-name(.))
                        else map:get($v:formal-object-title-placement, '_default')"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:choose>
      <xsl:when test="$placement = 'before'">
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <div class="fgbody">
          <xsl:apply-templates/>
        </div>
      </xsl:when>
      <xsl:otherwise>
        <div class="fgbody">
          <xsl:apply-templates/>
        </div>
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<xsl:template match="db:figure|db:equation|db:example|db:screenshot">
  <xsl:variable name="tp"
                select="if (parent::db:formalgroup)
                        then $v:formalgroup-nested-object-title-placement
                        else $v:formal-object-title-placement"/>
  <xsl:variable name="placement"
                select="if (map:get($tp, local-name(.)))
                        then map:get($tp, local-name(.))
                        else map:get($tp, '_default')"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:choose>
      <xsl:when test="$placement = 'before'">
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates/>
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<xsl:template match="math:*">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1998/Math/MathML">
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="node()"/>
  </xsl:element>
</xsl:template>

<xsl:template match="db:para">
  <xsl:choose>
    <xsl:when test="empty(*) or not(f:is-true($unwrap-paragraphs))">
      <p>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates/>
      </p>
    </xsl:when>
    <xsl:otherwise>
      <xsl:iterate select="node()">
        <xsl:param name="p" as="element(h:p)"><p/></xsl:param>
        <xsl:choose>
          <xsl:when test="self::db:address|self::db:bibliolist|self::db:blockquote
                          |self::db:bridgehead|self::db:calloutlist|self::db:caution
                          |self::db:classsynopsis|self::db:cmdsynopsis|self::db:constraintdef
                          |self::db:constructorsynopsis|self::db:danger
                          |self::db:destructorsynopsis|self::db:enumsynopsis|self::db:epigraph
                          |self::db:equation|self::db:example|self::db:fieldsynopsis
                          |self::db:figure|self::db:formalgroup|self::db:funcsynopsis
                          |self::db:glosslist|self::db:important|self::db:indexterm
                          |self::db:informalequation|self::db:informalexample
                          |self::db:informalfigure|self::db:informaltable|self::db:itemizedlist
                          |self::db:literallayout|self::db:macrosynopsis|self::db:mediaobject
                          |self::db:methodsynopsis|self::db:msgset|self::db:note
                          |self::db:orderedlist|self::db:packagesynopsis|self::db:procedure
                          |self::db:productionset|self::db:programlisting
                          |self::db:programlistingco|self::db:qandaset|self::db:remark
                          |self::db:revhistory|self::db:screen|self::db:screenco
                          |self::db:screenshot|self::db:segmentedlist|self::db:sidebar
                          |self::db:simplelist|self::db:synopsis|self::db:table|self::db:task
                          |self::db:tip|self::db:typedefsynopsis|self::db:unionsynopsis
                          |self::db:variablelist|self::db:warning">
            <xsl:if test="not(empty($p/node()))
                          and not(normalize-space(string($p)) = '')">
              <xsl:sequence select="$p"/>
            </xsl:if>
            <xsl:apply-templates select="."/>
            <xsl:next-iteration>
              <xsl:with-param name="p" as="element(h:p)"><p/></xsl:with-param>
            </xsl:next-iteration>
          </xsl:when>
          <xsl:when test="not(following-sibling::node())">
            <xsl:variable name="last-p" as="element(h:p)">
              <p>
                <xsl:sequence select="$p/node()"/>
                <xsl:apply-templates select="."/>
              </p>
            </xsl:variable>
            <xsl:if test="not(empty($last-p/node()))
                          and not(normalize-space(string($last-p)) = '')">
              <xsl:sequence select="$last-p"/>
            </xsl:if>
          </xsl:when>
          <xsl:otherwise>
            <xsl:next-iteration>
              <xsl:with-param name="p" as="element(h:p)">
                <p>
                  <xsl:sequence select="$p/node()"/>
                  <xsl:apply-templates select="."/>
                </p>
              </xsl:with-param>
            </xsl:next-iteration>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:iterate>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:simpara">
  <p>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </p>
</xsl:template>

<xsl:template match="db:formalpara">
  <p>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <span class="para">
      <xsl:apply-templates select="db:para" mode="m:attributes"/>
      <xsl:apply-templates select="db:para/node()"/>
    </span>
  </p>
</xsl:template>

<xsl:template match="db:blockquote|db:epigraph">
  <blockquote>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates select="* except db:attribution"/>
    <xsl:apply-templates select="db:attribution"/>
  </blockquote>
</xsl:template>

<xsl:template match="db:attribution">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:revhistory">
  <xsl:variable name="style"
                select="f:pi(., 'revhistory-style', $revhistory-style)"/>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:choose>
      <xsl:when test="$style = 'table'">
        <table>
          <colgroup>
            <col class="revnumber"/>
            <col class="date" style="text-align: right;"/>
            <col class="author"/>
            <col class="revremark"/>
          </colgroup>
          <tbody>
            <xsl:apply-templates select="db:revision" mode="m:revhistory-table"/>
          </tbody>
        </table>
      </xsl:when>
      <xsl:when test="$style = 'list'">
        <ul>
          <xsl:apply-templates select="db:revision" mode="m:revhistory-list"/>
        </ul>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'Uknown revhistory-style: ' || $style"/>
        <ul>
          <xsl:apply-templates select="db:revision" mode="m:revhistory-list"/>
        </ul>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<xsl:template match="db:revision" mode="m:revhistory-list">
  <li>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <div class="revnumber">
      <xsl:if test="db:revnumber">
        <xsl:apply-templates select="db:revnumber"/>
        <xsl:text>, </xsl:text>
      </xsl:if>
      <xsl:apply-templates select="db:date"/>
      <xsl:for-each select="db:author|db:authorinitials">
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="."/>
      </xsl:for-each>
    </div>
    <div class="description">
      <xsl:apply-templates select="db:revdescription|db:revremark"/>
    </div>
  </li>
</xsl:template>

<xsl:template match="db:revision" mode="m:revhistory-table">
  <tr>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <td>
      <xsl:apply-templates select="db:revnumber"/>
    </td>
    <td>
      <xsl:apply-templates select="db:date"/>
    </td>
    <td>
      <xsl:for-each select="db:author|db:authorinitials">
        <xsl:if test="position() gt 1">
          <xsl:text>, </xsl:text>
        </xsl:if>
        <xsl:apply-templates select="."/>
      </xsl:for-each>
    </td>
    <td>
      <xsl:apply-templates select="db:revdescription|db:revremark"/>
    </td>
  </tr>
</xsl:template>

<xsl:template match="db:revdescription">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:revremark">
  <p>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </p>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:sidebar">
  <xsl:element name="{if ($sidebar-as-aside) then 'aside' else 'div'}"
               namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:qandaset">
  <xsl:variable name="pis"
                select="f:pi-attributes(processing-instruction('db'))"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>

    <xsl:if test="(exists($pis/@toc) and f:is-true($pis/@toc))
                   or (not($pis/@toc) and f:is-true($qandaset-default-toc))">
      <xsl:apply-templates select="." mode="m:toc"/>
    </xsl:if>

    <xsl:choose>
      <xsl:when test="db:qandadiv">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:otherwise>
        <div class="qandalist">
          <xsl:apply-templates/>
        </div>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<xsl:template match="db:qandadiv">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:choose>
      <xsl:when test="db:qandadiv">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:otherwise>
        <div class="qandalist">
          <xsl:apply-templates/>
        </div>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<xsl:template match="db:qandaentry">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <div class="qanda">
      <xsl:apply-templates/>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:question|db:answer">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>

    <!-- This is a bit of a hack... -->
    <xsl:variable name="label">
      <xsl:apply-templates select="." mode="m:headline-label">
        <xsl:with-param name="purpose" select="'title'"/>
      </xsl:apply-templates>
    </xsl:variable>

    <div class="label">
      <xsl:sequence select="$label"/>
      <xsl:if test="normalize-space($label) != ''">
        <xsl:apply-templates select="." mode="m:gentext">
          <xsl:with-param name="group" select="'label-separator'"/>
        </xsl:apply-templates>
      </xsl:if>
    </div>
    <div class="body">
      <xsl:apply-templates select="node() except db:label"/>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:qandaset" mode="m:toc">
  <ul class="toc">
    <xsl:apply-templates select="db:qandadiv|db:qandaentry" mode="m:toc"/>
  </ul>
</xsl:template>

<xsl:template match="db:qandadiv" mode="m:toc">
  <li>
    <a href="#{f:id(.)}">
      <xsl:apply-templates select="." mode="m:headline">
        <xsl:with-param name="purpose" select="'lot'"/>
      </xsl:apply-templates>
    </a>

    <xsl:variable name="toc" select="f:pi(., 'toc')"/>

    <xsl:if test="($toc and f:is-true($toc))
                   or (empty($toc) and f:is-true($qandadiv-default-toc))">
      <ul>
        <xsl:apply-templates select="db:qandadiv|db:qandaentry" mode="m:toc"/>
      </ul>
    </xsl:if>
  </li>
</xsl:template>

<xsl:template match="db:qandaentry" mode="m:toc">
  <li>
    <a href="#{f:id(.)}">
      <xsl:apply-templates select="db:question" mode="m:headline">
        <xsl:with-param name="purpose" select="'lot'"/>
      </xsl:apply-templates>
    </a>
  </li>
</xsl:template>

<xsl:template match="db:question" mode="m:toc">
  <xsl:choose>
    <xsl:when test="(* except db:label)[1]/self::db:para">
      <xsl:apply-templates select="(* except db:label)[1]/node()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'No question title for ' || local-name(*[1])"/>
      <xsl:variable name="question" as="node()">
        <xsl:apply-templates select="."/>
      </xsl:variable>
      <xsl:variable name="question" select="string($question)"/>
      <xsl:sequence select="$question"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:label">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:remark">
  <xsl:if test="f:is-true($show-remarks)">
    <xsl:choose>
      <xsl:when test="ancestor::db:para">
        <!-- Assume it's an inline; this is not a foolproof test! -->
        <span>
          <xsl:apply-templates select="." mode="m:attributes"/>
          <xsl:apply-templates/>
        </span>
      </xsl:when>
      <xsl:otherwise>
        <div>
          <xsl:apply-templates select="." mode="m:attributes"/>
          <xsl:apply-templates/>
        </div>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>

admonitions.xsl

1 template

Instructions
Template match ≅ db:caution|db:danger|db:import…
Mode: m:docbook
Matches: db:caution, db:danger, db:important, db:note, db:tip, db:warning
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m t v xs"
                version="3.0">

<xsl:template match="db:note|db:tip|db:important|db:caution|db:warning|db:danger">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <div>
      <div class="icon">
        <xsl:apply-templates
            select="$v:admonition-icons/*[node-name(.) = node-name(current())]/node()"/>
      </div>
      <div class="body">
        <xsl:apply-templates select="." mode="m:generate-titlepage"/>
        <div>
          <xsl:apply-templates/>
        </div>
      </div>
    </div>
  </div>
</xsl:template>

</xsl:stylesheet>

programming.xsl

106 templates (1 used only in one other module)

Instructions
Template match ≅ db:productionset
Mode: m:docbook
Matches: db:productionset
Template match ≅ db:production
Mode: m:docbook
Matches: db:production
Template match ≅ db:productionrecap
Mode: m:docbook
Matches: db:productionrecap
Template match ≅ db:lhs
Mode: m:docbook
Matches: db:lhs
Template match ≅ db:production
Mode: m:production-number
Matches: db:production
Template match ≅ db:rhs
Mode: m:docbook
Priority: 10
Matches: db:rhs
Template match ≅ db:rhs
Mode: m:docbook
Matches: db:rhs
Template match ≅ db:constraint
Mode: m:docbook
Matches: db:constraint
Template match ≅ db:lineannotation
Mode: m:docbook
Matches: db:lineannotation
Template match ≅ db:sbr
Mode: m:docbook
Matches: db:sbr
Template match ≅ db:nonterminal
Mode: m:docbook
Matches: db:nonterminal
Template match ≅ db:constraintdef
Mode: m:docbook
Matches: db:constraintdef
Template match ≅ db:funcsynopsis
Calls: f:pi()
Mode: m:docbook
Matches: db:funcsynopsis
Template match ≅ element()
Mode: m:kr
Matches: element()
Template match ≅ db:funcsynopsisinfo
Mode: m:kr
Matches: db:funcsynopsisinfo
Template match ≅ db:funcprototype
Mode: m:kr
Matches: db:funcprototype
Template match ≅ db:funcdef
Mode: m:kr
Matches: db:funcdef
Template match ≅ db:type
Mode: m:kr
Matches: db:type
Template match ≅ db:function
Mode: m:kr
Matches: db:function
Template match ≅ db:void
Mode: m:kr-args
Matches: db:void
Template match ≅ db:varargs
Mode: m:kr-args
Matches: db:varargs
Template match ≅ db:paramdef
Mode: m:kr-args
Matches: db:paramdef
Template match ≅ db:parameter
Mode: m:kr-args
Matches: db:parameter
Template match ≅ db:void
Mode: m:kr
Matches: db:void
Template match ≅ db:varargs
Mode: m:kr
Matches: db:varargs
Template match ≅ db:paramdef
Mode: m:kr
Matches: db:paramdef
Template match ≅ db:parameter
Mode: m:kr
Matches: db:parameter
Template match ≅ db:funcparams
Mode: m:kr
Matches: db:funcparams
Template match ≅ text()
Mode: m:kr
Matches: text()
Template match ≅ comment()|processing-instructi…
Mode: m:kr
Matches: comment(), processing-instruction()
Template match ≅ attribute()
Mode: m:kr
Matches: attribute()
Template match ≅ text()
Mode: m:kr-args
Matches: text()
Template match ≅ comment()|processing-instructi…
Mode: m:kr-args
Matches: comment(), processing-instruction()
Template match ≅ attribute()
Mode: m:kr-args
Matches: attribute()
Template match ≅ element()
Mode: m:kr-table
Matches: element()
Template match ≅ db:funcsynopsisinfo
Mode: m:kr-table
Matches: db:funcsynopsisinfo
Template match ≅ db:funcprototype
Mode: m:kr-table
Matches: db:funcprototype
Template match ≅ db:funcdef
Mode: m:kr-table
Matches: db:funcdef
Template match ≅ db:type
Mode: m:kr-table
Matches: db:type
Template match ≅ db:function
Mode: m:kr-table
Matches: db:function
Template match ≅ db:void
Mode: m:kr-table-args
Matches: db:void
Template match ≅ db:varargs
Mode: m:kr-table-args
Matches: db:varargs
Template match ≅ db:parameter
Mode: m:kr-table-args
Matches: db:parameter
Template match ≅ db:void
Mode: m:kr-table
Matches: db:void
Template match ≅ db:varargs
Mode: m:kr-table
Matches: db:varargs
Template match ≅ db:paramdef
Mode: m:kr-table-args
Matches: db:paramdef
Template match ≅ db:paramdef
Mode: m:kr-table
Matches: db:paramdef
Template match ≅ db:parameter
Mode: m:kr-table
Matches: db:parameter
Template match ≅ db:funcparams
Mode: m:kr-table
Matches: db:funcparams
Template match ≅ text()
Mode: m:kr-table
Matches: text()
Template match ≅ comment()|processing-instructi…
Mode: m:kr-table
Matches: comment(), processing-instruction()
Template match ≅ attribute()
Mode: m:kr-table
Matches: attribute()
Template match ≅ text()
Mode: m:kr-table-args
Matches: text()
Template match ≅ comment()|processing-instructi…
Mode: m:kr-table-args
Matches: comment(), processing-instruction()
Template match ≅ attribute()|text()
Mode: m:kr-table-args
Matches: attribute(), text()
Template match ≅ element()
Mode: m:ansi
Matches: element()
Template match ≅ db:funcsynopsisinfo
Mode: m:ansi
Matches: db:funcsynopsisinfo
Template match ≅ db:funcprototype
Mode: m:ansi
Matches: db:funcprototype
Template match ≅ db:funcdef
Mode: m:ansi
Matches: db:funcdef
Template match ≅ db:type
Mode: m:ansi
Matches: db:type
Template match ≅ db:function
Mode: m:ansi
Matches: db:function
Template match ≅ db:void
Mode: m:ansi
Matches: db:void
Template match ≅ db:varargs
Mode: m:ansi
Matches: db:varargs
Template match ≅ db:paramdef
Mode: m:ansi
Matches: db:paramdef
Template match ≅ db:parameter
Mode: m:ansi
Matches: db:parameter
Template match ≅ db:funcparams
Mode: m:ansi
Matches: db:funcparams
Template match ≅ text()
Mode: m:ansi
Matches: text()
Template match ≅ comment()|processing-instructi…
Mode: m:ansi
Matches: comment(), processing-instruction()
Template match ≅ attribute()
Mode: m:ansi
Matches: attribute()
Template match ≅ element()
Mode: m:ansi-table
Matches: element()
Template match ≅ db:funcsynopsisinfo
Mode: m:ansi-table
Matches: db:funcsynopsisinfo
Template match ≅ db:funcprototype
Mode: m:ansi-table
Matches: db:funcprototype
Template match ≅ db:funcdef
Mode: m:ansi-table
Matches: db:funcdef
Template match ≅ db:type
Mode: m:ansi-table
Matches: db:type
Template match ≅ db:function
Mode: m:ansi-table
Matches: db:function
Template match ≅ db:void
Mode: m:ansi-table
Matches: db:void
Template match ≅ db:varargs
Mode: m:ansi-table
Matches: db:varargs
Template match ≅ db:paramdef
Mode: m:ansi-table
Matches: db:paramdef
Template match ≅ db:parameter
Mode: m:ansi-table
Matches: db:parameter
Template match ≅ db:funcparams
Mode: m:ansi-table
Matches: db:funcparams
Template match ≅ text()
Mode: m:ansi-table
Matches: text()
Template match ≅ comment()|processing-instructi…
Mode: m:ansi-table
Matches: comment(), processing-instruction()
Template match ≅ attribute()
Mode: m:ansi-table
Matches: attribute()
Template match ≅ db:classsynopsis|db:constructo…
Mode: m:docbook
Priority: 100
Matches: db:classsynopsis, db:constructorsynopsis, db:destructorsynopsis, db:enumsynopsis, db:fieldsynopsis, db:methodsynopsis, db:packagesynopsis
Template match ≅ db:packagesynopsis
Mode: m:docbook
Matches: db:packagesynopsis
Template match ≅ db:classsynopsis
Mode: m:docbook
Matches: db:classsynopsis
Template match ≅ db:fieldsynopsis
Mode: m:docbook
Matches: db:fieldsynopsis
Template match ≅ db:fieldsynopsis
Mode: m:synopsis
Matches: db:fieldsynopsis
Template match ≅ db:varname
Mode: m:synopsis
Matches: db:varname
Template match ≅ db:initializer
Mode: m:synopsis
Matches: db:initializer
Template match ≅ db:constructorsynopsis|db:dest…
Mode: m:docbook
Matches: db:constructorsynopsis, db:destructorsynopsis, db:methodsynopsis
Template match ≅ db:constructorsynopsis|db:dest…
Calls: f:spaces()
Mode: m:synopsis
Matches: db:constructorsynopsis, db:destructorsynopsis, db:methodsynopsis
Template match ≅ db:methodparam
Mode: m:synopsis
Matches: db:methodparam
Template match ≅ db:modifier|db:type
Mode: m:synopsis
Matches: db:modifier, db:type
Template match ≅ db:classname|db:methodname|db:…
Mode: m:synopsis
Matches: db:classname, db:methodname, db:parameter
Template match ≅ db:synopsisinfo
Mode: m:synopsis
Matches: db:synopsisinfo
Template match ≅ db:enumsynopsis
Mode: m:docbook
Matches: db:enumsynopsis
Template match ≅ db:enumitem
Mode: m:synopsis
Matches: db:enumitem
Template match ≅ db:enumitemdescription
Mode: m:synopsis
Matches: db:enumitemdescription
Template match ≅ db:cmdsynopsis
Mode: m:docbook
Matches: db:cmdsynopsis
Template match ≅ db:synopfragment
Mode: m:docbook
Matches: db:synopfragment
Template match ≅ db:synopfragmentref
Mode: m:docbook
Matches: db:synopfragmentref
Template match ≅ db:synopfragment
Mode: m:synopfragment-bug
Matches: db:synopfragment
Template match ≅ db:cmdsynopsis/db:command
Mode: m:docbook
Matches: db:cmdsynopsis/db:command
Template match ≅ db:group/db:arg
Mode: m:docbook
Matches: db:group/db:arg
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db dbe f m t tp v xs"
                version="3.0">

<xsl:template match="db:productionset">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <div class="productions">
      <xsl:apply-templates/>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:production">
  <xsl:param name="recap" select="false()"/>
  <xsl:apply-templates select="db:lhs">
    <xsl:with-param name="recap" select="$recap"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:productionrecap[@linkend]">
  <xsl:variable name="prod" select="key('id', @linkend)"/>
  <xsl:choose>
    <xsl:when test="empty($prod)">
      <xsl:message select="'Failed to find production: ' || @linkend"/>
    </xsl:when>
    <xsl:when test="$prod/self::db:production">
      <xsl:apply-templates select="$prod">
        <xsl:with-param name="recap" select="true()"/>
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="error($dbe:INVALID-PRODUCTIONRECAP,
                                  'Not a production: ' || @linkend)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:lhs">
  <xsl:param name="recap" select="false()"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <span class="lhs">
      <xsl:apply-templates select=".." mode="m:production-number">
        <xsl:with-param name="recap" select="$recap"/>
      </xsl:apply-templates>
      <xsl:apply-templates/>
    </span>
    <span class="lhssep lhs1sep">
      <xsl:sequence select="$productionset-lhs-rhs-separator"/>
    </span>
    <xsl:apply-templates select="(following-sibling::db:rhs)[1]"/>
  </div>
  <xsl:apply-templates
      select="(following-sibling::db:rhs)[1]/following-sibling::db:rhs"/>
</xsl:template>

<xsl:template match="db:production" mode="m:production-number">
  <xsl:param name="recap" select="false()"/>
  <span class="number">
    <xsl:if test="not($recap)">
      <xsl:attribute name="id" select="f:generate-id(.)"/>
    </xsl:if>
    <xsl:text>[</xsl:text>
    <xsl:number from="/" level="any"/>
    <xsl:text>] </xsl:text>
  </span>
</xsl:template>

<!-- the first rhs -->
<xsl:template match="db:rhs[not(preceding-sibling::db:rhs)]"
              priority="10">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
  <span class="constraint">
    <xsl:apply-templates select="../db:constraint"/>
  </span>
</xsl:template>

<!-- all the other rhs -->
<xsl:template match="db:rhs">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <span class="lhs ghost"></span>
    <span class="lhssep"></span>
    <span class="rhs">
      <xsl:apply-templates/>
    </span>
    <span class="constraint"></span>
  </div>
</xsl:template>

<xsl:template match="db:constraint[@linkend]">
  <xsl:variable name="cons" select="key('id', @linkend)"/>
  <xsl:choose>
    <xsl:when test="empty($cons)">
      <xsl:message select="'Failed to find constraint: ' || @linkend"/>
    </xsl:when>
    <xsl:when test="$cons/self::db:constraintdef">
      <a href="#{@linkend}">
        <xsl:sequence select="@linkend/string()"/>
      </a>
      <xsl:text> </xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="error($dbe:INVALID-CONSTRAINT,
                                  'Not a constraintdef: ' || @linkend)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:lineannotation">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:sbr">
  <br/>
</xsl:template>

<xsl:template match="db:nonterminal">
  <a href="{@def}">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </a>
</xsl:template>

<xsl:template match="db:constraintdef">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:funcsynopsis">
  <xsl:variable name="style"
                select="f:pi(., 'funcsynopsis-style', $funcsynopsis-default-style)"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>

    <xsl:choose>
      <xsl:when test="$style = 'kr'">
        <xsl:apply-templates mode="m:kr"/>
      </xsl:when>
      <xsl:when test="$style = 'ansi'">
        <xsl:apply-templates mode="m:ansi"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'Unrecognized funcsynopsis style: ' || $style"/>
        <xsl:apply-templates mode="m:kr"/>
      </xsl:otherwise>
    </xsl:choose>
  </div>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="element()" mode="m:kr">
  <xsl:message
      select="'Unexpected funcsynopsis element in kr: ' || node-name(.)"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
  </span>
</xsl:template>

<xsl:template match="db:funcsynopsisinfo" mode="m:kr">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="db:funcprototype" mode="m:kr">
  <xsl:variable name="width"
                select="sum(.//text() ! string-length(normalize-space(.)))"/>

  <xsl:choose>
    <xsl:when test="$width gt $funcsynopsis-table-threshold">
      <xsl:apply-templates select="." mode="m:kr-table"/>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <div>
          <xsl:apply-templates select="db:funcdef" mode="m:kr"/>
          <span class="arglist">
            <xsl:text>(</xsl:text>
            <xsl:apply-templates select="db:funcdef/following-sibling::*" mode="m:kr-args"/>
            <xsl:text>)</xsl:text>
            <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
          </span>
        </div>
        <xsl:apply-templates select="db:funcdef/following-sibling::*" mode="m:kr"/>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:funcdef" mode="m:kr">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
  </span>
</xsl:template>

<xsl:template match="db:type" mode="m:kr">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
  </span>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="db:function" mode="m:kr">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
  </span>
</xsl:template>

<xsl:template match="db:void" mode="m:kr-args">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
  </span>
</xsl:template>

<xsl:template match="db:varargs" mode="m:kr-args">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:text>...</xsl:text>
  </span>
</xsl:template>

<xsl:template match="db:paramdef" mode="m:kr-args">
  <span>
    <xsl:apply-templates select="db:parameter" mode="m:kr-args"/>
    <xsl:if test="following-sibling::db:paramdef">
      <span>, </span>
    </xsl:if>
  </span>
</xsl:template>

<xsl:template match="db:parameter" mode="m:kr-args">
  <span class="{local-name(.)}">
    <xsl:apply-templates mode="m:kr"/>
  </span>
</xsl:template>

<xsl:template match="db:void" mode="m:kr"/>
<xsl:template match="db:varargs" mode="m:kr"/>

<xsl:template match="db:paramdef" mode="m:kr">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
    <xsl:text>;</xsl:text>
  </div>
</xsl:template>

<xsl:template match="db:parameter" mode="m:kr">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
  </span>
</xsl:template>

<xsl:template match="db:funcparams" mode="m:kr">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr"/>
  </span>
</xsl:template>

<xsl:template match="text()" mode="m:kr">
  <xsl:sequence select="if (normalize-space(.) = '')
                        then ()
                        else ."/>
</xsl:template>

<xsl:template match="comment()|processing-instruction()" mode="m:kr"/>
<xsl:template match="attribute()" mode="m:kr">
  <xsl:copy/>
</xsl:template>

<xsl:template match="text()" mode="m:kr-args">
  <xsl:sequence select="if (normalize-space(.) = '')
                        then ()
                        else ."/>
</xsl:template>

<xsl:template match="comment()|processing-instruction()" mode="m:kr-args"/>
<xsl:template match="attribute()" mode="m:kr-args">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="element()" mode="m:kr-table">
  <xsl:message
      select="'Unexpected funcsynopsis element in kr-table: ' || node-name(.)"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
</xsl:template>

<xsl:template match="db:funcsynopsisinfo" mode="m:kr-table">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="db:funcprototype" mode="m:kr-table">
  <xsl:variable name="rowspan" select="count(*) - 2"/>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <table class="prototype">
      <tbody>
        <tr>
          <td>
            <xsl:apply-templates select="db:funcdef" mode="m:kr-table"/>
            <xsl:text>(</xsl:text>
          </td>
          <xsl:apply-templates select="db:funcdef/following-sibling::*[1]"
                               mode="m:kr-table-args"/>
        </tr>
        <xsl:for-each select="db:funcdef/following-sibling::*[position() gt 1]">
          <tr>
            <xsl:if test="position() = 1">
              <td rowspan="{$rowspan}"/>
            </xsl:if>
            <xsl:apply-templates select="." mode="m:kr-table-args"/>
          </tr>
        </xsl:for-each>
      </tbody>
    </table>
    <table class="params">
      <tbody>
        <xsl:apply-templates select="db:paramdef" mode="m:kr-table"/>
      </tbody>
    </table>
  </div>
</xsl:template>

<xsl:template match="db:funcdef" mode="m:kr-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
</xsl:template>

<xsl:template match="db:type" mode="m:kr-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="db:function" mode="m:kr-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
</xsl:template>

<xsl:template match="db:void" mode="m:kr-table-args">
  <td>
    <span>
      <xsl:apply-templates select="." mode="m:attributes"/>
    </span>
    <xsl:text>)</xsl:text>
    <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
  </td>
</xsl:template>

<xsl:template match="db:varargs" mode="m:kr-table-args">
  <td>
    <span>
      <xsl:apply-templates select="." mode="m:attributes"/>
      <xsl:text>...</xsl:text>
    </span>
    <xsl:text>)</xsl:text>
    <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
  </td>
</xsl:template>

<xsl:template match="db:parameter" mode="m:kr-table-args">
  <span class="{local-name(.)}">
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
</xsl:template>

<xsl:template match="db:void" mode="m:kr-table"/>
<xsl:template match="db:varargs" mode="m:kr-table"/>

<xsl:template match="db:paramdef" mode="m:kr-table-args">
  <td>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:parameter" mode="m:kr-table-args"/>
    <xsl:sequence select="if (following-sibling::db:paramdef)
                          then ','
                          else ')' || $funcsynopsis-trailing-punctuation"/>
  </td>
</xsl:template>

<xsl:template match="db:paramdef" mode="m:kr-table">
  <xsl:variable name="split" select="if (db:funcparams)
                                     then db:funcparams
                                     else db:parameter"/>
  <tr>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <td>
      <xsl:apply-templates select="$split/preceding-sibling::node()"
                           mode="m:kr-table"/>
    </td>
    <td>
      <xsl:apply-templates select="$split"
                           mode="m:kr-table"/>
      <xsl:apply-templates select="$split/following-sibling::node()"
                           mode="m:kr-table"/>
    </td>
  </tr>
</xsl:template>

<xsl:template match="db:parameter" mode="m:kr-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
</xsl:template>

<xsl:template match="db:funcparams" mode="m:kr-table">
  <xsl:text>(</xsl:text>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:kr-table"/>
  </span>
  <xsl:text>)</xsl:text>
</xsl:template>

<xsl:template match="text()" mode="m:kr-table">
  <xsl:sequence select="if (normalize-space(.) = '')
                        then ()
                        else ."/>
</xsl:template>

<xsl:template match="comment()|processing-instruction()" mode="m:kr-table"/>
<xsl:template match="attribute()" mode="m:kr-table">
  <xsl:copy/>
</xsl:template>

<xsl:template match="text()" mode="m:kr-table-args">
  <xsl:sequence select="if (normalize-space(.) = '')
                        then ()
                        else ."/>
</xsl:template>

<xsl:template match="comment()|processing-instruction()" mode="m:kr-table-args"/>
<xsl:template match="attribute()|text()" mode="m:kr-table-args">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="element()" mode="m:ansi">
  <xsl:message
      select="'Unexpected funcsynopsis element in kr: ' || node-name(.)"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi"/>
  </span>
</xsl:template>

<xsl:template match="db:funcsynopsisinfo" mode="m:ansi">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="db:funcprototype" mode="m:ansi">
  <xsl:variable name="width"
                select="sum(.//text() ! string-length(normalize-space(.)))"/>

  <xsl:choose>
    <xsl:when test="$width gt $funcsynopsis-table-threshold">
      <xsl:apply-templates select="." mode="m:ansi-table"/>
    </xsl:when>
    <xsl:otherwise>
      <div>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates select="db:funcdef" mode="m:ansi"/>
        <span class="arglist">
          <xsl:text>(</xsl:text>
          <xsl:apply-templates
              select="db:funcdef/following-sibling::*" mode="m:ansi"/>
          <xsl:text>)</xsl:text>
          <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
        </span>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:funcdef" mode="m:ansi">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi"/>
  </span>
</xsl:template>

<xsl:template match="db:type" mode="m:ansi">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi"/>
  </span>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="db:function" mode="m:ansi">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi"/>
  </span>
</xsl:template>

<xsl:template match="db:void" mode="m:ansi">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:text>void</xsl:text>
  </span>
</xsl:template>

<xsl:template match="db:varargs" mode="m:ansi">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:text>...</xsl:text>
  </span>
</xsl:template>

<xsl:template match="db:paramdef" mode="m:ansi">
  <span>
    <xsl:apply-templates mode="m:ansi"/>
    <xsl:if test="following-sibling::db:paramdef">
      <span>, </span>
    </xsl:if>
  </span>
</xsl:template>

<xsl:template match="db:parameter" mode="m:ansi">
  <span class="{local-name(.)}">
    <xsl:apply-templates mode="m:ansi"/>
  </span>
</xsl:template>

<xsl:template match="db:funcparams" mode="m:ansi">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi"/>
  </span>
</xsl:template>

<xsl:template match="text()" mode="m:ansi">
  <xsl:sequence select="if (normalize-space(.) = '')
                        then ()
                        else ."/>
</xsl:template>

<xsl:template match="comment()|processing-instruction()" mode="m:ansi"/>
<xsl:template match="attribute()" mode="m:ansi">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="element()" mode="m:ansi-table">
  <xsl:message
      select="'Unexpected funcsynopsis element in kr-table: ' || node-name(.)"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
  </span>
</xsl:template>

<xsl:template match="db:funcsynopsisinfo" mode="m:ansi-table">
  <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="db:funcprototype" mode="m:ansi-table">
  <xsl:variable name="rowspan" select="count(*) - 2"/>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <table class="prototype">
      <tbody>
        <tr>
          <td>
            <xsl:apply-templates select="db:funcdef" mode="m:ansi-table"/>
            <xsl:text>(</xsl:text>
          </td>
          <xsl:apply-templates select="db:funcdef/following-sibling::*[1]"
                               mode="m:ansi-table"/>
        </tr>
        <xsl:for-each select="db:funcdef/following-sibling::*[position() gt 1]">
          <tr>
            <xsl:if test="position() = 1">
              <td rowspan="{$rowspan}"/>
            </xsl:if>
            <xsl:apply-templates select="." mode="m:ansi-table"/>
          </tr>
        </xsl:for-each>
      </tbody>
    </table>
  </div>
</xsl:template>

<xsl:template match="db:funcdef" mode="m:ansi-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
  </span>
</xsl:template>

<xsl:template match="db:type" mode="m:ansi-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
  </span>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="db:function" mode="m:ansi-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
  </span>
</xsl:template>

<xsl:template match="db:void" mode="m:ansi-table">
  <td>
    <span>
      <xsl:apply-templates select="." mode="m:attributes"/>
      <xsl:apply-templates mode="m:ansi-table"/>
    </span>
    <xsl:text>)</xsl:text>
    <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
  </td>
</xsl:template>

<xsl:template match="db:varargs" mode="m:ansi-table">
  <td>
    <span>
      <xsl:apply-templates select="." mode="m:attributes"/>
      <xsl:text>...</xsl:text>
    </span>
    <xsl:text>)</xsl:text>
    <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
  </td>
</xsl:template>

<xsl:template match="db:paramdef" mode="m:ansi-table">
  <td>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
    <xsl:sequence select="if (following-sibling::*)
                          then ','
                          else ')' || $funcsynopsis-trailing-punctuation"/>
  </td>
</xsl:template>

<xsl:template match="db:parameter" mode="m:ansi-table">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
  </span>
</xsl:template>

<xsl:template match="db:funcparams" mode="m:ansi-table">
  <xsl:text>(</xsl:text>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:ansi-table"/>
  </span>
  <xsl:text>)</xsl:text>
</xsl:template>

<xsl:template match="text()" mode="m:ansi-table">
  <xsl:sequence select="if (normalize-space(.) = '')
                        then ()
                        else ."/>
</xsl:template>

<xsl:template match="comment()|processing-instruction()" mode="m:ansi-table"/>
<xsl:template match="attribute()" mode="m:ansi-table">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:packagesynopsis
                     |db:classsynopsis
                     |db:fieldsynopsis
                     |db:methodsynopsis
                     |db:constructorsynopsis
                     |db:destructorsynopsis
                     |db:enumsynopsis"
              priority="100">
  <xsl:if test="@language and @language ne 'java'">
    <xsl:message select="'Warning: no explicit support for', @language, 'synopses.'"/>
  </xsl:if>
  <xsl:next-match/>
</xsl:template>

<xsl:template match="db:packagesynopsis">
  <xsl:param name="indent" select="''"/>

  <xsl:variable name="package" select="db:package"/>
  <xsl:if test="count($package) != 1">
    <xsl:message>Malformed packagesynopisis.</xsl:message>
  </xsl:if>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <div class="pre-wrap {local-name()}-wrap">
      <pre>
        <xsl:apply-templates select="$package/preceding-sibling::*" mode="m:synopsis"/>
        <xsl:text>package </xsl:text>
        <xsl:apply-templates select="db:package"/>
        <xsl:text>;&#10;</xsl:text>
      </pre>
    </div>
    <xsl:apply-templates select="$package/following-sibling::*">
      <xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
    </xsl:apply-templates>
  </div>
</xsl:template>

<xsl:template match="db:classsynopsis">
  <xsl:param name="indent" select="''"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:classsynopsisinfo"/>
    <div class="pre-wrap {local-name()}-wrap">
      <pre>
        <xsl:apply-templates select="db:ooclass/db:modifier" mode="m:synopsis"/>
        <xsl:text>class </xsl:text>
        <xsl:apply-templates select="db:ooclass/db:classname" mode="m:synopsis"/>
        <xsl:text> {</xsl:text>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="* except (db:ooclass|db:classsynopsisinfo)"
                             mode="m:synopsis">
          <xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
        </xsl:apply-templates>
        <xsl:text>&#10;</xsl:text>
        <xsl:text>}</xsl:text>
      </pre>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:fieldsynopsis">
  <xsl:param name="indent" select="''"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <div class="pre-wrap {local-name()}-wrap">
      <pre class="synopsis">
        <xsl:apply-templates select="." mode="m:synopsis"/>
      </pre>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:fieldsynopsis" mode="m:synopsis">
  <xsl:param name="indent" select="''"/>

  <xsl:apply-templates
      select="db:synopsisinfo[empty(preceding-sibling::* except preceding-sibling::db:synopsisinfo)]"
      mode="m:synopsis"/>

  <xsl:sequence select="$indent"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:modifier|db:type" mode="m:synopsis"/>
    <xsl:apply-templates select="db:varname" mode="m:synopsis"/>
    <xsl:apply-templates select="db:initializer" mode="m:synopsis"/>
  </span>
  <xsl:text>&#10;</xsl:text>

  <xsl:apply-templates
      select="db:synopsisinfo[empty(following-sibling::* except following-sibling::db:synopsisinfo)]"
      mode="m:synopsis"/>

</xsl:template>

<xsl:template match="db:varname" mode="m:synopsis">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:synopsis"/>
    <xsl:if test="not(following-sibling::db:initializer)">;</xsl:if>
  </span>
</xsl:template>

<xsl:template match="db:initializer" mode="m:synopsis">
  <xsl:text> = </xsl:text>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:synopsis"/>
  </span>
  <xsl:text>;</xsl:text>
</xsl:template>

<xsl:template match="db:methodsynopsis
                     |db:constructorsynopsis
                     |db:destructorsynopsis">
  <xsl:param name="indent" select="''"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <div class="pre-wrap {local-name()}-wrap">
      <pre class="synopsis">
        <xsl:apply-templates select="." mode="m:synopsis"/>
      </pre>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:methodsynopsis
                     |db:constructorsynopsis
                     |db:destructorsynopsis"
              mode="m:synopsis">
  <xsl:param name="indent" select="''"/>

  <xsl:apply-templates
      select="db:synopsisinfo[empty(preceding-sibling::* except preceding-sibling::db:synopsisinfo)]"
      mode="m:synopsis"/>

  <xsl:sequence select="$indent"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:variable name="line" as="node()*">
      <xsl:apply-templates select="db:modifier|db:type" mode="m:synopsis"/>
      <xsl:apply-templates select="db:methodname" mode="m:synopsis"/>
      <xsl:text>(</xsl:text>
    </xsl:variable>
    <xsl:sequence select="$line"/>
    <xsl:apply-templates select="db:methodparam[1]" mode="m:synopsis">
      <xsl:with-param name="indent" select="''"/>
    </xsl:apply-templates>
    <xsl:choose>
      <xsl:when test="count(db:methodparam) gt 1">
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="db:methodparam[position() gt 1]"
                             mode="m:synopsis">
          <xsl:with-param name="indent" select="$indent || f:spaces($line)"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="count(db:methodparam) eq 0">
        <xsl:text>)</xsl:text>
        <xsl:sequence select="$funcsynopsis-trailing-punctuation"/>
        <xsl:text>&#10;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <!-- nop -->
      </xsl:otherwise>
    </xsl:choose>
  </span>

  <xsl:apply-templates
      select="db:synopsisinfo[empty(following-sibling::* except following-sibling::db:synopsisinfo)]"
      mode="m:synopsis"/>
</xsl:template>

<xsl:template match="db:methodparam" mode="m:synopsis">
  <xsl:param name="indent" select="''"/>

  <xsl:sequence select="$indent"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:modifier|db:type" mode="m:synopsis"/>
    <xsl:apply-templates select="db:parameter" mode="m:synopsis"/>
  </span>
  <xsl:sequence select="if (following-sibling::db:methodparam)
                        then ','
                        else ')' || $funcsynopsis-trailing-punctuation"/>
</xsl:template>

<xsl:template match="db:modifier|db:type" mode="m:synopsis">
  <xsl:apply-templates/>
  <xsl:text> </xsl:text>
</xsl:template>

<xsl:template match="db:classname|db:methodname|db:parameter" mode="m:synopsis">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:synopsisinfo" mode="m:synopsis">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:enumsynopsis">
  <xsl:param name="indent" select="''"/>
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:synopsisinfo"/>
    <div class="pre-wrap {local-name()}-wrap">
      <pre>
        <xsl:apply-templates select="db:modifier" mode="m:synopsis"/>
        <xsl:text>enum </xsl:text>
        <xsl:apply-templates select="db:enumname" mode="m:synopsis"/>
        <xsl:text> {</xsl:text>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="db:enumitem" mode="m:synopsis">
          <xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
        </xsl:apply-templates>
        <xsl:text>}</xsl:text>
      </pre>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:enumitem" mode="m:synopsis">
  <xsl:param name="indent" select="''"/>
  <xsl:sequence select="$indent"/>
  <xsl:apply-templates select="db:enumidentifier" mode="m:synopsis"/>
  <xsl:if test="following-sibling::db:enumitem">,</xsl:if>
  <xsl:if test="db:enumitemdescription">
    <xsl:variable name="width"
                  select="sum((string-length($indent),
                               string-length(db:enumidentifier),
                               (if (following-sibling::db:enumitem) then 1 else 0)))"/>
    <xsl:variable
        name="pad"
        select="max((2, 30 - $width))"/>
    <xsl:sequence select="substring('                              ', 1, $pad)"/>
    <xsl:sequence select="'// '"/>
    <xsl:apply-templates select="db:enumitemdescription" mode="m:synopsis"/>
  </xsl:if>
  <xsl:text>&#10;</xsl:text>
</xsl:template>

<xsl:template match="db:enumitemdescription" mode="m:synopsis">
  <xsl:apply-templates mode="m:synopsis"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:cmdsynopsis">
  <div class="cmdsynopsis-wrap">
    <div>
      <xsl:apply-templates select="." mode="m:attributes"/>
      <xsl:apply-templates/>
    </div>
  </div>
</xsl:template>

<xsl:template match="db:synopfragment">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:synopfragment-bug"/>
    <xsl:text> </xsl:text>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:synopfragmentref">
  <xsl:variable name="target" select="f:target(@linkend, .)"/>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <a href="{f:href(., $target)}">
      <xsl:apply-templates select="$target" mode="m:synopfragment-bug"/>
    </a>
    <xsl:text> </xsl:text>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:synopfragment" mode="m:synopfragment-bug">
  <xsl:variable name="number" as="item()*">
    <xsl:number from="db:cmdsynopsis" level="any"/>
  </xsl:variable>
  <xsl:variable name="number" select="xs:integer($number)"/>
  <span class="synopfragmentref-number">
    <xsl:sequence
        select="codepoints-to-string($callout-unicode-start + $number)"/>
  </span>
</xsl:template>

<xsl:template match="db:cmdsynopsis/db:command">
  <xsl:if test="preceding-sibling::*[1]">
    <br/>
  </xsl:if>
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </span>
</xsl:template>

<xsl:template match="db:group|db:arg" name="tp:group-or-arg">
  <xsl:variable name="choice" select="@choice/string()"/>
  <xsl:variable name="rep" select="@rep/string()"/>
  <xsl:variable name="sepchar"
                select="if (ancestor-or-self::*/@sepchar)
                        then ancestor-or-self::*/@sepchar/string()
                        else ' '"/>

  <xsl:if test="position() gt 1">
    <xsl:sequence select="$sepchar"/>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="$choice='plain'">
      <xsl:sequence select="$v:arg-choice-plain-open-str"/>
    </xsl:when>
    <xsl:when test="$choice='req'">
      <xsl:sequence select="$v:arg-choice-req-open-str"/>
    </xsl:when>
    <xsl:when test="$choice='opt'">
      <xsl:sequence select="$v:arg-choice-opt-open-str"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$v:arg-choice-def-open-str"/>
    </xsl:otherwise>
  </xsl:choose>

  <xsl:call-template name="t:inline"/>

  <xsl:choose>
    <xsl:when test="$rep='repeat'">
      <xsl:sequence select="$v:arg-rep-repeat-str"/>
    </xsl:when>
    <xsl:when test="$rep='norepeat'">
      <xsl:sequence select="$v:arg-rep-norepeat-str"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$v:arg-rep-def-str"/>
    </xsl:otherwise>
  </xsl:choose>

  <xsl:choose>
    <xsl:when test="$choice='plain'">
      <xsl:sequence select="$v:arg-choice-plain-close-str"/>
    </xsl:when>
    <xsl:when test="$choice='req'">
      <xsl:sequence select="$v:arg-choice-req-close-str"/>
    </xsl:when>
    <xsl:when test="$choice='opt'">
      <xsl:sequence select="$v:arg-choice-opt-close-str"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$v:arg-choice-def-close-str"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:group/db:arg">
  <xsl:variable name="choice" select="@choice"/>
  <xsl:variable name="rep" select="@rep"/>
  <xsl:if test="position()>1">
    <xsl:sequence select="$v:arg-or-sep"/>
  </xsl:if>
  <xsl:call-template name="tp:group-or-arg"/>
</xsl:template>

</xsl:stylesheet>

msgset.xsl

7 templates

Instructions
Template match ≅ db:msg|db:msgentry|db:msgexpla…
Mode: m:docbook
Matches: db:msg, db:msgentry, db:msgexplan, db:msgset
Template match ≅ db:msginfo|db:simplemsgentry
Mode: m:docbook
Matches: db:msginfo, db:simplemsgentry
Template match ≅ db:msgmain
Mode: m:docbook
Matches: db:msgmain
Template match ≅ db:msgsub
Mode: m:docbook
Matches: db:msgsub
Template match ≅ db:msgrel
Mode: m:docbook
Matches: db:msgrel
Template match ≅ db:msgtext
Mode: m:docbook
Matches: db:msgtext
Template match ≅ db:msgaud|db:msglevel|db:msgor…
Mode: m:docbook
Matches: db:msgaud, db:msglevel, db:msgorig
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m t xs"
                version="3.0">

<xsl:template match="db:msgset|db:msgentry|db:msg|db:msgexplan">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:simplemsgentry|db:msginfo">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:msgmain">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:msgsub">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:msgrel">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:msgtext">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:msglevel|db:msgorig|db:msgaud">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'label'"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="." mode="m:gentext">
      <xsl:with-param name="group" select="'label-separator'"/>
    </xsl:apply-templates>
    <xsl:apply-templates/>
  </div>
</xsl:template>

</xsl:stylesheet>

objects.xsl

28 templates (2 used only in one other module), 16 functions (1 unused, 15 used only in one other module)

Instructions
Template match ≅ db:inlinemediaobject|db:mediao…
Uses: $v:as-xml
Mode: m:docbook
Matches: db:inlinemediaobject, db:mediaobject
Template match ≅ db:imageobject
Mode: m:docbook
Matches: db:imageobject
Template match ≅ db:videoobject
Mode: m:docbook
Matches: db:videoobject
Template match ≅ db:audioobject
Mode: m:docbook
Matches: db:audioobject
Template match ≅ db:textobject
Mode: m:docbook
Matches: db:textobject
Template match ≅ db:textdata
Mode: m:docbook
Matches: db:textdata
Template match ≅ db:caption
Mode: m:docbook
Matches: db:caption
Template match ≅ db:multimediaparam
Mode: m:docbook
Matches: db:multimediaparam
Template match ≅ db:imageobjectco
Mode: m:imagemap
Matches: db:imageobjectco
Function f:mediaobject-viewport($info as map(*)) as map(*)
Template match ≅ svg:*
Mode: m:docbook
Matches: svg:*
Template tp:viewport match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ *
Mode: mp:imagedata
Matches: *
Template t:mediaobject-img match ≅
Used in: main.xsl
Mode: m:docbook
Function f:object-width($info as map(*)) as map(*)
Function f:object-height($info as map(*)) as map(*)
Function f:object-contentwidth($info as map(*), $intrinsicwidth as map(*)) as map(*)
Function f:object-contentheight($info as map(*), $intrinsicheight as map(*)) as map(*)
Function f:css-length($property as xs:string, $length as map(*)?) as xs:string?
Used in: main.xsl
Function f:css-property($property as xs:string, $value as xs:string?) as xs:string?
Used in: main.xsl
Function f:object-scalefit($info as map(*)) as xs:boolean
Function f:object-scale($info as map(*)) as xs:double
Function f:object-align($info as map(*)) as xs:string?
Used in: main.xsl
Function f:object-valign($info as map(*)) as xs:string?
Used in: main.xsl
Function f:object-properties#1($uri as xs:string) as map(xs:string, xs:anyAtomicType)
Unused
Function f:object-properties#2($uri as xs:string, $image-file as xs:boolean) as map(xs:string, xs:anyAtomicType)
Template match ≅ db:alt
Mode: m:docbook
Matches: db:alt
Template match ≅ db:alt|db:textobject
Mode: m:details
Matches: db:alt, db:textobject
Template match ≅ db:textobject
Mode: m:details
Matches: db:textobject
Template match ≅ db:audioobject|db:imageobject|… as map(*)
Mode: m:mediaobject-info
Matches: db:audioobject, db:imageobject, db:textobject, db:videoobject
Template match ≅ db:textobject as map(*)
Mode: m:mediaobject-info
Priority: 50
Matches: db:textobject
Template match ≅ db:textobject
Mode: m:mediaobject-info
Priority: 100
Matches: db:textobject
Template match ≅ db:audiodata|db:imagedata|db:t… as map(*)?
Mode: m:mediaobject-info
Matches: db:audiodata, db:imagedata, db:textdata, db:videodata
Function f:mediaobject-input-base-uri($node as element()) as xs:string
Template match ≅ db:audiodata|db:imagedata|db:t… as map(*)?
Mode: m:mediaobject-uris
Matches: db:audiodata, db:imagedata, db:textdata, db:videodata
Function f:mediaobject-amend-uri($uri as xs:string) as xs:string
Function f:mediaobject-type($uri as xs:string) as xs:string?
Used in: main.xsl
Template match ≅ db:mediaobject
Mode: m:mediaobject-start
Matches: db:mediaobject
Template match ≅ db:inlinemediaobject
Mode: m:mediaobject-start
Matches: db:inlinemediaobject
Template match ≅ db:meta
Mode: m:mediaobject-start
Matches: db:meta
Template match ≅ *
Mode: m:mediaobject-start
Matches: *
Template match ≅ db:mediaobject
Mode: m:mediaobject-end
Matches: db:mediaobject
Template match ≅ db:inlinemediaobject
Mode: m:mediaobject-end
Matches: db:inlinemediaobject
Template match ≅ *
Mode: m:mediaobject-end
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:array="http://www.w3.org/2005/xpath-functions/array"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:ext="http://docbook.org/extensions/xslt"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:svg="http://www.w3.org/2000/svg"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xlink='http://www.w3.org/1999/xlink'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="db:mediaobject|db:inlinemediaobject">
  <xsl:variable name="pi-properties"
                select="f:pi-attributes(./processing-instruction('db'))"/>

  <xsl:variable name="gi" select="if (self::db:mediaobject)
                                  then 'div'
                                  else 'span'"/>

  <xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:copy-of select="$pi-properties/@style"/>
    <xsl:apply-templates select="." mode="m:attributes"/>

    <xsl:apply-templates select="." mode="m:mediaobject-start"/>

    <xsl:message use-when="'objects' = $v:debug"
                 select="'Processing mediaobject with fileref=' || (//*[@fileref]/@fileref)[1]"/>

    <!-- Select the first object that contains something we (think) we can render -->
    <xsl:variable name="object-info" as="map(*)?">
      <xsl:iterate select="db:audioobject|db:imageobject|db:videoobject
                           |db:textobject
                           |db:imageobjectco/db:imageobject">
        <xsl:param name="force" as="map(*)?" select="()"/>
        <xsl:on-completion>
          <xsl:choose>
            <xsl:when test="empty($force)">
              <xsl:message select="'No media object found.'"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:variable name="ref"
                            select="if (array:size($force?datas) gt 0)
                                    then array:get($force?datas, 1)?fileref
                                    else ()"/>
              <xsl:choose>
                <xsl:when test="exists($ref)">
                  <xsl:message select="'No acceptable media object, using', $ref"/>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:message select="'No acceptable media object found'"/>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:otherwise>
          </xsl:choose>
          <xsl:sequence select="$force"/>
        </xsl:on-completion>

        <xsl:variable name="info" as="map(*)?">
          <xsl:apply-templates select="." mode="m:mediaobject-info"/>
        </xsl:variable>

        <xsl:variable name="alternatives"
                      select="count(db:audiodata|db:imagedata|db:videodata)"/>
        <xsl:variable name="selected-alternatives"
                      select="if (exists($info) and map:contains($info, 'datas'))
                              then array:size($info?datas)
                              else 0"/>

        <xsl:choose>
          <xsl:when test="./self::db:textobject and not(db:phrase)">
            <xsl:message use-when="'objects' = $v:debug"
                         select="'Selected ' || local-name(.) || ' at position ' || position()"/>
            <xsl:break select="$info"/>
          </xsl:when>
          <xsl:when test="empty($info) or $selected-alternatives = 0
                          or $selected-alternatives lt $alternatives">
            <xsl:message use-when="'objects' = $v:debug"
                         select="'Skipping ' || local-name(.) || ' at position ' || position()"/>
            <xsl:next-iteration>
              <xsl:with-param name="force"
                              select="if (empty($force)) then $info else $force"/>
            </xsl:next-iteration>
          </xsl:when>
          <xsl:otherwise>
            <xsl:message use-when="'objects' = $v:debug"
                         select="'Selected ' || local-name(.) || ' at position ' || position()"/>
            <xsl:break select="$info"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:iterate>
    </xsl:variable>

    <!-- Compute the viewport parameters from the properties of the
         last alternative -->
    <xsl:variable name="last-data"
                  select="array:flatten($object-info?datas)[last()]"/>
    <xsl:variable name="viewport"
                  select="$last-data ! f:mediaobject-viewport(.)"/>

    <xsl:variable name="display-width"
                  select="if (exists($last-data?contentwidth))
                          then $last-data?contentwidth
                          else $viewport?intrinsicwidth"/>
    <xsl:variable name="display-height"
                  select="if (exists($last-data?contentheight))
                          then $last-data?contentheight
                          else $viewport?intrinsicheight"/>

    <xsl:variable name="viewport-markup" as="element(h:span)">
      <xsl:call-template name="tp:viewport">
        <xsl:with-param name="class" select="'viewport-table'"/>
        <xsl:with-param name="width" select="$viewport?width"/>
        <xsl:with-param name="height" select="$viewport?height"/>
        <xsl:with-param
            name="pi-properties"
            select="f:pi-attributes($object-info?node/processing-instruction('db'))"/>
        <xsl:with-param name="content" as="element()+">
          <span class="viewport-row">
            <xsl:call-template name="tp:viewport">
              <xsl:with-param name="class" select="'viewport-cell'"/>
              <xsl:with-param name="align" select="$viewport?align"/>
              <xsl:with-param name="valign" select="$viewport?valign"/>
              <xsl:with-param
                  name="pi-properties"
                  select="f:pi-attributes($last-data?node/processing-instruction('db'))"/>
              <xsl:with-param name="content" as="element()+">
                <!-- This anonymous span is styled with CSS to avoid an
                     awkward bottom padding issue in Firefox. -->
                <span>
                  <span class="viewport">
                    <xsl:apply-templates select="$object-info?node">
                      <xsl:with-param name="viewport" select="$viewport"/>
                      <xsl:with-param name="datas"
                                      select="array:flatten($object-info?datas)"/>
                    </xsl:apply-templates>
                    <xsl:if test="$object-info?node/ancestor::db:imageobjectco
                                  and exists($display-width) and exists($display-height)">
                      <xsl:apply-templates select="$object-info?node/ancestor::db:imageobjectco"
                                           mode="m:imagemap">
                        <xsl:with-param name="intrinsicwidth" select="$display-width"/>
                        <xsl:with-param name="intrinsicheight" select="$display-height"/>
                      </xsl:apply-templates>
                    </xsl:if>
                  </span>
                </span>
              </xsl:with-param>
            </xsl:call-template>
          </span>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="table" select="$viewport-markup"/>
    <xsl:variable name="row" select="$table/*"/>
    <xsl:variable name="cell" select="$row/*"/>
    <xsl:variable name="viewport" select="$cell/*/*"/> <!-- Skip the anonymous wrapper! -->

    <xsl:message use-when="'image-markup' = $v:debug"
                 select="serialize($viewport-markup, $v:as-xml)"/>

    <xsl:variable name="mediatype" as="xs:string">
      <xsl:choose>
        <xsl:when test="db:videoobject">video</xsl:when>
        <xsl:when test="db:imageobject|db:imageobjectco">image</xsl:when>
        <xsl:when test="db:audioobject">audio</xsl:when>
        <xsl:otherwise>text</xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:element name="{if (self::db:inlinemediaobject) then 'span' else 'div'}"
                 namespace="http://www.w3.org/1999/xhtml">
      <xsl:attribute name="class" select="'media ' || $mediatype"/>
      <xsl:choose>
        <xsl:when test="empty($table/@* except $table/@class)
                        and empty($row/@* except $row/@class)
                        and empty($cell/@* except $cell/@class)
                        and empty($viewport/@* except $viewport/@class)">
          <xsl:sequence select="$viewport/node()"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$viewport-markup"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>

    <xsl:apply-templates select="." mode="m:mediaobject-end"/>

    <xsl:if test="$object-info?node/ancestor::db:imageobjectco
                  and $object-info?node/ancestor::db:imageobjectco/db:calloutlist">
      <xsl:if test="self::db:inlinemediaobject">
        <xsl:message>Callout list in inlinemediaobject produces invalid HTML</xsl:message>
      </xsl:if>
      <xsl:apply-templates select="$object-info?node/ancestor::db:imageobjectco/db:calloutlist"/>
    </xsl:if>
  </xsl:element>
</xsl:template>

<xsl:template match="db:imageobject">
  <xsl:param name="viewport" as="map(*)?"/>
  <xsl:param name="datas" as="map(*)*"/>

  <xsl:variable name="svg-ct" select="map:get($v:media-type-map, '.svg')"/>

  <xsl:choose>
    <xsl:when test="$datas[1]?node//svg:*">
      <xsl:if test="count($datas) gt 1">
        <xsl:message>Fallback is not possible with inline SVG.</xsl:message>
      </xsl:if>
      <xsl:apply-templates select="$datas[1]?node/svg:*"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="exists($datas ! .?node//svg:*)">
        <xsl:message>Inline SVG cannot be used as a fallback.</xsl:message>
      </xsl:if>

      <xsl:variable name="non-svg" as="map(*)*">
        <xsl:for-each select="$datas">
          <xsl:if test="empty(.?node//svg:*)">
            <xsl:sequence select="."/>
          </xsl:if>
        </xsl:for-each>
      </xsl:variable>

      <xsl:variable name="last" select="$non-svg[last()]"/>
      <xsl:variable name="params" select="array:flatten($last?params)"/>

      <picture>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:apply-templates select="$params"/>
        <xsl:for-each select="$non-svg">
          <xsl:choose>
            <xsl:when test="position() = last()">
              <xsl:apply-templates select=".?node" mode="mp:imagedata">
                <xsl:with-param name="viewport" select="$viewport"/>
                <xsl:with-param name="info" select="."/>
                <xsl:with-param name="last" select="true()"/>
              </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
              <source>
                <xsl:if test=".?content-type">
                  <xsl:attribute name="type" select=".?content-type"/>
                </xsl:if>
                <xsl:attribute name="srcset"
                               select=".?href"/>
              </source>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each>
      </picture>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:videoobject">
  <xsl:param name="viewport" as="map(*)?"/>
  <xsl:param name="datas" as="map(*)*"/>

  <xsl:variable name="last" select="$datas[last()]"/>
  <xsl:variable name="params" select="array:flatten($last?params)"/>

  <xsl:choose>
    <xsl:when test="f:pi(., 'video', $mediaobject-video-element) = 'iframe'">
      <xsl:if test="count($datas) gt 1">
        <xsl:message select="'Fallback is not supported with iframes'"/>
      </xsl:if>
      <iframe src="{$datas[1]?href}">
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:if test="exists($last?width)">
          <xsl:attribute name="width" select="f:absolute-length($last?width)"/>
        </xsl:if>
        <xsl:if test="exists($last?height)">
          <xsl:attribute name="height" select="f:absolute-length($last?height)"/>
        </xsl:if>
        <xsl:apply-templates select="$params"/>
        <xsl:if test="empty($params)">
          <xsl:attribute name="controls" select="'controls'"/>
        </xsl:if>
      </iframe>
    </xsl:when>
    <xsl:otherwise>
      <video>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:sequence select="fp:css-properties(db:videodata[last()])"/>
        <xsl:if test="exists($last?width)">
          <xsl:attribute name="width" select="f:absolute-length($last?width)"/>
        </xsl:if>
        <xsl:if test="exists($last?height)">
          <xsl:attribute name="height" select="f:absolute-length($last?height)"/>
        </xsl:if>
        <xsl:apply-templates select="$params"/>
        <xsl:if test="empty($params)">
          <xsl:attribute name="controls" select="'controls'"/>
        </xsl:if>

        <xsl:variable name="poster" as="map(*)?">
          <xsl:apply-templates
              select="(../db:imageobject/db:imagedata
                         [@fileref and contains-token(@role, 'poster')])[1]"
              mode="m:mediaobject-uris"/>
        </xsl:variable>
        <xsl:if test="exists($poster)">
          <xsl:attribute name="poster" select="$poster?href"/>
        </xsl:if>

        <xsl:apply-templates select="." mode="m:mediaobject-start"/>

        <xsl:for-each select="$datas">
          <xsl:apply-templates select=".?node" mode="mp:imagedata">
            <xsl:with-param name="viewport" select="$viewport"/>
            <xsl:with-param name="info" select="."/>
            <xsl:with-param name="last" select="position() eq last()"/>
          </xsl:apply-templates>
        </xsl:for-each>

        <xsl:apply-templates select="." mode="m:mediaobject-end"/>
      </video>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:audioobject">
  <xsl:param name="viewport" as="map(*)?"/>
  <xsl:param name="datas" as="map(*)*"/>

  <xsl:variable name="last" select="$datas[last()]"/>
  <xsl:variable name="params" select="array:flatten($last?params)"/>

  <audio>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:sequence select="fp:css-properties(db:audiodata[last()])"/>
    <xsl:apply-templates select="$params"/>
    <xsl:if test="empty($params)">
      <xsl:attribute name="controls" select="'controls'"/>
    </xsl:if>
    <xsl:for-each select="$datas">
      <xsl:apply-templates select=".?node" mode="mp:imagedata">
        <xsl:with-param name="viewport" select="$viewport"/>
        <xsl:with-param name="info" select="."/>
        <xsl:with-param name="last" select="position() eq last()"/>
      </xsl:apply-templates>
    </xsl:for-each>
  </audio>
</xsl:template>

<xsl:template match="db:textobject">
  <xsl:param name="viewport" as="map(*)?"/>
  <xsl:param name="datas" as="map(*)*"/>

  <xsl:variable name="content" as="item()*">
    <xsl:choose>
      <xsl:when test="db:textdata">
        <xsl:apply-templates select="db:textdata[1]">
          <xsl:with-param name="info" select="$datas[1]"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="parent::db:inlinemediaobject">
      <span>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:sequence select="$content"/>
      </span>
    </xsl:when>
    <xsl:when test="parent::db:mediaobject">
      <div>
        <xsl:apply-templates select="." mode="m:attributes"/>
        <xsl:sequence select="$content"/>
      </div>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$content"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:textdata">
  <xsl:param name="info" as="map(*)"/>
  <xsl:sequence select="unparsed-text($info?uri, ($info?node/@encoding, 'utf-8')[1])"/>
</xsl:template>

<xsl:template match="db:caption">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:multimediaparam">
  <xsl:attribute name="{@name}" select="@value"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:imageobjectco" mode="m:imagemap">
  <xsl:param name="intrinsicwidth" as="map(*)"/>
  <xsl:param name="intrinsicheight" as="map(*)"/>

  <map name="imap_{f:id(.)}">
    <xsl:for-each select="db:areaspec//db:area">
      <xsl:variable name="units" as="xs:string"
                    select="if (@units) then @units
                            else if (../@units) then ../@units
                            else 'calspair'"/>

      <xsl:variable name="coords" as="xs:string?">
        <xsl:choose>
          <xsl:when test="$units = 'calspair'">
            <xsl:variable name="coords"
                          select="tokenize(normalize-space(@coords),
                                  '[\s,]+')"/>

            <!-- CALS = LL to UR by percentage * 10000 -->
            <!-- HTML = UL to LR by pixel -->
            <xsl:variable name="x1p"
                          select="xs:decimal($coords[1]) div 10000.0"/>
            <xsl:variable name="y1p"
                          select="1 - (xs:decimal($coords[4]) div 10000.0)"/>
            <xsl:variable name="x2p"
                          select="xs:decimal($coords[3]) div 10000.0"/>
            <xsl:variable name="y2p"
                          select="1 - (xs:decimal($coords[2]) div 10000.0)"/>

            <xsl:sequence
                select="string-join((round($x1p * $intrinsicwidth?magnitude),
                                     round($y1p * $intrinsicheight?magnitude),
                                     round($x2p * $intrinsicwidth?magnitude),
                                     round($y2p * $intrinsicheight?magnitude)), ',')"/>
          </xsl:when>
          <xsl:when test="@units = 'other'
                          and (@otherunits = 'html-rect'
                               or @otherunits = 'html-circle'
                               or @otherunits = 'html-poly')">
            <xsl:sequence select="@coords/string()"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:message>
              <xsl:text>Warning: unsupported units in imagemap: </xsl:text>
              <xsl:sequence select="if (@units = 'other')
                                    then @otherunits/string()
                                    else @units/string()"/>
            </xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>

      <xsl:if test="exists($coords)">
        <area>
          <xsl:attribute name="shape">
            <xsl:choose>
              <xsl:when test="$units = 'calspair' or @otherunits = 'html-rect'">
                <xsl:sequence select="'rect'"/>
              </xsl:when>
              <xsl:when test="@otherunits = 'html-circle'">
                <xsl:sequence select="'circle'"/>
              </xsl:when>
              <xsl:when test="@otherunits = 'html-poly'">
                <xsl:sequence select="'poly'"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:message>
                  <xsl:text>Error: unexpected shape in imagemap: </xsl:text>
                  <xsl:sequence select="if (@units = 'other')
                                        then @otherunits/string()
                                        else @units/string()"/>
                  <xsl:sequence select="."/>
                </xsl:message>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:attribute>

          <xsl:apply-templates select="." mode="m:attributes"/>
          <xsl:choose>
            <xsl:when test="@linkends
                            or (parent::db:areaset and ../@linkends)">
              <xsl:variable name="idrefs"
                            select="if (@linkends)
                                    then normalize-space(@linkends)
                                    else normalize-space(../@linkends)"/>

              <xsl:variable name="target"
                            select="key('id', tokenize($idrefs, '[\s]'))[1]"/>

              <xsl:if test="$target">
                <xsl:attribute name="href" select="f:href(., $target)"/>
              </xsl:if>
            </xsl:when>
            <xsl:when test="@xlink:href">
              <xsl:attribute name="href" select="@xlink:href"/>
            </xsl:when>
          </xsl:choose>

          <xsl:attribute name="coords" select="$coords"/>
        </area>
      </xsl:if>
    </xsl:for-each>
  </map>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="f:mediaobject-viewport" as="map(*)">
  <xsl:param name="info" as="map(*)"/>

  <xsl:variable name="imageproperties" select="$info?properties"/>

  <xsl:variable name="intrinsicwidth"
                select="if (exists($imageproperties)
                            and map:contains($imageproperties, 'width'))
                        then f:make-length($imageproperties?width, 'px')
                        else $v:image-nominal-width"/>

  <xsl:variable name="intrinsicheight"
                select="if (exists($imageproperties)
                            and map:contains($imageproperties, 'height'))
                        then f:make-length($imageproperties?height, 'px')
                        else $v:image-nominal-height"/>

  <xsl:variable name="width" select="f:object-width($info)"/>
  <xsl:variable name="height" select="f:object-height($info)"/>

  <!-- Convert % widths into absolute widths if we can -->

  <xsl:variable
      name="width"
      select="if ($width?unit = '%' and not(f:is-empty-length($intrinsicwidth)))
              then f:make-length($intrinsicwidth?magnitude
                                 * $width?magnitude
                                 div 100.0,
                                 $intrinsicwidth?unit)
              else $width"/>

  <xsl:variable
      name="height"
      select="if ($height?unit = '%' and not(f:is-empty-length($intrinsicheight)))
              then f:make-length($intrinsicheight?magnitude
                                 * $height?magnitude
                                 div 100.0,
                                 $intrinsicheight?unit)
              else
                $height"/>

  <xsl:variable name="scalefit" select="f:object-scalefit($info)"/>
  <xsl:variable name="scale" select="f:object-scale($info)"/>

  <xsl:variable name="cw" select="f:object-contentwidth($info, $intrinsicwidth)"/>

  <!-- <xsl:message select="'cw:', serialize($cw, $v:as-json), $scale"/> -->

  <xsl:variable name="contentwidth" as="map(*)?">
    <xsl:choose>
      <xsl:when test="$cw?unit or (exists($width) and ($scalefit or $scale ne 1.0))">
        <xsl:variable name="cw" select="if (f:is-empty-length($cw))
                                        then $intrinsicwidth
                                        else $cw"/>

        <xsl:variable name="contentwidth" select="if ($scalefit) then $width else $cw"/>

        <xsl:variable name="cw" select="if (f:is-empty-length($contentwidth))
                                        then $v:image-nominal-width
                                        else $contentwidth"/>

        <xsl:variable name="contentwidth"
                      select="if ($scale ne 1.0)
                              then f:make-length($cw?magnitude * $scale, $cw?unit)
                              else $contentwidth"/>

        <xsl:sequence select="if (f:equal-lengths($contentwidth, $intrinsicwidth))
                              then ()
                              else $contentwidth"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$cw"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!-- ======================================== -->

  <xsl:variable name="ch" select="f:object-contentheight($info, $intrinsicheight)"/>

  <xsl:variable name="contentheight" as="map(*)?">
    <xsl:choose>
      <xsl:when test="$ch?unit or (exists($height) and ($scalefit or $scale ne 1.0))">
        <xsl:variable name="ch" select="if (f:is-empty-length($ch))
                                        then $intrinsicheight
                                        else $ch"/>

        <xsl:variable name="contentheight" select="if ($scalefit) then $height else $ch"/>

        <xsl:variable name="ch" select="if (f:is-empty-length($contentheight))
                                        then $v:image-nominal-height
                                        else $contentheight"/>

        <xsl:variable name="contentheight"
                      select="if ($scale ne 1.0)
                              then f:make-length($ch?magnitude * $scale, $ch?unit)
                              else $contentheight"/>

        <xsl:sequence select="if (f:equal-lengths($contentheight, $intrinsicheight))
                              then ()
                              else $contentheight"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$ch"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="align" select="f:object-align($info)"/>

  <!-- There's no point doing valign if there's no height -->
  <xsl:variable name="valign" select="if (f:is-empty-length($height)
                                          and f:is-empty-length($contentheight))
                                      then ()
                                      else f:object-valign($info)"/>

  <xsl:variable name="result" select="map {
      'intrinsicwidth': $intrinsicwidth,
      'intrinsicheight': $intrinsicheight,
      'width': $width,
      'height': $height,
      'contentwidth': $contentwidth,
      'contentheight': $contentheight,
      'scale': $scale,
      'scalefit': $scalefit,
      'align': $align,
      'valign': $valign
    }"/>

  <xsl:message use-when="'image-properties' = $v:debug"
               select="serialize($result, $v:as-json)"/>

  <xsl:sequence select="$result"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="svg:*">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="node()"/>
  </xsl:element>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="tp:viewport">
  <xsl:param name="class" as="xs:string" select="'viewport'"/>
  <xsl:param name="width" as="map(*)?"/>
  <xsl:param name="height" as="map(*)?"/>
  <xsl:param name="align" as="xs:string?"/>
  <xsl:param name="valign" as="xs:string?"/>
  <xsl:param name="pi-properties" as="element()?"/>
  <xsl:param name="content" as="element()+"/>

  <xsl:variable name="valign" select="f:css-property('vertical-align', $valign)"/>
  <xsl:variable name="width" select="f:css-length('width', $width)"/>
  <xsl:variable name="height" select="f:css-length('height', $height)"/>
  <xsl:variable name="align" select="f:css-property('text-align', $align)"/>

  <xsl:variable name="pi-styles"
                select="tokenize($pi-properties/@style, '\s*;\s*')"/>

  <xsl:variable name="styles" as="xs:string*">
    <xsl:sequence select="($width, $height, $align, $valign)"/>
    <xsl:for-each select="$pi-styles">
      <xsl:choose>
        <xsl:when test="normalize-space(.) = ''"/>
        <xsl:when test="starts-with(., 'width:')
                        or starts-with(., 'height:')
                        or starts-with(., 'text-align:')
                        or starts-with(., 'vertical-align:')">
          <xsl:message expand-text="yes"
                       >Ignoring ?db style property: {.}</xsl:message>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="."/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:variable>

  <span>
    <xsl:attribute name="class" select="$class"/>
    <xsl:if test="exists($styles)">
      <xsl:attribute name="style" select="string-join($styles, ';')||';'"/>
    </xsl:if>
    <xsl:sequence select="$content"/>
  </span>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="mp:imagedata">
  <xsl:param name="viewport" as="map(*)?"/>
  <xsl:param name="info" as="map(*)"/>
  <xsl:param name="last" as="xs:boolean"/>

  <xsl:variable name="width"
                select="if (exists($viewport?contentwidth))
                        then f:css-length('width', $viewport?contentwidth)
                        else if ($info?scalefit)
                             then f:css-length('width', $info?width)
                             else ()"/>
  <xsl:variable name="height"
                select="if (exists($viewport?contentheight))
                        then f:css-length('height', $viewport?contentheight)
                        else if ($info?scalefit)
                             then f:css-length('height', $info?height)
                             else ()"/>

  <xsl:variable name="styles" select="($width, $height)"/>

  <xsl:choose>
    <!-- attempt to handle audio and video data -->
    <xsl:when test="self::db:audiodata or self::db:videodata">
      <source src="{$info?href}">
        <xsl:if test="$info?content-type">
          <xsl:attribute name="type" select="$info?content-type"/>
        </xsl:if>
        <xsl:if test="$last and normalize-space($fallback-js) != ''">
          <xsl:attribute name="onerror" select="'docbook_object_fallback(parentNode)'"/>
        </xsl:if>
      </source>
    </xsl:when>

    <xsl:when test="svg:*">
      <div class="svg">
        <xsl:if test="ancestor::db:imageobjectco">
          <xsl:attribute name="usemap"
                         select="'#imap_' || f:id(ancestor::db:imageobjectco)"/>
        </xsl:if>
        <xsl:if test="exists($styles)">
          <xsl:attribute name="style" select="string-join($styles, ';')||';'"/>
        </xsl:if>
        <xsl:apply-templates/>
      </div>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="t:mediaobject-img">
        <xsl:with-param name="filename" select="$info?href"/>
        <xsl:with-param name="styles" select="$styles"/>
        <xsl:with-param name="viewport" select="$viewport"/>
        <xsl:with-param name="imageproperties" select="$info?properties"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="t:mediaobject-img">
  <xsl:param name="filename" as="xs:string"/>
  <xsl:param name="styles" as="xs:string*"/>
  <xsl:param name="viewport" as="map(*)?"/>
  <xsl:param name="imageproperties" as="map(*)?"/>

  <img src="{$filename}">
    <xsl:apply-templates select="." mode="m:attributes"/>
    
    <!-- Apply any alt text in the media object to the image tag. -->
    <xsl:variable name="short"
                  select="(ancestor::db:mediaobject|ancestor::db:inlinemediaobject)
                          /(db:alt,db:textobject[db:phrase])[1]"/>
    <xsl:if test="exists($short)">
      <xsl:attribute name="alt" select="normalize-space($short)"/>
    </xsl:if>

    <xsl:if test="exists($styles)">
      <xsl:variable name="css-properties" select="fp:css-properties(.)"/>
      <xsl:attribute name="style"
                     select="string-join($styles, ';')||';'
                             ||(if (exists($css-properties)) then string($css-properties) else '')"/>
    </xsl:if>

    <xsl:if test="ancestor::db:imageobjectco">
      <xsl:variable name="co"
                    select="ancestor::db:imageobjectco"/>
      <xsl:choose>
        <xsl:when test="empty($imageproperties)">
          <xsl:message>
            <xsl:text>Imagemaps require image </xsl:text>
            <xsl:text>intrinsics extension</xsl:text>
          </xsl:message>
        </xsl:when>
        <xsl:otherwise>
          <xsl:attribute name="usemap"
                         select="concat('#', 'imap_' || f:id($co))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </img>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="f:object-width" as="map(*)">
  <xsl:param name="info" as="map(*)"/>
  <xsl:sequence select="if ($image-ignore-scaling)
                        then f:empty-length()
                        else ($info?width,map{})[1]"/>
</xsl:function>

<xsl:function name="f:object-height" as="map(*)">
  <xsl:param name="info" as="map(*)"/>
  <xsl:sequence select="if ($image-ignore-scaling)
                        then f:empty-length()
                        else ($info?height,map{})[1]"/>
</xsl:function>

<xsl:function name="f:object-contentwidth" as="map(*)">
  <xsl:param name="info" as="map(*)"/>
  <xsl:param name="intrinsicwidth" as="map(*)"/>

  <xsl:choose>
    <xsl:when test="$image-ignore-scaling">
      <xsl:sequence select="f:empty-length()"/>
    </xsl:when>
    <xsl:when test="exists($info?contentwidth)">
      <xsl:variable name="width"
                    select="$info?contentwidth"/>
      <xsl:sequence
          select="if ($width?unit = '%')
                  then f:make-length($width?magnitude
                                     * $intrinsicwidth?magnitude
                                     div 100.0,
                                     $intrinsicwidth?unit)
                  else $width"/>
    </xsl:when>
    <xsl:when test="$info?scalefit and exists($info?width)">
      <xsl:sequence select="$info?width"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="f:empty-length()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:object-contentheight" as="map(*)">
  <xsl:param name="info" as="map(*)"/>
  <xsl:param name="intrinsicheight" as="map(*)"/>

  <xsl:choose>
    <xsl:when test="$image-ignore-scaling">
      <xsl:sequence select="f:empty-length()"/>
    </xsl:when>
    <xsl:when test="exists($info?contentheight)">
      <xsl:variable name="depth"
                    select="$info?contentheight"/>
      <xsl:sequence
          select="if ($depth?unit = '%')
                  then f:make-length($depth?magnitude
                                     * $intrinsicheight?magnitude
                                     div 100.0,
                                     $intrinsicheight?unit)
                  else $depth"/>
    </xsl:when>
    <xsl:when test="$info?scalefit and exists($info?height)">
      <xsl:sequence select="$info?height"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="f:empty-length()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:css-length" as="xs:string?">
  <xsl:param name="property" as="xs:string"/>
  <xsl:param name="length" as="map(*)?"/>
  <xsl:sequence
      select="if (exists($length) and $length?unit)
              then f:css-property($property, string(f:absolute-length($length))||'px')
              else ()"/>
</xsl:function>

<xsl:function name="f:css-property" as="xs:string?">
  <xsl:param name="property" as="xs:string"/>
  <xsl:param name="value" as="xs:string?"/>
  <xsl:sequence
      select="if (exists($value))
              then $property || ':' || $value
              else ()"/>
</xsl:function>

<xsl:function name="f:object-scalefit" as="xs:boolean">
  <xsl:param name="info" as="map(*)"/>
  <xsl:choose>
    <xsl:when test="$image-ignore-scaling
                    or exists($info?contentwidth)
                    or exists($info?contentheight)">
      <xsl:sequence select="false()"/>
    </xsl:when>
    <xsl:when test="$info?scale">
      <xsl:sequence select="false()"/>
    </xsl:when>
    <xsl:when test="exists($info?scalefit)">
      <xsl:sequence select="$info?scalefit"/>
    </xsl:when>
    <xsl:when test="f:object-width($info)?magnitude
                    or f:object-height($info)?magnitude">
      <!-- this is for backwards compatibility -->
      <xsl:sequence select="true()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:object-scale" as="xs:double">
  <xsl:param name="info" as="map(*)"/>
  <xsl:choose>
    <xsl:when test="$image-ignore-scaling or not($info?scale)">
      <xsl:sequence select="1.0"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="xs:double($info?scale) div 100.0"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:object-align" as="xs:string?">
  <xsl:param name="info" as="map(*)"/>
  <xsl:sequence select="$info?align"/>
</xsl:function>

<xsl:function name="f:object-valign" as="xs:string?">
  <xsl:param name="info" as="map(*)"/>
  <!-- Historically, middle seems to have been the default -->
  <xsl:sequence select="($info?valign, 'middle')[1]"/>
</xsl:function>

<xsl:function name="f:object-properties" as="map(xs:string, xs:anyAtomicType)">
  <xsl:param name="uri" as="xs:string"/>
  <xsl:sequence select="f:object-properties($uri, true())"/>
</xsl:function>

<xsl:function name="f:object-properties" as="map(xs:string, xs:anyAtomicType)">
  <xsl:param name="uri" as="xs:string"/>
  <xsl:param name="image-file" as="xs:boolean"/>

  <xsl:variable name="properties" as="map(xs:string, xs:anyAtomicType)"
                use-when="function-available('ext:image-metadata')">
    <xsl:sequence select="ext:image-metadata($uri, $image-file)"/>
  </xsl:variable>

  <xsl:variable name="properties" as="map(xs:string, xs:anyAtomicType)"
                use-when="function-available('ext:image-properties')
                          and not(function-available('ext:image-metadata'))">
    <xsl:sequence select="ext:image-properties($uri)"/>
  </xsl:variable>

  <xsl:variable name="properties" as="map(xs:string, xs:anyAtomicType)"
                use-when="not(function-available('ext:image-properties'))
                          and not(function-available('ext:image-properties'))"
                select="map {}"/>

  <xsl:if use-when="not(function-available('ext:image-properties'))
                    and not(function-available('ext:image-properties'))"
          test="$image-property-warning">
    <xsl:message>
      <xsl:text>Cannot read image properties (no extension)</xsl:text>
    </xsl:message>
  </xsl:if>

  <xsl:message use-when="$v:debug = 'image-properties'"
               select="$uri, serialize($properties, $v:as-json)"/>
  <xsl:sequence select="$properties"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="db:alt"/>

<!-- ============================================================ -->

<xsl:template match="db:alt|db:textobject[db:phrase]" mode="m:details">
  <xsl:if test="'summary' = $vp:mediaobject-accessibility">
    <xsl:attribute name="summary" select="normalize-space(.)"/>
  </xsl:if>
  <xsl:if test="'a11y-metadata' = $vp:mediaobject-accessibility">
    <meta property="a11y:accessibilitySummary" content="{normalize-space(.)}"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:textobject[not(db:phrase)]" mode="m:details">
  <xsl:if test="'details' = $vp:mediaobject-accessibility">
    <details>
      <xsl:apply-templates/>
    </details>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:audioobject|db:imageobject|db:textobject|db:videoobject"
              as="map(*)"
              mode="m:mediaobject-info">
  <xsl:variable name="datas" as="map(*)*">
    <xsl:apply-templates
        select="db:audiodata|db:imagedata|db:videodata|db:textdata"
        mode="m:mediaobject-info"/>
  </xsl:variable>

  <xsl:sequence select="map {
      'node': .,
      'datas': array { $datas },
      'content-types': array { distinct-values($datas ! .?content-type) },
      'extensions': array { distinct-values($datas ! .?extension) }
    }"/>
</xsl:template>

<xsl:template match="db:textobject[not(db:textdata)]" as="map(*)"
              mode="m:mediaobject-info"
              priority="50">
  <xsl:sequence select="map {
      'node': .,
      'datas': array { },
      'content-types': array { },
      'extensions': array { }
    }"/>
</xsl:template>

<xsl:template match="db:textobject[db:phrase]"
              mode="m:mediaobject-info"
              priority="100"/>

<xsl:template match="db:audiodata|db:imagedata|db:videodata|db:textdata"
              as="map(*)?"
              mode="m:mediaobject-info">

  <xsl:variable name="uris" as="map(*)">
    <xsl:apply-templates select="." mode="m:mediaobject-uris"/>
  </xsl:variable>

  <xsl:variable name="ext"
                select="tokenize($uris?fileref, '\.')[last()] ! concat('.', .)"/>

  <xsl:variable name="content-type" as="xs:string">
    <xsl:choose>
      <xsl:when test=".//svg:*">
        <xsl:sequence select="map:get($v:media-type-map, '.svg')"/>
      </xsl:when>
      <xsl:when test="empty($ext)">
        <xsl:sequence select="$v:media-type-default"/>
      </xsl:when>
      <xsl:when test="map:contains($v:media-type-map, $ext)">
        <xsl:sequence select="map:get($v:media-type-map, $ext)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$v:media-type-default"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="info" select="map {
      'node': .,
      'extension': $ext,
      'params': array { db:multimediaparam },
      'align': @align/string(),
      'valign': @valign/string(),
      'width': @width ! f:parse-length(.),
      'height': @depth ! f:parse-length(.),
      'contentwidth': @contentwidth ! f:parse-length(.),
      'contentheight': @contentdepth ! f:parse-length(.),
      'scale': @scale ! number(.),
      'scalefit': @scalefit/string() ! (. = '1'),
      'content-type': $content-type
    }"/>

  <xsl:sequence select="if ($ext = $v:mediaobject-exclude-extensions)
                        then ()
                        else map:merge(($uris, $info))"/>
</xsl:template>

<xsl:function name="f:mediaobject-input-base-uri" as="xs:string">
  <xsl:param name="node" as="element()"/>

  <!-- This is complicated. This used to be computed as a global variable,
       but in XSpec, the context item was sometimes missing, so it would
       fail. Outside of XSpec, I find that sometimes to document node
       is missing a base URI. (Maybe a bug in my XInclude function?)
       So in the short term, I'm using the base URI of the document element.
       Unless *that's* empty, in which case just use the base URI of
       the current node. But that's going to be wrong sometimes... -->

  <xsl:variable name="base"
                select="if (base-uri($node/root()/*) = '')
                        then base-uri($node)
                        else base-uri($node/root()/*)"/> 

<!--
  <xsl:message use-when="'mediaobject-uris' = $v:debug"
               select="'Mediaobject inp. base URI:',
                       if (empty($mediaobject-input-base-uri))
                       then $base
                       else resolve-uri($mediaobject-input-base-uri, $base)"/>
-->

  <xsl:sequence select="if (empty($mediaobject-input-base-uri))
                        then $base
                        else resolve-uri($mediaobject-input-base-uri, $base)"/>
</xsl:function>

<xsl:template match="db:audiodata|db:imagedata|db:videodata|db:textdata"
              as="map(*)?"
              mode="m:mediaobject-uris">

  <!-- imagedata, videodata, audiodata -->
  <xsl:variable name="uri" as="xs:string?">
    <xsl:choose>
      <xsl:when test="not(@fileref)">
        <xsl:sequence select="()"/>
      </xsl:when>
      <xsl:when test="empty($mediaobject-input-base-uri)">
        <xsl:sequence
            select="f:mediaobject-amend-uri(resolve-uri(@fileref, base-uri(.)))"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence
            select="f:mediaobject-amend-uri(
                       resolve-uri(@fileref, f:mediaobject-input-base-uri(.)))"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="base-uri"
                select="if (empty($mediaobject-input-base-uri))
                        then f:mediaobject-input-base-uri(.)
                        else resolve-uri($mediaobject-input-base-uri,
                                         f:mediaobject-input-base-uri(.))"/>

  <xsl:if test="'mediaobject-uris' = $v:debug">
    <xsl:message select="'&#10;  xml baseuri:', base-uri(.)"/>
    <xsl:if test="not(empty($mediaobject-input-base-uri))">
      <xsl:message select="'  m/o baseuri:', $mediaobject-input-base-uri"/>
    </xsl:if>
    <xsl:message select="'f:m/o baseuri:', f:mediaobject-input-base-uri(.)"/>
    <xsl:message select="'      fileref:', @fileref/string()"/>
    <xsl:message select="'     base-uri:', $base-uri"/>
    <xsl:message select="'          uri:', $uri"/>
    <xsl:message select="'     relative →',
                          (if (exists($uri))
                           then f:relative-path($base-uri, $uri)
                           else ())"/>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="exists($uri)">
      <xsl:sequence select="map {
        'fileref': @fileref/string(),
        'uri': $uri,
        'href': f:relative-path($base-uri, $uri),
        'properties': (if (exists($uri))
                       then f:object-properties($uri, exists(self::db:imagedata))
                       else ())
      }"/>
    </xsl:when>
    <xsl:otherwise>
      <!-- for an inline image, SVG for example -->
      <xsl:sequence select="map {
        'fileref': '',
        'uri': (),
        'href': (),
        'properties': ()
      }"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:function name="f:mediaobject-amend-uri" as="xs:string">
  <xsl:param name="uri" as="xs:string"/>

  <xsl:choose>
    <xsl:when test="exists(f:uri-scheme($uri)) and f:uri-scheme($uri) ne 'file'">
      <!-- It starts with a non-file: scheme, just assume it's absolute. -->
      <xsl:sequence select="$uri"/>
    </xsl:when>
    <xsl:when test="f:is-true($mediaobject-grouped-by-type)">
      <xsl:variable name="type" select="f:mediaobject-type($uri)"/>
      <xsl:sequence
          select="if (exists($type))
                  then string-join((tokenize($uri, '/')[position() lt last()],
                                    $type,
                                    tokenize($uri, '/')[position() eq last()]), '/')
                 else $uri"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$uri"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:mediaobject-type" as="xs:string?">
  <xsl:param name="uri" as="xs:string"/>

  <xsl:variable name="fn" select="tokenize($uri, '/')[last()]"/>

  <xsl:if test="contains($fn, '.')">
    <xsl:sequence select="tokenize($fn, '\.')[last()]"/>
  </xsl:if>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="db:mediaobject" mode="m:mediaobject-start">
  <xsl:variable name="pkind"
                select="string((db:* except (db:info|db:alt))[1] ! local-name(.))"/>
  <xsl:variable name="placement"
                select="if (map:get($v:mediaobject-details-placement, $pkind))
                        then map:get($v:mediaobject-details-placement, $pkind)
                        else map:get($v:mediaobject-details-placement, '_default')"/>

  <xsl:variable name="short" select="(db:alt|db:textobject[db:phrase])[1]"/>
  <xsl:variable name="long" select="db:textobject[not(db:phrase)]"/>

  <xsl:apply-templates select="$short" mode="m:details"/>
  <xsl:apply-templates select="db:info/db:meta" mode="m:mediaobject-start"/>

  <xsl:if test="$placement = 'before'
                and 'details' = $vp:mediaobject-accessibility
                and (db:imageobject|db:imageobjectco|db:audioobject|db:videoobject)">
    <xsl:apply-templates select="$long" mode="m:details"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:inlinemediaobject" mode="m:mediaobject-start">
  <xsl:variable name="short" select="(db:alt|db:textobject[db:phrase]/db:phrase)[1]"/>
  <xsl:variable name="long" select="db:textobject[not(db:phrase)]"/>
  <xsl:apply-templates select="$short" mode="m:details"/>
  <xsl:apply-templates select="db:info/db:meta" mode="m:mediaobject-start"/>
</xsl:template>

<xsl:template match="db:meta" mode="m:mediaobject-start">
  <meta property="{@name}" content="{(@content,normalize-space(.))[1]}"/>
</xsl:template>

<xsl:template match="*" mode="m:mediaobject-start">
  <xsl:apply-templates select="db:info/db:meta" mode="m:mediaobject-start"/>
</xsl:template>

<xsl:template match="db:mediaobject" mode="m:mediaobject-end">
  <xsl:variable name="pkind"
                select="string((db:* except (db:info|db:alt))[1] ! local-name(.))"/>
  <xsl:variable name="placement"
                select="if (map:get($v:mediaobject-details-placement, $pkind))
                        then map:get($v:mediaobject-details-placement, $pkind)
                        else map:get($v:mediaobject-details-placement, '_default')"/>

  <xsl:apply-templates select="db:caption"/>

  <xsl:if test="not($placement = 'before')
                and 'details' = $vp:mediaobject-accessibility
                and (db:imageobject|db:imageobjectco|db:audioobject|db:videoobject)">
    <xsl:apply-templates select="db:textobject[not(db:phrase)]"
                         mode="m:details"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:inlinemediaobject" mode="m:mediaobject-end">
</xsl:template>

<xsl:template match="*" mode="m:mediaobject-end">
</xsl:template>

</xsl:stylesheet>

footnotes.xsl

6 templates (1 used only in one other module), 2 functions (2 unused)

Instructions
Template match ≅ db:footnote
Mode: m:docbook
Matches: db:footnote
Template match ≅ db:footnoteref
Mode: m:docbook
Matches: db:footnoteref
Function fp:footnote-number#1($node as element(db:footnote)) as xs:integer
Unused
Template match ≅ db:footnote as xs:integer
Mode: mp:footnote-number
Matches: db:footnote
Template match ≅ db:footnote
Mode: m:footnote-number
Matches: db:footnote
Function fp:footnote-mark#2($number as xs:integer, $marks as xs:string+) as xs:string
Unused
Template match ≅ db:footnote
Mode: m:footnotes
Matches: db:footnote
Template t:table-footnotes match ≅
Used in: main.xsl
Mode: m:docbook
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xlink='http://www.w3.org/1999/xlink'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp m mp t xlink xs"
                version="3.0">

<xsl:template match="db:footnote">
  <sup id="{f:id(.)}-fref" db-footnote="{fp:footnote-number(.)}"
       class="footnote-number{
              if (ancestor::db:table or ancestor::db:informaltable)
              then ' table-footnote'
              else ()
              }">
    <a href="#{f:id(.)}-fnote">
      <xsl:apply-templates select="." mode="m:footnote-number"/>
    </a>
  </sup>
  <db-footnote id="{f:id(.)}-fnote" db-footnote="{fp:footnote-number(.)}">
    <xsl:apply-templates select="." mode="m:footnotes"/>
  </db-footnote>
</xsl:template>

<xsl:template match="db:footnoteref">
  <xsl:variable name="linkend"
                select="(@linkend,
                        if (starts-with(@xlink:href, '#'))
                        then substring-after(@xlink:href, '#')
                        else ())[1]"/>
  <xsl:variable name="target"
                select="if ($linkend)
                        then key('id', $linkend)[1]
                        else ()"/>
  <xsl:choose>
    <xsl:when test="empty($target)">
      <xsl:message select="'Footnote link to non-existent ID: ' || $linkend"/>
      <sup class="footnote-number">
        <xsl:sequence select="'[???' || $linkend || '???]'"/>
      </sup>
    </xsl:when>
    <xsl:otherwise>
      <sup id="{f:id(.)}-fref" db-footnote="{fp:footnote-number($target)}"
           class="footnote-number{
                  if ($target/ancestor::db:table or $target/ancestor::db:informaltable)
                  then ' table-footnote'
                  else ()
                  }">
        <a href="#{f:id($target)}-fnote">
          <xsl:apply-templates select="$target" mode="m:footnote-number"/>
        </a>
      </sup>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:function name="fp:footnote-number" as="xs:integer" cache="yes">
  <xsl:param name="node" as="element(db:footnote)"/>
  <xsl:apply-templates select="$node" mode="mp:footnote-number"/>
</xsl:function>

<xsl:template match="db:footnote" as="xs:integer" mode="mp:footnote-number">
  <xsl:variable name="nearest"
                select="(ancestor::db:table
                        |ancestor::db:informaltable)[last()]"/>

  <xsl:variable name="fnum" as="xs:string">
    <xsl:choose>
      <xsl:when test="empty($nearest)">
        <xsl:variable name="pfoot" select="count(preceding::db:footnote)"/>
        <xsl:variable name="ptfoot"
              select="count(preceding::db:footnote[ancestor::db:table])
                      + count(preceding::db:footnote[ancestor::db:informaltable])"/>
        <xsl:value-of select="$pfoot - $ptfoot + 1"/>
      </xsl:when>
      <xsl:when test="$nearest/self::db:informaltable">
        <xsl:number format="1" from="db:informaltable" level="any"/>
      </xsl:when>
      <xsl:when test="$nearest/self::db:table">
        <xsl:number format="1" from="db:table" level="any"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>Error: failed to enumerate footnote:</xsl:message>
        <xsl:message select="."/>
        <xsl:sequence select="1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:sequence select="xs:integer($fnum)"/>
</xsl:template>

<xsl:template match="db:footnote" mode="m:footnote-number">
  <xsl:variable name="nearest"
                select="(ancestor::db:table
                        |ancestor::db:informaltable)[last()]"/>

  <xsl:variable name="fnum" select="fp:footnote-number(.)"/>

  <xsl:variable name="marks"
                select="if (empty($nearest))
                        then $footnote-numeration
                        else $table-footnote-numeration"/>

  <xsl:sequence select="fp:footnote-mark($fnum, $marks)"/>
</xsl:template>

<xsl:function name="fp:footnote-mark" as="xs:string">
  <xsl:param name="number" as="xs:integer"/>
  <xsl:param name="marks" as="xs:string+"/>

  <xsl:choose>
    <xsl:when test="$number lt count($marks)">
      <xsl:sequence select="$marks[$number]"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:number value="$number" format="{$marks[count($marks)]}"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="db:footnote" mode="m:footnotes">
  <div class="footnote" id="{f:id(.)}-fnote">
    <div class="footnote-number">
      <sup db-footnote="{fp:footnote-number(.)}"
           class="footnote-number{
                  if (ancestor::db:table or ancestor::db:informaltable)
                  then ' table-footnote'
                  else ()
                  }">
        <a href="#{f:id(.)}-fref">
          <xsl:apply-templates select="." mode="m:footnote-number"/>
        </a>
      </sup>
    </div>
    <div class="footnote-body">
      <xsl:apply-templates/>
    </div>
  </div>
</xsl:template>

<xsl:template name="t:table-footnotes">
  <xsl:param name="footnotes" as="element(db:footnote)+" required="yes"/>

  <div class="footnotes table-footnotes">
    <xsl:apply-templates select="$footnotes" mode="m:footnotes"/>
  </div>
</xsl:template>

</xsl:stylesheet>

verbatim.xsl

1 import

highlight.xsl

2 templates, 5 functions (3 unused)

Instructions
Function f:syntax-highlight#1($source as xs:string)
Unused
Function f:syntax-highlight#2($source as xs:string, $language as xs:string)
Unused
Function f:syntax-highlight#3($source as xs:string, $options, $pyoptions as map(xs:string,xs:string)) as node()*
Function fp:syntax-highlight#3($source as xs:string, $options as map(xs:string,xs:string), $pyoptions as map(xs:string,xs:string)) as node()*
Function fp:syntax-highlight#0() as node()*
Template match ≅ element()
Mode: mp:fix-html
Matches: element()
Template match ≅ span
Mode: mp:fix-html
Matches: span
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:ext="http://docbook.org/extensions/xslt"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                default-mode="m:docbook"
                exclude-result-prefixes="dbe ext f fp h m mp v xs"
                version="3.0">

<xsl:function name="f:syntax-highlight">
  <xsl:param name="source" as="xs:string"/>
  <xsl:sequence select="f:syntax-highlight($source, map{}, map{})"/>
</xsl:function>

<xsl:function name="f:syntax-highlight">
  <xsl:param name="source" as="xs:string"/>
  <xsl:param name="language" as="xs:string"/>
  <xsl:sequence
      select="f:syntax-highlight($source, $language, map{})"/>
</xsl:function>

<xsl:function name="f:syntax-highlight" as="node()*">
  <xsl:param name="source" as="xs:string"/>
  <xsl:param name="options"/>
  <xsl:param name="pyoptions" as="map(xs:string,xs:string)"/>

  <!-- Special case for just the language option -->
  <xsl:variable name="options" as="map(xs:string,xs:string)">
    <xsl:choose>
      <xsl:when test="$options instance of xs:string">
        <xsl:sequence select="map { 'language': $options }"/>
      </xsl:when>
      <xsl:when test="$options instance of attribute()">
        <xsl:sequence select="map { 'language': string($options) }"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$options"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:message use-when="'highlight' = $v:debug">
    <xsl:text use-when="not(function-available('ext:pygmentize'))
                          or not(function-available('ext:pygmentize-available'))"
              >Syntax highlighting is not configured</xsl:text>
    <xsl:text use-when="function-available('ext:pygmentize')
                        and function-available('ext:pygmentize-available')
                        and not(ext:pygmentize-available())"
              >Syntax highlighting is not available</xsl:text>
    <xsl:text use-when="function-available('ext:pygmentize')
                        and function-available('ext:pygmentize-available')
                        and ext:pygmentize-available()"
              >Syntax highlighting is available</xsl:text>
  </xsl:message>

  <!-- N.B. xsl:value-of is intentional here; this function returns a node -->
  <xsl:value-of use-when="not(function-available('ext:pygmentize'))
                          or not(function-available('ext:pygmentize-available'))"
                select="$source"/>

  <xsl:if use-when="function-available('ext:pygmentize')
                    and function-available('ext:pygmentize-available')"
          test="ext:pygmentize-available()">
    <xsl:sequence select="fp:syntax-highlight($source, $options, $pyoptions)"/>
  </xsl:if>

  <xsl:if use-when="function-available('ext:pygmentize')
                    and function-available('ext:pygmentize-available')"
          test="not(ext:pygmentize-available())">
    <xsl:value-of select="$source"/>
  </xsl:if>
</xsl:function>

<xsl:function use-when="function-available('ext:pygmentize')"
              name="fp:syntax-highlight" as="node()*">
  <xsl:param name="source" as="xs:string"/>
  <xsl:param name="options" as="map(xs:string,xs:string)"/>
  <xsl:param name="pyoptions" as="map(xs:string,xs:string)"/>

  <xsl:variable name="string"
                select="ext:pygmentize($source, $options, $pyoptions)"/>
  <xsl:variable name="html">
    <xsl:apply-templates select="parse-xml($string)/node()" mode="mp:fix-html"/>
  </xsl:variable>

  <xsl:sequence select="$html/h:div/h:pre/node()"/>
</xsl:function>

<xsl:function use-when="not(function-available('ext:pygmentize'))"
              name="fp:syntax-highlight" as="node()*">
  <xsl:sequence select="error($dbe:INTERNAL-HIGHLIGHT-ERROR,
                              'Syntax highlighting called when not available')"/>
</xsl:function>

<xsl:mode name="mp:fix-html" on-no-match="shallow-copy"/>

<xsl:template match="element()" mode="mp:fix-html">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="@*,node()" mode="mp:fix-html"/>
  </xsl:element>
</xsl:template>

<xsl:template match="span[empty(node()) and empty(@*)]" mode="mp:fix-html"/>

</xsl:stylesheet>

20 templates (3 used only in one other module), 33 functions (33 used only in one other module), 2 variables (2 used only in one other module), 1 FIXME: comment

Instructions
Variable $v:invisible-characters
Used in: main.xsl
Template match ≅ db:programlistingco|db:screenc…
Mode: m:docbook
Matches: db:programlistingco, db:screenco
Template match ≅ db:address|db:classsynopsisinf…
Mode: mp:verbatim-pre-wrap
Matches: db:address, db:classsynopsisinfo, db:funcsynopsisinfo, db:literallayout, db:programlisting, db:screen, db:synopsis
Template match ≅ db:address|db:classsynopsisinf…
Mode: mp:verbatim-pre
Matches: db:address, db:classsynopsisinfo, db:funcsynopsisinfo, db:literallayout, db:programlisting, db:screen, db:synopsis
Template tp:filter-callouts match ≅
Used in: main.xsl
Mode: m:docbook
Template tp:verbatim-array match ≅ as array(*)
Function fp:make-lines($body as node()*, $trim-leading as xs:boolean, $trim-trailing as xs:boolean) as array(*)
Used in: main.xsl
Function fp:inject-array($lines as array(*), $inject as array(*)) as array(*)
Used in: main.xsl
Function fp:make-lines-array($body as node()*, $curline as item()*, $linearray as array(*)) as array(*)
Used in: main.xsl
Function fp:balance-markup#1($lines as array(*))
Used in: main.xsl
Function fp:balance-markup#3($lines as array(*), $open as element()*, $balanced as array(*))
Used in: main.xsl
Function fp:balance-line($open as element()*, $line as item()*) as item()*
Used in: main.xsl
Function fp:open($open as element()*, $line as item()*) as element()*
Used in: main.xsl
Function fp:unflatten#1($lines as array(*)) as array(*)
Used in: main.xsl
Function fp:unflatten#2($lines as array(*), $newlines as array(*)) as array(*)
Used in: main.xsl
Function fp:unflatten-line#1($line as item()*) as item()*
Used in: main.xsl
Function fp:unflatten-line#2($line as item()*, $newline as item()*) as item()*
Used in: main.xsl
Function fp:contains($line as item()*, $id as xs:string) as xs:boolean
Used in: main.xsl
Function fp:up-to($line as item()*, $id as xs:string)
Used in: main.xsl
Function fp:following($line as item()*, $id as xs:string)
Used in: main.xsl
Function fp:array-append($array as array(*), $seq as item()*)
Used in: main.xsl
Function fp:inject($lines as array(*), $item as item(), $lineno as xs:integer, $colno as xs:integer) as array(*)
Used in: main.xsl
Function fp:array-pad($lines as array(*), $lineno as xs:integer)
Used in: main.xsl
Function fp:inject-into-line($line as item()*, $item as item(), $colno as xs:integer) as item()*
Used in: main.xsl
Function fp:inject-into-chars($chars as item()*, $item as item(), $colno as xs:integer, $pos as xs:integer, $open as element()*) as item()*
Function fp:line-to-chars($line as item()*) as item()*
Used in: main.xsl
Template match ≅ element()
Mode: mp:flatten-markup
Matches: element()
Template match ≅ h:db-footnote
Mode: mp:flatten-markup
Matches: h:db-footnote
Template match ≅ * as map(xs:string, xs:string)
Mode: m:highlight-options
Matches: *
Template match ≅ * as map(xs:string, xs:string)
Mode: m:pygments-options
Matches: *
Function fp:injection-array($inject as item()?) as array(*)?
Function fp:validate-injection-array($array as array(*)) as array(*)
Template match ≅ db:area
Mode: mp:inj-map
Matches: db:area
Template match ≅ db:areaset
Mode: mp:inj-map
Matches: db:areaset
Template match ≅ *
Mode: mp:inj-map
Matches: *
Template match ≅ db:area
Mode: mp:callout-in-verbatim
Matches: db:area
Template match ≅ db:co
Mode: m:docbook
Matches: db:co
Template match ≅ db:co
Mode: m:callout-bug
Matches: db:co
Template match ≅ db:coref
Calls: t:inline
Mode: m:docbook
Matches: db:coref
Template match ≅ db:areaspec as xs:integer
Calls: fp:vpi()
Mode: m:callout-bug
Matches: db:areaspec
Template match ≅ db:area
Mode: m:callout-bug
Matches: db:area
Function fp:line-number($ln as xs:integer, $width as xs:integer, $display as xs:boolean) as xs:string
Used in: main.xsl
Function fp:verbatim-properties($context as element()) as map(*)
Function fp:vpi#2($context as element(), $property as xs:string) as item()*
Used in: main.xsl
Function fp:vpi#3($context as element(), $property as xs:string, $default as item()*) as item()*
Used in: main.xsl
Function f:verbatim-style($context as element()) as xs:string
Function f:verbatim-callout($context as element()) as xs:string*
Used in: main.xsl
Function f:verbatim-numbered($context as element()) as xs:boolean
Function f:verbatim-trim-trailing($context as element()) as xs:boolean
Function f:verbatim-trim-leading($context as element()) as xs:boolean
Function fp:verbatim-syntax-highlight($context as element()) as xs:boolean
Function f:verbatim-syntax-highlighter($context as element()) as xs:string
FIXME: comments
Comment
When counting characters in a line, these characters are invisible, just skip right past them. FIXME: get an authoritative list of invisible characters this is just a small selection of ones that occurred to me.
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:array="http://www.w3.org/2005/xpath-functions/array"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:g="http://docbook.org/ns/docbook/ghost"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xlink="http://www.w3.org/1999/xlink"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<!-- If there's any embedded markup in the verbatim environments, it has to be
     processed in the context of the original document, otherwise id/idref links
     and perhaps other cross-references won't be resolved correctly.

     Embellishments may then be added. There are three kinds of embellishments:

     1. Syntax highlighting (with Pygments)
     2. Line numbering
     3. Out-of-band callouts ("In-band" callouts, <co> elements, are just
        another kind of inline, they have no special role here.)

     There are two options: embellishments can be added by the stylesheets, or
     embellishments will be added by JavaScript in the browser (or more
     generally, "the rending engine"; it’s not impossible to imagine a paged
     media formatter that renders JavaScript, I just don’t know of any). It’s
     possible to imagine some ways to mix them, adding line numbers in the
     stylesheets, but leaving syntax highlighting to the browser, for example,
     but in practice it’s too complicated.

     First, let’s consider how the embellishments can be added by the
     stylesheets. There are four verbatim styles: lines, table, plain, and raw.
     (Table is a special-case of lines where the line numbers and actual lines
     are rendered in a table for the benefit of systems with less robust CSS
     support.)

     Which embellishments are possible depends on the style:

     |=======================+=======+=======+=======+=====|
     | Embellishment \ Style | Lines | Table | Plain | Raw |
     |=======================+=======+=======+=======+=====|
     | Pygments              | X     | X     | X     |     |
     | Line numbering        | X     | X     |       |     |
     | Out-of-band callouts  | X     | X     | X     |     |
     |=======================+=======+=======+=======+=====|

     Note that there's an unfortunate ambiguity here: adding information to
     identify callouts is also a kind of highlighting. I'm trying to use callout
     uniformly for that kind of markup, but for backwards compatibility the
     actual class value is still "highlight" in some cases. This is entirely
     distinct from syntax highlighting.
-->

<xsl:import href="highlight.xsl"/>

<!-- When counting characters in a line, these characters are
     invisible, just skip right past them.
     FIXME: get an authoritative list of invisible characters
     this is just a small selection of ones that occurred to me.
-->
<xsl:variable name="v:invisible-characters"
              select="('&#xFE00;', '&#xFE01;', '&#xFE02;', '&#xFE03;', 
                       '&#xFE04;', '&#xFE05;', '&#xFE06;', '&#xFE07;', 
                       '&#xFE08;', '&#xFE09;', '&#xFE0A;', '&#xFE0B;', 
                       '&#xFE0C;', '&#xFE0D;', '&#xFE0E;', '&#xFE0F;', 
                       '&#x200B;')"/>

<xsl:variable name="v:verbatim-properties" as="array(map(*))">
  <xsl:variable name="maps" as="map(*)+">
    <xsl:for-each select="('address', 'literallayout',
                           'programlisting', 'programlistingco',
                           'screen', 'screenco',
                           'synopsis', 'funcsynopsisinfo', 'classsynopsisinfo')">
      <xsl:variable name="style"
                    select="if (. = $v:verbatim-table-style)
                            then 'table'
                            else if (. = $v:verbatim-line-style)
                                 then 'lines'
                                 else if (. = $v:verbatim-plain-style)
                                      then 'plain'
                                      else 'raw'"/>
      <xsl:map>
        <xsl:map-entry key="'xpath'" select="'self::db:' || ."/>
        <xsl:map-entry key="'style'" select="$style"/>
        <xsl:map-entry key="'callout'"
                       select="if ($style = 'lines')
                               then $v:verbatim-callouts
                               else ()"/>
        <xsl:map-entry key="'numbered'"
                       select="string(. = $v:verbatim-numbered-elements)"/>
      </xsl:map>
    </xsl:for-each>
  </xsl:variable>
  <xsl:sequence select="array { $maps }"/>
</xsl:variable>

<xsl:template match="db:programlistingco|db:screenco">
  <xsl:variable name="style" select="f:verbatim-style((db:programlisting|db:screen)[1])"/>

  <xsl:variable name="areaspec" as="element(db:areaspec)?">
    <xsl:choose>
      <xsl:when test="empty(db:areaspec) or ($style = 'lines' or $style = 'table' or $style = 'plain')">
        <xsl:sequence select="db:areaspec"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="$message-level gt 0 and f:is-true($verbatim-embellishments)">
          <xsl:message>Out-of-band callouts are only supported for 'lines' and 'table' styles</xsl:message>
        </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:programlisting|db:screen">
      <xsl:with-param name="inject" select="$areaspec"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="db:calloutlist"/>
  </div>
</xsl:template>

<xsl:template match="db:programlisting|db:screen
                     |db:synopsis
                     |db:funcsynopsisinfo
                     |db:classsynopsisinfo
                     |db:literallayout|db:address">
  <xsl:param name="style" as="xs:string?" select="()"/>
  <xsl:param name="inject" as="element()?"/>

  <xsl:variable name="inject-array" select="fp:injection-array($inject)"/>

  <xsl:variable name="starting-line-number" as="xs:integer">
    <xsl:choose>
      <xsl:when test="@startinglinenumber">
        <xsl:sequence select="xs:integer(@startinglinenumber)"/>
      </xsl:when>
      <xsl:when test="@continuation = 'continues'">
        <xsl:variable name="name" select="node-name(.)"/>
        <xsl:variable name="prec" select="preceding::*[node-name(.) = $name][1]"/>
        <xsl:choose>
          <xsl:when test="empty($prec)">
            <xsl:sequence select="1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:variable name="formatted" as="element()">
              <xsl:apply-templates select="$prec"/>
            </xsl:variable>
            <xsl:sequence select="if ($formatted/@db-startinglinenumber
                                      and $formatted/@db-numberoflines)
                                  then
                                    xs:integer($formatted/@db-startinglinenumber)
                                    + xs:integer($formatted/@db-numberoflines)
                                  else
                                    1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="minlines" as="xs:integer">
    <xsl:choose>
      <xsl:when test="fp:vpi(., 'linenumbering-minlines',
                            fp:verbatim-properties(.)?minlines)">
        <xsl:sequence
            select="xs:integer(fp:vpi(., 'linenumbering-minlines',
                                     fp:verbatim-properties(.)?minlines))"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$v:verbatim-number-minlines"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="vconfig" as="map(*)">
    <xsl:map>
      <xsl:map-entry key="'style'" select="if ($style) then $style else f:verbatim-style(.)"/>
      <xsl:map-entry key="'callout'" select="f:verbatim-callout(.)"/>
      <xsl:map-entry key="'numbered'" select="f:verbatim-numbered(.)"/>
      <xsl:map-entry key="'trim-leading'" select="f:verbatim-trim-leading(.)"/>
      <xsl:map-entry key="'trim-trailing'" select="f:verbatim-trim-trailing(.)"/>
      <xsl:map-entry key="'highlighter'" select="f:verbatim-syntax-highlighter(.)"/>
      <xsl:map-entry key="'starting-line-number'" select="$starting-line-number"/>
      <xsl:map-entry key="'min-lines'" select="$minlines"/>
      <xsl:map-entry key="'inject'" select="$inject-array"/>
    </xsl:map>
  </xsl:variable>

  <xsl:message use-when="'verbatim' = $v:debug"
               select="'Verbatim:',
    local-name(.) || (@xml:id ! ('/' || .)),
    'style:' || $vconfig?style,
    'callout:' || string-join($vconfig?callout, ','),
    '&#10;         ',
    'numbered:' || $vconfig?numbered,
    'trim-leading:' || $vconfig?trim-leading,
    'trim-trailing:' || $vconfig?trim-trailing,
    'highlighter:' || $vconfig?highlighter,
    'start:' || $vconfig?starting-line-number,
    'minlines:' || $vconfig?min-lines,
    'inject:' || exists($vconfig?inject)"/>

  <xsl:variable name="formatted">
    <xsl:choose>
      <xsl:when test="not(f:is-true($verbatim-embellishments))">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:when test="$vconfig?style = ('lines', 'table', 'plain')">
        <xsl:choose>
          <xsl:when test="$vconfig?highlighter = 'pygments'">
            <xsl:variable name="options" as="map(xs:string,xs:string)">
              <xsl:apply-templates select="." mode="m:highlight-options"/>
            </xsl:variable>

            <xsl:variable name="pyoptions" as="map(xs:string,xs:string)">
              <xsl:apply-templates select="." mode="m:pygments-options"/>
            </xsl:variable>

            <xsl:sequence select="f:syntax-highlight(string(.),
                                  map:merge(($v:verbatim-syntax-highlight-options, $options)),
                                  map:merge(($v:verbatim-syntax-highlight-pygments-options, $pyoptions)))"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="$message-level gt 0 and $vconfig?highlighter = 'pygments'">
          <xsl:message>Pygments is only supported for 'lines', 'table', and 'plain' styles</xsl:message>
        </xsl:if>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="the-lines" as="map(*)*">
    <xsl:call-template name="tp:compute-lines">
      <xsl:with-param name="vconfig" select="$vconfig"/>
      <xsl:with-param name="formatted" select="$formatted"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:variable name="vconfig" select="map:put($vconfig, 'the-lines', $the-lines)"/>

  <xsl:apply-templates select="." mode="mp:verbatim-pre-wrap">
    <xsl:with-param name="vconfig" select="$vconfig"/>
    <xsl:with-param name="pre">
      <xsl:apply-templates select="." mode="mp:verbatim-pre">
        <xsl:with-param name="vconfig" select="$vconfig"/>
      </xsl:apply-templates>
    </xsl:with-param>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:programlisting|db:screen
                     |db:synopsis
                     |db:funcsynopsisinfo
                     |db:classsynopsisinfo
                     |db:literallayout|db:address"
              mode="mp:verbatim-pre-wrap">
  <xsl:param name="vconfig" as="map(*)"/>
  <xsl:param name="pre" required="true"/>

  <xsl:variable name="classes" as="xs:string+">
    <xsl:sequence select="'pre-wrap'"/>
    <xsl:sequence select="local-name() || '-wrap'"/>
    <xsl:if test="f:is-true($verbatim-embellishments) and $vconfig?highlighter = 'pygments'">
      <xsl:sequence select="'highlight'"/>
    </xsl:if>      
  </xsl:variable>

  <div class="{string-join($classes, ' ')}">
    <xsl:if test="f:is-true($verbatim-embellishments) and $vconfig?numbered">
      <xsl:attribute name="db-startinglinenumber" select="$vconfig?starting-line-number"/>
      <xsl:attribute name="db-numberoflines" select="count($vconfig?the-lines)"/>
    </xsl:if>
    <xsl:sequence select="$pre"/>
  </div>
</xsl:template>

<xsl:template match="db:programlisting|db:screen
                     |db:synopsis
                     |db:funcsynopsisinfo
                     |db:classsynopsisinfo
                     |db:literallayout|db:address"
              mode="mp:verbatim-pre">
  <xsl:param name="vconfig" as="map(*)"/>

  <xsl:variable name="use-code" select="not(self::db:literallayout or self::db:address)"/>

  <xsl:variable name="pre-attr" as="attribute()*">
    <xsl:apply-templates select="." mode="m:attributes">
      <xsl:with-param name="style" select="$vconfig?style"/>
      <xsl:with-param name="numbered"
                      select="f:is-true($verbatim-embellishments) and $vconfig?numbered"/>
      <xsl:with-param name="long"
                      select="f:is-true($verbatim-embellishments)
                              and count($vconfig?the-lines) ge $vconfig?min-lines"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="not(f:is-true($verbatim-embellishments)) or $vconfig?style = 'raw'">
      <pre>
        <xsl:sequence select="$pre-attr"/>
        <xsl:choose>
          <xsl:when test="$use-code">
            <code>
              <xsl:if test="$vconfig?highlighter = 'highlight.js' and not($vconfig?numbered)">
                <xsl:attribute name="class" select="'nohljsln'"/>
              </xsl:if>
              <xsl:apply-templates/>
            </code>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates/>
          </xsl:otherwise>
        </xsl:choose>
      </pre>
    </xsl:when>

    <xsl:when test="$vconfig?style = 'lines'">
      <pre>
        <xsl:sequence select="$pre-attr"/>
        <xsl:for-each select="$vconfig?the-lines">
          <span class="{string-join(.?classes, ' ')}">
            <xsl:if test="$vconfig?numbered">
              <xsl:attribute name="db-line" select=".?line-number"/>
            </xsl:if>
            <xsl:if test="$vconfig?numbered">
              <xsl:sequence select=".?formatted-number"/>
            </xsl:if>
            <span class="ld">
              <xsl:choose>
                <xsl:when test="$use-code">
                  <code>
                    <xsl:sequence select=".?line"/>
                  </code>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:sequence select=".?line"/>
                </xsl:otherwise>
              </xsl:choose>
            </span>
          </span>
          <xsl:if test="position() lt count($vconfig?the-lines)">
            <xsl:text>&#10;</xsl:text>
          </xsl:if>
        </xsl:for-each>
      </pre>
    </xsl:when>

    <xsl:when test="$vconfig?style = 'table'">
      <table class="verbatim">
        <tr>
          <xsl:if test="$vconfig?numbered">
            <td>
              <pre>
                <xsl:sequence select="$pre-attr"/>
                <xsl:for-each select="$vconfig?the-lines">
                  <span class="line">
                    <xsl:sequence select=".?formatted-number"/>
                  </span>
                  <xsl:if test="position() lt count($vconfig?the-lines)">
                    <xsl:text>&#10;</xsl:text>
                  </xsl:if>
                </xsl:for-each>
              </pre>
            </td>
          </xsl:if>
          <td>
            <pre>
              <xsl:sequence select="$pre-attr"/>
              <xsl:for-each select="$vconfig?the-lines">
                <span class="line">
                  <span class="ld">
                    <xsl:choose>
                      <xsl:when test="$use-code">
                        <code>
                          <xsl:sequence select=".?line"/>
                        </code>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:sequence select=".?line"/>
                      </xsl:otherwise>
                    </xsl:choose>
                  </span>
                </span>
                <xsl:if  test="position() lt count($vconfig?the-lines)">
                  <xsl:text>&#10;</xsl:text>
                </xsl:if>
              </xsl:for-each>
            </pre>
          </td>
        </tr>
      </table>
    </xsl:when>

    <xsl:when test="$vconfig?style = 'plain'">
      <xsl:variable name="content">
        <xsl:for-each select="$vconfig?the-lines">
          <xsl:sequence select=".?line"/>
          <xsl:if test="position() lt count($vconfig?the-lines)">
            <xsl:text>&#10;</xsl:text>
          </xsl:if>
        </xsl:for-each>
      </xsl:variable>

      <pre>
        <xsl:sequence select="$pre-attr"/>
        <xsl:choose>
          <xsl:when test="$use-code">
            <code>
              <xsl:if test="$vconfig?highlighter = 'highlight.js' and not($vconfig?numbered)">
                <xsl:attribute name="class" select="'nohljsln'"/>
              </xsl:if>
              <xsl:sequence select="$content"/>
            </code>
          </xsl:when>
          <xsl:otherwise>
            <xsl:sequence select="$content"/>
          </xsl:otherwise>
        </xsl:choose>
      </pre>
    </xsl:when>

    <xsl:otherwise>
      <xsl:message terminate="yes"
                   select="'Unrecognized verbatim style: ' || $vconfig?style"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
  
<!-- ============================================================ -->

<xsl:template name="tp:compute-lines" as="map(*)*">
  <xsl:param name="vconfig" as="map(*)"/>
  <xsl:param name="formatted" required="true"/>

  <xsl:variable name="starting-line-number" select="$vconfig?starting-line-number"/>

  <xsl:variable name="every-nth" as="xs:integer">
    <xsl:choose>
      <xsl:when test="fp:vpi(., 'linenumbering-everyNth',
                            fp:verbatim-properties(.)?everyNth)">
        <xsl:sequence
            select="xs:integer(fp:vpi(., 'linenumbering-everyNth',
                                     fp:verbatim-properties(.)?everyNth))"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$v:verbatim-number-every-nth"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="number-first" as="xs:boolean">
    <xsl:choose>
      <xsl:when test="fp:vpi(., 'linenumbering-first',
                            fp:verbatim-properties(.)?first)">
        <xsl:sequence
            select="f:is-true(fp:vpi(., 'linenumbering-first',
                                    fp:verbatim-properties(.)?first))"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$v:verbatim-number-first-line"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="lines" as="array(*)">
    <xsl:call-template name="tp:verbatim-array">
      <xsl:with-param name="vconfig" select="$vconfig"/>
      <xsl:with-param name="formatted" select="$formatted"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:variable name="numwidth"
                select="string-length(string(array:size($lines) + $starting-line-number - 1))"/>

  <xsl:for-each select="1 to array:size($lines)">
    <xsl:variable name="index" select="."/>
    <xsl:variable name="ln" select=". + $starting-line-number - 1"/>

    <!-- Make sure blank lines contain at least one space so that
         they don't get collapsed into oblivion by the renderer. -->
    <xsl:variable name="line"
                  select="if (count($lines(.)) = 1 and $lines(.) = '')
                          then ' '
                          else $lines(.)"/>

     <xsl:variable name="callouts"
                   select="$line[. instance of element()
                                 and contains-token(@class, 'callout-bug')]"/>

     <xsl:variable name="highlight" select="$vconfig?callout"/>

     <xsl:variable name="line" as="item()*">
       <xsl:for-each select="$line">
         <xsl:call-template name="tp:filter-callouts">
           <xsl:with-param name="highlight" select="$highlight"/>
           <xsl:with-param name="line" select="."/>
         </xsl:call-template>
       </xsl:for-each>
     </xsl:variable>

     <xsl:variable name="classes" as="xs:string+">
       <xsl:sequence select="'line'"/>
       <xsl:if test="(('lines' = $highlight) and $callouts[.[contains-token(@class, 'defcol')]])
                     or
                     (('lineranges' = $highlight
                       or 'lineranges-first' = $highlight
                       or 'lineranges-all' = $highlight)
                     and $callouts[.[contains-token(@class, 'linerange')]])">
         <xsl:sequence select="'highlight'"/>
         <xsl:sequence select="'line'||."/>
       </xsl:if>
     </xsl:variable>

    <xsl:map>
      <xsl:map-entry key="'line-number'" select="$ln"/>
      <xsl:map-entry key="'formatted-number'">
        <span class="ln">
          <xsl:sequence select="fp:line-number($ln, $numwidth,
                                 ($vconfig?numbered
                                   and (array:size($lines) ge $vconfig?min-lines)
                                        and (($index = 1 and $number-first)
                                             or ($ln mod $every-nth = 0))))"/>
          <xsl:if test="$verbatim-number-separator != ''">
            <span class="nsep">
              <xsl:sequence select="$verbatim-number-separator"/>
            </span>
          </xsl:if>
        </span>
      </xsl:map-entry>
      <xsl:map-entry key="'classes'" select="$classes"/>
      <xsl:map-entry key="'line'">
        <xsl:sequence select="$line"/>
      </xsl:map-entry>
    </xsl:map>
  </xsl:for-each>
</xsl:template>

<xsl:template name="tp:filter-callouts">
  <xsl:param name="highlight" as="xs:string*" required="yes"/>
  <xsl:param name="line" as="item()*" required="yes"/>

  <xsl:for-each select="$line">
    <xsl:choose>
      <xsl:when test=". instance of element()
                      and contains-token(@class, 'callout-bug')">
        <xsl:choose>
          <xsl:when test="'lineranges-first' = $highlight
                          and .[contains-token(@class, 'linerange')]">
            <xsl:sequence select="if (.[contains-token(@class, 'firstline')])
                                  then .
                                  else ()"/>
          </xsl:when>
          <xsl:when test="'lineranges-all' = $highlight
                          and .[contains-token(@class, 'linerange')]">
            <xsl:sequence select="."/>
          </xsl:when>
          <xsl:when test="'lines' = $highlight
                          and .[contains-token(@class, 'defcol')]">
            <xsl:sequence select="."/>
          </xsl:when>
          <xsl:when test="'linecolumn' = $highlight
                          and .[not(contains-token(@class, 'defcol'))
                                and not(contains-token(@class, 'linerange'))]">
            <xsl:sequence select="."/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:if test="$message-level gt 0">
              <xsl:message select="'Discarding callout #' || @id || ' for '
                                   || $highlight || ' highlighting.'"/>
            </xsl:if>
            <xsl:sequence select="()"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="tp:verbatim-array" as="array(*)">
  <xsl:param name="vconfig" as="map(*)"/>
  <xsl:param name="formatted" as="node()*" required="yes"/>

  <xsl:variable name="flattened">
    <xsl:apply-templates select="$formatted" mode="mp:flatten-markup"/>
  </xsl:variable>

  <xsl:variable name="lines"
                select="fp:make-lines($flattened/node(),
                                      $vconfig?trim-leading, $vconfig?trim-trailing)"/>
  <xsl:variable name="lines"
                select="fp:balance-markup($lines)"/>

  <xsl:variable name="lines"
                select="if (exists($vconfig?inject))
                        then fp:inject-array($lines, $vconfig?inject)
                        else $lines"/>

  <xsl:variable name="lines" select="fp:unflatten($lines)"/>

  <xsl:sequence select="$lines"/>
</xsl:template>

<xsl:function name="fp:make-lines" as="array(*)">
  <xsl:param name="body" as="node()*"/>
  <xsl:param name="trim-leading" as="xs:boolean"/>
  <xsl:param name="trim-trailing" as="xs:boolean"/>

  <xsl:variable name="lines" select="fp:make-lines-array($body, (), [])"/>

  <xsl:choose>
    <xsl:when test="array:size($lines) = 0">
      <xsl:sequence select="array { '' }"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="range" as="xs:integer+">
        <xsl:iterate select="1 to array:size($lines)">
          <xsl:param name="first" select="1"/>
          <xsl:param name="last" select="1"/>
          <xsl:on-completion select="($first, $last)"/>

          <xsl:variable name="line" select="array:get($lines, .)"/>
          <xsl:choose>
            <xsl:when test="count($line) = 1 and normalize-space($line) = ''">
              <xsl:next-iteration>
                <xsl:with-param name="first"
                                select="if ($trim-leading and $first eq .) then . + 1 else $first"/>
                <xsl:with-param name="last"
                                select="if ($trim-trailing) then $last else ."/>
              </xsl:next-iteration>
            </xsl:when>
            <xsl:otherwise>
              <xsl:next-iteration>
                <xsl:with-param name="first" select="$first"/>
                <xsl:with-param name="last" select="."/>
              </xsl:next-iteration>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:iterate>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="$range[1] gt $range[2]">
          <xsl:sequence select="array { }"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="array:subarray($lines, $range[1], $range[2] - $range[1] + 1)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:inject-array" as="array(*)">
  <xsl:param name="lines" as="array(*)" required="yes"/>
  <xsl:param name="inject" as="array(*)" required="yes"/>

  <xsl:choose>
    <xsl:when test="array:size($inject) = 0">
      <xsl:sequence select="$lines"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="newlines" as="array(*)"
           select="fp:inject($lines, $inject(1)?item, $inject(1)?line, $inject(1)?column)"/>
      <xsl:sequence select="fp:inject-array($newlines, array:remove($inject, 1))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:make-lines-array" as="array(*)">
  <xsl:param name="body" as="node()*"/>
  <xsl:param name="curline" as="item()*"/>
  <xsl:param name="linearray" as="array(*)"/>

  <xsl:variable name="car" select="subsequence($body, 1, 1)"/>
  <xsl:variable name="cdr" select="subsequence($body, 2)"/>

  <xsl:choose>
    <xsl:when test="empty($body)">
      <xsl:sequence select="if (empty($curline))
                            then $linearray
                            else array:append($linearray, $curline)"/>
    </xsl:when>
    <xsl:when test="$car/self::text()">
      <xsl:choose>
        <xsl:when test="contains($car, '&#10;')">
          <xsl:variable name="lines" select="tokenize($car, '&#10;')"/>
          <xsl:variable name="first" select="subsequence($lines, 1, 1)"/>
          <xsl:variable name="last"
                        select="if (count($lines) = 1)
                                then ()
                                else subsequence($lines, count($lines), 1)"/>
          <xsl:variable name="middle"
                        select="if (count($lines) lt 3)
                                then ()
                                else subsequence($lines, 2, count($lines) - 2)"/>
          <xsl:variable name="arr" select="array:append($linearray, ($curline, $first))"/>
          <xsl:variable name="arr" select="fp:array-append($arr, $middle)"/>
          <xsl:sequence select="fp:make-lines-array($cdr, $last, $arr)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="fp:make-lines-array($cdr, ($curline, $car), $linearray)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:make-lines-array($cdr, ($curline, $car), $linearray)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:balance-markup">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:sequence select="fp:balance-markup($lines, (), [])"/>
</xsl:function>

<xsl:function name="fp:balance-markup">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:param name="open" as="element()*"/>
  <xsl:param name="balanced" as="array(*)"/>

  <xsl:choose>
    <xsl:when test="array:size($lines) = 0">
      <xsl:sequence select="$balanced"/>
    </xsl:when>
    <xsl:otherwise>

      <!--
      <xsl:message>-OPEN: <xsl:sequence select="$open/@g:id/string()"/></xsl:message>
      <xsl:message> LINE: <xsl:sequence select="$lines(1)"/></xsl:message>
      -->

      <xsl:variable name="line" select="fp:balance-line($open, $lines(1))"/>

      <!--
      <xsl:message> BLNC: <xsl:sequence select="$line"/></xsl:message>
      -->

      <xsl:variable name="open" select="fp:open($open, ($open, $lines(1)))"/>

      <!--
      <xsl:message>+OPEN: <xsl:sequence select="$open/@g:id/string()"/></xsl:message>
      -->

      <xsl:sequence select="fp:balance-markup(array:remove($lines, 1),
                                              $open,
                                              array:append($balanced, $line))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:balance-line" as="item()*">
  <xsl:param name="open" as="element()*"/>
  <xsl:param name="line" as="item()*"/>

  <xsl:variable name="newline" as="item()*">
    <xsl:sequence select="$open"/>
    <xsl:sequence select="$line"/>
  </xsl:variable>

  <xsl:variable name="opened"
       select="$newline[. instance of element() and @g:id and not(@g:virtual)]"/>

  <xsl:variable name="closed"
       select="$newline[. instance of element()]/@g:start/string()"/>

  <!--
  <xsl:message> oped: <xsl:sequence select="$opened"/></xsl:message>
  <xsl:message> cled: <xsl:sequence select="$closed"/></xsl:message>
  -->

  <xsl:variable name="still-open" as="element()*">
    <xsl:for-each select="$open">
      <xsl:if test="not(@g:id = $closed)">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
    <xsl:for-each select="$opened">
      <xsl:if test="not(@g:id = $closed)">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <!--
  <xsl:message> BSTL: <xsl:sequence select="$still-open/@g:id/string()"/></xsl:message>
  -->

  <xsl:sequence select="$newline"/>
  <xsl:for-each select="reverse($still-open)">
    <xsl:element name="{node-name(.)}" namespace="{namespace-uri(.)}">
      <xsl:attribute name="g:start" select="@g:id"/>
      <xsl:attribute name="g:virtual" select="'true'"/>
    </xsl:element>
  </xsl:for-each>
</xsl:function>

<xsl:function name="fp:open" as="element()*">
  <xsl:param name="open" as="element()*"/>
  <xsl:param name="line" as="item()*"/>

  <xsl:variable name="closed"
       select="$line[. instance of element()]/@g:start/string()"/>

  <!--
  <xsl:message> CLOS: <xsl:sequence select="$closed"/></xsl:message>
  -->

  <xsl:variable name="still-open" as="element()*">
    <xsl:for-each select="$open">
      <xsl:if test="not(@g:id = $closed)">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <!--
  <xsl:message> STIL: <xsl:sequence select="$still-open"/></xsl:message>
  -->

  <xsl:variable name="new-open" as="element()*">
    <xsl:for-each select="$line[. instance of element()
                                and @g:id
                                and not(@g:virtual)]">
      <xsl:if test="not(@g:id = $closed)">
        <xsl:element name="{node-name(.)}" namespace="{namespace-uri(.)}">
          <xsl:copy-of select="@*"/>
          <xsl:attribute name="g:virtual" select="'true'"/>
        </xsl:element>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:sequence select="($still-open, $new-open)"/>
</xsl:function>

<xsl:function name="fp:unflatten" as="array(*)">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:sequence select="fp:unflatten($lines, [])"/>
</xsl:function>

<xsl:function name="fp:unflatten" as="array(*)">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:param name="newlines" as="array(*)"/>

  <xsl:choose>
    <xsl:when test="array:size($lines) = 0">
      <xsl:sequence select="$newlines"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="line"
                    select="fp:unflatten-line(array:get($lines,1))"/>
      <xsl:sequence select="fp:unflatten(array:remove($lines, 1),
                                         array:append($newlines, $line))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:unflatten-line" as="item()*">
  <xsl:param name="line" as="item()*"/>
  <!--
  <xsl:message>UNFLAT: <xsl:sequence select="$line"/></xsl:message>
  -->
  <xsl:sequence select="fp:unflatten-line($line, ())"/>
</xsl:function>

<xsl:function name="fp:unflatten-line" as="item()*">
  <xsl:param name="line" as="item()*"/>
  <xsl:param name="newline" as="item()*"/>

  <!--
  <xsl:message>LINE: <xsl:sequence select="$line"/></xsl:message>
  -->

  <xsl:choose>
    <xsl:when test="empty($line)">
      <xsl:sequence select="$newline"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="car" select="subsequence($line, 1, 1)"/>
      <xsl:variable name="cdr" select="subsequence($line, 2)"/>

      <!--
      <xsl:message>CAR: <xsl:sequence select="$car"/></xsl:message>
      <xsl:message>CDR: <xsl:sequence select="$cdr"/></xsl:message>
      -->

      <xsl:choose>
        <xsl:when test="$car instance of element()
                        and $car[@g:id]">
          <xsl:variable name="id" select="$car/@g:id"/>
          <!-- injecting items may have created more than one
               element with the same g:start value; we always
               want the "nearest" one.
          -->

          <!--
          <xsl:message>ID: <xsl:value-of select="$id"/></xsl:message>
          -->

          <xsl:variable name="rest"
                        select="if (fp:contains($line, $id))
                                then fp:following($line, $id)
                                else ()"/>
          <xsl:variable name="nodes"
                        select="if (fp:contains($line, $id))
                                then fp:up-to($cdr, $id)
                                else ()"/>

          <!--
          <xsl:message>REST: <xsl:value-of select="count($rest)"/>: <xsl:sequence select="$rest"/></xsl:message>
          <xsl:message>NODS: <xsl:value-of select="count($nodes)"/>: <xsl:sequence select="$nodes"/></xsl:message>
          -->

          <xsl:variable name="unflat" as="element()">
            <xsl:element name="{node-name($car)}"
                         namespace="{namespace-uri($car)}">
              <xsl:copy-of select="$car/@* except ($car/@g:*|$car/@id)"/>
              <xsl:if test="not($car/@g:virtual)">
                <xsl:copy-of select="$car/@id"/>
              </xsl:if>
              <xsl:sequence select="fp:unflatten-line($nodes)"/>
            </xsl:element>
          </xsl:variable>
          <xsl:sequence select="fp:unflatten-line($rest,
                                                  ($newline, $unflat))"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="fp:unflatten-line($cdr, ($newline, $car))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:contains" as="xs:boolean">
  <xsl:param name="line" as="item()*"/>
  <xsl:param name="id" as="xs:string"/>
  <xsl:variable name="found" as="element()*">
    <xsl:for-each select="$line">
      <xsl:if test=". instance of element()
                    and @g:start = $id">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <xsl:sequence select="exists($found)"/>
</xsl:function>

<xsl:function name="fp:up-to">
  <xsl:param name="line" as="item()*"/>
  <xsl:param name="id" as="xs:string"/>

  <xsl:variable name="car" select="subsequence($line, 1, 1)"/>
  <xsl:variable name="cdr" select="subsequence($line, 2)"/>

  <xsl:choose>
    <xsl:when test="empty($line)">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:when test="$car instance of element()
                    and $car/@g:start = $id">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="($car, fp:up-to($cdr, $id))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:following">
  <xsl:param name="line" as="item()*"/>
  <xsl:param name="id" as="xs:string"/>

  <xsl:variable name="car" select="subsequence($line, 1, 1)"/>
  <xsl:variable name="cdr" select="subsequence($line, 2)"/>

  <xsl:choose>
    <xsl:when test="empty($line)">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:when test="$car instance of element()
                    and $car/@g:start = $id">
      <xsl:sequence select="$cdr"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:following($cdr, $id)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:array-append">
  <!-- Surely there must be a better way? -->
  <xsl:param name="array" as="array(*)"/>
  <xsl:param name="seq" as="item()*"/>
  <xsl:sequence
      select="if (empty($seq))
                  then $array
                  else fp:array-append(array:append($array, subsequence($seq, 1, 1)),
                                       subsequence($seq, 2))"/>
</xsl:function>

<xsl:function name="fp:inject" as="array(*)">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:param name="item" as="item()"/>
  <xsl:param name="lineno" as="xs:integer"/>
  <xsl:param name="colno" as="xs:integer"/>

  <xsl:variable name="lines"
                select="if (array:size($lines) ge $lineno)
                        then $lines
                        else fp:array-pad($lines, $lineno)"/>

  <xsl:sequence
      select="fp:replace-element($lines, $lineno,
                 fp:inject-into-line($lines($lineno), $item, $colno))"/>
</xsl:function>

<xsl:function name="fp:array-pad">
  <xsl:param name="lines" as="array(*)"/>
  <xsl:param name="lineno" as="xs:integer"/>

  <xsl:choose>
    <xsl:when test="array:size($lines) ge $lineno">
      <xsl:sequence select="$lines"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:array-pad(array:append($lines, ''), $lineno)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:inject-into-line" as="item()*">
  <xsl:param name="line" as="item()*"/>
  <xsl:param name="item" as="item()"/>
  <xsl:param name="colno" as="xs:integer"/>

  <xsl:if test="$colno lt 0">
    <xsl:sequence select="error()"/>
  </xsl:if>

  <xsl:sequence
      select="fp:inject-into-chars(fp:line-to-chars($line), $item, $colno, 0, ())"/>
</xsl:function>

<xsl:function name="fp:inject-into-chars" as="item()*">
  <xsl:param name="chars" as="item()*"/>
  <xsl:param name="item" as="item()"/>
  <xsl:param name="colno" as="xs:integer"/>
  <xsl:param name="pos" as="xs:integer"/>
  <xsl:param name="open" as="element()*"/>

  <xsl:choose>
    <xsl:when test="$pos + 1 eq $colno">
      <xsl:for-each select="reverse($open)">
        <xsl:element name="{node-name(.)}" namespace="{namespace-uri(.)}">
          <xsl:attribute name="g:start" select="@g:id"/>
          <xsl:attribute name="g:virtual" select="'true'"/>
        </xsl:element>
      </xsl:for-each>

      <xsl:sequence select="$item"/>

      <xsl:for-each select="$open">
        <xsl:element name="{node-name(.)}" namespace="{namespace-uri(.)}">
          <xsl:attribute name="g:id" select="@g:id"/>
          <xsl:attribute name="g:virtual" select="'true'"/>
        </xsl:element>
      </xsl:for-each>
      <xsl:sequence select="$chars"/>
    </xsl:when>
    <xsl:when test="empty($chars)">
      <xsl:sequence
          select="($v:verbatim-space,
                   fp:inject-into-chars((), $item, $colno, $pos+1, $open))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="car" select="subsequence($chars, 1, 1)"/>
      <xsl:variable name="cdr" select="subsequence($chars, 2)"/>
      <xsl:choose>
        <xsl:when test="$car/self::text() and $car = $v:invisible-characters">
          <xsl:sequence
              select="($car, fp:inject-into-chars($cdr, $item, $colno, $pos, $open))"/>
        </xsl:when>
        <xsl:when test="$car/self::text()">
          <xsl:sequence
              select="($car, fp:inject-into-chars($cdr, $item, $colno, $pos+1, $open))"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="open" as="element()*"
                        select="if ($car/@g:start)
                                then subsequence($open, 1, count($open) - 1)
                                else ($open, $car)"/>
          <xsl:sequence
              select="($car, fp:inject-into-chars($cdr, $item, $colno, $pos, $open))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:line-to-chars" as="item()*">
  <xsl:param name="line" as="item()*"/>
  <xsl:choose>
    <xsl:when test="empty($line)">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="car" select="subsequence($line, 1, 1)"/>
      <xsl:variable name="cdr" select="subsequence($line, 2)"/>
      <xsl:choose>
        <xsl:when test="$car instance of xs:string or $car/self::text()">
          <xsl:for-each select="1 to string-length($car)">
            <!-- Use value-of so that they're text nodes -->
            <xsl:value-of select="substring($car, ., 1)"/>
          </xsl:for-each>
          <xsl:sequence select="fp:line-to-chars($cdr)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="($car, fp:line-to-chars($cdr))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:mode name="mp:flatten-markup" on-no-match="shallow-copy"/>

<xsl:template match="element()" mode="mp:flatten-markup">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:attribute name="g:id" select="generate-id(.)"/>
  </xsl:copy>
  <xsl:apply-templates select="node()" mode="mp:flatten-markup"/>
  <xsl:copy>
    <xsl:attribute name="g:start" select="generate-id(.)"/>
    <xsl:attribute name="g:virtual" select="'true'"/>
  </xsl:copy>
</xsl:template>

<!-- Don't flatten footnotes, they get rendered out-of-line -->
<xsl:template match="h:db-footnote" mode="mp:flatten-markup">
  <xsl:copy>
    <xsl:sequence select="@*,node()"/>
  </xsl:copy>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" as="map(xs:string, xs:string)" mode="m:highlight-options">
  <xsl:map>
    <xsl:if test="@language">
      <xsl:map-entry key="'language'" select="string(@language)"/>
    </xsl:if>
  </xsl:map>
</xsl:template>

<xsl:template match="*" as="map(xs:string, xs:string)" mode="m:pygments-options">
  <xsl:variable name="pyattr"
                select="f:pi-attributes(processing-instruction('db-pygments'))"/>
  <xsl:map>
    <xsl:for-each select="$pyattr/@*">
      <xsl:map-entry key="local-name(.)" select="string(.)"/>
    </xsl:for-each>
  </xsl:map>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="fp:injection-array" as="array(*)?">
  <xsl:param name="inject" as="item()?"/>

  <xsl:choose>
    <xsl:when test="empty($inject)">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:when test="$inject instance of array(*)">
      <xsl:sequence select="fp:validate-injection-array($inject)"/>
    </xsl:when>
    <xsl:when test="$inject instance of element(db:areaspec)">
      <xsl:variable name="maps" as="map(*)*">
        <xsl:apply-templates select="$inject/*" mode="mp:inj-map"/>
      </xsl:variable>
      <xsl:sequence select="fp:validate-injection-array(array { $maps })"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="error($dbe:INVALID-INJECT, 
                                 'Invalid type for $inject', $inject)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:validate-injection-array" as="array(*)">
  <xsl:param name="array" as="array(*)"/>

  <xsl:variable name="valid-maps" as="map(*)*">
    <xsl:for-each select="1 to array:size($array)">
      <xsl:variable name="map" select="$array(.)"/>
      <xsl:if test="empty($map?line)
                    or empty($map?column)
                    or empty($map?item)
                    or not($map?line castable as xs:integer)
                    or not($map?column castable as xs:integer)">
        <xsl:sequence select="error($dbe:INVALID-INJECT, 
                                    'Invalid map in $inject', $map)"/>
      </xsl:if>
      <xsl:sequence select="map { 'line': xs:integer($map?line),
                                  'column': xs:integer($map?column),
                                  'item': $map?item }"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="sorted-maps" as="map(*)*">
    <xsl:for-each select="$valid-maps">
      <xsl:sort select=".?line" order="descending"/>
      <xsl:sort select=".?column" order="descending"/>
      <xsl:sequence select="."/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:sequence select="array { $sorted-maps }"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="db:area" mode="mp:inj-map">
  <xsl:if test="@units and not(@units = ('linecolumn', 'linerange'))">
    <xsl:sequence select="error($dbe:INVALID-INJECT, 
                                'Invalid callout area: '
                                || @units || ' unsupported.')"/>
  </xsl:if>

  <xsl:variable name="coords"
                select="tokenize(normalize-space(@coords))"/>

  <xsl:if test="count($coords) lt 1 or count($coords) gt 2">
    <xsl:sequence select="error($dbe:INVALID-INJECT, 
                                'Invalid callout area: unparseable coordinates')"/>
  </xsl:if>

  <xsl:variable name="line" select="$coords[1]"/>
  <xsl:variable name="column" select="if (count($coords) gt 1
                                          and not(@units = 'linerange'))
                                      then $coords[2]
                                      else $callout-default-column"/>

  <xsl:variable name="content" as="node()+">
    <xsl:apply-templates select="." mode="mp:callout-in-verbatim">
      <xsl:with-param name="line" select="$line"/>
      <xsl:with-param name="column" select="$column"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="@units = 'linerange'">
      <xsl:variable name="fline" select="xs:integer($coords[1])"/>
      <xsl:variable name="lline" select="if ($coords[2])
                                         then xs:integer($coords[2])
                                         else $fline"/>
      <xsl:variable name="firstcontent" as="element()">
        <xsl:element name="span" namespace="http://www.w3.org/1999/xhtml">
          <xsl:copy-of select="$content/@* except $content/@class"/>
          <xsl:attribute name="class"
                         select="concat($content/@class, ' firstline')"/>
          <xsl:copy-of select="$content/node()"/>
        </xsl:element>
      </xsl:variable>

      <xsl:for-each select="$fline to $lline">
        <xsl:sequence select="map { 'line': .,
                                    'linerange': true(),
                                    'column': $column,
                                    'item': if (. = $fline)
                                            then $firstcontent
                                            else $content }"/>
      </xsl:for-each>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="map { 'line': $line,
                                  'column': $column,
                                  'item': $content }"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:areaset" mode="mp:inj-map">
  <xsl:apply-templates mode="mp:inj-map"/>
</xsl:template>

<xsl:template match="*" mode="mp:inj-map">
  <xsl:sequence select="error($dbe:INVALID-INJECT, 
                              'Invalid type for $inject: '
                              || local-name(.))"/>
</xsl:template>

<xsl:mode name="mp:inj-map" on-no-match="shallow-skip"/>

<!-- ============================================================ -->

<xsl:template match="db:area" mode="mp:callout-in-verbatim">
  <xsl:param name="line" select="()"/>
  <xsl:param name="column" select="()"/>

  <xsl:variable name="class" as="xs:string+">
    <xsl:sequence select="'callout-bug'"/>
    <xsl:sequence select="if (@otherunits)
                          then 'other ' || @otherunits
                          else if (@units)
                               then @units
                               else 'linecolumn'"/>
    <xsl:sequence select="if ((not(@units) or @units='linecolumn')
                              and count(tokenize(@coords)) ne 2)
                          then 'defcol'
                          else ()"/>
  </xsl:variable>

  <a id="{ if (parent::db:areaset and empty(preceding-sibling::*))
           then f:generate-id(parent::*)
           else f:generate-id(.) }"
     class="{ string-join($class, ' ') }">
    <xsl:if test="@linkends">
      <xsl:attribute name="href"
                     select="'#' || tokenize(normalize-space(@linkends), '\s+')[1]"/>
    </xsl:if>
    <xsl:if test="exists($line)">
      <xsl:attribute name="db-line" select="$line"/>
    </xsl:if>
    <xsl:if test="exists($column)">
      <xsl:attribute name="db-column" select="$column"/>
    </xsl:if>
    <xsl:if test="db:alt">
      <xsl:attribute name="title" select="string(db:alt)"/>
    </xsl:if>
    <xsl:apply-templates select="." mode="m:callout-bug"/>
  </a>
</xsl:template>

<xsl:template match="db:co">
  <a>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:callout-bug"/>
  </a>
</xsl:template>

<xsl:template match="db:co" mode="m:callout-bug">
  <xsl:variable name="conum">
    <xsl:choose>
      <xsl:when test="@label and @label castable as xs:decimal">
        <xsl:sequence select="@label"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:number count="db:co"
                    level="any"
                    format="1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:value-of select="codepoints-to-string($callout-unicode-start + xs:integer($conum))"/>
</xsl:template>

<xsl:template match="db:coref">
  <xsl:variable name="coid" as="xs:string?">
    <xsl:choose>
      <xsl:when test="@linkend">
        <xsl:sequence select="@linkend/string()"/>
      </xsl:when>
      <xsl:when test="@xlink:href[starts-with(., '#')]">
        <xsl:sequence select="substring(@xlink:href, 2)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="empty($coid)">
      <xsl:if test="$message-level gt 0">
        <xsl:message>Cannot find callout ID on coref</xsl:message>
      </xsl:if>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="co" select="key('id', $coid)"/>
      <xsl:if test="count($co) gt 1">
        <xsl:if test="$message-level gt 0">
          <xsl:message>Callout ID on coref is not unique: <xsl:sequence select="$coid"/></xsl:message>
        </xsl:if>
      </xsl:if>
      <xsl:variable name="co" select="$co[1]"/>
      <xsl:choose>
        <xsl:when test="empty($co)">
          <xsl:if test="$message-level gt 0">
            <xsl:message>Callout ID on coref does not exist: <xsl:sequence select="$coid"/></xsl:message>
          </xsl:if>
          <xsl:call-template name="t:inline"/>
        </xsl:when>
        <xsl:when test="not($co/self::db:co)">
          <xsl:if test="$message-level gt 0">
            <xsl:message>
              <xsl:text>Callout ID on coref does not point to a co: </xsl:text>
              <xsl:sequence select="$coid"/>
            </xsl:message>
          </xsl:if>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="$co"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:areaspec" mode="m:callout-bug" as="xs:integer">
  <xsl:param name="continues" as="xs:boolean" select="false()"/>

  <xsl:variable name="starting-pi"
                select="fp:vpi(., 'starting-callout-number')"/>

  <xsl:variable name="start-at-start" as="xs:integer">
    <xsl:choose>
      <xsl:when test="empty($starting-pi)">
        <xsl:sequence select="1"/>
      </xsl:when>
      <xsl:when test="$starting-pi[1] castable as xs:integer">
        <xsl:sequence select="$starting-pi[1] cast as xs:integer"/>
      </xsl:when>
      <xsl:when test="$starting-pi[1] = 'continues' and preceding::db:areaspec">
        <xsl:apply-templates select="preceding::db:areaspec[1]" mode="m:callout-bug">
          <xsl:with-param name="continues" select="true()"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="$starting-pi[1] = 'continues'">
        <xsl:sequence select="1"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="$message-level gt 0">
          <xsl:message select="'Unsupported starting-callout-number: ', $starting-pi"/>
        </xsl:if>
        <xsl:sequence select="1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:sequence select="if ($continues)
                        then $start-at-start + count(*)
                        else $start-at-start"/>
</xsl:template>

<xsl:template match="db:area" mode="m:callout-bug">
  <xsl:variable name="start-co-number" as="xs:integer">
    <xsl:apply-templates select="ancestor::db:areaspec[1]" mode="m:callout-bug"/>
  </xsl:variable>

  <xsl:variable name="num"
                select="if (parent::db:areaset)
                        then count(parent::*/preceding-sibling::*) + $start-co-number
                        else count(preceding-sibling::*) + $start-co-number"/>
  <xsl:value-of select="codepoints-to-string(9311 + $num)"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="fp:line-number" as="xs:string">
  <xsl:param name="ln" as="xs:integer"/>
  <xsl:param name="width" as="xs:integer"/>
  <xsl:param name="display" as="xs:boolean"/>

  <!-- Padding for numbers in line numbering -->
  <xsl:variable name="vp:padding" select="'                        '"/>

  <xsl:choose>
    <xsl:when test="$display">
      <xsl:sequence select="substring($vp:padding, 1, $width - string-length(string($ln)))
                            || $ln
                            || substring($vp:padding, 1, 1)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="substring($vp:padding, 1, $width + 1)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:verbatim-properties" as="map(*)">
  <xsl:param name="context" as="element()"/>

  <xsl:variable name="style"
                select="fp:properties($context, $v:verbatim-properties)"/>

  <xsl:sequence
      select="if (empty($style))
              then map { 'style': $verbatim-style-default,
                         'numbered': (local-name($context) = $v:verbatim-numbered-elements),
                         'callout': $v:verbatim-callouts }
              else $style"/>
</xsl:function>

<!-- This special version of pi handles the case where a programlisting
     in a programlistingco gets its ?db PIs from the parent programlistingco -->
<xsl:function name="fp:vpi" as="item()*" cache="yes">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="property" as="xs:string"/>
  <xsl:sequence select="fp:vpi($context, $property, ())"/>
</xsl:function>

<xsl:function name="fp:vpi" as="item()*" cache="yes">
  <xsl:param name="context" as="element()"/>
  <xsl:param name="property" as="xs:string"/>
  <xsl:param name="default" as="item()*"/>

  <!-- Where are we going to look? -->
  <xsl:choose>
    <xsl:when test="f:pi($context, $property)">
      <xsl:sequence select="f:pi($context, $property)"/>
    </xsl:when>
    <xsl:when test="f:pi(($context/parent::db:screenco|$context/parent::db:programlistingco), $property)">
      <xsl:sequence select="f:pi(($context/parent::db:screenco|$context/parent::db:programlistingco), $property)"/>
    </xsl:when>
    <xsl:when test="f:pi($context/root()/*, $property)">
      <xsl:sequence select="f:pi($context/root()/*, $property)"/>
    </xsl:when>
    <xsl:when test="f:pi($context/root()/*/db:info, $property)">
      <xsl:sequence select="f:pi($context/root()/*/db:info, $property)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$default"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:verbatim-style" as="xs:string">
  <xsl:param name="context" as="element()"/>
  <xsl:sequence
      select="fp:vpi($context, 'verbatim-style', fp:verbatim-properties($context)?style)"/>
</xsl:function>

<xsl:function name="f:verbatim-callout" as="xs:string*">
  <xsl:param name="context" as="element()"/>

  <xsl:variable name="value" as="xs:string*"
                select="fp:vpi($context, 'verbatim-highlight', fp:verbatim-properties($context)?callout)"/>

  <xsl:for-each select="$value">
    <xsl:sequence select="if (contains(., ','))
                          then tokenize(., ',\s*') ! normalize-space(.)
                          else ."/>
  </xsl:for-each>
</xsl:function>

<xsl:function name="f:verbatim-numbered" as="xs:boolean">
  <xsl:param name="context" as="element()"/>

  <xsl:choose>
    <xsl:when test="not(f:is-true($verbatim-embellish-linenumbers))">
      <xsl:sequence select="false()"/>
    </xsl:when>
    <xsl:when test="$context/@linenumbering">
      <xsl:sequence select="$context/@linenumbering = 'numbered'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence
          select="f:is-true(fp:vpi($context, 'verbatim-numbered',
                            fp:verbatim-properties($context)?numbered))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="f:verbatim-trim-trailing" as="xs:boolean">
  <xsl:param name="context" as="element()"/>
  <xsl:sequence select="f:is-true(fp:vpi($context, 'verbatim-trim-trailing',
                                        $verbatim-trim-trailing-blank-lines))"/>
</xsl:function>

<xsl:function name="f:verbatim-trim-leading" as="xs:boolean">
  <xsl:param name="context" as="element()"/>
  <xsl:sequence select="f:is-true(fp:vpi($context, 'verbatim-trim-leading',
                                        $verbatim-trim-leading-blank-lines))"/>
</xsl:function>

<xsl:function name="fp:verbatim-syntax-highlight" as="xs:boolean">
  <xsl:param name="context" as="element()"/>

  <xsl:variable name="default"
                select="('*', $context/@language) = $v:verbatim-syntax-highlight-languages"/>

  <xsl:sequence
      select="f:is-true(fp:vpi($context, 'syntax-highlight', string($default)))"/>
</xsl:function>

<xsl:function name="f:verbatim-syntax-highlighter" as="xs:string">
  <xsl:param name="context" as="element()"/>

  <xsl:variable name="global-highlighter" select="f:global-syntax-highlighter($context)"/>

  <xsl:variable name="highlighter"
                select="fp:vpi($context, 'syntax-highlighter',
                              $global-highlighter)"/>

  <xsl:choose>
    <xsl:when test="not(fp:verbatim-syntax-highlight($context))">
      <xsl:sequence select="'none'"/>
    </xsl:when>
    <xsl:when test="$highlighter = $global-highlighter
                    or $highlighter = 'none'
                    or $highlighter = 'pygments'">
      <xsl:sequence select="$highlighter"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="$message-level gt 0 and f:is-true($verbatim-embellishments)">
        <xsl:message select="'The', $highlighter, 'syntax highlighter cannot be used as an override'"/>
      </xsl:if>
      <xsl:sequence select="$global-highlighter"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

</xsl:stylesheet>

tablecals.xsl

8 templates (2 used only in one other module), 37 functions (37 used only in one other module), 1 FIXME: comment

Instructions
Template match ≅ db:table
Template match ≅ db:informaltable
Mode: m:docbook
Matches: db:informaltable
Template match ≅ db:tgroup
Mode: m:docbook
Matches: db:tgroup
Template match ≅ db:tbody|db:tfoot|db:thead
Mode: m:docbook
Matches: db:tbody, db:tfoot, db:thead
Template match ≅ db:row
Template tp:cell match ≅
Used in: main.xsl
Mode: m:docbook
Template tp:cals-colspec match ≅
Function fcals:colspec-for-column($tgroup as element(), $colnum as xs:integer) as element()?
Used in: main.xsl
Function fp:colspec-for-colnum($colspecs as element()*, $colnum as xs:integer, $curcol as xs:integer) as element()?
Used in: main.xsl
Function fcals:cell($row as element(db:row), $column as xs:integer, $overhang as array(xs:integer), $cells as map(*)*) as map(*)
Used in: main.xsl
Function fcals:overhang-into-row($row as element(db:row)) as array(xs:integer)
Used in: main.xsl
Function fcals:overhang($row as element(db:row)) as array(xs:integer)
Used in: main.xsl
Function fcals:cell-overhang($colmap as map(xs:integer, node()), $column as xs:integer)
Used in: main.xsl
Function fcals:next-empty-cell($column as xs:integer, $overhang as array(xs:integer))
Function fcals:decrement-overhang($overhang as array(xs:integer)) as array(xs:integer)
Used in: main.xsl
Function fcals:column-number($node as element(), $overhang as array(xs:integer)) as xs:integer
Used in: main.xsl
Function fcals:colspec-column-number($colspec as element(db:colspec)) as xs:integer
Used in: main.xsl
Function fcals:colspec($node as element(), $colname as xs:string) as element(db:colspec)
Function fcals:spanspec($node as element(), $spanname as xs:string) as element(db:spanspec)
Function fcals:colspan($node as element(), $colnum as xs:integer) as xs:integer
Used in: main.xsl
Function fcals:rowspan($node as element(), $colnum as xs:integer) as xs:integer
Used in: main.xsl
Function fcals:cell-decoration($row as element(db:row), $cell as element()?, $column as xs:integer) as map(*)
Used in: main.xsl
Function fcals:colsep($row as element(db:row), $node as element()?, $colnum as xs:integer) as xs:boolean
Used in: main.xsl
Function fcals:colsep-colspec($node as element(), $name as xs:string) as xs:boolean
Used in: main.xsl
Function fcals:colsep-spanspec($node as element(), $name as xs:string) as xs:boolean
Used in: main.xsl
Function fcals:empty-cell-colsep($row as element(db:row), $cell as map(*)) as xs:boolean
Used in: main.xsl
Function fcals:rowsep($row as element(db:row), $node as element()?, $colnum as xs:integer) as xs:boolean
Used in: main.xsl
Function fcals:rowsep-colspec($node as element(), $name as xs:string) as xs:boolean
Used in: main.xsl
Function fcals:rowsep-spanspec($node as element(), $name as xs:string) as xs:boolean
Used in: main.xsl
Function fcals:align($row as element(db:row), $node as element()?, $colnum as xs:integer) as xs:string?
Used in: main.xsl
Function fcals:align-colspec($node as element(), $name as xs:string) as xs:string?
Used in: main.xsl
Function fcals:align-spanspec($node as element(), $name as xs:string) as xs:string?
Used in: main.xsl
Function fcals:char($row as element(db:row), $node as element()?, $colnum as xs:integer) as xs:string?
Used in: main.xsl
Function fcals:char-colspec($node as element(), $name as xs:string) as xs:string?
Used in: main.xsl
Function fcals:char-spanspec($node as element(), $name as xs:string) as xs:string?
Used in: main.xsl
Function fcals:valign($row as element(db:row), $node as element()?) as xs:string?
Used in: main.xsl
Function f:cals-rowsep($row as element(db:row), $cell as map(*), $last-row-rowsep as xs:boolean) as xs:string?
Used in: main.xsl
Function f:cals-colsep($row as element(db:row), $cell as map(*), $last-col-colsep as xs:boolean) as xs:string?
Used in: main.xsl
Function fcals:empty-cell-rowsep($row as element(db:row)) as xs:boolean
Used in: main.xsl
Function fp:only-initial-pis($nodes as node()*) as processing-instruction()*
Used in: main.xsl
Function fp:align-char-pad($text as xs:string, $width as xs:integer, $pad as xs:string) as xs:string
Used in: main.xsl
Function fcals:table-columns($node as element()) as xs:integer
Used in: main.xsl
Function fcals:tgroup($node as element()) as element()
Used in: main.xsl
Function fcals:zeros($row as element(db:row))
Used in: main.xsl
FIXME: comments
Comment
FIXME: optimize this
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:array="http://www.w3.org/2005/xpath-functions/array"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fcals="http://docbook.org/ns/docbook/functions/private/cals"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="db:table[db:tgroup]">
  <xsl:variable name="tp"
                select="if (parent::db:formalgroup)
                        then $v:formalgroup-nested-object-title-placement
                        else $v:formal-object-title-placement"/>

  <xsl:variable name="placement"
                select="if (exists(f:pi(., 'title-placement')))
                        then f:pi(., 'title-placement')[1]
                        else if (map:get($tp, local-name(.)))
                             then map:get($tp, local-name(.))
                             else map:get($tp, '_default')"/>

  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>

    <xsl:if test="$placement = 'before'">
      <xsl:apply-templates select="." mode="m:generate-titlepage"/>
      <xsl:if test="'details' = $vp:table-accessibility">
        <xsl:apply-templates select="db:textobject[not(db:phrase)]" mode="m:details"/>
      </xsl:if>
    </xsl:if>

    <xsl:apply-templates select="db:tgroup"/>
    <xsl:if test=".//db:footnote">
      <xsl:call-template name="t:table-footnotes">
        <xsl:with-param name="footnotes" select=".//db:footnote"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:apply-templates select="db:caption"/>

    <xsl:if test="not($placement = 'before')">
      <xsl:if test="'details' = $vp:table-accessibility">
        <xsl:apply-templates select="db:textobject[not(db:phrase)]" mode="m:details"/>
      </xsl:if>
      <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    </xsl:if>
  </div>
</xsl:template>

<xsl:template match="db:informaltable[db:tgroup]">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:if test="'details' = $vp:table-accessibility">
      <xsl:apply-templates select="db:textobject[not(db:phrase)]" mode="m:details"/>
    </xsl:if>
    <xsl:apply-templates select="db:tgroup"/>
    <xsl:if test=".//db:footnote">
      <xsl:call-template name="t:table-footnotes">
        <xsl:with-param name="footnotes" select=".//db:footnote"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:apply-templates select="db:caption"/>
  </div>
</xsl:template>

<xsl:template match="db:tgroup">
  <table>
    <xsl:if test="'summary' = $vp:table-accessibility">
      <xsl:apply-templates select="../db:textobject[db:phrase]" mode="m:details"/>
    </xsl:if>
    <xsl:if test="db:colspec[@colwidth]">
      <xsl:call-template name="tp:cals-colspec"/>
    </xsl:if>
    <xsl:apply-templates select="db:thead"/>
    <xsl:apply-templates select="db:tfoot"/>
    <xsl:apply-templates select="db:tbody"/>
  </table>
</xsl:template>

<xsl:template match="db:tbody|db:thead|db:tfoot">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:template match="db:row">
  <xsl:variable name="row" select="."/>
  <xsl:variable name="overhang" select="fcals:overhang-into-row($row)"/>

  <xsl:variable name="cells" as="map(*)*">
    <xsl:for-each select="*">
      <xsl:variable name="colnum" select="fcals:column-number(., $overhang)"/>
      <xsl:variable name="colspan" select="fcals:colspan(., $colnum)"/>
      <xsl:variable name="rowspan" select="fcals:rowspan(., $colnum)"/>
      <xsl:sequence select="map {
        'row': $row,
        'node': .,
        'span': false(),
        'colspan': $colspan,
        'rowspan': $rowspan,
        'first-column': $colnum,
        'last-column': $colnum + $colspan - 1
      }"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:message use-when="'tables' = $v:debug"
               select="'========================================'"/>
  <tr>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:for-each select="1 to fcals:table-columns($row)">
      <xsl:variable name="cell" select="fcals:cell($row, ., $overhang, $cells)"/>
      <xsl:choose>
        <xsl:when test="$cell?span">
          <xsl:message use-when="'tables' = $v:debug"
                       select="., '--span--'"/>
        </xsl:when>
        <xsl:when test="empty($cell?node)">
          <xsl:message use-when="'tables' = $v:debug"
                       select="., '--empty--'"/>
          <xsl:call-template name="tp:cell">
            <xsl:with-param name="properties" select="map {
              'row': $row,
              'span': false(),
              'colspan': 1,
              'rowspan': 1,
              'first-column': .,
              'last-column': .
            }"/>
            <xsl:with-param name="td" select="if ($row/parent::db:thead) then 'th' else 'td'"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message use-when="'tables' = $v:debug"
                       select="., $cell?node/string()
                                  =&gt; normalize-space()
                                  =&gt; substring(1, 10)"/>
          <xsl:apply-templates select="$cell?node">
            <xsl:with-param name="properties" select="$cell"/>
          </xsl:apply-templates>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </tr>
</xsl:template>

<xsl:template match="db:entry|db:entrytbl">
  <xsl:param name="properties" as="map(*)"/>

  <xsl:call-template name="tp:cell">
    <xsl:with-param name="properties" select="$properties"/>
    <xsl:with-param name="pi-properties"
                    select="f:pi-attributes(./processing-instruction('db'))"/>
    <xsl:with-param name="td" select="if (parent::*/parent::db:thead) then 'th' else 'td'"/>
    <xsl:with-param name="content" as="item()*">
      <xsl:choose>
        <xsl:when test="self::db:entrytbl">
          <table>
            <xsl:if test="db:colspec[@colwidth]">
              <xsl:call-template name="tp:cals-colspec"/>
            </xsl:if>
            <xsl:apply-templates select="db:thead"/>
            <xsl:apply-templates select="db:tbody"/>
          </table>
        </xsl:when>
        <xsl:otherwise>
          <xsl:choose>
            <xsl:when test="$properties?align = 'char' and not(*)">
              <!-- This is all a bit fiddly. If you want to use ?db? PIs to
                   set the alignment details, they must follow the colspec for
                   the column in question. If there's no PI following the colspec,
                   we fall back to PIs following the tgroup. -->
              <xsl:variable name="piroot"
                            select="fcals:colspec-for-column(fcals:tgroup(.),
                                       $properties?first-column)"/>

              <xsl:variable name="pis"
                            select="if (empty($piroot)
                                        or empty($piroot/following-sibling::processing-instruction()))
                                    then fcals:tgroup(.)/node()
                                    else $piroot/following-sibling::node()"/>

              <xsl:variable name="pis" select="fp:only-initial-pis($pis)"/>

              <xsl:variable name="achar-width"
                            select="fp:pi-from-list($pis, 'align-char-width',
                                                    string($align-char-width))"/>

              <xsl:variable name="achar-width" as="xs:integer?">
                <xsl:choose>
                  <xsl:when test="empty($achar-width)">
                    <xsl:sequence select="()"/>
                  </xsl:when>
                  <xsl:when test="$achar-width castable as xs:integer">
                    <xsl:sequence select="xs:integer($achar-width)"/>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:message select="'Ignoring align-char-width:', $achar-width"/>
                    <xsl:sequence select="$align-char-width"/>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:variable>

              <xsl:variable name="achar-pad"
                            select="substring(fp:pi-from-list($pis, 'align-char-pad',
                                                              $align-char-pad), 1, 1)"/>

              <xsl:variable name="content" select="normalize-space(.)"/>
              <xsl:variable name="char"
                            select="if ($properties?char)
                                    then $properties?char
                                    else $align-char-default"/>
              <xsl:variable name="width"
                            select="if (exists($achar-width))
                                    then $achar-width
                                    else $align-char-width"/>

              <xsl:variable name="parts"
                            select="f:tokenize-on-char($content, $char)"/>

              <xsl:variable name="before"
                            select="if (contains($content, $char))
                                    then string-join($parts[position() lt last()], $char)
                                    else $content"/>

              <!-- If the pad character is a form space of some sort, use it for
                   padding; otherwise use the alignment character. -->
              <xsl:variable name="before"
                            select="if (contains($content, $char))
                                    then $before || $char
                                    else if (matches($achar-pad, '\p{Zs}'))
                                         then $before || $achar-pad
                                         else $before || $char"/>

              <xsl:variable name="after"
                            select="if (contains($content, $char))
                                    then $parts[last()]
                                    else ''"/>

              <xsl:variable name="after"
                            select="fp:align-char-pad($after, $width, $achar-pad)"/>

              <xsl:value-of select="$before || $after"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:apply-templates/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>

<xsl:template name="tp:cell">
  <xsl:param name="properties" as="map(*)"/>
  <xsl:param name="pi-properties" as="element()?"/>
  <xsl:param name="td" as="xs:string" select="'td'"/>
  <xsl:param name="content" as="item()*" select="()"/>

  <xsl:variable name="row" select="$properties?row"/>
  <xsl:variable name="table"
                select="($row/ancestor::db:table
                         |$row/ancestor::db:informaltable)[last()]"/>
  
  <xsl:variable name="table-part"
                select="($row/ancestor::db:thead
                         |$row/ancestor::db:tbody
                         |$row/ancestor::db:tfoot)[last()]"/>

  <xsl:variable name="frame" select="$table/@frame"/>
  <xsl:variable name="btop"
                select="$frame = 'all' or $frame = 'top' or $frame = 'topbot'"/>

  <!-- bbot:
       1. The frame includes the bottom border
       2. We're in the last row of a section and the cell has a rowsep
          and either this is a thead or it's a tbody and there is a tfoot
  -->
  <xsl:variable name="bbot"
                select="$frame = 'all' or $frame = 'bot' or $frame = 'topbot'
                        or ($properties?rowsep
                            and empty($row/following-sibling::db:row)
                            and ($table-part/self::db:thead
                                 or ($table-part/self::db:tbody
                                     and $table-part/preceding-sibling::db:tfoot)))"/>
  <xsl:variable name="bleft"
                select="$frame = 'all' or $frame = 'sides'"/>
  <xsl:variable name="bright"
                select="$frame = 'all' or $frame = 'sides'"/>

  <xsl:variable name="classes" as="xs:string*">
    <xsl:sequence select="if ($properties?first-column = 1 and $bleft)
                          then 'bleft'
                          else ()"/>
    <xsl:sequence select="if (empty($row/preceding-sibling::db:row) and $btop)
                          then 'btop'
                          else ()"/>
    <xsl:sequence select="f:cals-rowsep($row, $properties, $bbot)"/>
    <xsl:sequence select="f:cals-colsep($row, $properties, $bright)"/>
    <xsl:sequence select="if ($properties?node)
                          then ()
                          else 'empty'"/>
    <xsl:sequence select="$properties?align"/>
    <xsl:sequence select="$properties?valign"/>
    <xsl:if test="$properties?node/@role">
      <xsl:sequence select="tokenize($properties?node/@role, '\s+')"/>
    </xsl:if>
  </xsl:variable>

  <xsl:element name="{$td}" namespace="http://www.w3.org/1999/xhtml">
    <!-- Can't (easily) use the m:attributes mode here because we need
         to output the appropriate attributes even when there is no
         DocBook node. (For example, when a cell is omitted.) -->
    <xsl:if test="map:contains($properties, 'node')
                  and $properties?node/@xml:id">
      <xsl:attribute name="id" select="$properties?node/@xml:id"/>
    </xsl:if>
    <xsl:if test="exists($classes)">
      <xsl:variable name="sorted" as="xs:string+">
        <xsl:for-each select="$classes">
          <xsl:sort select="."/>
          <xsl:sequence select="."/>
        </xsl:for-each>
      </xsl:variable>
      <xsl:attribute name="class" select="string-join($sorted, ' ')"/>
    </xsl:if>
    <xsl:if test="$properties?colspan gt 1">
      <xsl:attribute name="colspan" select="$properties?colspan"/>
    </xsl:if>
    <xsl:if test="$properties?rowspan gt 1">
      <xsl:attribute name="rowspan" select="$properties?rowspan"/>
    </xsl:if>
    <xsl:copy-of select="$pi-properties/@style"/>
    <xsl:sequence select="$content"/>
  </xsl:element>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="tp:cals-colspec">
  <xsl:variable name="tgroup" select="."/>

  <xsl:variable name="widths" as="map(*)*">
    <xsl:for-each select="1 to xs:integer($tgroup/@cols)">
      <xsl:variable name="colspec" select="fcals:colspec-for-column($tgroup, .)"/>
      <xsl:choose>
        <xsl:when test="exists($colspec)">
          <xsl:choose>
            <xsl:when test="$colspec/@colwidth">
              <xsl:sequence select="f:parse-length(string($colspec/@colwidth))"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:sequence select="f:parse-length('1*')"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="f:parse-length('1*')"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:variable>

  <!--
  <xsl:for-each select="$widths">
    <xsl:message select="'R:' || .?relative || ' M:' || .?magnitude || ' U:' || .?units"/>
  </xsl:for-each>
  -->

  <xsl:variable name="absolute-width" as="xs:double"
                select="sum($widths ! f:absolute-length(.))"/>

  <xsl:variable name="relative-width" as="xs:double"
                select="sum($widths ! f:relative-length(.))"/>

  <xsl:variable name="absolute-remainder"
                select="f:absolute-length($v:nominal-page-width) - $absolute-width"/>

  <xsl:if test="$absolute-remainder lt 0">
    <xsl:message>Table width exceeds nominal width, ignoring relative width</xsl:message>
  </xsl:if>

  <xsl:variable name="absolute-remainder"
                select="if ($absolute-remainder lt 0)
                        then 0
                        else $absolute-remainder"/>

  <xsl:choose>
    <xsl:when test="$relative-width gt 0">
      <xsl:variable name="percent-widths" as="xs:integer*">
        <xsl:for-each select="$widths">
          <xsl:variable name="rel-part"
                        select="if (.?relative and .?relative gt 0)
                                then $absolute-remainder div $relative-width * .?relative
                                else 0"/>
          <xsl:sequence select="xs:integer(floor(($rel-part + f:absolute-length(.))
                                                 div f:absolute-length($v:nominal-page-width)
                                                 * 100.0))"/>
        </xsl:for-each>
      </xsl:variable>

      <!-- because I'm fussy about the details; make sure the sum = 100% -->
      <xsl:variable name="first-width" as="xs:integer">
        <xsl:sequence select="$percent-widths[1] + (100 - sum($percent-widths))"/>
      </xsl:variable>

      <colgroup>
        <xsl:for-each select="($first-width, subsequence($percent-widths, 2))">
          <col style="width: {.}%"/>
        </xsl:for-each>
      </colgroup>
    </xsl:when>
    <xsl:otherwise>
      <colgroup>
        <xsl:for-each select="$widths">
          <col style="width: {f:absolute-length(.)}px"/>
        </xsl:for-each>
      </colgroup>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:function name="fcals:colspec-for-column" as="element()?" cache="yes">
  <xsl:param name="tgroup" as="element()"/>
  <xsl:param name="colnum" as="xs:integer"/>
  <xsl:sequence select="fp:colspec-for-colnum($tgroup/db:colspec, $colnum, 1)"/>
</xsl:function>

<xsl:function name="fp:colspec-for-colnum" as="element()?" cache="yes">
  <xsl:param name="colspecs" as="element()*"/>
  <xsl:param name="colnum" as="xs:integer"/>
  <xsl:param name="curcol" as="xs:integer"/>

  <!--
  <xsl:message select="('c4c: ', $colnum, ', ', $curcol, ', ', $colspecs[1])"/>
  -->

  <xsl:choose>
    <xsl:when test="empty($colspecs)">
      <xsl:sequence select="()"/>
    </xsl:when>
    <xsl:when test="normalize-space($colspecs[1]/@colnum) = string($colnum)">
      <xsl:sequence select="$colspecs[1]"/>
    </xsl:when>
    <xsl:when test="normalize-space($colspecs[1]/@colnum)">
      <xsl:sequence
          select="fp:colspec-for-colnum(subsequence($colspecs,2), $colnum,
                                        xs:integer($colspecs[1]/@colnum)+1)"/>
    </xsl:when>
    <xsl:when test="$colnum = $curcol">
      <xsl:sequence select="$colspecs[1]"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence
          select="fp:colspec-for-colnum(subsequence($colspecs,2), $colnum, $curcol + 1)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fcals:cell" as="map(*)" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="column" as="xs:integer"/>
  <xsl:param name="overhang" as="array(xs:integer)"/>
  <xsl:param name="cells" as="map(*)*"/>

  <xsl:variable name="cell" as="map(*)*">
    <xsl:for-each select="$cells">
      <xsl:if test="$column ge .?first-column
                    and $column le .?last-column">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:if test="count($cell) gt 1">
    <xsl:variable name="rownum" select="count($row/preceding-sibling::db:row) + 1"/>
    <xsl:message terminate="yes"
                 select="'Overlapping cells in table at row '
                         || $rownum || ' for column ' || $column"/>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="array:get($overhang, $column) ne 0
                    or (exists($cell) and $cell?first-column ne $column)">
      <xsl:sequence select="map { 'span': true() }"/>
    </xsl:when>
    <xsl:when test="empty($cell)">
      <xsl:sequence select="fcals:cell-decoration($row, $cell?node, $column)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="map:merge(($cell,
                            fcals:cell-decoration($row, $cell?node, $column)))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:overhang-into-row" as="array(xs:integer)" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:choose>
    <xsl:when test="empty($row/preceding-sibling::*)">
      <xsl:sequence select="fcals:zeros($row)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:overhang($row/preceding-sibling::db:row[1])"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:overhang" as="array(xs:integer)" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>

  <xsl:variable name="overhang"
                select="if (empty($row/preceding-sibling::*))
                        then fcals:zeros($row)
                        else fcals:decrement-overhang(fcals:overhang($row/preceding-sibling::*[1]))"/>

  <xsl:variable name="colmap" as="map(xs:integer, node())">
    <xsl:map>
      <xsl:for-each select="$row/*">
        <xsl:map-entry key="fcals:column-number(., $overhang)" select="."/>
      </xsl:for-each>
    </xsl:map>
  </xsl:variable>

  <xsl:variable name="newoverhang" as="xs:integer*">
    <xsl:for-each select="array:flatten($overhang)">
      <xsl:sequence select=". + fcals:cell-overhang($colmap, position())"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:sequence select="array { $newoverhang }"/>
</xsl:function>

<xsl:function name="fcals:cell-overhang" cache="yes">
  <xsl:param name="colmap" as="map(xs:integer, node())"/>
  <xsl:param name="column" as="xs:integer"/>

  <xsl:variable name="over" as="xs:integer?">
    <xsl:for-each select="map:keys($colmap)">
      <xsl:variable name="fcol" select="."/>
      <xsl:variable name="cell" select="map:get($colmap, .)"/>
      <xsl:variable name="width" select="fcals:colspan($cell, .)"/>
      <xsl:variable name="lcol" select="$fcol + $width - 1"/>
      <xsl:if test="$column ge $fcol and $column le $lcol">
        <xsl:sequence select="fcals:rowspan($cell, .)"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:sequence select="if (exists($over)) then $over - 1 else 0"/>
</xsl:function>

<xsl:function name="fcals:next-empty-cell" cache="yes">
  <xsl:param name="column" as="xs:integer"/>
  <xsl:param name="overhang" as="array(xs:integer)"/>
  <xsl:choose>
    <xsl:when test="$column gt array:size($overhang)">
      <xsl:sequence select="error($dbe:INVALID-CALS,
                              'Columns exceed @cols (' || $column || ' gt ' || array:size($overhang) || ')')"/>
    </xsl:when>
    <xsl:when test="array:get($overhang, $column) eq 0">
      <xsl:sequence select="$column"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:next-empty-cell($column + 1, $overhang)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:decrement-overhang" as="array(xs:integer)" cache="yes">
  <xsl:param name="overhang" as="array(xs:integer)"/>
  <xsl:sequence select="array {
    for $hang in array:flatten($overhang) return max((0, $hang - 1))
  }"/>
</xsl:function>

<xsl:function name="fcals:column-number" as="xs:integer" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="overhang" as="array(xs:integer)"/>

  <xsl:choose>
    <xsl:when test="$node/@namest">
      <xsl:sequence
          select="fcals:colspec-column-number(
                     fcals:colspec($node, $node/@namest))"/>
    </xsl:when>
    <xsl:when test="$node/@colname">
      <xsl:sequence
          select="fcals:colspec-column-number(
                     fcals:colspec($node, $node/@colname))"/>
    </xsl:when>
    <xsl:when test="$node/preceding-sibling::*">
      <xsl:variable name="pcell" select="$node/preceding-sibling::*[1]"/>
      <xsl:variable name="column"
                    select="fcals:column-number($pcell, $overhang)"/>
      <xsl:variable name="nextcolumn"
                    select="$column + fcals:colspan($pcell, $column)"/>
      <xsl:sequence select="fcals:next-empty-cell($nextcolumn, $overhang)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:next-empty-cell(1, $overhang)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:colspec-column-number" as="xs:integer" cache="yes">
  <xsl:param name="colspec" as="element(db:colspec)" required="yes"/>

  <xsl:choose>
    <xsl:when test="$colspec/@colnum">
      <xsl:sequence select="xs:integer($colspec/@colnum)"/>
    </xsl:when>
    <xsl:when test="$colspec/preceding-sibling::db:colspec">
      <xsl:sequence
          select="fcals:colspec-column-number(
                     $colspec/preceding-sibling::db:colspec[1]) + 1 "/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="1"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:colspec" as="element(db:colspec)" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="colname" as="xs:string" required="yes"/>

  <xsl:variable name="colspec"
                select="fcals:tgroup($node)/db:colspec[@colname=$colname]"/>

  <xsl:if test="empty($colspec)">
    <xsl:sequence select="error($dbe:INVALID-CALS, 'No colspec named ' || $colname)"/>
  </xsl:if>

  <xsl:sequence select="$colspec"/>
</xsl:function>

<xsl:function name="fcals:spanspec" as="element(db:spanspec)" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="spanname" as="xs:string" required="yes"/>

  <xsl:variable name="spanspec"
                select="fcals:tgroup($node)/db:spanspec[@spanname=$spanname]"/>

  <xsl:if test="empty($spanspec)">
    <xsl:sequence select="error($dbe:INVALID-CALS, 'No spanspec named ' || $spanname)"/>
  </xsl:if>

  <xsl:sequence select="$spanspec"/>
</xsl:function>

<xsl:function name="fcals:colspan" as="xs:integer" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="colnum" as="xs:integer" required="yes"/>

  <xsl:choose>
    <xsl:when test="$node/@nameend">
      <xsl:variable name="last-colnum"
                    select="fcals:colspec-column-number(
                               fcals:colspec($node, $node/@nameend))"/>
      <xsl:sequence select="$last-colnum - $colnum + 1"/>
    </xsl:when>
    <xsl:when test="$node/@spanname">
      <xsl:variable name="spanspec" select="fcals:spanspec($node, $node/@spanname)"/>
      <xsl:variable name="last-colnum"
                    select="fcals:colspec-column-number(
                               fcals:colspec($node, $spanspec/@nameend))"/>
      <xsl:sequence select="$last-colnum - $colnum + 1"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="1"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:rowspan" as="xs:integer" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="colnum" as="xs:integer" required="yes"/>

  <xsl:choose>
    <xsl:when test="$node/@morerows">
      <xsl:sequence select="xs:integer($node/@morerows) + 1"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="1"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fcals:cell-decoration" as="map(*)">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="cell" as="element()?"/>
  <xsl:param name="column" as="xs:integer"/>
  <xsl:sequence select="map {
    'colsep': fcals:colsep($row, $cell, $column),
    'rowsep': fcals:rowsep($row, $cell, $column),
    'char': fcals:char($row, $cell, $column),
    'align': fcals:align($row, $cell, $column),
    'valign': fcals:valign($row, $cell)
  }"/>
<!--
-->
</xsl:function>

<xsl:function name="fcals:colsep" as="xs:boolean" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="node" as="element()?"/>
  <xsl:param name="colnum" as="xs:integer"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($row)"/>

  <xsl:choose>
    <xsl:when test="$node/@colsep">
      <xsl:sequence select="$node/@colsep = '1'"/>
    </xsl:when>
    <xsl:when test="$node/@nameend">
      <xsl:sequence select="fcals:colsep-colspec($node, $node/@nameend)"/>
    </xsl:when>
    <xsl:when test="$node/@colname">
      <xsl:sequence select="fcals:colsep-colspec($node, $node/@colname)"/>
    </xsl:when>
    <xsl:when test="$node/@spanname">
      <xsl:sequence select="fcals:colsep-spanspec($node, $node/@spanname)"/>
    </xsl:when>
    <xsl:when test="fcals:colspec-for-column($tgroup, $colnum)/@colsep">
      <xsl:sequence select="fcals:colspec-for-column($tgroup, $colnum)/@colsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/@colsep">
      <xsl:sequence select="$tgroup/@colsep = '1'"/>
    </xsl:when>
    <!-- exclude entry parent of entrytbl -->
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@colsep">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@colsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:colsep-colspec" as="xs:boolean" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="colspec" select="fcals:colspec($node, $name)"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($node)"/>

  <xsl:choose>
    <xsl:when test="$colspec/@colsep">
      <xsl:sequence select="$colspec/@colsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/@colsep">
      <xsl:sequence select="$tgroup/@colsep = '1'"/>
    </xsl:when>
    <!-- exclude entry parent of entrytbl -->
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@colsep">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@colsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:colsep-spanspec" as="xs:boolean" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="spanspec" select="fcals:spanspec($node, $name)"/>

  <xsl:choose>
    <xsl:when test="$spanspec/@colsep">
      <xsl:sequence select="$spanspec/@colsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:colsep-colspec($node, $spanspec/@nameend)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:empty-cell-colsep" as="xs:boolean" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="cell" as="map(*)"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($row)"/>

  <xsl:choose>
    <xsl:when test="fcals:colspec-for-column($tgroup, $cell?first-column)/@colsep">
      <xsl:sequence select="fcals:colspec-for-column($tgroup, $cell?first-column)/@colsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/@colsep">
      <xsl:sequence select="$tgroup/@colsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@colsep">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@colsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fcals:rowsep" as="xs:boolean" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="node" as="element()?"/>
  <xsl:param name="colnum" as="xs:integer"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($row)"/>

  <xsl:choose>
    <xsl:when test="$node/@rowsep">
      <xsl:sequence select="$node/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$node/@nameend">
      <xsl:sequence select="fcals:rowsep-colspec($node, $node/@nameend)"/>
    </xsl:when>
    <xsl:when test="$node/@colname">
      <xsl:sequence select="fcals:rowsep-colspec($node, $node/@colname)"/>
    </xsl:when>
    <xsl:when test="$node/@spanname">
      <xsl:sequence select="fcals:rowsep-spanspec($node, $node/@spanname)"/>
    </xsl:when>
    <xsl:when test="$node/parent::db:row/@rowsep">
      <xsl:sequence select="$node/parent::db:row/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/@rowsep">
      <xsl:sequence select="$tgroup/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@rowsep">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@rowsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:rowsep-colspec" as="xs:boolean" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="colspec" select="fcals:colspec($node, $name)"/>

  <xsl:variable name="tgroup"
                select="($node/ancestor::db:tgroup
                         |$node/ancestor::db:entrytbl)[last()]"/>

  <xsl:choose>
    <xsl:when test="$colspec/@rowsep">
      <xsl:sequence select="$colspec/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$node/parent::db:row/@rowsep">
      <xsl:sequence select="$node/parent::db:row/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/@rowsep">
      <xsl:sequence select="$tgroup/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@rowsep">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@rowsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:rowsep-spanspec" as="xs:boolean" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="spanspec" select="fcals:spanspec($node, $name)"/>

  <xsl:choose>
    <xsl:when test="$spanspec/@rowsep">
      <xsl:sequence select="$spanspec/@rowsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:rowsep-colspec($node, $spanspec/@nameend)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:align" as="xs:string?" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="node" as="element()?"/>
  <xsl:param name="colnum" as="xs:integer"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($row)"/>
  <xsl:choose>
    <xsl:when test="$node/@align">
      <xsl:sequence select="$node/@align"/>
    </xsl:when>
    <xsl:when test="$node/@nameend">
      <xsl:sequence select="fcals:align-colspec($node, $node/@nameend)"/>
    </xsl:when>
    <xsl:when test="$node/@colname">
      <xsl:sequence select="fcals:align-colspec($node, $node/@colname)"/>
    </xsl:when>
    <xsl:when test="$node/@spanname">
      <xsl:sequence select="fcals:align-spanspec($node, $node/@spanname)"/>
    </xsl:when>
    <xsl:when test="fcals:colspec-for-column($tgroup, $colnum)/@align">
      <xsl:sequence select="fcals:colspec-for-column($tgroup, $colnum)/@align"/>
    </xsl:when>
    <xsl:when test="$tgroup/@align">
      <xsl:sequence select="$tgroup/@align"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@align">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@align"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:align-colspec" as="xs:string?" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="colspec" select="fcals:colspec($node, $name)"/>

  <xsl:variable name="tgroup"
                select="($node/ancestor::db:tgroup
                         |$node/ancestor::db:entrytbl)[last()]"/>

  <xsl:choose>
    <xsl:when test="$colspec/@align">
      <xsl:sequence select="$colspec/@align"/>
    </xsl:when>
    <xsl:when test="$tgroup/@align">
      <xsl:sequence select="$tgroup/@align"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@align">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@align"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:align-spanspec" as="xs:string?" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="spanspec" select="fcals:spanspec($node, $name)"/>

  <xsl:choose>
    <xsl:when test="$spanspec/@align">
      <xsl:sequence select="$spanspec/@align"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:align-colspec($node, $spanspec/@nameend)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:char" as="xs:string?" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="node" as="element()?"/>
  <xsl:param name="colnum" as="xs:integer"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($row)"/>

  <xsl:choose>
    <xsl:when test="$node/@char">
      <xsl:sequence select="$node/@char"/>
    </xsl:when>
    <xsl:when test="$node/@nameend">
      <xsl:sequence select="fcals:char-colspec($node, $node/@nameend)"/>
    </xsl:when>
    <xsl:when test="$node/@colname">
      <xsl:sequence select="fcals:char-colspec($node, $node/@colname)"/>
    </xsl:when>
    <xsl:when test="$node/@spanname">
      <xsl:sequence select="fcals:char-spanspec($node, $node/@spanname)"/>
    </xsl:when>
    <xsl:when test="fcals:colspec-for-column($tgroup, $colnum)/@char">
      <xsl:sequence select="fcals:colspec-for-column($tgroup, $colnum)/@char"/>
    </xsl:when>
    <xsl:when test="$tgroup/@char">
      <xsl:sequence select="$tgroup/@char"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@char">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@char"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:char-colspec" as="xs:string?" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="colspec" select="fcals:colspec($node, $name)"/>

  <xsl:variable name="tgroup"
                select="($node/ancestor::db:tgroup
                         |$node/ancestor::db:entrytbl)[last()]"/>

  <xsl:choose>
    <xsl:when test="$colspec/@char">
      <xsl:sequence select="$colspec/@char"/>
    </xsl:when>
    <xsl:when test="$tgroup/@char">
      <xsl:sequence select="$tgroup/@char"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@char">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@char"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:char-spanspec" as="xs:string?" cache="yes">
  <xsl:param name="node" as="element()" required="yes"/>
  <xsl:param name="name" as="xs:string" required="yes"/>

  <xsl:variable name="spanspec" select="fcals:spanspec($node, $name)"/>

  <xsl:choose>
    <xsl:when test="$spanspec/@char">
      <xsl:sequence select="$spanspec/@char"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fcals:char-colspec($node, $spanspec/@nameend)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fcals:valign" as="xs:string?" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="node" as="element()?"/>

  <xsl:choose>
    <xsl:when test="$node/@valign">
      <xsl:sequence select="$node/@valign"/>
    </xsl:when>
    <xsl:when test="$node/parent::db:row/@valign">
      <xsl:sequence select="$node/parent::db:row/@valign"/>
    </xsl:when>
    <xsl:when test="$node/parent::db:row/parent::*/@valign">
      <xsl:sequence select="$node/parent::db:row/parent::*/@valign"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="f:cals-rowsep" as="xs:string?" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="cell" as="map(*)"/>
  <xsl:param name="last-row-rowsep" as="xs:boolean"/>

  <xsl:variable name="last-row"
                select="count($row/following-sibling::db:row) lt $cell?rowspan"/>

  <xsl:variable name="rowsep"
                select="if ($cell?node)
                        then $cell?rowsep
                        else fcals:empty-cell-rowsep($row)"/>

  <xsl:if test="($rowsep and not($last-row))
                or ($last-row and $last-row-rowsep)">
    <xsl:sequence select="'rowsep'"/>
  </xsl:if>
</xsl:function>

<xsl:function name="f:cals-colsep" as="xs:string?" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:param name="cell" as="map(*)"/>
  <xsl:param name="last-col-colsep" as="xs:boolean"/>

  <xsl:variable name="last-col"
                select="$cell?first-column + $cell?colspan
                        gt fcals:table-columns($row)"/>

  <xsl:variable name="colsep"
                select="if ($cell?node)
                        then $cell?colsep
                        else fcals:empty-cell-colsep($row, $cell)"/>

  <xsl:if test="($colsep and not($last-col))
                or ($last-col and $last-col-colsep)">
    <xsl:sequence select="'colsep'"/>
  </xsl:if>
</xsl:function>

<xsl:function name="fcals:empty-cell-rowsep" as="xs:boolean" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>

  <xsl:variable name="tgroup" select="fcals:tgroup($row)"/>

  <xsl:choose>
    <xsl:when test="$row/@rowsep">
      <xsl:sequence select="$row/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/@rowsep">
      <xsl:sequence select="$tgroup/@rowsep = '1'"/>
    </xsl:when>
    <xsl:when test="$tgroup/parent::*[not(self::db:entry)]/@rowsep">
      <xsl:sequence select="$tgroup/parent::*[not(self::db:entry)]/@rowsep = '1'"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:only-initial-pis" as="processing-instruction()*">
  <xsl:param name="nodes" as="node()*"/>

  <xsl:iterate select="$nodes">
    <xsl:param name="pis" select="()"/>
    <xsl:choose>
      <xsl:when test="self::processing-instruction()">
        <xsl:next-iteration>
          <xsl:with-param name="pis" select="($pis, .)"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$pis"/>
        <xsl:break/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:function>

<xsl:function name="fp:align-char-pad" as="xs:string">
  <!-- FIXME: optimize this -->
  <xsl:param name="text" as="xs:string"/>
  <xsl:param name="width" as="xs:integer"/>
  <xsl:param name="pad" as="xs:string"/>
  <xsl:sequence select="if (string-length($text) lt $width)
                        then fp:align-char-pad($text||$pad, $width, $pad)
                        else $text"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fcals:table-columns" as="xs:integer" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="xs:integer(fcals:tgroup($node)/@cols)"/>
</xsl:function>

<xsl:function name="fcals:tgroup" as="element()" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="($node/ancestor::db:tgroup
                         |$node/ancestor::db:entrytbl)[last()]"/>
</xsl:function>

<xsl:function name="fcals:zeros" cache="yes">
  <xsl:param name="row" as="element(db:row)"/>
  <xsl:variable name="cols" select="fcals:table-columns($row)"/>
  <xsl:sequence select="array { for $col in 1 to $cols return 0 }"/>
</xsl:function>

</xsl:stylesheet>

tablehtml.xsl

6 templates

Instructions
Template match ≅ db:table
Mode: m:docbook
Matches: db:table
Template match ≅ db:td/db:table|db:th/db:table
Mode: m:docbook
Priority: 100
Matches: db:td/db:table, db:th/db:table
Template match ≅ db:informaltable
Mode: m:docbook
Matches: db:informaltable
Template match ≅ db:td/db:informaltable|db:th/d…
Mode: m:docbook
Priority: 100
Matches: db:td/db:informaltable, db:th/db:informaltable
Template match ≅ db:caption|db:col|db:colgroup|…
Mode: m:htmltable
Matches: db:caption, db:col, db:colgroup, db:tbody, db:td, db:tfoot, db:th, db:thead, db:tr
Template match ≅ *
Mode: m:htmltable
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m t xs"
                version="3.0">

<xsl:template match="db:table[not(db:tgroup)]">
  <div class="formalobject {local-name(.)}{f:conditional-orientation-class(.) ! concat(' ', .)}">
    <xsl:choose>
      <xsl:when test="@xml:id">
        <xsl:attribute name="id" select="@xml:id"/>
      </xsl:when>
      <xsl:when test="parent::*">
        <xsl:attribute name="id" select="f:generate-id(.)"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- never mind -->
      </xsl:otherwise>
    </xsl:choose>

    <table>
      <xsl:apply-templates select="." mode="m:attributes"/>
      <xsl:apply-templates mode="m:htmltable"/>
    </table>
    <xsl:if test=".//db:footnote">
      <xsl:call-template name="t:table-footnotes">
        <xsl:with-param name="footnotes" select=".//db:footnote"/>
      </xsl:call-template>
    </xsl:if>
  </div>
</xsl:template>

<xsl:template match="db:td/db:table[not(db:tgroup)]
                     |db:th/db:table[not(db:tgroup)]"
              priority="100">
  <table>
    <xsl:choose>
      <xsl:when test="@xml:id">
        <xsl:attribute name="id" select="@xml:id"/>
      </xsl:when>
      <xsl:when test="parent::*">
        <xsl:attribute name="id" select="f:generate-id(.)"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- never mind -->
      </xsl:otherwise>
    </xsl:choose>

    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates mode="m:htmltable"/>
  </table>
</xsl:template>

<xsl:template match="db:informaltable[not(db:tgroup)]">
  <div class="informalobject {local-name(.)}">
    <xsl:if test="@xml:id">
      <xsl:attribute name="id" select="@xml:id"/>
    </xsl:if>
    <table>
      <xsl:apply-templates select="." mode="m:attributes"/>
      <xsl:apply-templates select="node() except db:info" mode="m:htmltable"/>
    </table>
    <xsl:if test=".//db:footnote">
      <xsl:call-template name="t:table-footnotes">
        <xsl:with-param name="footnotes" select=".//db:footnote"/>
      </xsl:call-template>
    </xsl:if>
  </div>
</xsl:template>

<xsl:template match="db:td/db:informaltable[not(db:tgroup)]
                     |db:th/db:informaltable[not(db:tgroup)]"
              priority="100">
  <table>
    <xsl:if test="@xml:id">
      <xsl:attribute name="id" select="@xml:id"/>
    </xsl:if>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="node() except db:info" mode="m:htmltable"/>
  </table>
</xsl:template>

<xsl:template match="db:tbody|db:thead|db:tfoot|db:tr|db:th|db:td|db:caption
                     |db:colgroup|db:col"
              mode="m:htmltable">
  <xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1999/xhtml">
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="m:htmltable"/>
  </xsl:element>
</xsl:template>

<xsl:template match="*" mode="m:htmltable">
  <xsl:apply-templates select="."/>
</xsl:template>

</xsl:stylesheet>

inlines.xsl

102 templates (1 used only in one other module), 1 function (1 used only in one other module)

Instructions
Template match ≅ db:abbrev
Calls: t:inline
Mode: m:docbook
Matches: db:abbrev
Template match ≅ db:accel
Calls: t:inline
Mode: m:docbook
Matches: db:accel
Template match ≅ db:acronym
Calls: t:inline
Mode: m:docbook
Matches: db:acronym
Template match ≅ db:application
Calls: t:inline
Mode: m:docbook
Matches: db:application
Template match ≅ db:author
Mode: m:docbook
Matches: db:author
Template match ≅ db:buildtarget
Calls: t:inline
Mode: m:docbook
Matches: db:buildtarget
Template match ≅ db:citation
Mode: m:docbook
Matches: db:citation
Template match ≅ db:citebiblioid
Calls: t:inline
Mode: m:docbook
Matches: db:citebiblioid
Template match ≅ db:citerefentry
Calls: t:inline
Mode: m:docbook
Matches: db:citerefentry
Template match ≅ db:citetitle
Calls: t:inline
Mode: m:docbook
Matches: db:citetitle
Template match ≅ db:classname
Calls: t:inline
Mode: m:docbook
Matches: db:classname
Template match ≅ db:code
Calls: t:inline
Mode: m:docbook
Matches: db:code
Template match ≅ db:command
Calls: t:inline
Mode: m:docbook
Matches: db:command
Template match ≅ db:computeroutput
Calls: t:inline
Mode: m:docbook
Matches: db:computeroutput
Template match ≅ db:constant
Calls: t:inline
Mode: m:docbook
Matches: db:constant
Template match ≅ db:database
Calls: t:inline
Mode: m:docbook
Matches: db:database
Template match ≅ db:date
Calls: t:inline
Mode: m:docbook
Matches: db:date
Template match ≅ db:editor
Calls: t:inline
Mode: m:docbook
Matches: db:editor
Template match ≅ db:email
Calls: t:inline
Mode: m:docbook
Matches: db:email
Template match ≅ db:emphasis
Calls: t:inline
Mode: m:docbook
Matches: db:emphasis
Template match ≅ db:enumidentifier
Calls: t:inline
Mode: m:docbook
Matches: db:enumidentifier
Template match ≅ db:enumname
Calls: t:inline
Mode: m:docbook
Matches: db:enumname
Template match ≅ db:enumvalue
Calls: t:inline
Mode: m:docbook
Matches: db:enumvalue
Template match ≅ db:envar
Calls: t:inline
Mode: m:docbook
Matches: db:envar
Template match ≅ db:errorcode
Calls: t:inline
Mode: m:docbook
Matches: db:errorcode
Template match ≅ db:errorname
Calls: t:inline
Mode: m:docbook
Matches: db:errorname
Template match ≅ db:errortext
Calls: t:inline
Mode: m:docbook
Matches: db:errortext
Template match ≅ db:errortype
Calls: t:inline
Mode: m:docbook
Matches: db:errortype
Template match ≅ db:exceptionname
Calls: t:inline
Mode: m:docbook
Matches: db:exceptionname
Template match ≅ db:filename
Calls: t:inline
Mode: m:docbook
Matches: db:filename
Template match ≅ db:firstterm
Calls: t:inline
Mode: m:docbook
Matches: db:firstterm
Template match ≅ db:foreignphrase
Calls: t:inline
Mode: m:docbook
Matches: db:foreignphrase
Template match ≅ db:function
Calls: t:inline
Mode: m:docbook
Matches: db:function
Template match ≅ db:guibutton
Calls: t:inline
Mode: m:docbook
Matches: db:guibutton
Template match ≅ db:guiicon
Calls: t:inline
Mode: m:docbook
Matches: db:guiicon
Template match ≅ db:guilabel
Calls: t:inline
Mode: m:docbook
Matches: db:guilabel
Template match ≅ db:guimenu
Calls: t:inline
Mode: m:docbook
Matches: db:guimenu
Template match ≅ db:guimenuitem
Calls: t:inline
Mode: m:docbook
Matches: db:guimenuitem
Template match ≅ db:guisubmenu
Calls: t:inline
Mode: m:docbook
Matches: db:guisubmenu
Template match ≅ db:hardware
Calls: t:inline
Mode: m:docbook
Matches: db:hardware
Template match ≅ db:initializer
Calls: t:inline
Mode: m:docbook
Matches: db:initializer
Template match ≅ db:inlineequation
Calls: t:inline
Mode: m:docbook
Matches: db:inlineequation
Template match ≅ db:interfacename
Calls: t:inline
Mode: m:docbook
Matches: db:interfacename
Template match ≅ db:jobtitle
Calls: t:inline
Mode: m:docbook
Matches: db:jobtitle
Template match ≅ db:keycap
Calls: t:inline
Mode: m:docbook
Matches: db:keycap
Template match ≅ db:keycode
Calls: t:inline
Mode: m:docbook
Matches: db:keycode
Template match ≅ db:keycombo
Calls: t:inline
Mode: m:docbook
Matches: db:keycombo
Template match ≅ db:keysym
Calls: t:inline
Mode: m:docbook
Matches: db:keysym
Template match ≅ db:literal
Calls: t:inline
Mode: m:docbook
Matches: db:literal
Template match ≅ db:macroname
Calls: t:inline
Mode: m:docbook
Matches: db:macroname
Template match ≅ db:markup
Calls: t:inline
Mode: m:docbook
Matches: db:markup
Template match ≅ db:mathphrase
Calls: t:inline
Mode: m:docbook
Matches: db:mathphrase
Template match ≅ db:medialabel
Calls: t:inline
Mode: m:docbook
Matches: db:medialabel
Template match ≅ db:menuchoice
Calls: t:inline
Mode: m:docbook
Matches: db:menuchoice
Template match ≅ db:methodname
Calls: t:inline
Mode: m:docbook
Matches: db:methodname
Template match ≅ db:modifier
Calls: t:inline
Mode: m:docbook
Matches: db:modifier
Template match ≅ db:mousebutton
Calls: t:inline
Mode: m:docbook
Matches: db:mousebutton
Template match ≅ db:namespace
Calls: t:inline
Mode: m:docbook
Matches: db:namespace
Template match ≅ db:namespacename
Calls: t:inline
Mode: m:docbook
Matches: db:namespacename
Template match ≅ db:ooclass
Calls: t:inline
Mode: m:docbook
Matches: db:ooclass
Template match ≅ db:ooexception
Calls: t:inline
Mode: m:docbook
Matches: db:ooexception
Template match ≅ db:oointerface
Calls: t:inline
Mode: m:docbook
Matches: db:oointerface
Template match ≅ db:option
Calls: t:inline
Mode: m:docbook
Matches: db:option
Template match ≅ db:optional
Calls: t:inline
Mode: m:docbook
Matches: db:optional
Template match ≅ db:org
Calls: t:inline
Mode: m:docbook
Matches: db:org
Template match ≅ db:orgname
Calls: t:inline
Mode: m:docbook
Matches: db:orgname
Template match ≅ db:package
Calls: t:inline
Mode: m:docbook
Matches: db:package
Template match ≅ db:parameter
Calls: t:inline
Mode: m:docbook
Matches: db:parameter
Template match ≅ db:person
Calls: t:inline
Mode: m:docbook
Matches: db:person
Template match ≅ db:phrase
Calls: t:inline
Mode: m:docbook
Matches: db:phrase
Template match ≅ db:productname
Calls: t:inline
Mode: m:docbook
Matches: db:productname
Template match ≅ db:productnumber
Calls: t:inline
Mode: m:docbook
Matches: db:productnumber
Template match ≅ db:prompt
Calls: t:inline
Mode: m:docbook
Matches: db:prompt
Template match ≅ db:property
Calls: t:inline
Mode: m:docbook
Matches: db:property
Template match ≅ db:quote
Calls: t:inline
Mode: m:docbook
Matches: db:quote
Template match ≅ db:replaceable
Calls: t:inline
Mode: m:docbook
Matches: db:replaceable
Template match ≅ db:returnvalue
Calls: t:inline
Mode: m:docbook
Matches: db:returnvalue
Template match ≅ db:revnumber
Calls: t:inline
Mode: m:docbook
Matches: db:revnumber
Template match ≅ db:shortcut
Calls: t:inline
Mode: m:docbook
Matches: db:shortcut
Template match ≅ db:structfield
Calls: t:inline
Mode: m:docbook
Matches: db:structfield
Template match ≅ db:subscript
Calls: t:inline
Mode: m:docbook
Matches: db:subscript
Template match ≅ db:superscript
Calls: t:inline
Mode: m:docbook
Matches: db:superscript
Template match ≅ db:symbol
Calls: t:inline
Mode: m:docbook
Matches: db:symbol
Template match ≅ db:systemitem
Calls: t:inline
Mode: m:docbook
Matches: db:systemitem
Template match ≅ db:tag
Calls: t:inline
Mode: m:docbook
Matches: db:tag
Template match ≅ db:templateid
Calls: t:inline
Mode: m:docbook
Matches: db:templateid
Template match ≅ db:templatename
Calls: t:inline
Mode: m:docbook
Matches: db:templatename
Template match ≅ db:termdef
Calls: t:inline
Mode: m:docbook
Matches: db:termdef
Template match ≅ db:token
Calls: t:inline
Mode: m:docbook
Matches: db:token
Template match ≅ db:trademark
Calls: t:inline
Mode: m:docbook
Matches: db:trademark
Template match ≅ db:type
Calls: t:inline
Mode: m:docbook
Matches: db:type
Template match ≅ db:typedefname
Calls: t:inline
Mode: m:docbook
Matches: db:typedefname
Template match ≅ db:unionname
Calls: t:inline
Mode: m:docbook
Matches: db:unionname
Template match ≅ db:uri
Calls: t:inline
Mode: m:docbook
Matches: db:uri
Template match ≅ db:userinput
Calls: t:inline
Mode: m:docbook
Matches: db:userinput
Template match ≅ db:varname
Calls: t:inline
Mode: m:docbook
Matches: db:varname
Template match ≅ db:wordasword
Calls: t:inline
Mode: m:docbook
Matches: db:wordasword
Template match ≅ processing-instruction('DocBoo… as text()
Mode: m:docbook
Matches: processing-instruction('DocBook-xslTNG-version')
Template match ≅ processing-instruction('system… as text()
Mode: m:docbook
Matches: processing-instruction('system-property')
Template match ≅ processing-instruction('curren… as text()
Mode: m:docbook
Matches: processing-instruction('current-dateTime')
Template match ≅ processing-instruction('eval') as item()*
Mode: m:docbook
Matches: processing-instruction('eval')
Function fp:in-verbatim($node as element()) as xs:boolean
Used in: main.xsl
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template name="t:inline">
  <xsl:param name="namemap" select="'span'"/>
  <xsl:param name="class" as="xs:string*"/>
  <xsl:param name="local-name-as-class" as="xs:boolean" select="true()"/>
  <xsl:param name="extra-attributes" as="attribute()*" select="()"/>
  <xsl:param name="content">
    <xsl:apply-templates/>
  </xsl:param>

  <xsl:variable name="map"
                select="if ($namemap instance of map(xs:string, xs:string))
                        then $namemap
                        else map { '*': $namemap }"/>

  <xsl:variable name="roles" select="tokenize(normalize-space(@role))"/>

  <xsl:variable name="mapped-names" as="xs:string*">
    <xsl:for-each select="$roles">
      <xsl:if test="exists($map(.))">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="mapped-names" as="xs:string+"
                select="if (empty($mapped-names))
                        then '*'
                        else $mapped-names"/>

  <xsl:variable name="classes" as="xs:string*">
    <xsl:for-each select="$roles">
      <xsl:sequence select="."/>
    </xsl:for-each>
    <xsl:sequence select="$class"/>
    <xsl:if test="$local-name-as-class">
      <xsl:sequence select="local-name(.)"/>
    </xsl:if>
  </xsl:variable>

  <!-- sort them and make them unique -->
  <xsl:variable name="classes" as="xs:string*">
    <xsl:for-each select="distinct-values($classes)">
      <xsl:sort select="."/>
      <xsl:sequence select="."/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="attrs" as="attribute()*">
    <xsl:apply-templates select="@*"/>
  </xsl:variable>

  <xsl:element namespace="http://www.w3.org/1999/xhtml"
               name="{$map($mapped-names[1])}">
    <xsl:sequence select="f:attributes(., $attrs,
                                       if ($local-name-as-class)
                                       then (local-name(.), $classes)
                                       else $classes, ())"/>
    <xsl:sequence select="$extra-attributes"/>
    <xsl:apply-templates select="." mode="m:link">
      <xsl:with-param name="primary-markup" select="false()"/>
      <xsl:with-param name="content" select="$content"/>
    </xsl:apply-templates>
  </xsl:element>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="db:abbrev">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:accel">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:acronym">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:application">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:author">
  <span>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="db:personname|db:orgname"/>
  </span>
</xsl:template>

<xsl:template match="db:buildtarget">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:citation">
  <!-- Can we find a bibliography entry for this citation? -->
  <xsl:variable name="bib" select="f:biblioentries(., $bibliography-collection)[1]"/>
  <xsl:choose>
    <xsl:when test="$bib">
      <xsl:call-template name="tp:xref">
        <xsl:with-param name="linkend" select="$bib/@xml:id"/>
        <xsl:with-param name="target" select="$bib"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="t:inline"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:citebiblioid">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:citerefentry">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:citetitle">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'cite'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:classname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:code">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="local-name-as-class" select="false()"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:command">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:computeroutput">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap"
                    select="if (fp:in-verbatim(.)) then 'span' else 'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:constant">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:database">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:date">
  <xsl:variable name="format" select="f:date-format(.)"/>
  <xsl:choose>
    <xsl:when test="$format = 'apply-templates'">
      <xsl:call-template name="t:inline"/>
    </xsl:when>
    <xsl:when test="string(.) castable as xs:dateTime">
      <xsl:call-template name="t:inline">
        <xsl:with-param name="content" as="xs:string">
          <!-- Don't attempt to use localization on Saxon HE -->
          <xsl:sequence
              use-when="system-property('xsl:product-name') = 'SAXON'
                        and not(
                           starts-with(system-property('xsl:product-version'), 'EE')
                           )"
              select="format-dateTime(xs:dateTime(.), $format)"/>

          <xsl:sequence
              use-when="not(system-property('xsl:product-name') = 'SAXON'
                            and not(
                              starts-with(system-property('xsl:product-version'), 'EE')
                              ))"
              select="format-dateTime(xs:dateTime(.), $format,
                                      f:l10n-language(.), (), ())"/>
        </xsl:with-param>
        <xsl:with-param name="extra-attributes" as="attribute()*">
          <xsl:attribute name="time" select="."/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="string(.) castable as xs:date">
      <xsl:call-template name="t:inline">
        <xsl:with-param name="content" as="xs:string">
          <!-- Don't attempt to use localization on Saxon HE -->
          <xsl:sequence
              use-when="system-property('xsl:product-name') = 'SAXON'
                        and not(
                           starts-with(system-property('xsl:product-version'), 'EE')
                           )"
              select="format-date(xs:date(.), $format)"/>
          <xsl:sequence
              use-when="not(system-property('xsl:product-name') = 'SAXON'
                            and not(
                              starts-with(system-property('xsl:product-version'), 'EE')
                              ))"
              select="format-date(xs:date(.), $format,
                                  f:l10n-language(.), (), ())"/>
        </xsl:with-param>
        <xsl:with-param name="extra-attributes" as="attribute()*">
          <xsl:attribute name="time" select="."/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="t:inline"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:editor">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:email">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:emphasis">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap"
                    select="map { '*': 'em',
                                  'strong': 'strong',
                                  'bold': 'strong' }"/>
    <xsl:with-param name="local-name-as-class" select="false()"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:enumidentifier">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:enumname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:enumvalue">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:envar">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:errorcode">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:errorname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:errortext">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:errortype">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:exceptionname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:filename">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:firstterm">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:foreignphrase">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:function">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:guibutton">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:guiicon">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:guilabel">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:guimenu">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:guimenuitem">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:guisubmenu">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:hardware">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:initializer">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:inlineequation">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:interfacename">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:jobtitle">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:keycap">
  <xsl:variable name="lookup"
                select="if (@function = 'other')
                        then @otherfunction/string()
                        else @function/string()"/>
  <xsl:choose>
    <xsl:when test="exists(node())">
      <xsl:call-template name="t:inline"/>
    </xsl:when>
    <xsl:when test="empty($lookup)">
      <xsl:message select="'Ignoring keycap without function attribute or content'"/>
    </xsl:when>
    <xsl:when test="exists(fp:localization-template(., 'keycap'))">
      <xsl:call-template name="t:inline">
        <xsl:with-param name="content" as="item()*">
          <xsl:apply-templates select="." mode="m:gentext">
            <xsl:with-param name="group" select="'keycap'"/>
          </xsl:apply-templates>
        </xsl:with-param>
        <xsl:with-param name="class" select="$lookup"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'No keycap for', $lookup"/>
      <xsl:call-template name="t:inline">
        <xsl:with-param name="content" as="item()*">
          <xsl:sequence select="'NOKEYCAP'"/>
        </xsl:with-param>
        <xsl:with-param name="class" select="$lookup"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:keycode">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:keycombo">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:keysym">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:literal">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:macroname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:markup">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:mathphrase">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:medialabel">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'em'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:menuchoice">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:methodname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:modifier">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:mousebutton">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:namespace">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:namespacename">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:ooclass">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:ooexception">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:oointerface">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:option">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:optional">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:org">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:orgname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:package">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:parameter">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:person">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:phrase">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:productname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:productnumber">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:prompt">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap"
                    select="if (fp:in-verbatim(.)) then 'span' else 'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:property">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:quote">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'q'"/>
    <xsl:with-param name="local-name-as-class" select="false()"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:replaceable">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'em'"/>
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:returnvalue">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:revnumber">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:shortcut">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:structfield">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:subscript">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'sub'"/>
    <xsl:with-param name="local-name-as-class" select="false()"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:superscript">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'sup'"/>
    <xsl:with-param name="local-name-as-class" select="false()"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:symbol">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:systemitem">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:tag">
  <xsl:variable name="class"
                select="if (@class)
                        then @class/string()
                        else 'element'"/>

  <xsl:variable name="content" as="item()*">
    <xsl:choose>
      <xsl:when test="$class='attribute'">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:when test="$class='attvalue'">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:when test="$class='element'">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:when test="$class='endtag'">
        <xsl:text>&lt;/</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>&gt;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='genentity'">
        <xsl:text>&amp;</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='numcharref'">
        <xsl:text>&amp;#</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='paramentity'">
        <xsl:text>%</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='pi'">
        <xsl:text>&lt;?</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>&gt;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='xmlpi'">
        <xsl:text>&lt;?</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>?&gt;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='starttag'">
        <xsl:text>&lt;</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>&gt;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='emptytag'">
        <xsl:text>&lt;</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>/&gt;</xsl:text>
      </xsl:when>
      <xsl:when test="$class='sgmlcomment'">
        <xsl:text>&lt;!--</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>--&gt;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="local-name-as-class" select="false()"/>
    <xsl:with-param name="class" select="'tag tag-' || $class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:templateid">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:templatename">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:termdef">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:token">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:trademark">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="class" select="@class"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:type">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:typedefname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:unionname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:uri">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
    <xsl:with-param name="class" select="@type"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:userinput">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap"
                    select="if (fp:in-verbatim(.)) then 'span' else 'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:varname">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'code'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="db:wordasword">
  <xsl:call-template name="t:inline">
    <xsl:with-param name="namemap" select="'em'"/>
  </xsl:call-template>
</xsl:template>

<xsl:template match="processing-instruction('DocBook-xslTNG-version')" as="text()">
  <xsl:value-of select="$v:VERSION"/>
</xsl:template>

<xsl:template match="processing-instruction('system-property')" as="text()">
  <xsl:value-of select="system-property(normalize-space(.))"/>
</xsl:template>

<xsl:template match="processing-instruction('current-dateTime')" as="text()">
  <xsl:variable name="attr" select="f:pi-attributes(.)"/>
  <xsl:variable name="then" as="xs:dateTime">
    <xsl:choose>
      <xsl:when test="empty($attr/@dateTime)">
        <xsl:sequence select="current-dateTime()"/>
      </xsl:when>
      <xsl:when test="$attr/@dateTime castable as xs:dateTime">
        <xsl:sequence select="xs:dateTime($attr/@dateTime)"/>
      </xsl:when>
      <xsl:when test="$attr/@dateTime castable as xs:date">
        <xsl:sequence select="xs:dateTime(xs:date($attr/@dateTime) + xs:dayTimeDuration('PT12H'))"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'Cannot parse', $attr/@dateTime/string(), 'as a date/time'"/>
        <xsl:sequence select="current-dateTime()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="offset" as="xs:duration">
    <xsl:choose>
      <xsl:when test="empty($attr/@offset)">
        <xsl:sequence select="xs:dayTimeDuration('PT0S')"/>
      </xsl:when>
      <xsl:when test="$attr/@offset castable as xs:dayTimeDuration">
        <xsl:sequence select="xs:dayTimeDuration($attr/@offset)"/>
      </xsl:when>
      <xsl:when test="$attr/@offset castable as xs:yearMonthDuration">
        <xsl:sequence select="xs:yearMonthDuration($attr/@offset)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="'Cannot parse', $attr/@offset/string(), 'as a duration'"/>
        <xsl:sequence select="xs:dayTimeDuration('PT0S')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="format"
                select="($attr/@format, $date-dateTime-format)[1]"/>
  <xsl:value-of select="format-dateTime($then + $offset, $format)"/>
</xsl:template>

<xsl:template match="processing-instruction('eval')" as="item()*">
  <xsl:if test="f:is-true($allow-eval)">
    <xsl:evaluate xpath="string(.)"
                  with-params="map:merge(($vp:static-parameters, $vp:dynamic-parameters))"
                  context-item="."
                  namespace-context="."/>
  </xsl:if>
</xsl:template>

<xsl:function name="fp:in-verbatim" as="xs:boolean">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="$node/ancestor::db:programlisting
                        or $node/ancestor::db:screen
                        or $node/ancestor::db:synopsis"/>
</xsl:function>

</xsl:stylesheet>

xlink.xsl

8 templates (3 used only in one other module), 9 functions (9 used only in one other module), 2 variables (2 used only in one other module)

Instructions
Variable $vp:xmlns-scheme
Used in: main.xsl
Variable $vp:xpath-scheme
Used in: main.xsl
Function fp:pmuj-enabled($context as node()) as xs:boolean
Function fp:pmuj($node as element(), $id as xs:string)
Template match ≅ *
Mode: m:docbook
Priority: 100
Matches: *
Template match ≅ *
Calls: f:href()
Mode: mp:out-of-line-pmuj
Matches: *
Function f:xpointer-idref($xpointer) as xs:string?
Used in: main.xsl
Template match ≅ *
Mode: mp:xlink-sources
Matches: *
Template match ≅ *
Mode: mp:xlink-targets
Matches: *
Template match ≅ *
Mode: mp:locator
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xlink='http://www.w3.org/1999/xlink'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp h m map mp t tp v vp xlink xs"
                version="3.0">

<!-- This module does some XLink processing. It doesn't generate fully
     explicit XLink elements, so it doesn't do exactly conformant
     XLink processing. -->

<xsl:key name="exlink" match="*" use="@xlink:type"/>
<xsl:key name="linkend" match="*" use="@linkend"/>
<xsl:key name="href" match="*" use="@xlink:href"/>

<!-- N.B. parens in the scheme expression are not handled! -->
<xsl:variable name="vp:xmlns-scheme"
              select="'^\s*xmlns\s*\(\s*(\c+)\s*=\s*(.*?)\)\s*$'"/>
<xsl:variable name="vp:xpath-scheme"
              select="'^\s*xpath\s*\((.*?)\)\s*$'"/>

<!-- Boy, do you wanna make sure this is cached if you have
     a lot of extended XLinks. This is going to get called for
     at least every inline. -->
<xsl:function name="fp:xlink-sources" as="document-node()" cache="yes">
  <xsl:param name="document" as="document-node()"/>
  <xsl:document>
    <xsl:apply-templates select="key('exlink', 'extended', $document)"
                         mode="mp:xlink-sources"/>
  </xsl:document>
</xsl:function>

<xsl:function name="fp:xlink-targets" as="document-node()" cache="yes">
  <xsl:param name="document" as="document-node()"/>
  <xsl:document>
    <xsl:apply-templates select="key('exlink', 'extended', $document)"
                         mode="mp:xlink-targets"/>
  </xsl:document>
</xsl:function>

<xsl:function name="f:xlink-style" as="xs:string">
  <xsl:param name="document" as="document-node()"/>

  <xsl:choose>
    <xsl:when test="$xlink-style != 'document'">
      <xsl:sequence select="$xlink-style"/>
    </xsl:when>
    <xsl:when test="$document/h:html">
      <!-- We're processing chunks, so look where we squirreled away
           the answer when we were processing the DocBook document. -->
      <xsl:sequence select="($document/h:html/h:div/@db-xlink/string(),
                             $xlink-style-default)[1]"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence
          select="f:pi($document/*/db:info, 'xlink-style', $xlink-style-default)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:pmuj-enabled" as="xs:boolean" cache="yes">
  <xsl:param name="context" as="node()"/>
  <xsl:sequence select="f:is-true(f:pi(root($context)/*/db:info,
                                  'pmuj', $experimental-pmuj))"/>
</xsl:function>

<xsl:function name="fp:pmuj">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="id" as="xs:string"/>

  <xsl:variable name="sources" as="xs:string*">
    <xsl:for-each select="(key('linkend', $id, root($node)),
                           key('href', '#'||$id, root($node)))">
      <xsl:if test="not(@xlink:type='locator')">
        <xsl:sequence select="'#' || f:generate-id(.)"/>
      </xsl:if>
    </xsl:for-each>

    <xsl:variable name="xlinks" select="fp:xlink-targets(root($node))"/>
    <xsl:variable name="sources" select="key('id', generate-id($node), $xlinks)"/>
    <xsl:if test="$sources">
      <xsl:variable name="locators" as="element()*">
        <xsl:apply-templates select="$node" mode="mp:out-of-line-pmuj">
          <xsl:with-param name="document" select="root($node)"/>
          <xsl:with-param name="locators" select="$sources"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:for-each select="$locators[self::db:locator]">
        <xsl:sequence select="@xlink:href/string()"/>
      </xsl:for-each>
    </xsl:if>
  </xsl:variable>

  <xsl:for-each select="distinct-values($sources)">
    <a class="pmuj" href="{.}">◎</a>
  </xsl:for-each>
</xsl:function>

<xsl:template name="t:xlink">
  <xsl:param name="content">
    <xsl:apply-templates/>
  </xsl:param>

  <xsl:if test="fp:pmuj-enabled(/)">
    <xsl:sequence select="@xml:id ! fp:pmuj(./parent::*, ./string())"/>
  </xsl:if>

  <xsl:variable name="xlinks" select="fp:xlink-sources(root(.))"/>

  <xsl:variable name="targets"
                select="key('id', generate-id(.), $xlinks)"/>

  <!-- Not handled: an explicitly extended link that is also
       the target of out-of-band links. -->

  <xsl:choose>
    <xsl:when test="@xlink:type = 'simple' or @xlink:href">
      <xsl:call-template name="tp:simple-xlink">
        <xsl:with-param name="content">
          <xsl:sequence select="$content"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>

    <xsl:when test="$targets">
      <xsl:call-template name="tp:out-of-line-xlink">
        <xsl:with-param name="document" select="root(.)"/>
        <xsl:with-param name="locators" select="$targets"/>
      </xsl:call-template>
    </xsl:when>

    <xsl:otherwise>
      <!-- It's just a normal, unlinked element -->
      <xsl:sequence select="$content"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="*[@xlink:type = 'extended']" priority="100">
  <xsl:choose>
    <xsl:when test="*[@xlink:type='resource']">
      <xsl:apply-templates select="*[@xlink:type='resource']"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Inline extended XLink with no resource:', ."/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="tp:simple-xlink">
  <xsl:param name="content">
    <xsl:apply-templates/>
  </xsl:param>

  <xsl:variable name="link">
    <xsl:choose>
      <xsl:when test="@xlink:href
                      and (not(@xlink:type)
                           or @xlink:type='simple')">
        <a>
          <xsl:if test="fp:pmuj-enabled(/)">
            <xsl:attribute name="id" select="f:generate-id(.)"/>
          </xsl:if>

          <xsl:if test="@xlink.title">
            <xsl:attribute name="title" select="@xlink:title"/>
          </xsl:if>

          <xsl:attribute name="href">
            <xsl:choose>
              <!-- if the href starts with # and does not contain an "(" -->
              <!-- or if the href starts with #xpointer(id(, it's just an ID -->
              <xsl:when test="starts-with(@xlink:href,'#')
                              and (not(contains(@xlink:href,'&#40;'))
                              or starts-with(@xlink:href,
                                             '#xpointer&#40;id&#40;'))">
                <xsl:variable name="idref" select="f:xpointer-idref(@xlink:href)"/>
                <xsl:variable name="target" select="key('id',$idref)[1]"/>

                <xsl:choose>
                  <xsl:when test="not($target)">
                    <xsl:message>
                      <xsl:text>XLink to nonexistent id: </xsl:text>
                      <xsl:sequence select="@xlink:href/string()"/>
                    </xsl:message>
                    <xsl:text>???</xsl:text>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:attribute name="href" select="f:href(/,$target)"/>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:when>

              <!-- otherwise it's a URI -->
              <xsl:otherwise>
                <xsl:sequence select="@xlink:href/string()"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:attribute>
          <xsl:copy-of select="$content"/>
        </a>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="$content"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:sequence select="$link"/>
</xsl:template>

<!-- An "out-of-line" xlink. That is, this element is identified as the
     source of an arc. -->
<xsl:template name="tp:out-of-line-xlink">
  <xsl:param name="document" as="document-node()" required="yes"/>
  <xsl:param name="locators" as="element()+" required="yes"/>

  <xsl:variable name="context" select="."/>
  <xsl:variable name="arcs"
                select="$locators/@arc ! key('genid', ., $document)"/>

  <xsl:variable name="to" as="node()*">
    <xsl:for-each select="$arcs">
      <xsl:variable name="arc" select="."/>
      <xsl:variable name="locators"
                    select="$arc/../*[@xlink:label = $arc/@xlink:to]"/>
      <xsl:for-each select="$locators">
        <xsl:choose>
          <xsl:when test="@xlink:type = 'locator'">
            <xsl:sequence select="."/>
          </xsl:when>
          <xsl:when test="@xlink:type = 'resource'">
            <generated xlink:type="locator"
                       xlink:href="{f:href($context, .)}"/>
          </xsl:when>
        </xsl:choose>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="count($to) eq 1">
      <a href="{$to/@xlink:href}">
        <xsl:if test="$to/@xlink:title">
          <xsl:attribute name="title" select="$to/@xlink:title/string()"/>
        </xsl:if>
        <xsl:apply-templates/>
      </a>
    </xsl:when>
    <xsl:when test="f:xlink-style($document) = 'none'">
      <xsl:apply-templates/>
    </xsl:when>
    <xsl:otherwise>
      <span class="xlink">
        <span class="source">
          <xsl:apply-templates/>
        </span>
        <span class="xlink-arc-list" db-arcs="{f:generate-id(.)}-arcs"/>
        <span class="nhrefs" id="{f:generate-id(.)}-arcs">
          <span class="xlink-arc-delim before">
            <xsl:sequence select="$xlink-arclist-before"/>
          </span>
          <span class="xlink-arc-title">
            <xsl:if test="$to/../@xlink:title">
              <xsl:sequence select="$to/../@xlink:title/string()"/>
            </xsl:if>
          </span>
          <xsl:if test="$to/../@xlink:title">
            <span class="xlink-arc-delim sep">
              <xsl:sequence select="$xlink-arclist-titlesep"/>
            </span>
          </xsl:if>
          <xsl:for-each select="$to">
            <xsl:if test="position() gt 1">
              <span class="xlink-arc-delim sep">
                <xsl:sequence select="$xlink-arclist-sep"/>
              </span>
            </xsl:if>
            <span class="arc">
              <a href="{@xlink:href}">
                <xsl:choose>
                  <xsl:when test="*[@xlink:type='title']">
                    <xsl:apply-templates select="*[@xlink:type='title'][1]"/>
                  </xsl:when>
                  <xsl:when test="@xlink:title">
                    <xsl:sequence select="@xlink:title/string()"/>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:message
                        select="'Warning: inline extended link locator without title', ."/>
                    <xsl:text>???</xsl:text>
                  </xsl:otherwise>
                </xsl:choose>
              </a>
              <xsl:if test="f:is-true($inline-xlink-href)">
                <xsl:text> (</xsl:text>
                <xsl:value-of select="@xlink:href"/>
                <xsl:text>)</xsl:text>
              </xsl:if>
            </span>
          </xsl:for-each>
          <span class="xlink-arc-delim after">
            <xsl:sequence select="$xlink-arclist-after"/>
          </span>
        </span>
      </span>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- An "out-of-line" pmuj. That is, this element is identified as the
     target of an arc. -->
<xsl:template match="*" mode="mp:out-of-line-pmuj">
  <xsl:param name="document" as="document-node()" required="yes"/>
  <xsl:param name="locators" as="element()+" required="yes"/>

  <xsl:variable name="context" select="."/>
  <xsl:variable name="arcs"
                select="$locators/@arc ! key('genid', ., $document)"/>

  <xsl:variable name="from" as="node()*">
    <xsl:for-each select="$arcs">
      <xsl:variable name="arc" select="."/>
      <xsl:variable name="locators"
                    select="$arc/../*[@xlink:label = $arc/@xlink:from]"/>
      <xsl:for-each select="$locators">
        <xsl:choose>
          <xsl:when test="@xlink:type = 'locator'">
            <xsl:sequence select="."/>
          </xsl:when>
          <xsl:when test="@xlink:type = 'resource'">
            <generated xlink:type="locator"
                       xlink:href="{f:href($context, .)}"/>
          </xsl:when>
        </xsl:choose>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:variable>

  <xsl:sequence select="$from"/>
</xsl:template>

<xsl:function name="f:xpointer-idref" as="xs:string?">
  <xsl:param name="xpointer"/>

  <xsl:choose>
    <xsl:when test="starts-with($xpointer, '#xpointer(id(')">
      <xsl:variable name="rest"
                    select="substring-after($xpointer, '#xpointer(id(')"/>
      <xsl:variable name="quote" select="substring($rest, 1, 1)"/>
      <xsl:sequence select="substring-before(substring-after($xpointer, $quote), $quote)"/>
    </xsl:when>
    <xsl:when test="starts-with($xpointer, '#')">
      <xsl:sequence select="substring-after($xpointer, '#')"/>
    </xsl:when>
    <xsl:otherwise>
      <!-- otherwise it's a pointer to some other document -->
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="*" mode="mp:xlink-sources">
  <xsl:for-each select="*[@xlink:type='arc' and @xlink:from and @xlink:to]">
    <xsl:variable name="arc" select="."/>
    <xsl:variable name="from-label" select="@xlink:from"/>
    <xsl:variable name="from-locator" select="../*[@xlink:label = $from-label]"/>

    <xsl:variable name="sources" as="element()*">
      <xsl:for-each select="$from-locator">
        <xsl:choose>
          <xsl:when test="@xlink:href">
            <xsl:sequence
                select="fp:find-xlink-nodes(root(.), @xlink:href, map { })"/>
          </xsl:when>
          <xsl:when test="@xlink:type='resource'">
            <source>
              <xsl:attribute name="xml:id" select="generate-id(.)"/>
            </source>
          </xsl:when>
          <xsl:otherwise>
            <xsl:message>
              <xsl:text>XLink locator without @xlink:href </xsl:text>
              <xsl:text>that isn’t a resource? </xsl:text>
              <xsl:sequence select="."/>
            </xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </xsl:variable>

    <xsl:for-each select="$sources">
      <xsl:copy>
        <xsl:sequence select="@*"/>
        <xsl:attribute name="arc" select="generate-id($arc)"/>
      </xsl:copy>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

<xsl:template match="*" mode="mp:xlink-targets">
  <xsl:for-each select="*[@xlink:type='arc' and @xlink:from and @xlink:to]">
    <xsl:variable name="arc" select="."/>
    <xsl:variable name="to-label" select="@xlink:to"/>
    <xsl:variable name="to-locator" select="../*[@xlink:label = $to-label]"/>

    <xsl:variable name="sources" as="element()*">
      <xsl:for-each select="$to-locator">
        <xsl:choose>
          <xsl:when test="@xlink:href">
            <xsl:sequence
                select="fp:find-xlink-nodes(root(.), @xlink:href, map { })"/>
          </xsl:when>
          <xsl:when test="@xlink:type='resource'">
            <source>
              <xsl:attribute name="xml:id" select="generate-id(.)"/>
            </source>
          </xsl:when>
          <xsl:otherwise>
            <xsl:message>
              <xsl:text>XLink locator without @xlink:href </xsl:text>
              <xsl:text>that isn’t a resource? </xsl:text>
              <xsl:sequence select="."/>
            </xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </xsl:variable>

    <xsl:for-each select="$sources">
      <xsl:copy>
        <xsl:sequence select="@*"/>
        <xsl:attribute name="arc" select="generate-id($arc)"/>
      </xsl:copy>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="mp:locator">
  <source>
    <xsl:attribute name="xml:id" select="generate-id(.)"/>
    <xsl:attribute name="hint"
                   select="node-name(.)
                           || (if (@xml:id) then '/' || @xml:id else '')
                           || ': '
                           || (if (string-length(string(.)) gt 10)
                               then substring(string(.), 1, 10) || '…'
                               else string(.))"/>
  </source>
</xsl:template>

<xsl:function name="fp:find-xlink-nodes">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="locator" as="xs:string"/>
  <xsl:param name="nsmap" as="map(*)"/>

  <xsl:variable name="locator" select="normalize-space($locator)"/>

  <xsl:choose>
    <xsl:when test="$locator = ''"/>
    <xsl:when test="starts-with($locator, '#')">
      <xsl:variable name="id"
                    select="if (contains($locator, ' '))
                            then substring-before(substring($locator, 2), ' ')
                            else substring($locator, 2)"/>
      <xsl:apply-templates select="key('id', $id, $document)" mode="mp:locator"/>
    </xsl:when>
    <xsl:when test="matches($locator, '^\c+\s*\(.*?\)')">
      <xsl:variable name="scheme"
                    select="replace($locator, '^(\c+\s*\(.*?\)).*', '$1')"/>
      <xsl:variable name="rest"
                    select="substring($locator, string-length($scheme)+1)"/>
      <xsl:variable name="name"
                    select="replace($scheme, '^(\c+).*?$', '$1')"/>

      <xsl:choose>
        <xsl:when test="$name eq 'xmlns'">
          <xsl:sequence select="fp:xlink-xmlns-scheme($document, $locator, $nsmap, $scheme, $rest)"/>
        </xsl:when>
        <xsl:when test="$name eq 'xpath'">
          <xsl:sequence select="fp:xlink-xpath-scheme($document, $locator, $nsmap, $scheme, $rest)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message use-when="'xlink' = $v:debug"
                       select="'Ignoring unknown xpointer scheme: ' || $name"/>
          <xsl:sequence select="fp:find-xlink-nodes($document, $rest, $nsmap)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message use-when="'xlink' = $v:debug"
                   select="'Ignoring unrecognized xpointer: ' || $locator"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:xlink-xmlns-scheme">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="locator" as="xs:string"/>
  <xsl:param name="nsmap" as="map(*)"/>
  <xsl:param name="scheme" as="xs:string"/>
  <xsl:param name="href" as="xs:string"/>

  <xsl:choose>
    <xsl:when test="matches($scheme, $vp:xmlns-scheme)">
      <xsl:variable name="prefix" select="replace($scheme, $vp:xmlns-scheme, '$1')"/>
      <xsl:variable name="uri" select="replace($scheme, $vp:xmlns-scheme, '$2')"/>

      <!-- You can't put : in URIs...? -->
      <xsl:variable name="uri" select="replace($uri, '%3A', ':', 'i')"/>

      <xsl:message use-when="'xlink' = $v:debug"
                   select="'xpointer: xmlns('||$prefix||'='||$uri||')'"/>
      <xsl:sequence
          select="fp:find-xlink-nodes($document, $href,
                                      map:merge(($nsmap, map:entry($prefix, $uri))))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message use-when="'xlink' = $v:debug"
                   select="'Unparseable xpointer scheme: ' || $scheme"/>
      <xsl:sequence select="fp:find-xlink-nodes($document, $href, $nsmap)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:xlink-xpath-scheme">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="locator" as="xs:string"/>
  <xsl:param name="nsmap" as="map(*)"/>
  <xsl:param name="scheme" as="xs:string"/>
  <xsl:param name="href" as="xs:string"/>

  <xsl:choose>
    <xsl:when test="matches($scheme, $vp:xpath-scheme)">
      <xsl:variable name="expr" select="replace($scheme, $vp:xpath-scheme, '$1')"/>

      <!-- You can't put [ and ] in URIs; so they're escaped, so unescape them -->
      <xsl:variable name="expr" select="replace($expr, '%5B', '[', 'i')"/>
      <xsl:variable name="expr" select="replace($expr, '%5D', ']', 'i')"/>

      <xsl:message use-when="'xlink' = $v:debug"
                   select="'xpointer: xpath('||$expr||')'"/>

      <!-- Construct the namespace context -->
      <xsl:variable name="nscontext" as="element()">
        <context>
          <xsl:for-each select="map:keys($nsmap)">
            <xsl:namespace name="{.}" select="map:get($nsmap, .)"/>
          </xsl:for-each>
        </context>
      </xsl:variable>

      <xsl:variable name="found" as="node()*">
        <xsl:evaluate context-item="$document" xpath="$expr"
                      namespace-context="$nscontext"/>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="empty($found)">
          <xsl:message use-when="'xlink' = $v:debug"
                       select="'No nodes found for xpath scheme: ' || $expr"/>
          <xsl:sequence select="fp:find-xlink-nodes($document, $href, $nsmap)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="$found" mode="mp:locator"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message use-when="'xlink' = $v:debug"
                   select="'Unparseable xpath scheme: ' || $scheme"/>
      <xsl:sequence select="fp:find-xlink-nodes($document, $href, $nsmap)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

</xsl:stylesheet>

links.xsl

8 templates (2 used only in one other module), 1 function (1 used only in one other module)

Instructions
Template match ≅ db:anchor
Calls: t:inline
Mode: m:docbook
Matches: db:anchor
Template match ≅ db:biblioref
Calls: tp:xref
Mode: m:docbook
Matches: db:biblioref
Template match ≅ db:link
Calls: f:href()
Mode: m:docbook
Matches: db:link
Template match ≅ *
Mode: m:link
Matches: *
Template match ≅ db:firstterm|db:glossterm
Calls: f:href()
Calls: tp:link
Mode: m:link
Matches: db:firstterm, db:glossterm
Template tp:xref match ≅ db:xref
Function fp:localization-template-from-xrefstyle($xref as element(), $target as element()) as element(l:template)
Used in: main.xsl
Template match ≅ db:olink
Mode: m:docbook
Matches: db:olink
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xlink='http://www.w3.org/1999/xlink'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp h l lt m t tp v vp xlink xs"
                version="3.0">

<xsl:template match="db:anchor">
  <xsl:call-template name="t:inline"/>
</xsl:template>

<xsl:template match="db:biblioref">
  <xsl:call-template name="tp:xref"/>
</xsl:template>

<xsl:template match="db:link">
  <xsl:choose>
    <xsl:when test="@linkend and empty(node())">
      <xsl:call-template name="tp:xref"/>
    </xsl:when>
    <xsl:when test="@linkend">
      <xsl:variable name="linkend"
                    select="(@linkend,
                            if (starts-with(@xlink:href, '#'))
                            then substring-after(@xlink:href, '#')
                            else ())[1]"/>
      <xsl:variable name="target"
                    select="if ($linkend)
                            then key('id', $linkend)[1]
                            else ()"/>
      <xsl:choose>
        <xsl:when test="empty($target)">
          <xsl:message select="'Link to non-existent ID: ' || $linkend"/>
          <xsl:sequence select="'[???' || $linkend || '???]'"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="tp:link">
            <xsl:with-param name="href" select="f:href(., $target)"/>
            <xsl:with-param name="content" as="node()*">
              <xsl:apply-templates select="node()"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="tp:link">
        <xsl:with-param name="content" as="item()*">
          <xsl:choose>
            <xsl:when test="empty(node())">
              <xsl:sequence select="@xlink:href/string()"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:apply-templates select="node()"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="*" mode="m:link">
  <xsl:param name="primary-markup" select="false()"/>
  <xsl:param name="content" as="item()*"/>

  <xsl:choose>
    <xsl:when test="@linkend">
      <xsl:call-template name="tp:link">
        <xsl:with-param name="primary-markup" select="$primary-markup"/>
        <xsl:with-param name="href" select="'#' || @linkend"/>
        <xsl:with-param name="content" select="$content"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="@xlink:href">
      <xsl:call-template name="tp:link">
        <xsl:with-param name="primary-markup" select="$primary-markup"/>
        <xsl:with-param name="href" select="@xlink:href/string()"/>
        <xsl:with-param name="content" select="$content"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="t:xlink">
        <xsl:with-param name="content" select="$content"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:firstterm|db:glossterm" mode="m:link">
  <xsl:variable name="target"
                select="key('glossterm', (@baseform,normalize-space(.))[1])"/>

  <xsl:choose>
    <xsl:when test="empty($target)">
      <xsl:message select="'Gloss term has no entry:',
                           (@baseform/string(), normalize-space(.))[1]"/>
      <xsl:apply-templates/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="count($target) gt 1">
        <xsl:message select="'Gloss term has multiple entries:',
                             (@baseform/string(), normalize-space(.))[1]"/>
      </xsl:if>
      <xsl:call-template name="tp:link">
        <xsl:with-param name="primary-markup" select="false()"/>
        <xsl:with-param name="href" select="f:href(., $target[1])"/>
        <xsl:with-param name="content" as="item()*">
          <xsl:apply-templates/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="tp:link">
  <xsl:param name="title" select="db:alt[1]/string()" as="xs:string?"/>
  <xsl:param name="href" select="iri-to-uri(@xlink:href)" as="xs:string"/>
  <xsl:param name="content" as="item()*" required="yes"/>
  <xsl:param name="primary-markup" as="xs:boolean" select="true()"/>

  <xsl:choose>
    <xsl:when test="$href != ''">
      <a href="{$href}">
        <xsl:if test="$primary-markup">
          <xsl:apply-templates select="." mode="m:attributes"/>
        </xsl:if>
        <xsl:if test="fp:pmuj-enabled(/)">
          <xsl:attribute name="id" select="f:generate-id(.)"/>
        </xsl:if>
        <xsl:if test="exists($title)">
          <xsl:attribute name="title" select="$title"/>
        </xsl:if>
        <xsl:sequence select="$content"/>
      </a>
    </xsl:when>
    <xsl:when test="@linkend">
      <xsl:variable name="target" select="f:target(@linkend, .)"/>
      <xsl:choose>
        <xsl:when test="empty($target)">
          <xsl:message select="'Link to undefined ID:', string(@linkend)"/>
          <span class="markup-error">
            <xsl:if test="$primary-markup">
              <xsl:apply-templates select="." mode="m:attributes"/>
            </xsl:if>
            <xsl:if test="exists($title)">
              <xsl:attribute name="title" select="$title"/>
            </xsl:if>
            <xsl:sequence select="'@@LINKEND: ' || @linkend || '@@'"/>
          </span>
        </xsl:when>
        <xsl:otherwise>
          <a href="{f:href(., $target)}">
            <xsl:if test="$primary-markup">
              <xsl:apply-templates select="." mode="m:attributes"/>
            </xsl:if>
            <xsl:if test="exists($title)">
              <xsl:attribute name="title" select="$title"/>
            </xsl:if>
            <xsl:sequence select="$content"/>
          </a>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message select="'Link element with no target?', ."/>
      <span class="markup-error">
        <xsl:if test="$primary-markup">
          <xsl:apply-templates select="." mode="m:attributes"/>
        </xsl:if>
        <xsl:if test="exists($title)">
          <xsl:attribute name="title" select="$title"/>
        </xsl:if>
        <xsl:sequence select="$content"/>
      </span>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:xref" name="tp:xref">
  <xsl:param name="linkend"
             select="(@linkend,
                     if (starts-with(@xlink:href, '#'))
                     then substring-after(@xlink:href, '#')
                     else ())[1]"/>
  <xsl:param name="target"
             select="if ($linkend)
                     then key('id', $linkend)[1]
                     else ()"/>

  <xsl:choose>
    <xsl:when test="empty($target)">
      <xsl:message select="'Link to non-existent ID: ' || $linkend"/>
      <span class='error'>???</span>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="content" as="item()*">
        <xsl:choose>
          <xsl:when test="@endterm">
            <xsl:variable name="label" select="key('id', @endterm)[1]"/>
            <xsl:choose>
              <xsl:when test="empty($label)">
                <xsl:message select="'Endterm to non-existent ID: '
                                     || @endterm/string()"/>
                <xsl:apply-templates select="$target" mode="m:crossref"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="$label/node()"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <xsl:when test="$target/@xreflabel">
            <xsl:sequence select="$target/@xreflabel/string()"/>
          </xsl:when>
          <xsl:when test="@xrefstyle">
            <xsl:call-template name="tp:apply-localization-template">
              <xsl:with-param name="target" select="$target"/>
              <xsl:with-param name="localization-template"
                              select="fp:localization-template-from-xrefstyle(., $target)"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="$target" mode="m:crossref"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>

      <xsl:variable name="vp:pmuj" as="xs:boolean" select="fp:pmuj-enabled(/)"/>

      <!-- page number needs its own html:a element -->
      <xsl:for-each-group select="$content"
                          group-adjacent=". instance of node() and local-name() = ('pagenum')">
        <xsl:choose>
          <xsl:when test="boolean(current-grouping-key())">
            <a href="#{f:id($target)}" class="xref xref-{local-name($target)} xref-{local-name(.)}"/>
          </xsl:when>
          <xsl:otherwise>
            <a href="#{f:id($target)}" class="xref xref-{local-name($target)}">
              <xsl:if test="$vp:pmuj">
                <xsl:attribute name="id" select="f:generate-id(.)"/>
              </xsl:if>
              <xsl:sequence select="current-group()"/>
            </a>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
  
<xsl:function name="fp:localization-template-from-xrefstyle" as="element(l:template)">
  <xsl:param name="xref" as="element()"/>
  <xsl:param name="target" as="element()"/>

  <xsl:variable name="content" as="item()*">
    <xsl:choose>
      <xsl:when test="starts-with($xref/@xrefstyle, 'template:')">
        <!-- Legacy XSLT 1.0 Stylesheets,
             see http://www.sagehill.net/docbookxsl/CustomXrefs.html#UsingTemplate -->
        <xsl:analyze-string select="substring-after($xref/@xrefstyle, 'template:')" regex="%n|%t">
          <xsl:matching-substring>
            <xsl:choose>
              <xsl:when test=". eq '%n'"><lt:label/></xsl:when>
              <xsl:when test=". eq '%t'"><lt:content/></xsl:when>
            </xsl:choose>
          </xsl:matching-substring>
          <xsl:non-matching-substring>
            <lt:text><xsl:sequence select="."/></lt:text>
          </xsl:non-matching-substring>
        </xsl:analyze-string>
      </xsl:when>
      <xsl:when test="starts-with($xref/@xrefstyle, 'select:')">
        <!-- Legacy XSLT 1.0 Stylesheets,
             see http://www.sagehill.net/docbookxsl/CustomXrefs.html#UsingSelect -->
        <xsl:message select="'Warning: xref/@xrefstyle with select: Syntax is not supported'"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- See xslTNG Reference Table 4.1. Template %-letter substitutions -->
        <xsl:analyze-string select="$xref/@xrefstyle" regex="%label|%l|%c|%p">
          <xsl:matching-substring>
            <xsl:choose>
              <xsl:when test=". eq '%label'">
                <xsl:sequence select="fp:localization-template($target,'xref-number')/*"/>
              </xsl:when>
              <xsl:when test=". eq '%l'"><lt:label/></xsl:when>
              <xsl:when test=". eq '%c'"><lt:content/></xsl:when>
              <xsl:when test=". eq '%p'"><lt:pagenum/></xsl:when>
            </xsl:choose>
          </xsl:matching-substring>
          <xsl:non-matching-substring>
            <lt:text><xsl:sequence select="."></xsl:sequence></lt:text>
          </xsl:non-matching-substring>
        </xsl:analyze-string>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <l:template>
    <xsl:sequence select="$content"/>
  </l:template>
</xsl:function>

<xsl:template match="db:olink">
  <xsl:variable name="targetdoc" select="@targetdoc/string()"/>
  <xsl:variable name="targetptr" select="@targetptr/string()"/>
  <xsl:variable name="targetdb" select="($v:olink-databases[@targetdoc = $targetdoc])[1]"/>
  <xsl:variable name="obj" select="key('targetptr', $targetptr, root($targetdb))"/>

  <xsl:choose>
    <xsl:when test="empty($targetdb)">
      <xsl:message select="'olink: no targetdoc:', $targetdoc"/>
      <span class="error">
        <xsl:sequence select="'olink: ' || $targetdoc || '/' || $targetptr"/>
      </span>
    </xsl:when>
    <xsl:when test="empty($obj)">
      <xsl:message select="'olink: no targetptr: ' || $targetdoc || '/' || $targetptr"/>
      <span class="error">
        <xsl:sequence select="'olink: ' || $targetdoc || '/' || $targetptr"/>
      </span>
    </xsl:when>
    <xsl:when test="empty(node())">
      <a href="{$obj/@href}">
        <xsl:sequence select="$obj/h:xreftext/node()"/>
      </a>
    </xsl:when>
    <xsl:otherwise>
      <a href="{$obj/@href}">
        <xsl:apply-templates/>
      </a>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

xref.xsl

20 templates (1 used only in one other module), 2 variables (2 used only in one other module)

Instructions
Variable $v:user-xref-groups as element()*
Used in: main.xsl
Variable $v:xref-groups as element()+
Template match ≅ *
Template tp:apply-localization-template match ≅
Used in: main.xsl
Mode: m:docbook
Template match ≅ * as item()*
Mode: m:crossref-label
Matches: *
Template match ≅ db:varlistentry as item()*
Mode: m:crossref-label
Matches: db:varlistentry
Template match ≅ db:answer as item()*
Mode: m:crossref-label
Matches: db:answer
Template match ≅ db:biblioentry|db:bibliomixed
Mode: m:crossref-label
Matches: db:biblioentry, db:bibliomixed
Template match ≅ *
Mode: m:crossref-number
Matches: *
Template match ≅ *
Mode: m:crossref-number-separator
Matches: *
Template match ≅ *
Mode: m:crossref-title
Matches: *
Template match ≅ db:bridgehead
Mode: m:crossref-title
Matches: db:bridgehead
Template match ≅ db:varlistentry
Mode: m:crossref-title
Matches: db:varlistentry
Template match ≅ db:glossentry
Mode: m:crossref-label
Matches: db:glossentry
Template match ≅ db:glossterm
Mode: m:crossref-label
Matches: db:glossterm
Template match ≅ db:see|db:seealso
Mode: m:crossref-label
Matches: db:see, db:seealso
Template match ≅ db:area|db:areaset|db:co
Mode: m:crossref-label
Matches: db:area, db:areaset, db:co
Template match ≅ db:production
Mode: m:crossref-title
Matches: db:production
Template match ≅ db:refentry
Mode: m:crossref-label
Matches: db:refentry
Template match ≅ db:refnamediv
Mode: m:crossref-title
Matches: db:refnamediv
Template match ≅ db:biblioentry|db:bibliomixed
Mode: m:crossref-title
Matches: db:biblioentry, db:bibliomixed
Template match ≅ *
Mode: m:crossref-suffix
Matches: *
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:variable name="v:user-xref-groups" as="element()*"/>

<xsl:variable name="v:xref-groups" as="element()+"
              xmlns:db="http://docbook.org/ns/docbook">
  <xsl:sequence select="$v:user-xref-groups"/>

  <crossref xpath="self::db:section[ancestor::db:preface]"
            group="xref"
            template="section-in-preface"/>

  <crossref xpath="self::db:section"
            group="xref-number-and-title"/>

  <crossref xpath="self::db:chapter|self::db:appendix"
            group="xref-number-and-title"/>

  <crossref xpath="self::db:part|self::db:reference"
            group="xref-number-and-title"/>

  <crossref xpath="self::db:figure|self::db:example|self::db:table
                   |self::db:procedure|self::db:equation
                   |self::db:formalgroup"
            group="xref-number-and-title"/>

  <crossref xpath="self::*"
            group="xref"/>
</xsl:variable>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:crossref">
  <xsl:param name="context" as="xs:string?"/>
  <xsl:param name="template" as="xs:string?"/>

  <xsl:variable name="this" select="."/>
  <xsl:variable name="prop" as="element()?">
    <xsl:iterate select="$v:xref-groups">
      <xsl:variable name="test" as="element()*">
        <xsl:evaluate context-item="$this" xpath="@xpath"/>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="$test">
          <xsl:sequence select="."/>
          <xsl:break/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:next-iteration/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:iterate>
  </xsl:variable>

  <xsl:variable name="context"
                select="($context, $prop/@group/string())[1]"/>
  <xsl:variable name="template"
                select="($template, $prop/@template/string(), local-name(.))[1]"/>

  <xsl:variable name="template"
                select="fp:localization-template(., $context)"/>

  <!--
  <xsl:message select="local-name(.), $context, $template"/>
  -->

  <xsl:call-template name="tp:apply-localization-template">
    <xsl:with-param name="localization-template" select="$template"/>
  </xsl:call-template>
</xsl:template>
  
<xsl:template name="tp:apply-localization-template">
  <xsl:param name="localization-template"/>
  <xsl:param name="target" as="element()" select="."/>

  <xsl:variable name="label" as="item()*">
    <xsl:if test="$localization-template/lt:label">
      <xsl:apply-templates select="$target" mode="m:crossref-label"/>
    </xsl:if>
  </xsl:variable>

  <xsl:variable name="title" as="node()*">
    <xsl:if test="$localization-template/lt:content">
      <xsl:apply-templates select="$target" mode="m:crossref-title">
        <xsl:with-param name="purpose" select="'crossref'"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:variable>

  <xsl:apply-templates select="$localization-template" mode="mp:localization">
    <xsl:with-param name="context" select="$target"/>
    <xsl:with-param name="label" select="$label"/>
    <xsl:with-param name="content" select="$title"/>
  </xsl:apply-templates>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:crossref-label" as="item()*">
  <xsl:apply-templates select="." mode="m:headline-label">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:varlistentry" mode="m:crossref-label" as="item()*">
  <xsl:apply-templates select="db:term[1]"/>
</xsl:template>

<xsl:template match="db:answer" mode="m:crossref-label" as="item()*">
  <xsl:variable name="label"
                select="ancestor::db:qandaset[@defaultlabel][1]/@defaultlabel/string()"/>
  <xsl:variable name="label"
                select="if ($label)
                        then $label
                        else $qandaset-default-label"/>

  <xsl:choose>
    <xsl:when test="$label = 'none' or $label='number'">
      <xsl:apply-templates select="preceding-sibling::db:question"
                           mode="m:crossref-label"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:bibliomixed|db:biblioentry" mode="m:crossref-label">
  <xsl:choose>
    <xsl:when test="*[1]/self::db:abbrev">
      <xsl:apply-templates select="db:abbrev[1]"/>
    </xsl:when>
    <xsl:when test="@xml:id">
      <xsl:value-of select="@xml:id"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:crossref-number">
  <xsl:apply-templates select="." mode="m:headline-number">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:crossref-number-separator">
  <xsl:param name="number" as="item()*" required="yes"/>
  <xsl:param name="title" as="item()*" required="yes"/>
  <xsl:text>, </xsl:text>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:crossref-title">
  <xsl:apply-templates select="(db:info/db:titleabbrev, db:info/db:title)[1]"
                       mode="m:title">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:bridgehead" mode="m:crossref-title">
  <xsl:apply-templates select="." mode="m:title"/>
</xsl:template>

<xsl:template match="db:varlistentry" mode="m:crossref-title">
  <xsl:apply-templates select="db:term[1]" mode="m:title">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:glossentry" mode="m:crossref-label">
  <xsl:apply-templates select="db:glossterm[1]/node()"/>
</xsl:template>

<xsl:template match="db:glossterm" mode="m:crossref-label">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:see|db:seealso" mode="m:crossref-label">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="db:area|db:areaset|db:co" mode="m:crossref-label">
  <xsl:apply-templates select="." mode="m:callout-bug"/>
</xsl:template>

<xsl:template match="db:production" mode="m:crossref-title">
  <xsl:apply-templates select="db:lhs[1]" mode="m:title">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:refentry" mode="m:crossref-label">
  <xsl:apply-templates select="." mode="m:headline-title">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:refnamediv" mode="m:crossref-title">
  <xsl:apply-templates select="db:refname[1]" mode="m:headline-title">
    <xsl:with-param name="purpose" select="'crossref'"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="db:bibliomixed|db:biblioentry" mode="m:crossref-title">
  <xsl:choose>
    <xsl:when test="node()[1]/self::db:abbrev
                    or (node()[1]/text()
                        and normalize-space(node()[1]) = ''
                        and node()[2]/self::db:abbrev)">
      <xsl:apply-templates select="db:abbrev[1]"/>
    </xsl:when>
    <xsl:when test="@xml:id">
      <xsl:value-of select="@xml:id"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:crossref-suffix">
  <xsl:param name="title" as="item()*" required="yes"/>

  <xsl:sequence select="()"/>
</xsl:template>

</xsl:stylesheet>

attributes.xsl

35 templates, 2 functions (2 used only in one other module)

Instructions
Function fp:common-attributes#1($node as element()) as attribute()*
Used in: main.xsl
Function fp:common-attributes#2($node as element(), $genid as xs:boolean) as attribute()*
Used in: main.xsl
Template match ≅ db:article as attribute()*
Mode: m:attributes
Matches: db:article
Template match ≅ db:book|db:part|db:reference|d… as attribute()*
Mode: m:attributes
Matches: db:book, db:part, db:reference, db:set
Template match ≅ db:acknowledgements|db:appendi… as attribute()*
Mode: m:attributes
Matches: db:acknowledgements, db:appendix, db:chapter, db:colophon, db:dedication, db:partintro, db:preface, db:refentry
Template match ≅ db:refsect1|db:refsect2|db:ref… as attribute()*
Mode: m:attributes
Matches: db:refsect1, db:refsect2, db:refsect3, db:refsection, db:sect1, db:sect2, db:sect3, db:sect4, db:sect5, db:section
Template match ≅ db:refnamediv|db:refsynopsisdi… as attribute()*
Mode: m:attributes
Matches: db:refnamediv, db:refsynopsisdiv
Template match ≅ db:topic as attribute()*
Mode: m:attributes
Matches: db:topic
Template match ≅ db:procedure as attribute()*
Mode: m:attributes
Matches: db:procedure
Template match ≅ db:orderedlist as attribute()*
Mode: m:attributes
Matches: db:orderedlist
Template match ≅ db:listitem|db:member|db:para|… as attribute()*
Mode: m:attributes
Matches: db:listitem, db:member, db:para, db:tbody, db:tfoot, db:thead
Template match ≅ db:simplelist as attribute()*
Mode: m:attributes
Matches: db:simplelist
Template match ≅ db:variablelist as attribute()*
Mode: m:attributes
Matches: db:variablelist
Template match ≅ db:glossentry as attribute()*
Mode: m:attributes
Matches: db:glossentry
Template match ≅ db:bridgehead as attribute()*
Mode: m:attributes
Matches: db:bridgehead
Template match ≅ db:informaltable|db:table as attribute()*
Mode: m:attributes
Matches: db:informaltable, db:table
Template match ≅ db:informaltable|db:table as attribute()*
Mode: m:attributes
Matches: db:informaltable, db:table
Template match ≅ db:row as attribute()*
Mode: m:attributes
Matches: db:row
Template match ≅ db:audiodata|db:imagedata|db:v… as attribute()*
Mode: m:attributes
Matches: db:audiodata, db:imagedata, db:videodata
Template match ≅ db:caution|db:danger|db:import… as attribute()*
Mode: m:attributes
Matches: db:caution, db:danger, db:important, db:note, db:tip, db:warning
Template match ≅ db:address|db:classsynopsisinf… as attribute()*
Mode: m:attributes
Matches: db:address, db:classsynopsisinfo, db:funcsynopsisinfo, db:literallayout, db:programlisting, db:screen, db:synopsis
Template match ≅ db:lhs as attribute()*
Mode: m:attributes
Matches: db:lhs
Template match ≅ db:rhs as attribute()*
Mode: m:attributes
Matches: db:rhs
Template match ≅ db:personname as attribute()*
Mode: m:attributes
Matches: db:personname
Template match ≅ db:link as attribute()*
Mode: m:attributes
Matches: db:link
Template match ≅ db:equation|db:example|db:figu… as attribute()*
Mode: m:attributes
Matches: db:equation, db:example, db:figure, db:formalgroup, db:informalequation, db:informalexample, db:informalfigure
Template match ≅ db:bibliography|db:glossary|db… as attribute()*
Mode: m:attributes
Matches: db:bibliography, db:glossary, db:index
Template match ≅ db:biblioset as attribute()*
Mode: m:attributes
Matches: db:biblioset
Template match ≅ db:biblioentry as attribute()*
Mode: m:attributes
Matches: db:biblioentry
Template match ≅ db:answer|db:bibliodiv|db:glos… as attribute()*
Mode: m:attributes
Matches: db:answer, db:bibliodiv, db:glossdiv, db:indexdiv, db:qandadiv, db:qandaentry, db:qandaset, db:question
Template match ≅ db:annotation as attribute()*
Mode: m:attributes
Matches: db:annotation
Template match ≅ db:toc as attribute()*
Mode: m:attributes
Matches: db:toc
Template match ≅ * as attribute()*
Mode: m:attributes
Matches: *
Template match ≅ @*
Mode: m:docbook
Matches: @*
Template match ≅ @xml:id
Mode: m:docbook
Matches: @xml:id
Template match ≅ @xml:lang
Mode: m:docbook
Matches: @xml:lang
Template match ≅ @dir
Mode: m:docbook
Matches: @dir
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f fp m xs"
                version="3.0">

<xsl:function name="fp:common-attributes" as="attribute()*">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="fp:common-attributes($node, true())"/>
</xsl:function>

<xsl:function name="fp:common-attributes" as="attribute()*">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="genid" as="xs:boolean"/>
  <xsl:apply-templates select="$node/@*"/>
  <xsl:if test="$genid and not($node/@xml:id) and $node/parent::*">
    <xsl:attribute name="id" select="f:generate-id($node)"/>
  </xsl:if>
  <xsl:sequence select="f:chunk($node)"/>
  <xsl:sequence select="fp:css-properties($node)"/>
</xsl:function>

<xsl:template match="db:article" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  
  <xsl:sequence select="f:attributes(., $attr,
                                     (local-name(.), 'component',
                                     @status, @otherclass,
                                     if (@otherclass) then 'otherclass' else @class,
                                     f:conditional-orientation-class(.)),
                                     ())"/>
  <xsl:if test="@label">
    <xsl:attribute name="db-label" select="@label"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:set|db:book|db:part|db:reference"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  
  <xsl:sequence
      select="f:attributes(., $attr,
              (local-name(.), 'division', @status, f:conditional-orientation-class(.)), ())"/>
  <xsl:if test="@label">
    <xsl:attribute name="db-label" select="@label"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:partintro|db:refentry
                     |db:dedication|db:acknowledgements|db:colophon
                     |db:preface|db:chapter|db:appendix"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  
  <xsl:sequence
      select="f:attributes(., $attr,
              (local-name(.), 'component', @status, f:conditional-orientation-class(.)), ())"/>
  <xsl:if test="@label">
    <xsl:attribute name="db-label" select="@label"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:sect1|db:sect2|db:sect3|db:sect4|db:sect5|db:section
                     |db:refsect1|db:refsect2|db:refsect3|db:refsection"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <!-- tempting to add a 'section' class here, but note that if we did
       it would become much harder to distinguish a section from a sect1.
       (I'm not sure that matters, but ...) -->
  
  <xsl:sequence
      select="f:attributes(., $attr, (local-name(.), @status, f:conditional-orientation-class(.)), ())"/>
  <xsl:if test="@label">
    <xsl:attribute name="db-label" select="@label"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:refnamediv|db:refsynopsisdiv" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., true())"/>
  <xsl:sequence select="f:attributes(., $attr)"/>
</xsl:template>

<xsl:template match="db:topic"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:sequence
      select="f:attributes(., $attr, (local-name(.), @status), ())"/>
  <xsl:if test="@label">
    <xsl:attribute name="db-label" select="@label"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:procedure" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., true())"/>
  <xsl:variable name="type" as="xs:string"
                select="if (db:info/db:title)
                        then 'formalobject'
                        else 'informalobject'"/>
  <xsl:sequence select="f:attributes(., $attr, (local-name(.), $type), ())"/>
</xsl:template>  

<xsl:template match="db:orderedlist" mode="m:attributes" as="attribute()*">
  <xsl:param name="exclude-classes" as="xs:string*"/>

  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr,
                                     (local-name(.),
                                      if (@inheritnum = 'inherit')
                                      then 'inheritnum'
                                      else ()),
                                      ())"/>
</xsl:template>

<xsl:template match="db:listitem|db:para|db:member
                    |db:tbody|db:thead|db:tfoot"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr, (), local-name(.))"/>
</xsl:template>

<xsl:template match="db:simplelist" mode="m:attributes" as="attribute()*">
  <xsl:param name="extra-classes" as="xs:string*"/>
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence
      select="f:attributes(., $attr, (local-name(.), $extra-classes), ())"/>
</xsl:template>

<xsl:template match="db:variablelist" mode="m:attributes" as="attribute()*">
  <xsl:param name="term-length" as="item()?"/>
  <xsl:param name="exclude-id" as="xs:boolean" select="false()"/>

  <xsl:variable name="long"
                select="if ($term-length castable as xs:integer
                            and xs:integer($term-length)
                                gt $variablelist-termlength-threshold)
                        then 'long-terms'
                        else ()"/>

  <xsl:variable name="attr" as="attribute()*">
    <xsl:apply-templates select="if ($exclude-id)
                                 then @* except @xml:id
                                 else @*"/>
    <xsl:if test="@termlength">
      <xsl:attribute name="db-termlength" select="@termlength"/>
    </xsl:if>
    <xsl:sequence select="f:chunk(.)"/>
  </xsl:variable>

  <xsl:sequence
      select="f:attributes(., $attr, (local-name(.), $long), ())"/>
</xsl:template>

<xsl:template match="db:glossentry" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:sequence select="f:attributes(., $attr, (local-name(.)), ())"/>
</xsl:template>

<xsl:template match="db:bridgehead" mode="m:attributes" as="attribute()*">
  <xsl:param name="extra-classes" as="xs:string*"/>

  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence
      select="f:attributes(., $attr, (local-name(.), $extra-classes), ())"/>
</xsl:template>

<xsl:template match="db:table[db:tgroup]|db:informaltable[db:tgroup]"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>

  <xsl:variable name="pgwide" as="xs:string?">
    <xsl:if test="@pgwide and @pgwide != '0'">
      <xsl:sequence select="'pgwide'"/>
    </xsl:if>
  </xsl:variable>

  <xsl:variable name="type" as="xs:string"
                select="if (starts-with(local-name(.),'informal'))
                        then 'informalobject'
                        else 'formalobject'"/>

  <xsl:variable name="style"
                select="tokenize(@tabstyle)"/>

  <xsl:sequence
      select="f:attributes(., $attr,
                           (local-name(.), $pgwide, $type, $style, f:conditional-orientation-class(.)),
                           ())"/>
</xsl:template>

<xsl:template match="db:table[not(db:tgroup)]|db:informaltable[not(db:tgroup)]"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" as="attribute()*">
    <xsl:apply-templates select="@* except @xml:id"/>
    <xsl:copy-of
        select="@* except (@xml:id|@xml:lang|@xml:base|@version
                           |@pgwide|@orient|@tabstyle)"/>
    <xsl:sequence select="f:chunk(.)"/>
  </xsl:variable>

  <xsl:variable name="pgwide" as="xs:string?">
    <xsl:if test="@pgwide and @pgwide != '0'">
      <xsl:sequence select="'pgwide'"/>
    </xsl:if>
  </xsl:variable>

  <!-- N.B. We can't handle landscape here, it has to be on the table's parent -->

  <xsl:variable name="type" as="xs:string"
                select="if (starts-with(local-name(.),'informal'))
                        then 'informalobject'
                        else 'formalobject'"/>

  <xsl:variable name="style"
                select="tokenize(@tabstyle)"/>

  <xsl:sequence
      select="f:attributes(., $attr,
                           (local-name(.), $pgwide, $type, $style),
                           ())"/>
</xsl:template>

<xsl:template match="db:row"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" as="attribute()*">
    <xsl:apply-templates select="@*"/>
  </xsl:variable>
  <xsl:sequence select="f:attributes(., $attr, (), ())"/>
</xsl:template>

<xsl:template match="db:imagedata|db:videodata|db:audiodata"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr, (), local-name(.))"/>
</xsl:template>

<xsl:template match="db:note|db:tip|db:important|db:caution|db:warning|db:danger"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence
      select="f:attributes(., $attr, (local-name(.), 'admonition'), ())"/>
</xsl:template>  

<xsl:template match="db:programlisting|db:screen|db:address|db:literallayout
                     |db:synopsis|db:funcsynopsisinfo|db:classsynopsisinfo"
              mode="m:attributes" as="attribute()*">
  <xsl:param name="style" as="xs:string" select="f:verbatim-style(.)"/>
  <xsl:param name="numbered" as="xs:boolean" select="f:verbatim-numbered(.)"/>
  <xsl:param name="long" as="xs:boolean" select="false()"/>
  <xsl:param name="highlight" as="xs:boolean" select="false()"/>

  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>

  <xsl:variable name="lang" as="xs:string?"
                select="if (@language)
                        then 'language-' || @language
                        else if (self::db:programlisting and exists($verbatim-default-language))
                             then 'language-' || $verbatim-default-language
                             else ()"/>

  <xsl:variable name="style" as="xs:string*"
                select="if ($style = 'lines' or $style = 'table')
                        then ('verbatim', 'verblines')
                        else 'verbatim'"/>

  <xsl:variable name="long" as="xs:string?"
                select="if ($long and $numbered)
                        then 'long'
                        else ()"/>

  <xsl:variable name="numbered" as="xs:string?"
                select="if ($numbered)
                        then if (f:verbatim-syntax-highlighter(.) = 'prism')
                             then 'line-numbers'
                             else 'numbered'
                        else ()"/>

  <!-- Doesn't actually work in highlight.js 11.10.0
       https://github.com/highlightjs/highlight.js/issues/4160 -->
  <xsl:variable name="no-highlight" as="xs:string?"
                select="if (f:global-syntax-highlighter(.) = 'highlight.js'
                            and not($highlight))
                        then 'no-highlight'
                        else ()"/>

  <xsl:sequence
      select="f:attributes(., $attr,
                 (local-name(.), $lang, @class, $style, $numbered, $long, $no-highlight), ())"/>
</xsl:template>

<xsl:template match="db:lhs" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr, ('production'), ())"/>
</xsl:template>

<xsl:template match="db:rhs" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="if (preceding-sibling::db:rhs)
                        then f:attributes(., $attr, ('production'), ())
                        else f:attributes(., $attr)"/>
</xsl:template>

<xsl:template match="db:personname" mode="m:attributes" as="attribute()*">
  <xsl:param name="style" as="xs:string?" select="()"/>
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr, (local-name(.), $style), ())"/>
</xsl:template>

<xsl:template match="db:link" mode="m:attributes" as="attribute()*">
  <xsl:param name="title" as="xs:string?" select="()"/>
  <xsl:variable name="attr" as="attribute()*">
    <xsl:apply-templates select="@*"/>
    <xsl:if test="exists($title)">
      <xsl:attribute name="title" select="$title"/>
    </xsl:if>
    <xsl:sequence select="f:chunk(.)"/>
  </xsl:variable>
  <xsl:sequence select="f:attributes(., $attr)"/>
</xsl:template>

<xsl:template match="db:formalgroup
                     |db:figure|db:informalfigure
                     |db:example|db:informalexample
                     |db:equation|db:informalequation"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:variable name="class"
                select="if (@floatstyle)
                        then if (@floatstyle = 'float')
                             then 'float' || $default-float-style
                             else 'float' || @floatstyle
                        else ()"/>

  <xsl:variable name="pgwide" as="xs:string?">
    <xsl:if test="@pgwide and @pgwide != '0'">
      <xsl:sequence select="'pgwide'"/>
    </xsl:if>
  </xsl:variable>
  
  <!-- landscapeFigure is legacy PI from XSLT 1.0 Stylesheets, 
       see http://www.sagehill.net/docbookxsl/LandscapeImage.html -->
  <xsl:variable name="landscape" as="xs:string?">
    <xsl:if test="contains-token(@role, 'landscape') or processing-instruction('landscapeFigure')">
      <xsl:sequence select="'landscape'"/>
    </xsl:if>
  </xsl:variable>

  <xsl:variable name="type"
                select="if (starts-with(local-name(.), 'informal'))
                        then 'informalobject'
                        else 'formalobject'"/>

  <xsl:sequence select="f:attributes(., $attr, (local-name(.), $type, $pgwide, $landscape, $class), ())"/>
</xsl:template>

<xsl:template match="db:index|db:bibliography|db:glossary"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:sequence select="f:attributes(., $attr, (local-name(.), 'component'), ())"/>
</xsl:template>

<xsl:template match="db:biblioset"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr, (local-name(.), @relation), ())"/>
</xsl:template>

<xsl:template match="db:biblioentry"
              mode="m:attributes" as="attribute()*">
  <xsl:param name="style" select="$bibliography-style"/>
  <xsl:choose>
    <xsl:when test="$style = 'iso690'">
      <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
      <xsl:sequence select="f:attributes(., $attr, (local-name(.), 'iso690'), ())"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
      <xsl:sequence select="f:attributes(., $attr)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="db:qandaset|db:qandadiv|db:qandaentry|db:question|db:answer
                     |db:indexdiv|db:bibliodiv|db:glossdiv"
              mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:sequence select="f:attributes(., $attr)"/>
</xsl:template>

<xsl:template match="db:annotation" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:sequence select="f:attributes(., $attr, ('annotation-wrapper'), ())"/>
</xsl:template>

<xsl:template match="db:toc" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(.)"/>
  <xsl:sequence select="f:attributes(., $attr, ('list-of-titles'), ())"/>
</xsl:template>

<xsl:template match="*" mode="m:attributes" as="attribute()*">
  <xsl:variable name="attr" select="fp:common-attributes(., false())"/>
  <xsl:sequence select="f:attributes(., $attr)"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="@*">
  <!-- by default, attributes are suppressed -->
</xsl:template>

<xsl:template match="@xml:id">
  <xsl:attribute name="id" select="."/>
</xsl:template>

<xsl:template match="@xml:lang">
  <xsl:attribute name="lang" select="."/>
</xsl:template>

<xsl:template match="@dir">
  <xsl:attribute name="dir" select="."/>
</xsl:template>

</xsl:stylesheet>

publishers.xsl

3 templates

Instructions
Template match ≅ db:dialogue|db:drama|db:poetry…
Mode: m:docbook
Matches: db:dialogue, db:drama, db:poetry, db:stagedir
Template match ≅ db:line|db:linegroup|db:speake…
Mode: m:docbook
Matches: db:line, db:linegroup, db:speaker
Template match ≅ db:inlinestagedir
Calls: t:inline
Mode: m:docbook
Matches: db:inlinestagedir
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f m t xs"
                version="3.0">

<xsl:template match="db:dialogue|db:drama|db:poetry|db:stagedir">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates select="." mode="m:generate-titlepage"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:linegroup|db:speaker|db:line">
  <div>
    <xsl:apply-templates select="." mode="m:attributes"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="db:inlinestagedir">
  <xsl:call-template name="t:inline"/>
</xsl:template>

</xsl:stylesheet>

annotations.xsl

3 templates

Instructions
Template match ≅ ghost:annotation
Mode: m:docbook
Matches: ghost:annotation
Template match ≅ db:annotation
Mode: m:docbook
Matches: db:annotation
Template match ≅ db:annotation
Mode: m:annotation-content
Matches: db:annotation
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:ghost="http://docbook.org/ns/docbook/ephemeral"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="db f ghost m v xs"
                version="3.0">

<xsl:template match="ghost:annotation">
  <xsl:variable name="number"
                select="count(key('id', @linkend)/preceding::db:annotation)+1"/>
  <db-annotation-marker target="{@linkend}"
                        placement="{@placement}"
                        db-annotation="{$number}">
    <a class="annomark" href="#{@linkend}"
       db-annotation="{@linkend}">
      <xsl:sequence select="$annotation-mark"/>
      <sup class="num">
        <xsl:value-of select="$number"/>
      </sup>
    </a>
  </db-annotation-marker>
</xsl:template>

<xsl:template match="db:annotation">
  <db-annotation id="{f:generate-id(.)}"
                 db-annotation="{count(preceding::db:annotation)+1}">
    <xsl:apply-templates select="." mode="m:annotation-content"/>
  </db-annotation>
</xsl:template>

<xsl:template match="db:annotation" mode="m:annotation-content">
  <div>
    <xsl:apply-templates select="." mode="m:attributes">
    </xsl:apply-templates>
    <div class="annotation-body">
      <div class="annotation-header">
        <div class="annotation-close">
        </div>
        <div class="annotation-title">
          <span class="annomark">
            <xsl:sequence select="$annotation-mark"/>
            <sup class="num">
              <xsl:value-of select="count(preceding::db:annotation)+1"/>
            </sup>
            <xsl:text> </xsl:text>
          </span>
          <xsl:apply-templates select="." mode="m:headline-title"/>
        </div>
      </div>
      <div class="annotation-content">
        <xsl:apply-templates/>
      </div>
    </div>
  </div>
</xsl:template>

</xsl:stylesheet>

chunk.xsl

19 templates, 9 functions (3 unused, 1 used only in one other module)

Instructions
Function f:chunk($node as element()) as attribute()*
Uses: $v:chunk
Used in: main.xsl
Function fp:chunk-include($node as element()) as xs:boolean
Function fp:chunk-exclude($node as element()) as xs:boolean
Function fp:matches-expr($node as element(), $expr as xs:string*) as xs:boolean
Function f:chunk-filename($node as element()) as xs:string
Function fp:chunk-navigation($node as element()) as attribute()*
Function fp:trim-common-prefix#2($source as xs:string, $target as xs:string) as xs:string
Unused
Function fp:trim-common-parts#2($source as xs:string*, $target as xs:string*) as xs:string*
Unused
Function fp:root-base-uri#1($node as element()) as xs:anyURI
Template match ≅ *
Mode: m:chunk-filename
Matches: *
Template match ≅ db:set
Mode: m:chunk-filename
Matches: db:set
Template match ≅ db:book
Mode: m:chunk-filename
Matches: db:book
Template match ≅ db:acknowledgements
Mode: m:chunk-filename
Matches: db:acknowledgements
Template match ≅ db:appendix
Mode: m:chunk-filename
Matches: db:appendix
Template match ≅ db:article
Mode: m:chunk-filename
Matches: db:article
Template match ≅ db:bibliography
Mode: m:chunk-filename
Matches: db:bibliography
Template match ≅ db:chapter
Mode: m:chunk-filename
Matches: db:chapter
Template match ≅ db:colophon
Mode: m:chunk-filename
Matches: db:colophon
Template match ≅ db:dedication
Mode: m:chunk-filename
Matches: db:dedication
Template match ≅ db:glossary
Mode: m:chunk-filename
Matches: db:glossary
Template match ≅ db:index
Mode: m:chunk-filename
Matches: db:index
Template match ≅ db:part
Mode: m:chunk-filename
Matches: db:part
Template match ≅ db:preface
Mode: m:chunk-filename
Matches: db:preface
Template match ≅ db:reference
Mode: m:chunk-filename
Matches: db:reference
Template match ≅ db:refentry
Mode: m:chunk-filename
Matches: db:refentry
Template match ≅ db:topic
Mode: m:chunk-filename
Matches: db:topic
Template match ≅ db:sect1|db:sect2|db:sect3|db:…
Mode: m:chunk-filename
Matches: db:sect1, db:sect2, db:sect3, db:sect4, db:sect5
Template match ≅ db:section
Mode: m:chunk-filename
Matches: db:section
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:function name="f:chunk" as="attribute()*">
  <xsl:param name="node" as="element()"/>

  <xsl:if test="$v:chunk">
    <xsl:if test="fp:chunk-include($node) and not(fp:chunk-exclude($node))">
      <xsl:attribute name="db-chunk"
                     select="f:chunk-filename($node)"/>
      <xsl:attribute name="db-id" select="generate-id($node)"/>
      <xsl:sequence select="fp:chunk-navigation($node)"/>
    </xsl:if>
  </xsl:if>
</xsl:function>

<xsl:function name="fp:chunk-include" as="xs:boolean">
  <xsl:param name="node" as="element()"/>
  <xsl:choose>
    <xsl:when test="fp:matches-expr($node, $chunk-include)">
      <xsl:choose>
        <xsl:when test="$node/self::db:sect1">
          <xsl:sequence select="$chunk-section-depth gt 0"/>
        </xsl:when>
        <xsl:when test="$node/self::db:sect2">
          <xsl:sequence select="$chunk-section-depth gt 1"/>
        </xsl:when>
        <xsl:when test="$node/self::db:sect3">
          <xsl:sequence select="$chunk-section-depth gt 2"/>
        </xsl:when>
        <xsl:when test="$node/self::db:sect3">
          <xsl:sequence select="$chunk-section-depth gt 3"/>
        </xsl:when>
        <xsl:when test="$node/self::db:sect4">
          <xsl:sequence select="$chunk-section-depth gt 4"/>
        </xsl:when>
        <xsl:when test="$node/self::db:sect5">
          <xsl:sequence select="$chunk-section-depth gt 5"/>
        </xsl:when>
        <xsl:when test="$node/self::db:section">
          <xsl:variable name="depth"
                        select="count($node/ancestor::db:section)"/>
          <xsl:sequence select="$chunk-section-depth gt $depth"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="true()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="false()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:chunk-exclude" as="xs:boolean">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="fp:matches-expr($node, $chunk-exclude)"/>
</xsl:function>
  
<xsl:function name="fp:matches-expr" as="xs:boolean">
  <xsl:param name="node" as="element()"/>
  <xsl:param name="expr" as="xs:string*"/>

  <xsl:variable name="nscontext" as="element()">
    <ns>
      <xsl:copy-of select="$v:chunk-filter-namespaces"/>
    </ns>
  </xsl:variable>

  <xsl:variable name="matched" as="xs:boolean?">
    <xsl:iterate select="$expr">
      <xsl:variable name="match" as="element()?">
        <xsl:evaluate context-item="$node" xpath="."
                      namespace-context="$nscontext"/>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="$match">
          <xsl:sequence select="true()"/>
          <xsl:break/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:next-iteration/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:iterate>
  </xsl:variable>

  <xsl:sequence select="exists($matched)"/>
</xsl:function>

<xsl:function name="f:chunk-filename" as="xs:string">
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="pi-filename" as="xs:string?">
    <xsl:choose>
      <xsl:when test="f:pi($node, 'filename')">
        <xsl:sequence select="f:pi($node, 'filename')"/>
      </xsl:when>
      <xsl:when test="f:pi($node/db:info, 'filename')">
        <xsl:sequence select="f:pi($node/db:info, 'filename')"/>
      </xsl:when>
      <!-- href is commonly used instead of filename -->
      <xsl:when test="f:pi($node, 'href')">
        <xsl:sequence select="f:pi($node, 'href')"/>
      </xsl:when>
      <xsl:when test="f:pi($node/db:info, 'href')">
        <xsl:sequence select="f:pi($node/db:info, 'href')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="exists($pi-filename)">
      <xsl:sequence select="if (contains($pi-filename, '.'))
                            then $pi-filename
                            else $pi-filename || $html-extension"/>
    </xsl:when>
    <xsl:when test="f:pi($node, 'basename')">
      <xsl:sequence select="f:pi($node, 'basename') || $html-extension"/>
    </xsl:when>
    <xsl:when test="f:pi($node/db:info, 'basename')">
      <xsl:sequence select="f:pi($node/db:info, 'basename') || $html-extension"/>
    </xsl:when>
    <xsl:when test="$node/@xml:id and f:is-true($use-id-as-filename)">
      <xsl:sequence select="$node/@xml:id || $html-extension"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="name" as="xs:string?">
        <xsl:apply-templates select="$node" mode="m:chunk-filename"/>
      </xsl:variable>
      <xsl:sequence select="if (empty($name))
                            then f:generate-id($node) || $html-extension
                            else $name || $html-extension"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:chunk-navigation" as="attribute()*">
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="nav" select="(f:pi($node, 'navigation'),
                                    f:pi($node/db:info, 'navigation'))[1]"/>
  <xsl:if test="$nav">
    <xsl:attribute name="db-navigation" select="$nav"/>
  </xsl:if>

  <xsl:variable name="nav" select="(f:pi($node, 'top-navigation'),
                                    f:pi($node/db:info, 'top-navigation'))[1]"/>
  <xsl:if test="$nav">
    <xsl:attribute name="db-top-navigation" select="$nav"/>
  </xsl:if>

  <xsl:variable name="nav" select="(f:pi($node, 'bottom-navigation'),
                                    f:pi($node/db:info, 'bottom-navigation'))[1]"/>
  <xsl:if test="$nav">
    <xsl:attribute name="db-bottom-navigation" select="$nav"/>
  </xsl:if>
</xsl:function>


<xsl:function name="fp:trim-common-prefix" as="xs:string" cache="yes">
  <xsl:param name="source" as="xs:string"/>
  <xsl:param name="target" as="xs:string"/>

  <xsl:variable name="tail"
                select="fp:trim-common-parts(
                           tokenize($source, '/'),
                           tokenize($target, '/'))"/>

  <xsl:sequence select="string-join($tail, '/')"/>
</xsl:function>

<xsl:function name="fp:trim-common-parts" as="xs:string*">
  <xsl:param name="source" as="xs:string*"/>
  <xsl:param name="target" as="xs:string*"/>

  <xsl:choose>
    <xsl:when test="empty($source) or empty($target)">
      <xsl:sequence select="$source"/>
    </xsl:when>
    <xsl:when test="$source[1] ne $target[1]">
      <xsl:sequence select="$source"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:trim-common-parts(subsequence($source, 2),
                                                 subsequence($target, 2))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:root-base-uri" as="xs:anyURI" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="$vp:chunk-output-base-uri"/>
</xsl:function>

<!-- ============================================================ -->

<xsl:template match="*" mode="m:chunk-filename">
  <xsl:value-of select="f:generate-id(.)"/>
</xsl:template>

<xsl:template match="db:set" mode="m:chunk-filename">
  <xsl:if test="preceding-sibling::* or following-sibling::*">
    <xsl:variable name="number" as="xs:string">
      <xsl:number format="01" level="single"/>
    </xsl:variable>
    <xsl:sequence select="'set'||$number"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:book" mode="m:chunk-filename">
  <xsl:if test="preceding-sibling::* or following-sibling::*">
    <xsl:variable name="number" as="xs:string">
      <xsl:number format="01" level="single"/>
    </xsl:variable>
    <xsl:variable name="parent" as="xs:string?">
      <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
    </xsl:variable>
    <xsl:sequence select="string-join(($parent, 'bk', $number), '')"/>
  </xsl:if>
</xsl:template>

<xsl:template match="db:acknowledgements" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'ack', $number), '')"/>
</xsl:template>

<xsl:template match="db:appendix" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="a" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'app', $number), '')"/>
</xsl:template>

<xsl:template match="db:article" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'ar', $number), '')"/>
</xsl:template>

<xsl:template match="db:bibliography" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'bib', $number), '')"/>
</xsl:template>

<xsl:template match="db:chapter" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'ch', $number), '')"/>
</xsl:template>

<xsl:template match="db:colophon" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'col', $number), '')"/>
</xsl:template>

<xsl:template match="db:dedication" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'ded', $number), '')"/>
</xsl:template>

<xsl:template match="db:glossary" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'gloss', $number), '')"/>
</xsl:template>

<xsl:template match="db:index" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'idx', $number), '')"/>
</xsl:template>

<xsl:template match="db:part" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="i" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'part', $number), '')"/>
</xsl:template>

<xsl:template match="db:preface" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'pf', $number), '')"/>
</xsl:template>

<xsl:template match="db:reference" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'ref', $number), '')"/>
</xsl:template>

<xsl:template match="db:refentry" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="001" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'mp', $number), '')"/>
</xsl:template>

<xsl:template match="db:topic" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="001" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 'top', $number), '')"/>
</xsl:template>

<xsl:template match="db:sect1|db:sect2|db:sect3|db:sect4|db:sect5"
                     mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 's', $number), '')"/>
</xsl:template>

<xsl:template match="db:section" mode="m:chunk-filename">
  <xsl:variable name="number" as="xs:string">
    <xsl:number format="01" level="single"
                count="db:section"/>
  </xsl:variable>
  <xsl:variable name="parent" as="xs:string?">
    <xsl:apply-templates select="parent::*" mode="m:chunk-filename"/>
  </xsl:variable>
  <xsl:sequence select="string-join(($parent, 's', $number), '')"/>
</xsl:template>

<!-- ============================================================ -->

</xsl:stylesheet>

chunk-cleanup.xsl

24 templates, 10 functions (5 shadow)

Instructions
Template match ≅ /
Mode: m:chunk-cleanup
Matches: /
Template match ≅ h:db-annotation|h:db-annotatio…
Mode: m:chunk-cleanup
Matches: h:db-annotation, h:db-annotation-script, h:db-copy-verbatim-script, h:db-fallback-script, h:db-footnote, h:db-mathml-script, h:db-pagetoc-script, h:db-script, h:db-toc-script, h:db-xlink-script, h:head
Template match ≅ h:db-annotation-marker
Mode: m:chunk-cleanup
Matches: h:db-annotation-marker
Template t:chunk-footnotes match ≅
Used by: template
Mode: m:chunk-cleanup
Function fp:chunk-output-filename($node as element()) as xs:anyURI
Template match ≅ h:head/h:link
Mode: m:chunk-cleanup
Matches: h:head/h:link
Template match ≅ h:script
Mode: m:chunk-cleanup
Matches: h:script
Function fp:relative-uri($rootbaseuri as xs:string, $chunkbaseuri as xs:string, $href as xs:string) as xs:string
Template match ≅ h:a
Mode: m:chunk-cleanup
Matches: h:a
Template match ≅ h:sup
Mode: m:chunk-cleanup
Matches: h:sup
Template match ≅ text()
Mode: m:chunk-cleanup
Priority: 100
Matches: text()
Template match ≅ h:audio//h:a/@href|h:audio/h:s…
Mode: m:chunk-cleanup
Matches: h:audio//h:a/@href, h:audio/h:source/@src, h:iframe/@src, h:img/@src, h:source/@srcset, h:video//h:a/@href, h:video/h:source/@src
Template match ≅ @*
Mode: m:mediaobject-output-adjust
Matches: @*
Template match ≅ element()
Mode: m:chunk-cleanup
Matches: element()
Template match ≅ attribute()|comment()|processi…
Mode: m:chunk-cleanup
Matches: attribute(), comment(), processing-instruction(), text()
Template match ≅ *
Mode: mp:footnote-renumber
Matches: *
Template match ≅ h:db-footnote
Mode: mp:footnote-renumber
Matches: h:db-footnote
Function fp:relative-link($source as element(), $target as element()) as xs:string
Function fp:root-base-uri($node as element()) as xs:anyURI
Function f:chunk-title($chunk as element()?) as node()*
Template match ≅ h:db-annotation|h:db-footnote
Mode: m:chunk-title
Matches: h:db-annotation, h:db-footnote
Template match ≅ h:a
Mode: m:chunk-title
Matches: h:a
Template match ≅ element()
Mode: m:chunk-title
Matches: element()
Template match ≅ attribute()|comment()|processi…
Mode: m:chunk-title
Matches: attribute(), comment(), processing-instruction(), text()
Template t:top-nav match ≅
Template t:bottom-nav match ≅
Function fp:footnote-number($node as element(db:footnote)) as xs:integer
Template match ≅ db:footnote as xs:integer
Mode: mp:footnote-number
Matches: db:footnote
Template match ≅ db:footnote
Mode: m:footnote-number
Matches: db:footnote
Function fp:footnote-mark($number as xs:integer, $marks as xs:string+) as xs:string
Function fp:navigable($node as element()) as xs:boolean
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:chunk-cleanup"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:key name="hid" match="*" use="@id"/>
<xsl:key name="hfootnote" match="h:db-footnote" use="@id"/>
<xsl:key name="hanno" match="h:db-annotation" use="@id"/>

<xsl:mode name="m:chunk-cleanup" on-no-match="shallow-copy"/>

<xsl:template match="/">
  <xsl:param name="docbook" as="document-node()" tunnel="yes" required="yes"/>
  <xsl:document>
    <xsl:apply-templates/>
  </xsl:document>
</xsl:template>

<xsl:template match="h:db-footnote|h:db-annotation|h:head
                     |h:db-annotation-script|h:db-xlink-script
                     |h:db-toc-script|h:db-pagetoc-script|h:db-fallback-script
                     |h:db-copy-verbatim-script
                     |h:db-mathml-script|h:db-script">
  <!-- discard -->
</xsl:template>

<xsl:template match="h:db-annotation-marker">
  <xsl:if test="not(@placement = 'before')">
    <xsl:apply-templates/>
  </xsl:if>
</xsl:template>

<xsl:template match="*[@db-chunk]" priority="10">
  <xsl:param name="docbook" as="document-node()" tunnel="yes"/>

  <xsl:variable name="self" select="."/>

  <!-- Saxonica bug #4632 -->
  <xsl:sequence select="base-uri(root(.)/*)[. = '-no match-']"/>

  <xsl:if test="'chunk-cleanup' = $v:debug">
    <xsl:message select="'Chunk cleanup:', local-name(.), @db-chunk/string()"/>
  </xsl:if>

  <xsl:variable name="footnotes" as="element(h:db-footnote)*">
    <xsl:for-each select=".//h:db-footnote[not(ancestor::h:table)
                                           or ancestor::h:table[contains-token(@class,'verbatim')]]">
      <xsl:variable name="chunk" select="ancestor::*[@db-chunk][1]"/>
      <xsl:if test="$chunk is $self">
        <xsl:sequence select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="annotations" as="xs:string*">
    <xsl:for-each select=".//h:db-annotation-marker">
      <xsl:variable name="chunk" select="ancestor::*[@db-chunk][1]"/>
      <xsl:if test="$chunk is $self">
        <xsl:sequence select="@target/string()"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="annotations" select="distinct-values($annotations)"/>

  <xsl:variable name="head" select="/h:html/h:head"/>

  <xsl:variable name="heads" as="element(h:head)+">
    <xsl:for-each select="ancestor-or-self::*">
      <xsl:sequence select="h:head"/>
    </xsl:for-each>
  </xsl:variable>

  <!-- If the chunk selected for previous or next navigation is an
       annotation and annotations are being handled by JavaScript,
       then disregard the chunk identified. -->
  <xsl:variable name="prev"
                select="(ancestor::*[@db-chunk and fp:navigable(.)][1]
                         |preceding::*[@db-chunk and fp:navigable(.)][1])[last()]"/>
  <xsl:variable name="prev" as="element()?">
    <xsl:choose>
      <xsl:when test="$annotation-style = 'javascript'
                      and $prev/*[contains-token(@class, 'annotation-body')]">
        <xsl:sequence select="()"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$prev"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="next"
                select="((.//*[@db-chunk and fp:navigable(.)])[1]
                          |following::*[@db-chunk and fp:navigable(.)][1])[1]"/>
  <xsl:variable name="next" as="element()?">
    <xsl:choose>
      <xsl:when test="$annotation-style = 'javascript'
                      and $next/*[contains-token(@class, 'annotation-body')]">
        <xsl:sequence select="()"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$next"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="up"
                select="ancestor::*[@db-chunk and fp:navigable(.)][1]"/>
  <xsl:variable name="top"
                select="ancestor::*[@db-chunk and fp:navigable(.)][last()]"/>

  <xsl:variable name="rbu" select="fp:root-base-uri(.)"/>
  <xsl:variable name="cbu" select="fp:chunk-output-filename(.)"/>

  <xsl:variable name="classes" 
                select="if (@db-chunk = '')
                        then tokenize(*/@class)
                        else tokenize(@class)"/>
  <xsl:variable name="pagetoc"
                select="$classes = $vp:pagetoc-elements"/>

  <xsl:variable name="scripts" as="element(h:script)*">
    <xsl:if test="exists(.//mml:*)"
            xmlns:mml="http://www.w3.org/1998/Math/MathML">
      <xsl:apply-templates select="/h:html/h:db-mathml-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <!-- We save the annotation-style on the root div -->
    <xsl:if test="exists($annotations) and $annotation-style = 'javascript'">
      <xsl:apply-templates select="/h:html/h:db-annotation-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <!-- We save the xlink-style on the root div -->
    <xsl:if test="/h:html/h:div/@db-xlink/string() = 'javascript'">
      <xsl:apply-templates select="/h:html/h:db-xlink-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <xsl:if test="f:is-true($persistent-toc)">
      <xsl:apply-templates select="/h:html/h:db-toc-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <xsl:if test="$pagetoc">
      <xsl:apply-templates select="/h:html/h:db-pagetoc-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <xsl:if test=".//h:video|.//h:audio">
      <xsl:apply-templates select="/h:html/h:db-fallback-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <!-- The copy-verbatim script doesn't handle the highlight.js markup
         and highlight.js manages the selection correctly anyway. -->
    <xsl:if test="f:is-true($verbatim-embellishments)
                  and f:global-syntax-highlighter($docbook) = 'pygments'
                  and .//h:div[contains-token(@class, 'pre-wrap')]">
      <xsl:apply-templates select="/h:html/h:db-copy-verbatim-script/*">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </xsl:if>

    <!-- Unconditionally add h:db-script children. -->
    <xsl:apply-templates select="/h:html/h:db-script/*">
      <xsl:with-param name="rootbaseuri" select="$rbu"/>
      <xsl:with-param name="chunkbaseuri" select="$cbu"/>
    </xsl:apply-templates>
  </xsl:variable>

  <!-- class=no-js is a hook for setting CSS styles when js isn't
       available; see the script element a few lines below. -->
  <html class="no-js" db-chunk="{fp:chunk-output-filename(.)}">
    <xsl:if test="normalize-space($default-theme) ne ''">
      <xsl:attribute name="class" select="$default-theme"/>
    </xsl:if>
    <head>
      <!-- When serialized, this always comes first, so make sure it's first
           here. (It doesn't really matter in practice, but the XSpec tests
           will fail if it isn't also first here.) -->
      <xsl:variable name="ctype" select="$head/h:meta[@http-equiv='Content-Type']"/>
      <xsl:apply-templates select="$ctype"/>

      <!-- If js is available, turn that no-js class into a js class, per
           https://www.paulirish.com/2009/avoiding-the-fouc-v3/ -->
      <script>
        <xsl:text>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})</xsl:text>
        <xsl:text>(document.documentElement)</xsl:text>
      </script>

      <xsl:variable name="title" select="$head/h:title"/>
      <title>
        <!-- because value-of introduces extra spaces -->
        <xsl:sequence select="f:chunk-title(.) ! ./descendant-or-self::text()"/>
      </title>

      <xsl:apply-templates select="$head/node() except ($ctype|$title)">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>

      <xsl:if test="$prev">
        <link rel="prev" href="{fp:relative-link(., $prev)}"/>
      </xsl:if>
      <xsl:if test="$next">
        <link rel="next" href="{fp:relative-link(., $next)}"/>
      </xsl:if>
      <xsl:if test="$up">
        <link rel="up" href="{fp:relative-link(., $up)}"/>
      </xsl:if>
      <xsl:if test="$top">
        <link rel="home" href="{fp:relative-link(., $top)}"/>
      </xsl:if>

      <xsl:sequence select="$scripts[not(@type) or contains(@type,'javascript')]"/>
      <xsl:apply-templates select="$heads[position() gt 1]/node()">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </head>
    <body>
      <xsl:variable name="class-list" as="xs:string*">
        <xsl:if test="parent::h:html">
          <xsl:sequence select="'home'"/>
        </xsl:if>
        <xsl:sequence select="$docbook/*/@status/string()"/>
        <xsl:sequence select="tokenize(@class, '\s+')"/>
      </xsl:variable>
      <xsl:if test="exists($class-list)">
        <xsl:attribute name="class"
                       select="normalize-space(string-join(distinct-values($class-list), ' '))"/>
      </xsl:if>

      <nav class="top">
        <xsl:if test="(empty(@db-navigation)
                       or f:is-true(@db-navigation))
                      and (empty(@db-top-navigation)
                           or f:is-true(@db-top-navigation))">
          <xsl:call-template name="t:top-nav">
            <xsl:with-param name="chunk" select="exists($chunk)"/>
            <xsl:with-param name="node" select="$self"/>
            <xsl:with-param name="prev" select="$prev"/>
            <xsl:with-param name="next" select="$next"/>
            <xsl:with-param name="up" select="$up"/>
            <xsl:with-param name="top" select="$top"/>
          </xsl:call-template>
        </xsl:if>
      </nav>

      <xsl:variable name="main" as="element()">
        <main>
          <xsl:copy>
            <xsl:apply-templates select="@*,node()">
              <xsl:with-param name="rootbaseuri" select="$rbu" tunnel="yes"/>
              <xsl:with-param name="chunkbaseuri" select="$cbu" tunnel="yes"/>
            </xsl:apply-templates>
          </xsl:copy>

          <xsl:if test="$footnotes or exists($annotations)">
            <footer>
              <xsl:if test="$footnotes">
                <xsl:call-template name="t:chunk-footnotes">
                  <xsl:with-param name="footnotes" select="$footnotes"/>
                </xsl:call-template>
              </xsl:if>

              <xsl:if test="exists($annotations)">
                <xsl:variable name="style"
                              select="key('hanno', $annotations[1])[1]/@style/string()"/>
                <div class="annotations">
                  <div class="annotation-wrapper title"
                       >Annotations</div>
                  <xsl:for-each select="$annotations">
                    <xsl:apply-templates select="key('hanno', ., root($self))/node()"
                                         mode="m:docbook"/>
                  </xsl:for-each>
                </div>
              </xsl:if>
            </footer>
          </xsl:if>
        </main>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="$pagetoc">
          <div class="pagebody">
            <xsl:sequence select="$main"/>
            <nav class="pagetoc">
              <div class="tocwrapper">
              </div>
            </nav>
          </div>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$main"/>
        </xsl:otherwise>
      </xsl:choose>

      <nav class="bottom">
        <xsl:if test="(empty(@db-navigation)
                       or f:is-true(@db-navigation))
                      and (empty(@db-bottom-navigation)
                           or f:is-true(@db-bottom-navigation))">
          <xsl:call-template name="t:bottom-nav">
            <xsl:with-param name="chunk" select="exists($chunk)"/>
            <xsl:with-param name="node" select="$self"/>
            <xsl:with-param name="prev" select="$prev"/>
            <xsl:with-param name="next" select="$next"/>
            <xsl:with-param name="up" select="$up"/>
            <xsl:with-param name="top" select="$top"/>
          </xsl:call-template>
        </xsl:if>
      </nav>

      <xsl:sequence select="$scripts[@type and not(contains(@type,'javascript'))]"/>

      <xsl:variable name="more-scripts" as="element()*">
        <xsl:apply-templates select="." mode="m:html-body-script">
          <xsl:with-param name="rootbaseuri" select="$rbu"/>
          <xsl:with-param name="chunkbaseuri" select="$cbu"/>
        </xsl:apply-templates>
      </xsl:variable>

      <xsl:apply-templates select="$more-scripts">
        <xsl:with-param name="rootbaseuri" select="$rbu"/>
        <xsl:with-param name="chunkbaseuri" select="$cbu"/>
      </xsl:apply-templates>
    </body>
  </html>
</xsl:template>

<xsl:template name="t:chunk-footnotes">
  <xsl:param name="footnotes" as="element()*"/>

  <div class="footnotes">
    <hr/>
    <xsl:for-each select="$footnotes">
      <xsl:apply-templates select="./node()"/>
    </xsl:for-each>
  </div>
</xsl:template>

<xsl:function name="fp:chunk-output-filename" as="xs:anyURI" cache="yes">
  <xsl:param name="node" as="element()"/>

  <xsl:variable name="pchunk" select="$node/ancestor::*[@db-chunk][1]"/>

  <xsl:variable name="fn" as="xs:anyURI">
    <xsl:choose>
      <xsl:when test="exists($pchunk)">
        <xsl:sequence
            select="resolve-uri($node/@db-chunk,
                                fp:chunk-output-filename($pchunk))"/>
      </xsl:when>
      <xsl:when test="not($v:chunk)">
        <xsl:sequence select="base-uri(root($node)/*)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence
            select="resolve-uri($node/@db-chunk,
                                $vp:chunk-output-base-uri)"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!--<xsl:message select="'COF:', $fn"/>-->
  <xsl:sequence select="$fn"/>
</xsl:function>

<xsl:template match="h:head/h:link[@href]">
  <xsl:param name="rootbaseuri" as="xs:anyURI" required="yes"/>
  <xsl:param name="chunkbaseuri" as="xs:anyURI" required="yes"/>

  <xsl:choose>
    <xsl:when test="ends-with(@href, 'css/pygments.css')
                    and empty(../..//h:div[contains-token(@class, 'highlight')])">
      <!-- We don't need this one. Note that this is a slightly
           under-zealous test. It will preserve the pygments
           stylesheet in all of the ancestors of a chunk that
           has a highlighted listing. -->
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:attribute name="href"
                       select="fp:relative-uri($rootbaseuri, $chunkbaseuri, @href)"/>
        <xsl:apply-templates select="@* except @href"/>
        <xsl:apply-templates select="node()"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="h:script[@src]">
  <xsl:param name="rootbaseuri" as="xs:anyURI" required="yes"/>
  <xsl:param name="chunkbaseuri" as="xs:anyURI" required="yes"/>

  <xsl:copy>
    <xsl:attribute name="src"
                   select="fp:relative-uri($rootbaseuri, $chunkbaseuri, @src)"/>
    <xsl:apply-templates select="@* except @src"/>
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

<xsl:function name="fp:relative-uri" as="xs:string">
  <xsl:param name="rootbaseuri" as="xs:string" required="yes"/>
  <xsl:param name="chunkbaseuri" as="xs:string" required="yes"/>
  <xsl:param name="href" as="xs:string" required="yes"/>

  <xsl:variable name="absuri"
                select="if ($v:chunk)
                        then resolve-uri($href, $rootbaseuri)
                        else $rootbaseuri"/>

  <xsl:variable name="rchunk"
                select="fp:trim-common-prefix($chunkbaseuri, $absuri)"/>

  <xsl:choose>
    <!-- Attempt to leave absolute path references alone -->
    <xsl:when test="starts-with($href, '/')">
      <xsl:sequence select="$href"/>
    </xsl:when>

    <!-- if $rchunk = $chunkbaseuri, they have no common prefix -->
    <!-- if $rchunk doesn't contain a /, then it's at the root -->
    <xsl:when test="($rchunk ne $chunkbaseuri) and contains($rchunk, '/')">
      <xsl:variable name="rhref"
                    select="fp:trim-common-prefix($absuri, $chunkbaseuri)"/>
      <xsl:variable name="parts" as="xs:string+">
        <xsl:for-each select="2 to count(tokenize($rchunk, '/'))">
          <xsl:sequence select="'..'"/>
        </xsl:for-each>
      </xsl:variable>
      <xsl:sequence select="string-join($parts, '/') || '/' || $rhref"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$href"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="h:a[starts-with(@href, '#')]">
  <xsl:variable name="id" select="substring-after(@href, '#')"/>
  <xsl:variable name="target" select="key('hid', $id)"/>
  <xsl:variable name="pchunk" select="(ancestor::*[@db-chunk])[last()]"/>
  <xsl:variable name="tchunk" select="($target/ancestor-or-self::*[@db-chunk])[last()]"/>

  <xsl:if test="$target[1]/@db-chunk and count($target) != 1">
    <xsl:choose>
      <xsl:when test="count($target) = 0">
        <xsl:if test="$message-level gt 0">
          <xsl:message select="'Error: cannot find ' || $id || ' in document'"/>
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="$message-level gt 0">
          <xsl:message select="'Error: multiple elements match ' || $id || ' in document'"/>
        </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="empty($target)">
      <xsl:if test="$message-level gt 0">
        <xsl:message select="'No id for #' || $id"/>
      </xsl:if>
      <span class="error broken-link">
        <xsl:copy>
          <xsl:apply-templates select="@*,node()"/>
        </xsl:copy>
      </span>
    </xsl:when>
    <xsl:when test="contains-token(@class, 'annomark')">
      <!-- Annotations are special, they're always local references because
           we'll copy the relevant annotations into this chunk. -->
      <xsl:if test="'intra-chunk-refs' = $v:debug">
        <xsl:message select="'Link:', @href/string(), 'is an annotation'"/>
      </xsl:if>
      <a>
        <xsl:copy-of select="@* except @db-annotation"/>
        <xsl:apply-templates/>
      </a>
    </xsl:when>
    <xsl:when test="$pchunk is $tchunk">
      <xsl:if test="'intra-chunk-refs' = $v:debug">
        <xsl:message select="'Link:', @href/string(), 'in same chunk as target'"/>
      </xsl:if>
      <a>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
      </a>
    </xsl:when>
    <xsl:when test="ancestor::*[@db-persistent-toc]">
      <!-- ignore it, we'll clean up the persistent ToC later -->
      <xsl:sequence select="."/>
    </xsl:when>
    <xsl:when test="$tchunk/@id = $id">
      <xsl:if test="'intra-chunk-refs' = $v:debug">
        <xsl:message select="'Link:', @href/string(),
                             'to root of', $tchunk/@db-chunk/string()"/>
      </xsl:if>
      <a href="{fp:relative-link($pchunk, $tchunk)}">
        <xsl:copy-of select="@* except @href"/>
        <xsl:apply-templates/>
      </a>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="'intra-chunk-refs' = $v:debug">
        <xsl:message select="'Link:', @href/string(),
                             'in chunk', $tchunk/@db-chunk/string()"/>
      </xsl:if>
      <a href="{fp:relative-link($pchunk, $tchunk)}{@href}">
        <xsl:copy-of select="@* except @href"/>
        <xsl:apply-templates/>
      </a>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="h:sup[@db-footnote]">
  <xsl:copy>
    <xsl:apply-templates select="@* except @db-footnote"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

<!-- If we're renumbering footnotes and this is a text node in an h:a that's
     a child of an h:sup that identifies a footnote and there are no other
     nodes inside this h:a, then we'll try to renumber the footnote. -->
<xsl:template match="text()[$v:chunk-renumber-footnotes
                            and parent::h:a
                                /parent::h:sup[contains-token(@class, 'footnote-number')
                                               and not(contains-token(@class, 'table-footnote'))
                                               and @db-footnote]
                            and empty(preceding-sibling::node())
                            and empty(following-sibling::node())]"
              priority="100">
  <xsl:variable name="id" select="substring-after(../@href, '#')"/>
  <xsl:variable name="renumber" as="xs:string">
    <xsl:choose>
      <xsl:when test="key('hfootnote', $id)">
        <xsl:apply-templates select="key('hfootnote', $id)"
                             mode="mp:footnote-renumber"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- this must be the number in the footnote itself... -->
        <xsl:apply-templates select="ancestor::h:db-footnote[1]"
                             mode="mp:footnote-renumber"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:sequence select="$renumber"/>
</xsl:template>

<xsl:template match="h:img/@src
                     |h:video/h:source/@src
                     |h:audio/h:source/@src
                     |h:iframe/@src
                     |h:audio//h:a/@href
                     |h:video//h:a/@href
                     |h:source/@srcset">
  <xsl:param name="rootbaseuri" tunnel="yes"/>
  <xsl:param name="chunkbaseuri" tunnel="yes"/>

  <xsl:variable name="uri" as="xs:string">
    <xsl:choose>
      <xsl:when test="exists(f:uri-scheme(.)) and f:uri-scheme(.) != 'file'">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test="exists($mediaobject-output-base-uri)">
        <xsl:choose>
          <xsl:when test="f:is-true($mediaobject-output-paths)">
            <xsl:sequence
                select="fp:relative-uri($rootbaseuri, $chunkbaseuri, '')
                        || $mediaobject-output-base-uri
                        || ."/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:sequence
                select="fp:relative-uri($rootbaseuri, $chunkbaseuri, '')
                        || $mediaobject-output-base-uri
                        || tokenize(., '/')[last()]"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:when test="true()">
        <xsl:sequence
            select="fp:relative-uri($rootbaseuri, $chunkbaseuri, '') || ."/>
      </xsl:when>
      <xsl:otherwise>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="adjusted-uri" as="xs:string">
    <xsl:apply-templates select="." mode="m:mediaobject-output-adjust">
      <xsl:with-param name="adjusted-uri" select="$uri"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:message use-when="'mediaobject-uris' = $v:debug">
    <xsl:text>Cleanup </xsl:text>
    <xsl:value-of
        select="substring(local-name((parent::h:img,
                                      parent::h:iframe,
                                      parent::h:a,
                                      ../..)[1]), 1, 3)"/>
    <xsl:choose>
      <xsl:when test="$uri = $adjusted-uri">
        <xsl:value-of select="': ' || . || ' &#9;→ ' || $adjusted-uri"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="': ' || . || ' &#9;→ ' || $uri || ' → &#9;' || $adjusted-uri"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:message>

  <xsl:attribute name="{local-name(.)}" select="$adjusted-uri"/>
</xsl:template>

<xsl:template match="@*" mode="m:mediaobject-output-adjust">
  <xsl:param name="adjusted-uri" as="xs:string"/>
  <xsl:sequence select="$adjusted-uri"/>
</xsl:template>

<xsl:template match="element()">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <!-- annotations inside the constructed db-bfs div are moved to the top
         so ignore them when we're *inside* the db-bfs div -->
    <xsl:if test="not(contains-token(@class, 'db-bfs'))">
      <xsl:sequence select="h:db-annotation-marker[@placement='before']/node()
                            |h:div[@class='db-bfs']/h:db-annotation-marker[@placement='before']/node()"/>
    </xsl:if>
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="attribute()|text()|comment()|processing-instruction()">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="*" mode="mp:footnote-renumber">
  <xsl:sequence
      select="error($dbe:INTERNAL-RENUMBER-ERROR,
                    'Attempt to renumber ' || local-name(.)
                    || ': ' || @id/string())"/>
</xsl:template>

<xsl:template match="h:db-footnote" mode="mp:footnote-renumber">
  <xsl:variable name="new-number" as="xs:string">
    <xsl:number from="*[@db-chunk]"
                count="h:db-footnote[not(ancestor::h:table)
                                     or ancestor::h:table[contains-token(@class, 'verbatim')]]"
                level="any"/>
  </xsl:variable>
  <xsl:sequence select="fp:footnote-mark(xs:integer($new-number), $footnote-numeration)"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="fp:relative-link" as="xs:string">
  <xsl:param name="source" as="element()"/>
  <xsl:param name="target" as="element()"/>

  <xsl:variable name="suri" 
                select="fp:chunk-output-filename($source)"/>
  <xsl:variable name="turi"
                select="fp:chunk-output-filename($target)"/>

  <xsl:variable name="tsuri" select="fp:trim-common-prefix($suri, $turi)"/>
  <xsl:variable name="tturi" select="fp:trim-common-prefix($turi, $suri)"/>

  <xsl:variable name="path" as="xs:string">
    <xsl:choose>
      <xsl:when test="contains($tsuri, '/')">
        <xsl:variable name="parts" as="xs:string+">
          <xsl:for-each select="2 to count(tokenize($tsuri, '/'))">
            <xsl:sequence select="'..'"/>
          </xsl:for-each>
        </xsl:variable>
        <xsl:sequence select="string-join($parts, '/') || '/' || $tturi"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$tturi"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:if test="'intra-chunk-links' = $v:debug">
    <xsl:message select="$tsuri,'→',$tturi,':',$path"/>
  </xsl:if>

  <xsl:sequence select="$path"/>
</xsl:function>

<xsl:function name="fp:trim-common-prefix" as="xs:string" cache="yes">
  <xsl:param name="source" as="xs:string"/>
  <xsl:param name="target" as="xs:string"/>

  <xsl:variable name="tail"
                select="fp:trim-common-parts(
                           tokenize($source, '/'),
                           tokenize($target, '/'))"/>

  <xsl:sequence select="string-join($tail, '/')"/>
</xsl:function>

<xsl:function name="fp:trim-common-parts" as="xs:string*">
  <xsl:param name="source" as="xs:string*"/>
  <xsl:param name="target" as="xs:string*"/>

  <xsl:choose>
    <xsl:when test="empty($source) or empty($target)">
      <xsl:sequence select="$source"/>
    </xsl:when>
    <xsl:when test="$source[1] ne $target[1]">
      <xsl:sequence select="$source"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="fp:trim-common-parts(subsequence($source, 2),
                                                 subsequence($target, 2))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:function name="fp:root-base-uri" as="xs:anyURI" cache="yes">
  <xsl:param name="node" as="element()"/>
  <!-- Saxonica bug #4632 -->
  <xsl:sequence select="base-uri(root($node)/*)[. = '-no match-']"/>
  <xsl:choose>
    <xsl:when test="not($v:chunk)">
      <xsl:sequence select="base-uri(root($node)/*)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$vp:chunk-output-base-uri"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="f:chunk-title" as="node()*" cache="yes">
  <xsl:param name="chunk" as="element()?"/>

  <xsl:choose>
    <xsl:when test="$chunk/h:div[contains-token(@class, 'refnamediv')]">
      <!-- refentry chunks are special -->
      <xsl:apply-templates
          select="(($chunk/h:div[contains-token(@class, 'refnamediv')])[1]
                   //h:span[contains-token(@class, 'refname')])[1]/node()"
          mode="m:chunk-title"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="header" select="($chunk/h:header
                                           |$chunk/*/h:header
                                           |$chunk/*/*/h:header)[1]"/>

      <xsl:variable name="hx" select="(($header//h:h1)[1],
                                       ($header//h:h2)[1],
                                       ($header//h:h3)[1],
                                       ($header//h:h4)[1],
                                       $header//h:h5)[1]"/>

      <xsl:apply-templates select="$hx/node()" mode="m:chunk-title"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="h:db-footnote|h:db-annotation" mode="m:chunk-title"/>

<xsl:template match="h:a" mode="m:chunk-title">
  <xsl:apply-templates mode="m:chunk-title"/>
</xsl:template>

<xsl:template match="element()" mode="m:chunk-title">
  <xsl:copy>
    <xsl:apply-templates select="@*,node()" mode="m:chunk-title"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="attribute()|text()|comment()|processing-instruction()"
              mode="m:chunk-title">
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:template name="t:top-nav">
  <xsl:param name="chunk" as="xs:boolean"/>
  <xsl:param name="node" as="element()"/>
  <xsl:param name="prev" as="element()?"/>
  <xsl:param name="next" as="element()?"/>
  <xsl:param name="up" as="element()?"/>
  <xsl:param name="top" as="element()?"/>

  <!-- We don't have the actual DocBook sources here, so ... -->
  <xsl:variable name="lang"
                select="($node/ancestor-or-self::*/@lang/string(), $default-language)[1]"/>

  <xsl:if test="$chunk">
    <div>
      <xsl:if test="$top">
        <a href="{fp:relative-link(., $top)}">
          <xsl:sequence select="fp:l10n-token($lang, 'nav-home')"/>
        </a>
      </xsl:if>
      <xsl:text> </xsl:text>
      <xsl:if test="$up">
        <a href="{fp:relative-link(., $up)}">
          <xsl:sequence select="fp:l10n-token($lang, 'nav-up')"/>
        </a>
      </xsl:if>
      <xsl:text> </xsl:text>
      <xsl:if test="$next">
        <a href="{fp:relative-link(., $next)}">
          <xsl:sequence select="fp:l10n-token($lang, 'nav-next')"/>
        </a>
      </xsl:if>
      <xsl:text> </xsl:text>
      <xsl:if test="$prev">
        <a href="{fp:relative-link(., $prev)}">
          <xsl:sequence select="fp:l10n-token($lang, 'nav-prev')"/>
        </a>
      </xsl:if>
    </div>
  </xsl:if>
</xsl:template>

<xsl:template name="t:bottom-nav">
  <xsl:param name="chunk" as="xs:boolean"/>
  <xsl:param name="node" as="element()"/>
  <xsl:param name="prev" as="element()?"/>
  <xsl:param name="next" as="element()?"/>
  <xsl:param name="up" as="element()?"/>
  <xsl:param name="top" as="element()?"/>

  <!-- We don't have the actual DocBook sources here, so ... -->
  <xsl:variable name="lang"
                select="($node/ancestor-or-self::*/@lang/string(), $default-language)[1]"/>

  <xsl:if test="$chunk">
    <table>
      <tr>
        <td class="previous">
          <xsl:if test="$prev">
            <a href="{fp:relative-link(., $prev)}">
              <xsl:sequence select="fp:l10n-token($lang, 'nav-prev')"/>
            </a>
          </xsl:if>
        </td>
        <td class="up">
          <xsl:if test="$up">
            <a href="{fp:relative-link(., $up)}">
              <xsl:sequence select="fp:l10n-token($lang, 'nav-up')"/>
            </a>
          </xsl:if>
        </td>
        <td class="next">
          <xsl:if test="$next">
            <a href="{fp:relative-link(., $next)}">
              <xsl:sequence select="fp:l10n-token($lang, 'nav-next')"/>
            </a>
          </xsl:if>
        </td>
      </tr>
      <tr>
        <td class="previous">
          <xsl:sequence select="f:chunk-title($prev)"/>
        </td>
        <td class="up">
          <xsl:if test="$top">
            <a href="{fp:relative-link(., $top)}">
              <xsl:sequence select="fp:l10n-token($lang, 'nav-home')"/>
            </a>
          </xsl:if>
        </td>
        <td class="next">
          <xsl:sequence select="f:chunk-title($next)"/>
        </td>
      </tr>
    </table>
  </xsl:if>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="fp:footnote-number" as="xs:integer" cache="yes">
  <xsl:param name="node" as="element(db:footnote)"/>
  <xsl:apply-templates select="$node" mode="mp:footnote-number"/>
</xsl:function>

<xsl:template match="db:footnote" as="xs:integer" mode="mp:footnote-number">
  <xsl:variable name="nearest"
                select="(ancestor::db:table
                        |ancestor::db:informaltable)[last()]"/>

  <xsl:variable name="fnum" as="xs:string">
    <xsl:choose>
      <xsl:when test="empty($nearest)">
        <xsl:variable name="pfoot" select="count(preceding::db:footnote)"/>
        <xsl:variable name="ptfoot"
              select="count(preceding::db:footnote[ancestor::db:table])
                      + count(preceding::db:footnote[ancestor::db:informaltable])"/>
        <xsl:value-of select="$pfoot - $ptfoot + 1"/>
      </xsl:when>
      <xsl:when test="$nearest/self::db:informaltable">
        <xsl:number format="1" from="db:informaltable" level="any"/>
      </xsl:when>
      <xsl:when test="$nearest/self::db:table">
        <xsl:number format="1" from="db:table" level="any"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="$message-level gt 0">
          <xsl:message>Error: failed to enumerate footnote:</xsl:message>
          <xsl:message select="."/>
        </xsl:if>
        <xsl:sequence select="1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:sequence select="xs:integer($fnum)"/>
</xsl:template>

<xsl:template match="db:footnote" mode="m:footnote-number">
  <xsl:variable name="nearest"
                select="(ancestor::db:table
                        |ancestor::db:informaltable)[last()]"/>

  <xsl:variable name="fnum" select="fp:footnote-number(.)"/>

  <xsl:variable name="marks"
                select="if (empty($nearest))
                        then $footnote-numeration
                        else $table-footnote-numeration"/>

  <xsl:sequence select="fp:footnote-mark($fnum, $marks)"/>
</xsl:template>

<xsl:function name="fp:footnote-mark" as="xs:string">
  <xsl:param name="number" as="xs:integer"/>
  <xsl:param name="marks" as="xs:string+"/>

  <xsl:choose>
    <xsl:when test="$number lt count($marks)">
      <xsl:sequence select="$marks[$number]"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:number value="$number" format="{$marks[count($marks)]}"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- ============================================================ -->

<xsl:function name="fp:navigable" as="xs:boolean" cache="yes">
  <xsl:param name="node" as="element()"/>
  <xsl:sequence select="$node/@db-chunk and not($node/@db-navigable='false')"/>
</xsl:function>

</xsl:stylesheet>

chunk-output.xsl

8 templates, 2 functions

Instructions
Template match ≅ / as map(xs:string, item()*)
Template match ≅ h:html
Mode: m:chunk-output
Matches: h:html
Template match ≅ h:div
Mode: m:chunk-output
Matches: h:div
Template tp:resolve-persistent-toc-uris match ≅
Used by: template, template
Mode: m:chunk-output
Function fp:resolve-persistent-toc-prefix($html as document-node(), $context as element()?) as xs:string?
Function fp:resolve-persistent-toc($toc as element(h:div)) as element(h:div)
Template match ≅ element()
Mode: m:chunk-output
Matches: element()
Template match ≅ attribute()|comment()|processi…
Mode: m:chunk-output
Matches: attribute(), comment(), processing-instruction(), text()
Template match ≅ h:a
Mode: mp:copy-patch-toc
Matches: h:a
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:chunk-output"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:template match="/" as="map(xs:string, item()*)">
  <xsl:choose>
    <xsl:when test="$v:chunk">
      <xsl:map>
        <xsl:apply-templates select="/h:html//h:html">
          <xsl:with-param name="map" select="true()"/>
        </xsl:apply-templates>
        <xsl:if test="normalize-space($persistent-toc-filename) != '' and f:is-true($persistent-toc)">
          <xsl:variable name="rootfn"
                        select="(.//*[@db-chunk])[1]/@db-chunk/string()"/>
          <xsl:map-entry key="string(resolve-uri($persistent-toc-filename, $rootfn))">
            <html>
              <head>
                <xsl:sequence select="/h:html/h:html[1]/h:head/h:title"/>
              </head>
              <body>
                <xsl:call-template name="tp:resolve-persistent-toc-uris">
                  <xsl:with-param name="html" select="."/>
                </xsl:call-template>
              </body>
            </html>
          </xsl:map-entry>
        </xsl:if>
      </xsl:map>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="result">
        <xsl:choose>
          <xsl:when test="/h:html/h:html">
            <xsl:apply-templates select="/h:html/h:html"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:sequence select="."/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:sequence select="map {'output': $result}"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="h:html[@db-chunk]">
  <xsl:param name="map" as="xs:boolean" select="false()"/>

  <xsl:variable name="copy-attributes"
                select="@* except @*[starts-with(local-name(.), 'db-')]"/>

  <xsl:choose>
    <xsl:when test="not($v:chunk)">
      <xsl:copy>
        <xsl:apply-templates select="$copy-attributes"/>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:when>
    <xsl:when test="not($map)"/>
    <xsl:when test="@db-chunk != ''">
      <xsl:if test="'chunks' = $v:debug">
        <xsl:message select="'Chunk:', @db-chunk/string()"/>
      </xsl:if>
      <xsl:map-entry key="@db-chunk/string()">
        <xsl:copy>
          <xsl:apply-templates select="$copy-attributes"/>
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:map-entry>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:apply-templates select="$copy-attributes"/>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="h:div[@db-chunk]">
  <xsl:variable name="copy-attributes"
                select="@* except @*[starts-with(local-name(.), 'db-')]"/>

  <xsl:choose>
    <xsl:when test="empty($copy-attributes)">
      <xsl:apply-templates/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:apply-templates select="$copy-attributes"/>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="tp:resolve-persistent-toc-uris">
  <xsl:param name="html" as="document-node()"/>
  <xsl:param name="context" as="element()?"/>

  <xsl:variable name="toc"
                select="fp:resolve-persistent-toc(/*/*[@db-persistent-toc])"/>

  <xsl:sequence select="$toc/h:div/h:div[contains-token(@class, 'toc')]/h:ul"/>
  <xsl:for-each select="$toc/h:div/h:div[not(contains-token(@class, 'toc'))]">
    <ul class="nav-title">
      <li class="nav-title">
        <xsl:sequence select="h:div[contains-token(@class, 'title')]/node()"/>
      </li>
      <xsl:sequence select="h:ul/h:li"/>
    </ul>
  </xsl:for-each>
</xsl:template>

<xsl:function name="fp:resolve-persistent-toc-prefix" as="xs:string?">
  <xsl:param name="html" as="document-node()"/>
  <xsl:param name="context" as="element()?"/>

  <!-- If this chunk and the "root" chunk are in different directories,
       work out what prefix (how many '../') is required to get back
       to the root level. Prefix all of the relative URIs in the ToC
       with that prefix so the links will work.
  -->
  <xsl:variable name="docroot" select="$html/h:html/h:html/@db-chunk/string()"/>
  <xsl:variable name="chroot"
                select="$context/ancestor-or-self::h:html[@db-chunk][1]/@db-chunk/string()"/>
  <xsl:variable name="rel"
                select="fp:trim-common-prefix(($chroot, '')[1], $docroot)"/>

  <xsl:variable name="parts" select="tokenize($rel, '/')"/>
  <xsl:variable name="ancestors" as="xs:string*">
    <xsl:for-each select="2 to count($parts)">
      <xsl:sequence select="'..'"/>
    </xsl:for-each>
  </xsl:variable>
  <xsl:sequence select="if (exists($ancestors))
                        then string-join($ancestors, '/') || '/'
                        else ()"/>
</xsl:function>

<xsl:template match="h:nav[contains-token(@class, 'bottom')]">
  <xsl:copy>
    <xsl:apply-templates select="@*,node()"/>

    <xsl:if test="f:is-true($persistent-toc)">
      <!-- N.B. This is cheating slightly. The completely clean way to do
           this would be to add the ToC elements during the chunk-cleanup pass,
           but on big document, that can result in tens of megabytes of extra
           data as the whole ToC is repeated in every chunk. Since we know what's
           in the ToC, we know it'll be safe to cheat...
      -->

      <nav class="tocopen"/>
      <nav class="toc"/>
      <xsl:comment> Hide ToC details from user agents that don’t support JS </xsl:comment>
      <script type="text/html" class="tocopen">
        <xsl:sequence select="$v:toc-open"/>
      </script>
      <script type="text/html" class="toc">
        <header>
          <span>
            <xsl:apply-templates select="." mode="m:gentext">
              <xsl:with-param name="group" select="'table-of-contents'"/>
            </xsl:apply-templates>
          </span>
          <span class="close">
            <xsl:sequence select="$v:toc-close"/>
          </span>
          <xsl:if test="$persistent-toc-search">
            <p class="ptoc-search">
              <input class="ptoc-search" placeholder="Search" style="width: 80%"/>
            </p>
          </xsl:if>
        </header>
        <xsl:choose>
          <xsl:when test="$v:chunk and normalize-space($persistent-toc-filename) != ''">
            <div db-persistent-toc="{$persistent-toc-filename}">
              <xsl:attribute name="db-prefix"
                             select="fp:resolve-persistent-toc-prefix(/, .)"/>
              <xsl:sequence select="f:l10n-token(., 'loading')"/>
            </div>
          </xsl:when>
          <xsl:otherwise>
            <div db-prefix="{fp:resolve-persistent-toc-prefix(/, .)}">
              <xsl:call-template name="tp:resolve-persistent-toc-uris">
                <xsl:with-param name="html" select="/"/>
                <xsl:with-param name="context" select="."/>
              </xsl:call-template>
            </div>
          </xsl:otherwise>
        </xsl:choose>
      </script>
    </xsl:if>
  </xsl:copy>
</xsl:template>

<xsl:function name="fp:resolve-persistent-toc" cache="yes" as="element(h:div)">
  <xsl:param name="toc" as="element(h:div)"/>
  <xsl:apply-templates select="$toc" mode="mp:copy-patch-toc"/>
</xsl:function>

<xsl:template match="element()">
  <xsl:copy>
    <xsl:apply-templates select="@*,node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="attribute()|text()|comment()|processing-instruction()"
             >
  <xsl:copy/>
</xsl:template>

<!-- ============================================================ -->

<xsl:mode name="mp:copy-patch-toc" on-no-match="shallow-copy"/>

<xsl:template match="h:a[@href]" mode="mp:copy-patch-toc">
  <xsl:variable name="href" as="xs:string">
    <xsl:choose>
      <xsl:when test="starts-with(@href, '#') and $v:chunk">
        <xsl:variable name="id" select="substring-after(@href, '#')"/>
        <xsl:variable name="target" select="key('hid', $id, root(.))"/>
        <xsl:variable name="chunk"
                      select="$target/ancestor-or-self::h:html[@db-chunk][1]"/>
        <xsl:variable name="href"
                      select="if ($chunk/h:body/h:main/h:div/@id = $id)
                              then ''
                              else @href/string()"/>

        <xsl:if test="count($target/ancestor-or-self::h:html[@db-chunk][1]/@db-chunk) gt 1">
          <xsl:message select="'Multiple chunks identified for ''' || $id || ''''"/>
        </xsl:if>

        <xsl:sequence
            select="substring-after(
                      $target/ancestor-or-self::h:html[@db-chunk][1]/@db-chunk/string(),
                      $vp:chunk-output-base-uri) || $href"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="@href/string()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:copy>
    <xsl:copy-of select="@* except @href"/>
    <xsl:attribute name="href" select="$href"/>
    <xsl:apply-templates select="node()" mode="mp:copy-patch-toc"/>
  </xsl:copy>
</xsl:template>

<!-- ============================================================ -->

</xsl:stylesheet>

xform-locale.xsl

15 templates, 1 function

Instructions
Template match ≅ ls:locale
Mode: mp:transform-locale
Matches: ls:locale
Template match ≅ ls:mappings
Mode: mp:transform-locale
Matches: ls:mappings
Template match ≅ ls:gentext
Mode: mp:transform-locale
Matches: ls:gentext
Template match ≅ ls:group
Mode: mp:transform-locale
Matches: ls:group
Template match ≅ ls:ref
Mode: mp:transform-locale
Matches: ls:ref
Template match ≅ ls:list
Mode: mp:transform-locale
Matches: ls:list
Template match ≅ ls:items|ls:repeat|ls:template
Mode: mp:transform-locale
Matches: ls:items, ls:repeat, ls:template
Template match ≅ ls:letters
Mode: mp:transform-locale
Matches: ls:letters
Template match ≅ ls:l
Mode: mp:transform-locale
Matches: ls:l
Template match ≅ *
Mode: mp:transform-locale
Matches: *
Template match ≅ ls:ref
Mode: mp:expand-l10n-template
Matches: ls:ref
Template match ≅ ls:items
Mode: mp:expand-l10n-template
Matches: ls:items
Template match ≅ ls:repeat
Mode: mp:expand-l10n-template
Matches: ls:repeat
Template match ≅ *
Mode: mp:expand-l10n-template
Matches: *
Template match ≅ text()
Mode: mp:expand-l10n-template
Matches: text()
Function fp:fix-text($text as xs:string) as node()*
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:l="http://docbook.org/ns/docbook/l10n"
                xmlns:ls="http://docbook.org/ns/docbook/l10n/source"
                xmlns:lt="http://docbook.org/ns/docbook/l10n/templates"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://docbook.org/ns/docbook"
                default-mode="mp:transform-locale"
                exclude-result-prefixes="f fp ls map mp xs"
                expand-text="yes"
                version="3.0">

<xsl:output method="xml" encoding="utf-8" indent="yes"/>

<xsl:key name="gentext" match="ls:gentext" use="@key"/>

<xsl:template match="ls:locale">
  <l:l10n language="{@language}"
          english-language-name="{@english-language-name}">
    <xsl:apply-templates select="* except ls:info"/>
  </l:l10n>
</xsl:template>

<xsl:template match="ls:mappings">
  <l:gentext>
    <xsl:apply-templates select="ls:gentext">
      <xsl:sort select="lower-case(@key)" case-order="lower-first"/>
    </xsl:apply-templates>
  </l:gentext>
</xsl:template>

<xsl:template match="ls:gentext">
  <l:token key="{@key}">
    <xsl:sequence select="node()"/>
  </l:token>
</xsl:template>

<xsl:template match="ls:group">
  <l:group>
    <xsl:copy-of select="@*,namespace::*[local-name(.) != '']"/>
    <xsl:apply-templates select="ls:template"/>
  </l:group>
</xsl:template>

<xsl:template match="ls:ref">
  <l:ref>
    <xsl:copy-of select="@*,namespace::*[local-name(.) != '']"/>
  </l:ref>
</xsl:template>

<xsl:template match="ls:list">
  <l:list>
    <xsl:copy-of select="@*,namespace::*[local-name(.) != '']"/>
    <xsl:apply-templates select="ls:items"/>
  </l:list>
</xsl:template>

<xsl:template match="ls:template|ls:repeat|ls:items">
  <xsl:variable name="expanded" as="node()*">
    <xsl:apply-templates mode="mp:expand-l10n-template"/>
  </xsl:variable>

  <xsl:element name="l:{local-name(.)}" 
               namespace="http://docbook.org/ns/docbook/l10n">
    <xsl:copy-of select="@*,namespace::*[local-name(.) != '']"/>
    <xsl:iterate select="$expanded">
      <xsl:param name="result" select="()"/>
      <xsl:param name="last" select="()"/>
      <xsl:on-completion select="$result"/>
      <xsl:choose>
        <xsl:when test="./self::lt:text and $last/self::lt:text">
          <xsl:variable name="text" as="element()">
            <xsl:element name="lt:text"
                         namespace="http://docbook.org/ns/docbook/l10n/templates">
              <xsl:sequence select="string($last) || string(.)"/>
            </xsl:element>
          </xsl:variable>
          <xsl:next-iteration>
            <xsl:with-param name="result" select="($result[position() lt last()], $text)"/>
            <xsl:with-param name="last" select="$text"/>
          </xsl:next-iteration>
        </xsl:when>
        <xsl:otherwise>
          <xsl:next-iteration>
            <xsl:with-param name="result" select="($result, .)"/>
            <xsl:with-param name="last" select="."/>
          </xsl:next-iteration>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:iterate>
  </xsl:element>
</xsl:template>

<xsl:template match="ls:letters">
  <l:letters>
    <xsl:copy-of select="@*,namespace::*[local-name(.) != '']"/>
    <xsl:apply-templates select="*"/>
  </l:letters>
</xsl:template>

<xsl:template match="ls:l">
  <l:l>
    <xsl:copy-of select="@*,namespace::*[local-name(.) != '']"/>
    <xsl:sequence select="string(.)"/>
  </l:l>
</xsl:template>

<xsl:template match="*">
  <xsl:message terminate="yes">No template for {local-name(.)}</xsl:message>
</xsl:template>

<!-- ============================================================ -->

<xsl:template match="ls:ref" mode="mp:expand-l10n-template">
  <lt:ref>
    <xsl:copy-of select="@*"/>
  </lt:ref>
</xsl:template>

<xsl:template match="ls:items" mode="mp:expand-l10n-template">
  <l:items>
    <xsl:apply-templates mode="mp:expand-l10n-template"/>
  </l:items>
</xsl:template>

<xsl:template match="ls:repeat" mode="mp:expand-l10n-template">
  <l:repeat>
    <xsl:apply-templates mode="mp:expand-l10n-template"/>
  </l:repeat>
</xsl:template>

<xsl:template match="*" mode="mp:expand-l10n-template">
  <xsl:message select="."/>

  <lt:token key="{local-name(.)}"/>
  <xsl:if test="not(key('gentext', local-name(.)))">
    <xsl:message>Warning: no gentext for {local-name(.)}</xsl:message>
  </xsl:if>
</xsl:template>

<xsl:template match="text()" mode="mp:expand-l10n-template">
  <xsl:sequence select="fp:fix-text(.)"/>
</xsl:template>

<!-- ============================================================ -->

<xsl:function name="fp:fix-text" as="node()*">
  <xsl:param name="text" as="xs:string"/>

  <xsl:variable name="bpos"
                select="if (contains($text, '{'))
                        then string-length(substring-before($text, '{'))
                        else -1"/>
  <xsl:variable name="ppos"
                select="if (contains($text, '%'))
                        then string-length(substring-before($text, '%'))
                        else -1"/>
  <xsl:choose>
    <xsl:when test="$text = ''"/>
    <xsl:when test="$bpos lt 0 and $ppos lt 0">
      <xsl:element name="lt:text"
                   namespace="http://docbook.org/ns/docbook/l10n/templates">
        <xsl:value-of select="$text"/>
      </xsl:element>
    </xsl:when>
    <xsl:when test="$bpos lt 0 or ($bpos ge 0 and $ppos ge 0 and $ppos lt $bpos)">
      <xsl:if test="$ppos gt 0">
        <xsl:element name="lt:text"
                     namespace="http://docbook.org/ns/docbook/l10n/templates">
          <xsl:value-of select="substring-before($text, '%')"/>
        </xsl:element>
      </xsl:if>

      <xsl:variable name="perc" select="substring($text, $ppos+2, 1)"/>
      <xsl:variable name="rest" select="substring($text, $ppos+3)"/>

      <!--
      <xsl:message select="'p:', '[' || $perc || ']', $rest, '::', $text"/>
      -->

      <xsl:choose>
        <xsl:when test="$perc = 'c'"><lt:content/></xsl:when>
        <xsl:when test="$perc = 'l'"><lt:label/></xsl:when>
        <xsl:when test="$perc = '%'"><lt:percent/></xsl:when>
        <xsl:when test="$perc = '.'"><lt:separator/></xsl:when>
        <xsl:when test="$perc = 'p'"><lt:pagenum/></xsl:when>
        <xsl:when test="$perc = 'o'"><lt:olink-title/></xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes"
                       select="'Unexpected percent code: %'||$perc"/>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:sequence select="fp:fix-text($rest)"/>
    </xsl:when>
    <xsl:when test="$ppos lt 0 or ($bpos ge 0 and $ppos ge 0 and $bpos lt $ppos)">
      <xsl:if test="$bpos gt 0">
        <xsl:element name="lt:text"
                     namespace="http://docbook.org/ns/docbook/l10n/templates">
          <xsl:value-of select="substring-before($text, '{')"/>
        </xsl:element>
      </xsl:if>
      <xsl:variable name="text"
                    select="substring($text, $bpos+2)"/>

      <xsl:variable name="epos" select="string-length(substring-before($text, '}'))"/>
      <xsl:variable name="token" select="substring($text, 1, $epos)"/>
      <xsl:variable name="rest" select="substring($text, $epos+2)"/>

      <!--
      <xsl:message select="'b:', $bpos, $epos, '|' || $token || '|', $rest, '::', $text"/>
      -->

      <lt:token key="{$token}"/>

      <xsl:sequence select="fp:fix-text($rest)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message terminate="yes">This can’t happen.</xsl:message>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

</xsl:stylesheet>

2 templates, 1 param

Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                default-mode="m:docbook"
                exclude-result-prefixes="#all"
                version="3.0">

<xsl:import href="param.xsl"/>

<xsl:include href="VERSION.xsl"/>
<xsl:include href="modules/variable.xsl"/>
<xsl:include href="modules/space.xsl"/>
<xsl:include href="modules/unhandled.xsl"/>
<xsl:include href="modules/errors.xsl"/>
<xsl:include href="modules/head.xsl"/>
<xsl:include href="modules/titles.xsl"/>
<xsl:include href="modules/numbers.xsl"/>
<xsl:include href="modules/units.xsl"/>
<xsl:include href="modules/gentext.xsl"/>
<xsl:include href="modules/l10n.xsl"/>
<xsl:include href="modules/functions.xsl"/>
<xsl:include href="modules/toc.xsl"/>
<xsl:include href="modules/divisions.xsl"/>
<xsl:include href="modules/components.xsl"/>
<xsl:include href="modules/refentry.xsl"/>
<xsl:include href="modules/bibliography.xsl"/>
<xsl:include href="modules/biblio690.xsl"/>
<xsl:include href="modules/glossary.xsl"/>
<xsl:include href="modules/index.xsl"/>
<xsl:include href="modules/sections.xsl"/>
<xsl:include href="modules/templates.xsl"/>
<xsl:include href="modules/titlepage.xsl"/>
<xsl:include href="modules/info.xsl"/>
<xsl:include href="modules/lists.xsl"/>
<xsl:include href="modules/blocks.xsl"/>
<xsl:include href="modules/admonitions.xsl"/>
<xsl:include href="modules/programming.xsl"/>
<xsl:include href="modules/msgset.xsl"/>
<xsl:include href="modules/objects.xsl"/>
<xsl:include href="modules/footnotes.xsl"/>
<xsl:include href="modules/verbatim.xsl"/>
<xsl:include href="modules/tablecals.xsl"/>
<xsl:include href="modules/tablehtml.xsl"/>
<xsl:include href="modules/inlines.xsl"/>
<xsl:include href="modules/xlink.xsl"/>
<xsl:include href="modules/links.xsl"/>
<xsl:include href="modules/xref.xsl"/>
<xsl:include href="modules/attributes.xsl"/>
<xsl:include href="modules/publishers.xsl"/>
<xsl:include href="modules/annotations.xsl"/>

<xsl:import href="modules/chunk.xsl"/>
<xsl:import href="modules/chunk-cleanup.xsl"/>
<xsl:import href="modules/chunk-output.xsl"/>
<xsl:import href="modules/xform-locale.xsl"/>

<xsl:output method="xhtml" encoding="utf-8" indent="no" html-version="5"
            omit-xml-declaration="yes"/>

<xsl:key name="targetptr" match="*" use="@targetptr"/>

<xsl:param name="output-media" select="'screen'"/>

<xsl:template match="/" mode="m:docbook">
  <xsl:document>
    <html>
      <xsl:attribute name="xml:base" select="base-uri(/*)"/>
      <xsl:apply-templates select="(/*/db:info,/*)[1]" mode="m:html-head"/>

      <xsl:if test="f:is-true($persistent-toc)">
        <div db-persistent-toc="true">
          <xsl:apply-templates select="*" mode="m:persistent-toc"/>
        </div>
      </xsl:if>

      <!-- N.B. Any filename specified in a PI is ignored for the root -->
      <div db-chunk="{$chunk}"
           db-xlink="{f:xlink-style(/)}">
        <xsl:sequence select="fp:chunk-navigation(/*)"/>
        <xsl:apply-templates/>
      </div>

      <xsl:if test="f:is-true($theme-picker) and $vp:js-controls">
        <db-script>
          <script type="text/html" id="db-js-controls">
            <xsl:sequence select="$vp:js-controls"/>
          </script>
        </db-script>
      </xsl:if>

      <!-- These get copied into the chunks that need them... -->
      <xsl:if test="exists($resource-base-uri)">
        <db-annotation-script>
          <script type="text/html" class="annotation-close">
            <xsl:sequence select="$v:annotation-close"/>
          </script>
          <script src="{$resource-base-uri}{$annotations-js}" defer="defer"/>
        </db-annotation-script>
        <db-xlink-script>
          <xsl:if test="$xlink-icon-open">
            <script type="text/html" class="xlink-icon-open">
              <xsl:sequence select="$xlink-icon-open"/>
            </script>
          </xsl:if>
          <xsl:if test="$xlink-icon-closed">
            <script type="text/html" class="xlink-icon-closed">
              <xsl:sequence select="$xlink-icon-closed"/>
            </script>
          </xsl:if>
          <script src="{$resource-base-uri}{$xlink-js}" defer="defer"/>
        </db-xlink-script>
        <db-toc-script>
          <script src="{$resource-base-uri}{$persistent-toc-js}" defer="defer"/>
        </db-toc-script>
        <db-pagetoc-script>
          <script src="{$resource-base-uri}{$pagetoc-js}"
                  data-dynamic-pagetoc="{f:is-true($pagetoc-dynamic)}"
                  defer="defer"/>
        </db-pagetoc-script>
        <db-mathml-script>
          <script src="{if (starts-with($mathml-js, 'http:')
                            or starts-with($mathml-js, 'https:'))
                        then $mathml-js
                        else $resource-base-uri || $mathml-js}"
                  defer="defer"/>
        </db-mathml-script>
        <db-script>
          <xsl:if test="exists($chunk) and f:is-true($chunk-nav)">
            <script src="{$resource-base-uri}{$chunk-nav-js}" defer="defer"/>
          </xsl:if>
          <xsl:if test="f:is-true($theme-picker) and $vp:js-controls">
            <script src="{$resource-base-uri}{$control-js}" defer="defer"/>
          </xsl:if>
        </db-script>
        <db-copy-verbatim-script>
          <xsl:if test="normalize-space($copy-verbatim-js) != ''">
            <script src="{$resource-base-uri}{$copy-verbatim-js}" defer="defer"/>
          </xsl:if>
        </db-copy-verbatim-script>
        <db-fallback-script>
          <!-- NOT deferred! -->
          <xsl:if test="normalize-space($fallback-js) != ''">
            <script src="{$resource-base-uri}{$fallback-js}"/>
          </xsl:if>
        </db-fallback-script>
      </xsl:if>
    </html>
  </xsl:document>
</xsl:template>

<xsl:template match="text()">
  <xsl:copy/>
</xsl:template>

</xsl:stylesheet>

5 templates, 2 functions, 2 variables

Instructions
Variable $v:standard-transforms as map(*)*
Template match ≅ /
Calls: t:docbook
Matches: /
Template t:chunk-cleanup match ≅ as document-node()
Used by: t:docbook
Used in: «root»
Template t:chunk-output match ≅ as map(xs:string, item()*)
Used by: t:docbook
Used in: «root»
Template match ≅ node()
Mode: m:chunk-write
Matches: node()
Function fp:run-transforms#2($document as document-node(), $transforms as map(*)*) as document-node()
Used by: t:docbook
Used in: «root»
Function fp:run-transforms#3($document as document-node(), $transforms as map(*)*, $extra-parameters as map(*)?) as document-node()
Source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:err='http://www.w3.org/2005/xqt-errors'
                xmlns:ext="http://docbook.org/extensions/xslt"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xi='http://www.w3.org/2001/XInclude'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                exclude-result-prefixes="#all"
                version="3.0">

<!-- This will all be in XProc 3.0 eventually, hack for now... -->
<xsl:import href="main.xsl"/>

<!--
<xsl:mode xmlns:saxon="http://saxon.sf.net/"
          name="m:docbook" saxon:trace="yes"/>
-->

<xsl:variable name="v:standard-transforms" as="map(*)*">
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/00-logstruct.xsl', static-base-uri())"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/10-xinclude.xsl', static-base-uri())"/>
    <xsl:map-entry key="'functions'" select="'Q{http://docbook.org/extensions/xslt}xinclude'"/>
    <xsl:map-entry key="'test'" select="'exists(//xi:include)'"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/20-db4to5.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'">
      not(namespace-uri(/*) = 'http://docbook.org/ns/docbook')
    </xsl:map-entry>
    <xsl:map-entry key="'extra-params'"
                   select="map { QName('', 'base-uri'): 'base-uri(/*)' }"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/30-transclude.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'" select="'f:is-true($docbook-transclusion)'"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/40-profile.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'">
         f:is-true($dynamic-profiles)
      or $profile-lang != ''         or $profile-revisionflag != ''
      or $profile-role != ''         or $profile-arch != ''
      or $profile-audience != ''     or $profile-condition != ''
      or $profile-conformance != ''  or $profile-os != ''
      or $profile-outputformat != '' or $profile-revision != ''
      or $profile-security != ''     or $profile-userlevel != ''
      or $profile-vendor != ''       or $profile-wordsize != ''
    </xsl:map-entry>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/50-normalize.xsl', static-base-uri())"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/60-annotations.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'" select="'exists(//db:annotation)'"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/70-xlinkbase.xsl', static-base-uri())"/>
  </xsl:map>
  <xsl:if test="exists($local-conventions)">
    <xsl:map>
      <xsl:map-entry key="'stylesheet-location'" select="$local-conventions"/>
    </xsl:map>
  </xsl:if>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/75-validate.xsl', static-base-uri())"/>
    <xsl:map-entry key="'functions'"
                   select="'Q{http://docbook.org/extensions/xslt}validate-with-relax-ng'"/>
    <xsl:map-entry key="'test'"
                   select="'normalize-space($relax-ng-grammar) != '''''"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/80-oxy-markup.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'">
      f:is-true(f:pi(/*/db:info, 'oxy-markup', $oxy-markup))
      and exists(//processing-instruction()[starts-with(name(), 'oxy_')])
    </xsl:map-entry>
  </xsl:map>
</xsl:variable>

<xsl:variable name="vp:transforms" as="map(*)*">
  <xsl:for-each select="$transform-original">
    <xsl:choose>
      <xsl:when test=". instance of map(*)">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test=". instance of xs:string or . instance of xs:untypedAtomic">
        <xsl:sequence select="map {
            'stylesheet-location': resolve-uri(., static-base-uri())
          }"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="error($dbe:INVALID-TRANSFORM, 
                                    'Each $transform-original must be a string or a map')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>

  <xsl:sequence select="$v:standard-transforms"/>

  <xsl:for-each select="$transform-before">
    <xsl:choose>
      <xsl:when test=". instance of map(*)">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test=". instance of xs:string or . instance of xs:untypedAtomic">
        <xsl:sequence select="map {
            'stylesheet-location': resolve-uri(., static-base-uri())
          }"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="error($dbe:INVALID-TRANSFORM, 
                                    'Each $transform-preprocessed must be a string or a map')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:variable>

<!-- If a document or element is being processed in the default
     mode (and not the m:docbook mode), assume we're starting 
     a transformation. -->
<xsl:template match="/">
  <xsl:choose>
    <!-- Hack: if the source is a localization source document, transform
         it into a localization document. -->
    <xsl:when xmlns:ls="http://docbook.org/ns/docbook/l10n/source"
              test="/ls:locale">
      <xsl:result-document method="xml" indent="yes">
        <xsl:apply-templates select="/ls:locale" mode="mp:transform-locale"/>
      </xsl:result-document>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="t:docbook"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="t:docbook">
  <xsl:param name="vp:loop-count" select="0" tunnel="yes"/>
  <xsl:param name="return" as="xs:string" select="'main-document'"/>

  <xsl:if test="$vp:loop-count gt 0">
    <xsl:message terminate="yes">
      <xsl:text>Loop detected, perhaps a mode is missing?</xsl:text>
    </xsl:message>
  </xsl:if>

  <xsl:variable name="starting-base-uri" as="xs:string">
    <xsl:choose>
      <xsl:when test="true()" use-when="function-available('ext:cwd')">
        <xsl:sequence select="resolve-uri(base-uri(.), ext:cwd())"/>
      </xsl:when>
      <xsl:when test="true()">
        <xsl:sequence select="resolve-uri(base-uri(.), static-base-uri())"/>
      </xsl:when>
    </xsl:choose>
  </xsl:variable>
  
  <!--
  <xsl:message select="'Starting base uri:', $starting-base-uri"/>
  -->

  <xsl:variable name="document" as="document-node()">
    <xsl:choose>
      <xsl:when test="./self::document-node()">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:document>
          <xsl:sequence select="."/>
        </xsl:document>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="document" as="document-node()">
    <xsl:sequence
        select="fp:run-transforms($document, $vp:transforms,
                                  map { xs:QName('vp:starting-base-uri'): $starting-base-uri })"/>
  </xsl:variable>

  <xsl:if test="string($transformed-docbook-input) != ''">
    <xsl:try>
      <xsl:variable name="href" 
                    select="resolve-uri($transformed-docbook-input, base-uri(/))"/>
      <xsl:result-document href="{$href}" method="xml" indent="no"
                           exclude-result-prefixes="#all">
        <xsl:sequence select="$document"/>
      </xsl:result-document>
      <xsl:if test="$v:debug = 'intermediate-results'">
        <xsl:message select="'Transformed DocBook input saved:', $href"/>
      </xsl:if>
      <xsl:catch>
        <xsl:message select="'Failed to save transformed input:', $transformed-docbook-input"/>
        <xsl:message select="$err:description"/>
      </xsl:catch>
    </xsl:try>
  </xsl:if>

  <xsl:variable name="transformed-html" as="document-node()">
    <xsl:apply-templates select="$document" mode="m:docbook">
      <xsl:with-param name="vp:loop-count" select="1" tunnel="yes"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="string($transformed-docbook-output) != ''">
    <xsl:try>
      <xsl:variable name="href" 
                    select="resolve-uri($transformed-docbook-output, base-uri(/))"/>
      <xsl:result-document href="{$href}" method="xml" indent="no"
                           exclude-result-prefixes="#all">
        <xsl:sequence select="$transformed-html"/>
      </xsl:result-document>
      <xsl:if test="$v:debug = 'intermediate-results'">
        <xsl:message select="'Transformed DocBook output saved:', $href"/>
      </xsl:if>
      <xsl:catch>
        <xsl:message select="'Failed to save transformed output:', $transformed-docbook-output"/>
        <xsl:message select="$err:description"/>
      </xsl:catch>
    </xsl:try>
  </xsl:if>

  <xsl:variable name="result" as="document-node()">
    <xsl:call-template name="t:chunk-cleanup">
      <xsl:with-param name="docbook" select="$document"/>
      <xsl:with-param name="source" select="$transformed-html"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:variable name="post-processing" as="map(*)*">
    <xsl:for-each select="$transform-after">
      <xsl:choose>
        <xsl:when test=". instance of map(*)">
          <xsl:sequence select="."/>
        </xsl:when>
        <xsl:when test=". instance of xs:string or . instance of xs:untypedAtomic">
          <xsl:sequence select="map {
                                  'stylesheet-location': resolve-uri(., static-base-uri())
                                }"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="error($dbe:INVALID-TRANSFORM, 
                                      'Each $transform-preprocessed must be a string or a map')"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="result" as="document-node()">
    <xsl:sequence select="fp:run-transforms($result, $post-processing)"/>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$return = 'raw-results'">
      <xsl:sequence select="map {
          'document': $document,
          'output': $result
        }"/>
    </xsl:when>
    <xsl:when test="$return = 'chunked-results'">
      <xsl:variable name="chunks" as="map(xs:string, item()*)">
        <xsl:call-template name="t:chunk-output">
          <xsl:with-param name="docbook" select="$document"/>
          <xsl:with-param name="source" select="$result"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:sequence select="map {
          'document': $document,
          'chunks': $chunks
        }"/>
    </xsl:when>
    <xsl:when test="$return = 'main-document'">
      <xsl:variable name="result" as="map(xs:string, item()*)">
        <xsl:call-template name="t:chunk-output">
          <xsl:with-param name="docbook" select="$document"/>
          <xsl:with-param name="source" select="$result"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:for-each select="map:keys($result)">
        <xsl:if test=". != 'output'">
          <xsl:apply-templates select="map:get($result, .)" mode="m:chunk-write">
            <xsl:with-param name="href" select="."/>
          </xsl:apply-templates>
        </xsl:if>
      </xsl:for-each>

      <xsl:choose>
        <xsl:when test="not($result?output/h:html)">
          <xsl:sequence select="$result?output"/>
        </xsl:when>
        <xsl:when test="f:is-true($generate-html-page)">
          <xsl:sequence select="$result?output"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$result?output/h:html/h:body/node()
                                except $result?output/h:html/h:body/h:script"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
        <xsl:sequence select="error($dbe:INVALID-RESULTS-REQUESTED,
                                    'Unexepcted return: ' || $return)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="t:chunk-cleanup" as="document-node()">
  <xsl:param name="source" as="document-node()" select="."/>
  <xsl:param name="docbook" as="document-node()" required="yes"/>

  <xsl:apply-templates select="$source" mode="m:chunk-cleanup">
    <xsl:with-param name="docbook" select="$docbook" tunnel="yes"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template name="t:chunk-output" as="map(xs:string, item()*)">
  <xsl:param name="source" as="document-node()" select="."/>
  <xsl:param name="docbook" as="document-node()" required="yes"/>

  <xsl:apply-templates select="$source" mode="m:chunk-output">
    <xsl:with-param name="docbook" select="$docbook" tunnel="yes"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="node()" mode="m:chunk-write">
  <xsl:param name="href" as="xs:string" required="yes"/>

  <xsl:result-document href="{$href}">
    <xsl:choose>
      <xsl:when test="not(self::h:html)">
        <!-- If this happens not to be an h:html element, just output it. -->
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test="f:is-true($generate-html-page)">
        <!-- If this is an h:html element, and generate-html-page is true,
             output just output it. -->
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:otherwise>
        <!-- We got an h:html, but the user has requested 'raw' output.
             Attempt to strip out the generated html page wrapper. -->
        <xsl:sequence select="h:body/node() except h:body/h:script"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:result-document>
</xsl:template>

<xsl:function name="fp:run-transforms" as="document-node()">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="transforms" as="map(*)*"/>
  <xsl:sequence select="fp:run-transforms($document, $transforms, ())"/>
</xsl:function>

<xsl:function name="fp:run-transforms" as="document-node()">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="transforms" as="map(*)*"/>
  <xsl:param name="extra-parameters" as="map(*)?"/>

  <xsl:choose>
    <xsl:when test="empty($transforms)">
      <xsl:sequence select="$document"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:iterate select="$transforms">
        <xsl:param name="document" as="document-node()" select="$document"/>
        <xsl:param name="extra-parameters" as="map(*)?" select="$extra-parameters"/>
        <xsl:on-completion select="$document"/>
        <xsl:next-iteration>
          <xsl:with-param name="document">
            <xsl:variable name="functions" as="xs:boolean*">
              <xsl:for-each select=".?functions">
                <xsl:sequence select="function-available(.)"/>
              </xsl:for-each>
            </xsl:variable>

            <xsl:variable name="process" as="xs:boolean">
              <xsl:choose>
                <xsl:when test="exists($functions) and false() = $functions">
                  <xsl:sequence select="false()"/>
                </xsl:when>
                <xsl:when test="exists(.?test)">
                  <xsl:evaluate xpath=".?test" as="xs:boolean"
                                with-params="$vp:dynamic-parameters"
                                context-item="$document"/>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:sequence select="true()"/>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:variable>

            <xsl:choose>
              <xsl:when test="exists($functions) and false() = $functions">
                <xsl:message use-when="'pipeline' = $v:debug"
                             select="'Unavailable: ' || .?stylesheet-location"/>
                <xsl:sequence select="$document"/>
              </xsl:when>
              <xsl:when test="not($process)">
                <xsl:message use-when="'pipeline' = $v:debug"
                             select="'Unnecessary: ' || .?stylesheet-location"/>
                <xsl:sequence select="$document"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:message use-when="'pipeline' = $v:debug"
                             select="'Processing : ' || .?stylesheet-location"/>

                <xsl:sequence select="transform(map {
                                        'stylesheet-location': .?stylesheet-location,
                                        'source-node': $document,
                                        'static-params': $vp:static-parameters,
                                        'stylesheet-params': map:merge(($vp:dynamic-parameters,
                                                                        $extra-parameters,
                                                                        .?extra-params))
                                      })?output"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:with-param>
          <xsl:with-param name="extra-parameters" select="()"/>
        </xsl:next-iteration>
      </xsl:iterate>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

</xsl:stylesheet>