@pretextbook/pretext-html 0.17.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,11 +10,13 @@ CLI uses via lxml), so the HTML matches a real `pretext build`. It is a fork of
10
10
  with a larger WASM stack so whole-book documents render without overflowing
11
11
  (see [Forking libxslt-wasm](#forking-libxslt-wasm)).
12
12
 
13
- The output is a single, standalone HTML page: PreTeXt's _portable HTML_ mode
14
- is forced on, so theme css/js and MathJax load from public CDNs and the whole
15
- document is rendered as one page. This is what powers the "Instant Preview"
16
- in the PreTeXt-tools VS Code extension, and it is published so other tools
17
- (e.g. pretext.plus) can render previews the same way.
13
+ The output is a single, standalone HTML page. The theme css/js and MathJax
14
+ load from public CDNs, the whole document is rendered as one page, and images
15
+ are linked rather than embedded, so the host serves them from wherever it
16
+ keeps them (see [Images and other assets](#images-and-other-assets)). This is
17
+ what powers the "Live Preview" in the PreTeXt-tools VS Code extension, and it
18
+ is published so other tools (e.g. pretext.plus) can render previews the same
19
+ way.
18
20
 
19
21
  ## Usage
20
22
 
@@ -66,9 +68,19 @@ const { html } = await renderHtml({
66
68
  element has no HTML id of its own, and `findSourceMapEntry(entries, line)`
67
69
  picks the element nearest a cursor line. (This powers the two-way sync in
68
70
  the VS Code Instant Preview; the same map works for any embedder.)
69
- - The user's publication file is respected, except
70
- `<html><platform portable="yes"/></html>` is always forced — that is what
71
- makes single-page in-memory output possible.
71
+ - The user's publication file is respected, except for four settings that
72
+ single-page in-memory output needs, which are always forced (see
73
+ `src/publication.ts`):
74
+ - `<html><resources host="cdn"/>`: css/js, theme and icon font from the CDN.
75
+ - `<common><chunking level="0"/>`: the whole document on one page.
76
+ - `<html><cross-references knowled="never"/>`: cross-references are links,
77
+ because the knowl files they would open are never written.
78
+ - `<html><platform portable="no"/>`: PreTeXt's _portable HTML_ mode is off,
79
+ even for a project that builds portable. Portable mode would do the first
80
+ three as well, and earlier releases relied on it, but it also inlines
81
+ every latex-image and PreFigure SVG into the page. Without it those are
82
+ linked like every other image. The search index stays embedded in the
83
+ page, as portable mode had it.
72
84
  - `cssTheme: "denver"` supplies the PreTeXt HTML theme for projects that do not
73
85
  choose one, by adding `<html><css theme="…"/></html>` to the publication file
74
86
  used for the build (including the minimal one synthesized when you pass no
@@ -76,9 +88,9 @@ const { html } = await renderHtml({
76
88
  that already names a theme — `@theme`, or the deprecated `@style`/`@shell` —
77
89
  keeps it, so the preview goes on matching what `pretext build` produces. The
78
90
  themes the bundled stylesheets know are `default-modern` (PreTeXt's own
79
- default), `denver`, `tacoma`, `salem`, `greeley` and `boulder`; a portable
80
- build loads `theme-<name>.min.css` from the CDN, so an unrecognised name
81
- gives an unstyled page. Not to be confused with `theme` below, which is the
91
+ default), `denver`, `tacoma`, `salem`, `greeley` and `boulder`; the preview
92
+ loads `theme-<name>.min.css` from the CDN, so an unrecognised name gives an
93
+ unstyled page. Not to be confused with `theme` below, which is the
82
94
  light/dark mode _within_ whichever css theme is in force.
83
95
  - The compiled stylesheet is cached per `xslDir` (~1s to compile, then
84
96
  ~100ms–1s per render depending on document size).
@@ -91,6 +103,65 @@ const { html } = await renderHtml({
91
103
  alone. Pass `openKnowls: false` (`--no-open-knowls` on the CLI) for the
92
104
  collapsed reading experience of a real `pretext build`.
93
105
 
106
+ ### Images and other assets
107
+
108
+ Every image the page shows is an ordinary link under one of two fixed
109
+ prefixes, exactly as in a real build. Author-supplied files are under
110
+ `external/`, and everything `pretext generate` makes (latex-image, PreFigure,
111
+ sageplot, asymptote, …) is under `generated/`:
112
+
113
+ ```html
114
+ <img src="external/kitten.png" />
115
+ <img src="generated/latex-image/diagram.svg" />
116
+ ```
117
+
118
+ A real build copies both directories next to its pages. A preview has no such
119
+ directory, so the host maps each URL to wherever it keeps the file:
120
+
121
+ ```js
122
+ import { renderHtml } from "@pretextbook/pretext-html";
123
+ import {
124
+ rewriteAssetUrls,
125
+ missingAssetPlaceholder,
126
+ } from "@pretextbook/pretext-html/assets"; // dependency-free subpath
127
+
128
+ const { html, assetDirs } = await renderHtml({ sourcePath: "source/main.ptx" });
129
+
130
+ const page = rewriteAssetUrls(html, (kind, relPath) => {
131
+ // kind is "external" or "generated"; relPath is e.g. "latex-image/diagram.svg"
132
+ const url = lookUp(kind, relPath); // a webview URI, a blob URL, a hosted URL…
133
+ return url ?? missingAssetPlaceholder(kind, relPath);
134
+ });
135
+ ```
136
+
137
+ - `assetDirs` gives the absolute directories behind the two prefixes, resolved
138
+ the way PreTeXt resolves them. `external` comes from the document's
139
+ `<docinfo><directories external="…"/>`, falling back to the publication
140
+ file's deprecated `source/directories/@external`; it is absent when neither
141
+ declares one, which PreTeXt reads as "no external assets". `generated` comes
142
+ from the publication file's `source/directories/@generated` and defaults to
143
+ `generated/` beside the main source file.
144
+ - A host without a filesystem (a browser editor) ignores `assetDirs` and
145
+ answers from its own store of uploaded or generated files. Nothing reads
146
+ image files during the render itself.
147
+ - Give a file a new URL when it changes, for instance with a version or
148
+ modification-time query. A page never fetches an `<img>` again while its
149
+ `src` is unchanged, and a live patch leaves an unchanged element alone, so a
150
+ regenerated image behind the same URL keeps showing the old picture. With a
151
+ new URL, a live patch updates just that `src` in place and only that image
152
+ reloads. Re-deliver the page when the files change (the VS Code preview
153
+ watches the asset directories), not only when the source does.
154
+ - `missingAssetPlaceholder` returns a data-URI SVG that names the missing file
155
+ and says what to do (`pretext generate latex-image`, or "check @source"),
156
+ which reads better than the browser's broken-image glyph. Return `undefined`
157
+ from the resolver instead to leave a URL as it is.
158
+ - Only `src` attributes are rewritten. An annotated PreFigure diagram (one with
159
+ `<annotations>`) is drawn by a script from `data-src`/`data-cml` attributes,
160
+ which are left alone.
161
+ - The CLI applies this itself when it writes a file (`--output`), linking each
162
+ asset relative to the output file. HTML written to stdout keeps PreTeXt's
163
+ own URLs.
164
+
94
165
  ### Previewing one division of a large document
95
166
 
96
167
  A render costs what PreTeXt's assembly costs, and that tracks the number of
@@ -483,7 +554,7 @@ through `stringParams` to link no Runestone code at all.
483
554
  | ---------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------- |
484
555
  | No multi-file output (`exsl:document`) | Compiled with `FILESYSTEM=0` | Rebuild with `FILESYSTEM=1` (files land in MEMFS) |
485
556
  | JSPI required (flagged on Node) | Loader fetches resources mid-transform via JSPI | Feature-detect with `isJspiAvailable()`; engines without it need a fallback |
486
- | No generated images (latex-image, sageplot, …) | Produced by the Python toolchain, not XSLT | Out of scope; run `pretext generate` and the preview will pick the files up |
557
+ | No generated images (latex-image, sageplot, …) | Produced by the Python toolchain, not XSLT | Out of scope; run `pretext generate` and the page links the files it made |
487
558
  | `xi:include` with `xpointer` unsupported | JS resolver stands in for libxml2's | Rebuild adding `xmlXIncludeProcessFlags` to the JSPI export list |
488
559
  | Renders run one at a time, never in parallel | Shared libxslt state; the transform suspends mid-run | Inherent; `renderHtml` queues concurrent calls for you |
489
560
 
@@ -510,9 +581,9 @@ later call for the lifetime of the process. Once that has happened, only a
510
581
  restart (or a page reload) recovers; `renderHtml` detects the state and says so
511
582
  rather than repeating the underlying assertion.
512
583
 
513
- Missing generated assets degrade gracefully: the transform serves a stub for
514
- missing files, PreTeXt emits its usual `PTX:ERROR` advice, and the preview
515
- renders without the image.
584
+ Missing generated assets cost the render nothing: the page links every image
585
+ whether or not the file exists, and deciding what to show for a missing one is
586
+ the host's (see [Images and other assets](#images-and-other-assets)).
516
587
 
517
588
  ## Forking libxslt-wasm
518
589
 
@@ -4,12 +4,15 @@
4
4
  Regenerate with: npm run refresh-xsl -w @pretextbook/pretext-html
5
5
 
6
6
  Wrapper around pretext-html.xsl for single-page in-memory HTML builds
7
- (previews). The "file-wrap" template below is a verbatim copy of the
8
- upstream template with the <exsl:document> element removed, so the complete
9
- page is emitted on the main result tree. Intended to be applied together
10
- with a publication file that sets <html><platform portable="yes"/></html>,
11
- which forces chunk level 0 (one page) and CDN-hosted css/js, and suppresses
12
- most auxiliary file output. Remaining file writers are stubbed at the end.
7
+ (previews). The "file-wrap" template below is a copy of the upstream
8
+ template with two changes: the <exsl:document> element is removed, so the
9
+ complete page is emitted on the main result tree, and the search index is
10
+ embedded in the page whether or not the build is portable. Intended to be
11
+ applied together with a publication file that forces CDN-hosted css/js
12
+ (<html><resources host="cdn"/>), chunk level 0 (one page), cross-references
13
+ as links rather than knowls, and portable mode off, so that latex-image and
14
+ PreFigure SVGs are linked rather than inlined. Remaining file writers are
15
+ stubbed at the end.
13
16
  -->
14
17
  <xsl:stylesheet
15
18
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
@@ -29,7 +32,9 @@
29
32
  <xsl:import href="pretext-html.xsl"/>
30
33
  <xsl:output method="html" encoding="UTF-8" doctype-system="about:legacy-compat"/>
31
34
 
32
- <!-- Copied from pretext-html.xsl (mode="file-wrap"), exsl:document removed -->
35
+ <!-- Copied from pretext-html.xsl (mode="file-wrap"), exsl:document removed, -->
36
+ <!-- and the search index embedded in every build, not only portable ones. -->
37
+ <!-- See EMBEDDED_SEARCH_TEST in refresh-xsl.mjs. -->
33
38
  <xsl:template match="*" mode="file-wrap">
34
39
  <xsl:param name="content" />
35
40
  <xsl:param name="title" select="''"/>
@@ -199,7 +204,7 @@
199
204
  </div>
200
205
  <xsl:copy-of select="$file-wrap-full-endbody-cache"/>
201
206
  <!-- For portable builds we stash the lunr search here -->
202
- <xsl:if test="$b-portable-html and $has-native-search">
207
+ <xsl:if test="$has-native-search">
203
208
  <xsl:call-template name="embedded-search-construction"/>
204
209
  </xsl:if>
205
210
  <!-- optional body content that needs to be inserted after all content -->
@@ -211,8 +216,8 @@
211
216
  </xsl:template>
212
217
 
213
218
  <!-- Chunk at the subtree root's own level, so a fragment preview can name a -->
214
- <!-- division of any depth as $subtree. Without this, portable's chunk level -->
215
- <!-- of 0 makes upstream abort on every division below the document root. -->
219
+ <!-- division of any depth as $subtree. Without this, the preview's chunk -->
220
+ <!-- level of 0 makes upstream abort on every division below the root. -->
216
221
  <!-- See SUBTREE_CHUNK_LEVEL_OVERRIDE in refresh-xsl.mjs. -->
217
222
  <xsl:param name="subtree-level" select="''"/>
218
223
  <xsl:variable name="chunk-level">
@@ -311,11 +316,17 @@
311
316
  </div>
312
317
  </xsl:template>
313
318
 
319
+ <!-- Skip the passes whose only product is a file the preview never writes: -->
320
+ <!-- knowl files for cross-references, and standalone pages for media. -->
321
+ <!-- See SKIPPED_WORK_STUBS in refresh-xsl.mjs. -->
322
+ <xsl:template match="*" mode="make-xref-knowls"/>
323
+ <xsl:template match="audio|video" mode="media-standalone-page"/>
324
+
314
325
  <!-- Stub out the remaining file writers (every other template reachable -->
315
- <!-- from pretext-html.xsl that contains exsl:document). Under portable- -->
316
- <!-- html several of these are already suppressed; the stubs cover the -->
317
- <!-- rest and act as a safety net if publication settings change. Any -->
318
- <!-- exsl:document that does fire aborts the FILESYSTEM=0 WASM build. -->
326
+ <!-- from pretext-html.xsl that contains exsl:document). The preview's -->
327
+ <!-- publication settings keep some of these from being reached; the stubs -->
328
+ <!-- cover the rest and act as a safety net if publication settings change. -->
329
+ <!-- Any exsl:document that does fire aborts the FILESYSTEM=0 WASM build. -->
319
330
  <xsl:template name="index-redirect-page"/>
320
331
  <xsl:template match="*" mode="manufacture-knowl"/>
321
332
  <xsl:template name="ol-marker-styles"/>
@@ -11,7 +11,8 @@
11
11
 
12
12
  Intended to be applied together with a publication file that forces
13
13
  <revealjs><resources host="cdn" math="online"/></revealjs>, since neither
14
- locally hosted nor embedded reveal.js resources exist for an in-memory build.
14
+ locally hosted nor embedded reveal.js resources exist for an in-memory build,
15
+ and <html><resources host="cdn"/> for PreTeXt's own css/js on the slides.
15
16
  -->
16
17
  <xsl:stylesheet
17
18
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
@@ -65,7 +66,11 @@
65
66
  </xsl:for-each>
66
67
  </xsl:template>
67
68
 
68
- <!-- Same file-writer stubs as preview-html.xsl; see FILE_WRITER_STUBS. -->
69
+ <!-- Same skipped passes and file-writer stubs as preview-html.xsl (a video -->
70
+ <!-- on a slide renders through the same templates); see SKIPPED_WORK_STUBS -->
71
+ <!-- and FILE_WRITER_STUBS. -->
72
+ <xsl:template match="*" mode="make-xref-knowls"/>
73
+ <xsl:template match="audio|video" mode="media-standalone-page"/>
69
74
  <xsl:template name="index-redirect-page"/>
70
75
  <xsl:template match="*" mode="manufacture-knowl"/>
71
76
  <xsl:template name="ol-marker-styles"/>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "repository": "PreTeXtBook/pretext",
4
- "ref": "ee0a7e135e3346f0b0c6192822d6942424cb4af0"
4
+ "ref": "da193419f0746242287b2358094a496029ca9671"
5
5
  },
6
- "refreshed": "2026-10-01T06:28:22.049Z"
6
+ "refreshed": "2026-10-24T03:13:47.656Z"
7
7
  }
@@ -0,0 +1,34 @@
1
+ <!--********************************************************************
2
+ Copyright (C) 2026 Robert A. Beezer
3
+
4
+ This file is part of PreTeXt.
5
+
6
+ PreTeXt is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 2 or version 3 of the
9
+ License (at your option).
10
+
11
+ PreTeXt is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
18
+ *****************************************************************-->
19
+
20
+ # Stylesheets
21
+
22
+ - Use Extensible Stylesheet Language Transformations (XSLT) 1.0 with Extensions to XSLT (EXSLT) only. Declare each EXSLT namespace you use, and list its prefix in `extension-element-prefixes` whether you use its extension elements, such as `exsl:document`, or only its functions: a prefix that is declared but not listed is copied onto literal result elements as a namespace declaration (`pretext-common.xsl` declares and lists `exsl`, `date`, `str`, and `dyn`). libxslt is the reference processor, through `lxml` in `pretext/pretext`.
23
+ - PreTeXt entry stylesheets load `entities.ent` as an external parameter entity in a `DOCTYPE` (document type declaration). Add shared `-LIKE` and `-FILTER` unions there; its comments identify lists that mirror the schema or require a Guide update.
24
+ - Project stylesheets import one format entry point, and every entry point reaches `pretext-common.xsl` through its imports. `pretext-html.xsl` directly imports `publisher-variables.xsl`, `pretext-assembly.xsl`, and `pretext-common.xsl`; publisher variables and assembly are designed to travel together. Treat `-common` stylesheets as libraries rather than entry points.
25
+ - Obtain the identification value of an element by applying `mode="unique-id"` to it, never by reading the attribute that holds it; the template in `pretext-common.xsl` is the one place that knows how the value is stored. `mode="assembly-id"` yields a different value, which belongs to `pretext-assembly.xsl` and the `extract-*.xsl` stylesheets that feed it. A conversion never reads that one: the two values agree for most elements but not for all.
26
+ - Do not put a `priority` attribute on a template when a more specific match pattern, a mode, or import precedence will settle which template applies. Where nothing else will, a comment beside the template gives the reason.
27
+ - Prefix internal logic defects with `PTX:BUG:`. Use `PTX:WARNING:`, `PTX:ERROR:`, and `PTX:FATAL:` according to the [coding chapter](../doc/guide/developer/coding.xml).
28
+ - A message that an author will read describes the outcome and does not name the `pretext/pretext` script, since the PreTeXt command-line interface (CLI) can do the same work. A comment may name the script freely.
29
+ - Write a comment of several lines as single-line comments, one `<!-- ... -->` per line, with every closing `-->` in the same column.
30
+ - A publisher option change pairs the implementing stylesheet with `../schema/publication-schema.xml`, regenerated schemas, a Guide entry, and an example. A theme option also updates `html-theme-option-list` in `publisher-variables.xsl`.
31
+ - Follow the [localization contract](localizations/README.md): add new string identifiers to `en-US.xml`, and register each new language file in `localizations.xml`. Verify localization changes manually.
32
+ - Follow the [support-file procedure](support/README.md) when updating Runestone Services versions.
33
+ - Copy the copyright header from a sibling onto every new `.xsl` file.
34
+ - Run `(cd tests && xsltproc pretext-text-utilities-test.xsl null.xml)`. Add tests there for new low-level text utilities.
@@ -183,9 +183,11 @@
183
183
  <!ENTITY SOLUTION-LIKE "hint|answer|solution">
184
184
  <!ENTITY SOLUTION-FILTER "self::hint or self::answer or self::solution">
185
185
 
186
- <!-- appendages of OPENPROBLEM-LIKE -->
187
- <!ENTITY DISCUSSION-LIKE "context|discussion|opinion|status|suggestion">
188
- <!ENTITY DISCUSSION-FILTER "self::context or self::discussion or self::opinion or self::status or self::suggestion">
186
+ <!-- appendages of OPENPROBLEM-LIKE -->
187
+ <!-- A "status" is also a field of a bibliographic entry, so this one -->
188
+ <!-- is recognized by its parent: an OPENPROBLEM-LIKE, or one's "task" -->
189
+ <!ENTITY DISCUSSION-LIKE "context|discussion|opinion|status[parent::*[&OPENPROBLEM-FILTER;] or parent::task]|suggestion">
190
+ <!ENTITY DISCUSSION-FILTER "self::context or self::discussion or self::opinion or (self::status and (parent::*[&OPENPROBLEM-FILTER;] or parent::task)) or self::suggestion">
189
191
 
190
192
  <!-- Elements the schema gives text-only content: no PreTeXt markup is -->
191
193
  <!-- permitted inside them, whether because they are verbatim (code, a -->
@@ -19,15 +19,17 @@ You should have received a copy of the GNU General Public License
19
19
  along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
20
20
  *********************************************************************-->
21
21
 
22
- <!-- This stylesheet locates video/@youtube elements and -->
23
- <!-- prepares a Python dictionary necessary to extract a -->
24
- <!-- thumbnail for each video from the YouTube servers -->
22
+ <!-- This stylesheet locates the "biblio" of a back matter -->
23
+ <!-- "references", and the "xref" that cite them, and writes -->
24
+ <!-- the first as CSL-JSON and the second as lists of targets, -->
25
+ <!-- which a CSL processor needs to format both -->
25
26
 
26
27
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
27
28
  xmlns:xml="http://www.w3.org/XML/1998/namespace"
28
29
  xmlns:pi="http://pretextbook.org/2020/pretext/internal"
30
+ xmlns:exsl="http://exslt.org/common"
29
31
  xmlns:str="http://exslt.org/strings"
30
- exclude-result-prefixes="str"
32
+ exclude-result-prefixes="exsl str"
31
33
  >
32
34
  <!-- exclude-result-prefixes="pi" -->
33
35
 
@@ -103,12 +105,9 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
103
105
  <xsl:message>PTX:BUG: PreTeXt "biblio" markup using a "<xsl:value-of select="local-name()"/>" element is not implemented</xsl:message>
104
106
  </xsl:template>
105
107
 
106
- <!-- Gross JSON array structure, duplicate an ID for the division -->
108
+ <!-- Gross JSON array structure -->
107
109
  <xsl:template match="backmatter/references" mode="biblio-to-json">
108
110
  <xsl:element name="biblio-csl" namespace="http://pretextbook.org/2020/pretext/internal">
109
- <xsl:attribute name="biblio-id">
110
- <xsl:apply-templates select="." mode="assembly-id"/>
111
- </xsl:attribute>
112
111
  <xsl:text>[&#xa;</xsl:text>
113
112
  <xsl:apply-templates select="biblio" mode="biblio-to-json"/>
114
113
  <xsl:text>&#xa;]</xsl:text>
@@ -119,9 +118,11 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
119
118
  <!-- TODO: refine "select" to only match to-level possibilities -->
120
119
  <xsl:template match="biblio" mode="biblio-to-json">
121
120
  <xsl:text>{&#xa;</xsl:text>
122
- <xsl:apply-templates select="@xml:id|@type" mode="biblio-to-json"/>
123
- <xsl:apply-templates select="*" mode="biblio-to-json"/>
124
- <xsl:text>&#xa;}</xsl:text>
121
+ <xsl:call-template name="json-members">
122
+ <xsl:with-param name="nodes" select="@xml:id|@type|*"/>
123
+ <xsl:with-param name="separator" select="',&#xa;'"/>
124
+ </xsl:call-template>
125
+ <xsl:text>&#xa;}</xsl:text>
125
126
  <xsl:if test="following-sibling::biblio">
126
127
  <xsl:text>,&#xa;</xsl:text>
127
128
  <!-- intra-biblio visual formatting help -->
@@ -129,6 +130,37 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
129
130
  </xsl:if>
130
131
  </xsl:template>
131
132
 
133
+ <!-- The members of a JSON object are the conversions of "nodes", with -->
134
+ <!-- "separator" between one member and the next. A node that converts -->
135
+ <!-- to nothing (markup not implemented, caught above) is no member, so -->
136
+ <!-- the separators are right whichever nodes those are. Each template -->
137
+ <!-- for a member writes the member alone, and nothing after it. -->
138
+ <!-- TODO: "xml-to-json.xsl" converts a description of JSON, made of -->
139
+ <!-- elements, and supplies the separators and the quoting; built on -->
140
+ <!-- it, this template and the quoting in each template could go. -->
141
+ <xsl:template name="json-members">
142
+ <xsl:param name="nodes"/>
143
+ <xsl:param name="separator"/>
144
+ <xsl:variable name="members-rtf">
145
+ <xsl:for-each select="$nodes">
146
+ <xsl:variable name="json">
147
+ <xsl:apply-templates select="." mode="biblio-to-json"/>
148
+ </xsl:variable>
149
+ <xsl:if test="not($json = '')">
150
+ <member>
151
+ <xsl:value-of select="$json"/>
152
+ </member>
153
+ </xsl:if>
154
+ </xsl:for-each>
155
+ </xsl:variable>
156
+ <xsl:for-each select="exsl:node-set($members-rtf)/member">
157
+ <xsl:value-of select="."/>
158
+ <xsl:if test="not(position() = last())">
159
+ <xsl:value-of select="$separator"/>
160
+ </xsl:if>
161
+ </xsl:for-each>
162
+ </xsl:template>
163
+
132
164
  <!-- Attributes -->
133
165
  <!-- Happy accident that local-name(@xml:id) is JSON "id" key for CSL -->
134
166
 
@@ -151,15 +183,14 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
151
183
  <xsl:value-of select="."/>
152
184
  </xsl:with-param>
153
185
  </xsl:call-template>
154
- <xsl:text>",&#xa;</xsl:text>
186
+ <xsl:text>"</xsl:text>
155
187
  </xsl:template>
156
188
 
157
- <!-- Simple string fields to key/value pair -->
158
- <!-- NB: in the order presented in CSL-JSON schema, except as noted. -->
159
- <!-- NB: simple fields for parts of a name are later, and grouped together. -->
160
- <!-- NB: simple fields for alternate date model are at the end, and grouped together. -->
161
- <!-- TODO: many more, from "abstract" to "year-suffix", plus for names and dates. -->
162
- <xsl:template match="publisher|publisher-place|page|volume|title|collection-title|page-first|number-of-pages|URL|name/family|name/given|name/static-ordering|season|circa" mode="biblio-to-json">
189
+ <!-- Simple string fields to key/value pair -->
190
+ <!-- NB: in the order presented in CSL-JSON schema, except as noted. -->
191
+ <!-- NB: simple fields for parts of a name are later, and grouped together. -->
192
+ <!-- TODO: many more, from "abstract" to "year-suffix", plus for names and dates. -->
193
+ <xsl:template match="archive|archive_location|chapter-number|collection-number|collection-title|container-title|container-title-short|DOI|edition|event|genre|ISBN|ISSN|issue|number|number-of-pages|number-of-volumes|page|page-first|publisher|publisher-place|status|title|URL|version|volume|name/family|name/given|name/dropping-particle|name/non-dropping-particle|name/suffix|name/static-ordering|name/literal" mode="biblio-to-json">
163
194
  <xsl:text>"</xsl:text>
164
195
  <xsl:value-of select="local-name()"/>
165
196
  <xsl:text>"</xsl:text>
@@ -175,18 +206,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
175
206
  </xsl:with-param>
176
207
  </xsl:call-template>
177
208
  <xsl:text>"</xsl:text>
178
- <xsl:if test="following-sibling::*">
179
- <xsl:text>,</xsl:text>
180
- <xsl:choose>
181
- <!-- top-label get a newline, else a space -->
182
- <xsl:when test="parent::biblio">
183
- <xsl:text>&#xa;</xsl:text>
184
- </xsl:when>
185
- <xsl:otherwise>
186
- <xsl:text> </xsl:text>
187
- </xsl:otherwise>
188
- </xsl:choose>
189
- </xsl:if>
190
209
  </xsl:template>
191
210
 
192
211
  <!-- ############### -->
@@ -196,7 +215,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
196
215
  <!-- Structured name variables. -->
197
216
  <!-- TODO: see "$ref": "#/definitions/name-variable" in CSL-JSON -->
198
217
  <!-- schema to identify many more. "author" through "translator". -->
199
- <xsl:template match="biblio/author|biblio/editor" mode="biblio-to-json">
218
+ <xsl:template match="biblio/author|biblio/editor|biblio/translator" mode="biblio-to-json">
200
219
  <xsl:text>"</xsl:text>
201
220
  <xsl:value-of select="local-name()"/>
202
221
  <xsl:text>"</xsl:text>
@@ -204,9 +223,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
204
223
  <xsl:text>[</xsl:text>
205
224
  <xsl:apply-templates select="name" mode="biblio-to-json"/>
206
225
  <xsl:text>]</xsl:text>
207
- <xsl:if test="following-sibling::*">
208
- <xsl:text>,&#xa;</xsl:text>
209
- </xsl:if>
210
226
  </xsl:template>
211
227
 
212
228
  <!-- Name structure -->
@@ -216,7 +232,10 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
216
232
  <!-- family, given, suffix, etc. -->
217
233
  <!-- They will use the simple template above, where -->
218
234
  <!-- fields are qualified with the "name" parent -->
219
- <xsl:apply-templates select="*" mode="biblio-to-json"/>
235
+ <xsl:call-template name="json-members">
236
+ <xsl:with-param name="nodes" select="*"/>
237
+ <xsl:with-param name="separator" select="', '"/>
238
+ </xsl:call-template>
220
239
  <xsl:text>}</xsl:text>
221
240
  <xsl:if test="following-sibling::name">
222
241
  <xsl:text>, </xsl:text>
@@ -230,7 +249,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
230
249
  <!-- Structured date variables. -->
231
250
  <!-- TODO: see "$ref": "#/definitions/date-variable" in CSL-JSON -->
232
251
  <!-- schema to identify many more. "accessed" through "submitted". -->
233
- <xsl:template match="submitted|issued" mode="biblio-to-json">
252
+ <xsl:template match="accessed|issued" mode="biblio-to-json">
234
253
  <xsl:text>"</xsl:text>
235
254
  <xsl:value-of select="local-name()"/>
236
255
  <xsl:text>"</xsl:text>
@@ -244,18 +263,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
244
263
  <xsl:text>]</xsl:text>
245
264
  </xsl:if>
246
265
  <xsl:text>&#xa;}</xsl:text>
247
- <xsl:if test="following-sibling::*">
248
- <xsl:text>,</xsl:text>
249
- <xsl:choose>
250
- <!-- likely always first test? -->
251
- <xsl:when test="parent::biblio">
252
- <xsl:text>&#xa;</xsl:text>
253
- </xsl:when>
254
- <xsl:otherwise>
255
- <xsl:text> </xsl:text>
256
- </xsl:otherwise>
257
- </xsl:choose>
258
- </xsl:if>
259
266
  </xsl:template>
260
267
 
261
268
  <!-- An array of length 1 to 3, with spaces after commas -->