@pretextbook/pretext-html 0.3.0 → 0.4.0

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.
Files changed (43) hide show
  1. package/README.md +20 -9
  2. package/assets/preview-html.xsl +8 -4
  3. package/assets/upstream.json +1 -1
  4. package/assets/xsl/README.md +25 -24
  5. package/assets/xsl/entities.ent +20 -0
  6. package/assets/xsl/extract-biblio-csl.xsl +1 -1
  7. package/assets/xsl/extract-pg.xsl +111 -297
  8. package/assets/xsl/extract-qrcode.xsl +11 -1
  9. package/assets/xsl/extract-sageplot.xsl +1 -1
  10. package/assets/xsl/latex/pretext-latex-texstyle.xsl +1 -2
  11. package/assets/xsl/localizations/README.md +24 -24
  12. package/assets/xsl/pretext-assembly.xsl +101 -11
  13. package/assets/xsl/pretext-beamer.xsl +43 -146
  14. package/assets/xsl/pretext-braille-preprint.xsl +457 -201
  15. package/assets/xsl/pretext-common.xsl +388 -423
  16. package/assets/xsl/pretext-epub.xsl +301 -72
  17. package/assets/xsl/pretext-fo.xsl +70 -86
  18. package/assets/xsl/pretext-html.xsl +331 -259
  19. package/assets/xsl/pretext-jupyter.xsl +534 -322
  20. package/assets/xsl/pretext-latex-classic.xsl +14 -15
  21. package/assets/xsl/pretext-latex-common.xsl +385 -2389
  22. package/assets/xsl/pretext-latex.xsl +1995 -5
  23. package/assets/xsl/pretext-markdown.xsl +919 -0
  24. package/assets/xsl/pretext-numbers.xsl +22 -4
  25. package/assets/xsl/pretext-revealjs.xsl +241 -33
  26. package/assets/xsl/pretext-runestone-fitb.xsl +20 -22
  27. package/assets/xsl/pretext-runestone.xsl +3 -3
  28. package/assets/xsl/pretext-sage-doctest.xsl +2 -2
  29. package/assets/xsl/pretext-text-utilities.xsl +69 -1
  30. package/assets/xsl/pretext-text.xsl +1077 -107
  31. package/assets/xsl/publisher-variables.xsl +396 -452
  32. package/assets/xsl/support/play-button/README.md +1 -3
  33. package/assets/xsl/tests/README.md +1 -1
  34. package/assets/xsl/utilities/README.md +6 -8
  35. package/assets/xsl/utilities/report-publisher-variables.xsl +15 -0
  36. package/assets/xsl-bundle.json +1 -1
  37. package/dist/{cli-CzoinNle.js → cli-Cp6C0sif.js} +220 -205
  38. package/dist/cli.js +1 -1
  39. package/dist/index.browser.js +351 -342
  40. package/dist/index.js +1 -1
  41. package/dist/publication.d.ts +9 -1
  42. package/dist/renderer.d.ts +20 -0
  43. package/package.json +1 -1
@@ -28,7 +28,9 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
28
28
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
29
29
  xmlns:exsl="http://exslt.org/common"
30
30
  xmlns:str="http://exslt.org/strings"
31
+ xmlns:pi="http://pretextbook.org/2020/pretext/internal"
31
32
  extension-element-prefixes="exsl str"
33
+ exclude-result-prefixes="pi"
32
34
  >
33
35
 
34
36
  <!-- Trade on HTML markup, numbering, chunking, etc. -->
@@ -36,8 +38,13 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
36
38
  <!-- NB: this will import -assembly and -common stylesheets -->
37
39
  <xsl:import href="./pretext-html.xsl" />
38
40
 
39
- <!-- Output is JSON, enriched with serialized HTML -->
40
- <xsl:output method="text" />
41
+ <!-- Output is an XML description of a notebook: a "notebook" -->
42
+ <!-- element containing a flat list of "cell" elements, whose -->
43
+ <!-- content is serialized HTML (markdown cells) or program text -->
44
+ <!-- (code cells). The Python routine jupyter() converts each -->
45
+ <!-- such file into the JSON of a Jupyter notebook via "nbformat", -->
46
+ <!-- which owns all JSON escaping and schema conformance. -->
47
+ <xsl:output method="xml" encoding="UTF-8" />
41
48
 
42
49
  <!-- ######### -->
43
50
  <!-- Variables -->
@@ -54,8 +61,6 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
54
61
  <!-- iPython files as output -->
55
62
  <xsl:variable name="file-extension" select="'.ipynb'" />
56
63
 
57
- <xsl:param name="jupyter.kernel" select="''" />
58
-
59
64
  <!-- Disable clipboardable -->
60
65
  <xsl:template name="insert-clipboardable-class"/>
61
66
 
@@ -122,7 +127,12 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
122
127
  <!-- We have entire cells for division headings. -->
123
128
  <xsl:template match="&STRUCTURAL;" mode="pretext-heading">
124
129
  <xsl:variable name="html-rtf">
125
- <xsl:apply-templates select="." mode="section-heading" />
130
+ <!-- each division heads its own notebook, and the document -->
131
+ <!-- title is a level below the notebook's styling cell, so -->
132
+ <!-- level 2 mirrors the HTML conversion's chunked pages -->
133
+ <xsl:apply-templates select="." mode="section-heading">
134
+ <xsl:with-param name="heading-level" select="2"/>
135
+ </xsl:apply-templates>
126
136
  </xsl:variable>
127
137
  <xsl:variable name="html-node-set" select="exsl:node-set($html-rtf)" />
128
138
  <xsl:call-template name="pretext-cell">
@@ -136,7 +146,10 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
136
146
 
137
147
  <xsl:template match="paragraphs|introduction|conclusion" mode="pretext-heading">
138
148
  <xsl:variable name="html-rtf">
139
- <xsl:apply-templates select="." mode="heading-title" />
149
+ <!-- a level below a division heading (see above) -->
150
+ <xsl:apply-templates select="." mode="heading-title">
151
+ <xsl:with-param name="heading-level" select="3"/>
152
+ </xsl:apply-templates>
140
153
  </xsl:variable>
141
154
  <xsl:variable name="html-node-set" select="exsl:node-set($html-rtf)" />
142
155
  <xsl:call-template name="pretext-cell">
@@ -149,7 +162,32 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
149
162
  </xsl:template>
150
163
 
151
164
  <xsl:template match="*" mode="pretext-heading">
152
- <xsl:message>pretext-heading unmatched <xsl:value-of select="local-name(.)" /></xsl:message>
165
+ <xsl:message>PTX:BUG: pretext-heading unmatched <xsl:value-of select="local-name(.)" /></xsl:message>
166
+ </xsl:template>
167
+
168
+ <!-- A division companion as its own notebook: a heading cell from -->
169
+ <!-- the localized type-name (titles ignored, pending deprecation) -->
170
+ <!-- and then the children as cells -->
171
+ <xsl:template match="introduction|conclusion" mode="division-companion-page">
172
+ <xsl:variable name="html-rtf">
173
+ <h1>
174
+ <xsl:apply-templates select="." mode="division-companion-text"/>
175
+ </h1>
176
+ </xsl:variable>
177
+ <xsl:variable name="html-node-set" select="exsl:node-set($html-rtf)" />
178
+ <xsl:call-template name="pretext-cell">
179
+ <xsl:with-param name="content">
180
+ <xsl:call-template name="begin-string" />
181
+ <xsl:apply-templates select="$html-node-set" mode="xml-to-string" />
182
+ <xsl:call-template name="end-string" />
183
+ </xsl:with-param>
184
+ </xsl:call-template>
185
+ <xsl:apply-templates select="*"/>
186
+ </xsl:template>
187
+
188
+ <!-- The ordinary block realization is the whole notebook -->
189
+ <xsl:template match="objectives|outcomes" mode="division-companion-page">
190
+ <xsl:apply-templates select="."/>
153
191
  </xsl:template>
154
192
 
155
193
  <!-- Three modal templates accomodate all document structure nodes -->
@@ -165,7 +203,6 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
165
203
  <xsl:with-param name="content">
166
204
  <!-- should perhaps initialize/pass $heading-level = 2 here -->
167
205
  <!-- perhaps irrelevant since headings are done in markown? -->
168
- <xsl:apply-templates select="objectives|introduction" />
169
206
  <xsl:variable name="html-rtf">
170
207
  <nav class="summary-links">
171
208
  <xsl:apply-templates select="*" mode="summary-nav" />
@@ -192,8 +229,9 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
192
229
  <!-- to adjust the page-oriented flavor of the base HTML (which exists -->
193
230
  <!-- as part of accommodating printing from a web browser). All children -->
194
231
  <!-- of a "page" get processed, and elsewhere get recognized as items -->
195
- <!-- deserving of their own cells. -->
196
- <xsl:template match="worksheet/page">
232
+ <!-- deserving of their own cells. A "handout" structures its content -->
233
+ <!-- with "page" identically. -->
234
+ <xsl:template match="worksheet/page|handout/page">
197
235
  <xsl:apply-templates select="*"/>
198
236
  </xsl:template>
199
237
 
@@ -228,122 +266,94 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
228
266
  <!-- the real content of the page -->
229
267
  <xsl:copy-of select="$content" />
230
268
  </xsl:variable>
231
- <exsl:document href="{$the-filename}" method="text">
232
- <!-- <xsl:call-template name="converter-blurb-html" /> -->
233
- <!-- begin outermost group -->
234
- <xsl:text>{&#xa;</xsl:text>
235
- <!-- cell list first, majority of notebook, metadata to finish -->
236
- <xsl:text>"cells": [&#xa;</xsl:text>
237
- <!-- Escape JSON strings now, be sure later adjustments -->
238
- <!-- conform to JSON syntax in this regard -->
239
- <xsl:variable name="escaped-cell-list">
240
- <xsl:call-template name="escape-json-string">
241
- <xsl:with-param name="text" select="$cell-list"/>
242
- </xsl:call-template>
243
- </xsl:variable>
244
- <!-- Multiple strings in a cell are merged into one by -->
245
- <!-- combining adjoining end/begin pairs, leaving only -->
246
- <!-- leading and trailing delimiters (next substitution). -->
247
- <!-- This is one solution of the problem of $n-1$ -->
248
- <!-- separators for $n$ items. -->
249
- <xsl:variable name="split-strings" select="str:replace($escaped-cell-list, $ESBS, '')" />
250
- <xsl:variable name="finalize-strings" select="str:replace(str:replace($split-strings, $ES, '&quot;'), $BS, '&quot;')" />
251
- <!-- The only pseudo-markup left is that of the two types -->
252
- <!-- of cells possible in a Jupyter notebook. We split -->
253
- <!-- just the adjacent brackets with comma-newline, so -->
254
- <!-- source has each cell entirely on its own line. This -->
255
- <!-- is the other solution of the problem of $n-1$ -->
256
- <!-- separators for $n$ items. -->
257
- <xsl:variable name="split-cells" select="str:replace($finalize-strings, $RBLB, $RBLB-comma)" />
258
- <!-- Now we consider the actual markers and replace -->
259
- <!-- with the JSON that Jupyter expects as source. -->
260
- <!-- We are done, so "value-of" is good enough, -->
261
- <!-- rather than having a final $code-cells. The -->
262
- <!-- four *-wrap variables here are just conveniences -->
263
- <xsl:variable name="markdown-cells" select="str:replace(str:replace($split-cells, $BM, $begin-markdown-wrap), $EM, $end-markdown-wrap)" />
264
- <xsl:value-of select="str:replace(str:replace($markdown-cells, $BC, $begin-code-wrap), $EC, $end-code-wrap)" />
265
- <!-- end cell list -->
266
- <xsl:text>&#xa;],&#xa;</xsl:text>
267
- <!-- version identifiers -->
268
- <xsl:text>"nbformat": 4, "nbformat_minor": 0, </xsl:text>
269
- <!-- metadata copied from blank SMC notebook -->
270
- <xsl:text>"metadata": {</xsl:text>
271
- <xsl:text>"kernelspec": {</xsl:text>
272
- <!-- TODO: configure kernel in "docinfo" -->
273
- <!-- "display_name" seems ineffective, but is required -->
274
- <xsl:text>"display_name": "", </xsl:text>
275
- <!-- TODO: language not needed? -->
276
- <!-- <xsl:text>"language": "python", </xsl:text> -->
277
- <!-- TODO: make kernelspec configurable? -->
278
- <!-- <xsl:text>"name": "python2"</xsl:text> -->
279
- <!-- "sagemath" as "name" will be latest kernel -->
280
- <!-- in Sage distribution Jupyter, and in CoCalc -->
281
- <xsl:choose>
282
- <xsl:when test="contains('|python3|Python3|python 3|Python 3|py|Py|python|Python|'
283
- , concat('|', $jupyter.kernel, '|'))">
284
- <xsl:text>"name": "python3"</xsl:text>
285
- </xsl:when>
286
- <xsl:otherwise>
287
- <xsl:text>"name": "sagemath"</xsl:text>
288
- </xsl:otherwise>
289
- </xsl:choose>
290
- <!-- TODO: how much of the following is necessary before loading? -->
291
- <xsl:text>}, </xsl:text>
292
- <xsl:text>"language_info": {</xsl:text>
293
- <xsl:text>"codemirror_mode": {</xsl:text>
294
- <xsl:text>"name": "ipython", </xsl:text>
295
- <xsl:text>"version": 3</xsl:text>
296
- <xsl:text>}, </xsl:text>
297
- <xsl:text>"file_extension": ".py", </xsl:text>
298
- <xsl:text>"mimetype": "text/x-python", </xsl:text>
299
- <xsl:text>"name": "python", </xsl:text>
300
- <xsl:text>"nbconvert_exporter": "python", </xsl:text>
301
- <xsl:text>"pygments_lexer": "ipython3", </xsl:text>
302
- <xsl:text>"version": "3.6.4"</xsl:text>
303
- <xsl:text>}, </xsl:text>
304
- <xsl:text>"name": "</xsl:text>
305
- <xsl:value-of select="$the-filename" />
306
- <xsl:text>"</xsl:text>
307
- <xsl:text>}&#xa;</xsl:text>
308
- <!-- end outermost group -->
309
- <xsl:text>}</xsl:text>
269
+ <!-- The file written here is the *description* of a notebook, -->
270
+ <!-- so an "xml" suffix is appended to the eventual notebook -->
271
+ <!-- filename. Hyperlinks within cell content are relative -->
272
+ <!-- references to the final "ipynb" filenames, so the Python -->
273
+ <!-- conversion must strip the suffix when writing final files. -->
274
+ <exsl:document href="{$the-filename}.xml" method="xml" encoding="UTF-8">
275
+ <notebook>
276
+ <!-- The kernel comes from the publication file (with a -->
277
+ <!-- deprecated stringparam honored), and is communicated to -->
278
+ <!-- the Python routine, which owns all remaining -->
279
+ <!-- notebook-level metadata. -->
280
+ <xsl:attribute name="kernel">
281
+ <xsl:value-of select="$jupyter-kernel"/>
282
+ </xsl:attribute>
283
+ <xsl:copy-of select="$cell-list"/>
284
+ </notebook>
310
285
  </exsl:document>
311
286
  </xsl:template>
312
287
 
313
288
  <!-- a code cell with HTML magic -->
314
289
  <!-- allows reader to activate styling -->
315
290
  <!-- Code first, so it begins with focus -->
291
+ <!-- The styling is deliberately self-contained: no network -->
292
+ <!-- requests (a reader's activity is nobody's business), no -->
293
+ <!-- scripts (host applications routinely remove them), and no -->
294
+ <!-- colors that fight a host application's light or dark theme. -->
295
+ <!-- The notebook is fully readable if this cell is never run: -->
296
+ <!-- styling is a progressive enhancement, never structural. -->
316
297
  <xsl:template name="load-css">
317
298
  <!-- HTML as one-off code cell -->
318
299
  <!-- Serialize HTML by hand here -->
319
300
  <xsl:call-template name="code-cell">
301
+ <xsl:with-param name="purpose" select="'styling'"/>
320
302
  <xsl:with-param name="content">
321
303
  <xsl:call-template name="begin-string" />
322
304
  <xsl:text>%%html&#xa;</xsl:text>
323
- <!-- for offline testing -->
324
- <!-- <xsl:text>&lt;link href="./mathbook-content.css" rel="stylesheet" type="text/css" /&gt;&#xa;</xsl:text> -->
325
- <xsl:text>&lt;link href="https://pretextbook.org/beta/mathbook-content.css" rel="stylesheet" type="text/css" /&gt;&#xa;</xsl:text>
326
- <xsl:text>&lt;link href="https://aimath.org/mathbook/mathbook-add-on.css" rel="stylesheet" type="text/css" /&gt;&#xa;</xsl:text>
327
- <!-- A bad hack since "subtitle" is in masthead code, better CSS should take care of this -->
328
- <xsl:if test="$document-root/subtitle">
329
- <xsl:text>&lt;style&gt;.subtitle {font-size:medium; display:block}&lt;/style&gt;&#xa;</xsl:text>
330
- </xsl:if>
331
- <xsl:text>&lt;link href="https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,600,600italic" rel="stylesheet" type="text/css" /&gt;&#xa;</xsl:text>
332
- <xsl:text>&lt;link href="https://fonts.googleapis.com/css?family=Inconsolata:400,700&amp;subset=latin,latin-ext" rel="stylesheet" type="text/css" /&gt;</xsl:text>
333
- <!-- Cell hider is unwrapped from some notebook display command that injects HTML: -->
334
- <!-- https://nbviewer.jupyter.org/github/shashi/ijulia-notebooks/blob/master/funcgeo/Functional%20Geometry.ipynb -->
335
- <xsl:text>&lt;!-- Hide this cell. --&gt;&#xa;</xsl:text>
336
- <xsl:text>&lt;script&gt;&#xa;</xsl:text>
337
- <xsl:text>var cell = $(".container .cell").eq(0), ia = cell.find(".input_area")&#xa;</xsl:text>
338
- <xsl:text>if (cell.find(".toggle-button").length == 0) {&#xa;</xsl:text>
339
- <xsl:text>ia.after(&#xa;</xsl:text>
340
- <xsl:text> $('&lt;button class="toggle-button"&gt;Toggle hidden code&lt;/button&gt;').click(&#xa;</xsl:text>
341
- <xsl:text> function (){ ia.toggle() }&#xa;</xsl:text>
342
- <xsl:text> )&#xa;</xsl:text>
343
- <xsl:text> )&#xa;</xsl:text>
344
- <xsl:text>ia.hide()&#xa;</xsl:text>
345
- <xsl:text>}&#xa;</xsl:text>
346
- <xsl:text>&lt;/script&gt;&#xa;</xsl:text>
305
+ <xsl:text>&lt;style&gt;&#xa;</xsl:text>
306
+ <xsl:text>.mathbook-content {line-height: 1.4;}&#xa;</xsl:text>
307
+ <xsl:text>.mathbook-content .heading {margin-bottom: 0.5em;}&#xa;</xsl:text>
308
+ <xsl:text>.mathbook-content article .heading {font-size: 1em; margin: 0 0 0.5em;}&#xa;</xsl:text>
309
+ <xsl:text>.mathbook-content code.code-inline {padding: 0 0.2em; border: 0.05em solid #bbbbbb; border-radius: 0.2em;}&#xa;</xsl:text>
310
+ <xsl:text>.mathbook-content .heading .codenumber {margin-left: 0.25em;}&#xa;</xsl:text>
311
+ <xsl:text>.mathbook-content .heading .title {margin-left: 0.25em; font-style: italic;}&#xa;</xsl:text>
312
+ <xsl:text>.mathbook-content .subtitle {font-size: medium; display: block; font-weight: normal;}&#xa;</xsl:text>
313
+ <xsl:text>.mathbook-content article {border-left: 0.15em solid #888888; padding-left: 0.75em; margin: 0.75em 0;}&#xa;</xsl:text>
314
+ <xsl:text>.mathbook-content article .para:last-child {margin-bottom: 0;}&#xa;</xsl:text>
315
+ <xsl:text>.mathbook-content article.proof {border-left: 0.1em dotted #888888;}&#xa;</xsl:text>
316
+ <xsl:text>.mathbook-content article.fragment-first {margin-bottom: 0;}&#xa;</xsl:text>
317
+ <xsl:text>.mathbook-content article.fragment-interior, .mathbook-content article.fragment-last {margin-top: 0;}&#xa;</xsl:text>
318
+ <xsl:text>.mathbook-content .para {margin-top: 0.75em;}&#xa;</xsl:text>
319
+ <xsl:text>.mathbook-content pre.code-display {border: 0.05em solid #888888; padding: 0.5em; overflow-x: auto;}&#xa;</xsl:text>
320
+ <xsl:text>.mathbook-content figure {margin: 1em auto; text-align: center;}&#xa;</xsl:text>
321
+ <xsl:text>.mathbook-content .image-box {margin-left: auto; margin-right: auto;}&#xa;</xsl:text>
322
+ <xsl:text>.mathbook-content img.contained {width: 100%;}&#xa;</xsl:text>
323
+ <xsl:text>.mathbook-content figcaption {font-style: italic; margin-top: 0.25em;}&#xa;</xsl:text>
324
+ <xsl:text>.mathbook-content figcaption .type, .mathbook-content figcaption .codenumber {font-style: normal; font-weight: bold;}&#xa;</xsl:text>
325
+ <xsl:text>.mathbook-content .summary-links li {list-style: none;}&#xa;</xsl:text>
326
+ <xsl:text>.mathbook-content .summary-links a {display: block; margin: 0.5em 0; padding: 0.6em 0.9em; border: 0.05em solid #888888; border-radius: 0.5em; font-size: 1.2em; font-weight: bold; text-decoration: none;}&#xa;</xsl:text>
327
+ <xsl:text>.mathbook-content .summary-links a .codenumber {margin-right: 0.5em;}&#xa;</xsl:text>
328
+ <xsl:text>.mathbook-content .sbsrow {display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); column-gap: 3%; align-items: start;}&#xa;</xsl:text>
329
+ <xsl:text>.mathbook-content .sbspanel.middle, .mathbook-content .sbspanel--middle {align-self: center;}&#xa;</xsl:text>
330
+ <xsl:text>.mathbook-content .sbspanel.bottom, .mathbook-content .sbspanel--bottom {align-self: end;}&#xa;</xsl:text>
331
+ <xsl:text>.mathbook-content table {border-collapse: collapse;}&#xa;</xsl:text>
332
+ <xsl:text>.mathbook-content table td, .mathbook-content table th {padding: 0.2em 0.5em; border: 0 solid;}&#xa;</xsl:text>
333
+ <!-- host themes stripe and highlight table rows; authored -->
334
+ <!-- tables speak through their rules, never backgrounds. -->
335
+ <!-- The full "background" shorthand, explicit alternating -->
336
+ <!-- selectors, and !important outrank any host theme. -->
337
+ <xsl:text>.mathbook-content table tr, .mathbook-content table td, .mathbook-content table th {background: transparent !important;}&#xa;</xsl:text>
338
+ <xsl:text>.mathbook-content table tbody tr:nth-child(odd), .mathbook-content table tbody tr:nth-child(even), .mathbook-content table tbody tr:hover {background: transparent !important;}&#xa;</xsl:text>
339
+ <xsl:text>.mathbook-content td.t1, .mathbook-content th.t1 {border-top-width: 1px;}&#xa;</xsl:text>
340
+ <xsl:text>.mathbook-content td.t2, .mathbook-content th.t2 {border-top-width: 2px;}&#xa;</xsl:text>
341
+ <xsl:text>.mathbook-content td.t3, .mathbook-content th.t3 {border-top-width: 3px;}&#xa;</xsl:text>
342
+ <xsl:text>.mathbook-content td.b1, .mathbook-content th.b1 {border-bottom-width: 1px;}&#xa;</xsl:text>
343
+ <xsl:text>.mathbook-content td.b2, .mathbook-content th.b2 {border-bottom-width: 2px;}&#xa;</xsl:text>
344
+ <xsl:text>.mathbook-content td.b3, .mathbook-content th.b3 {border-bottom-width: 3px;}&#xa;</xsl:text>
345
+ <xsl:text>.mathbook-content td.l1, .mathbook-content th.l1 {border-left-width: 1px;}&#xa;</xsl:text>
346
+ <xsl:text>.mathbook-content td.l2, .mathbook-content th.l2 {border-left-width: 2px;}&#xa;</xsl:text>
347
+ <xsl:text>.mathbook-content td.l3, .mathbook-content th.l3 {border-left-width: 3px;}&#xa;</xsl:text>
348
+ <xsl:text>.mathbook-content td.r1, .mathbook-content th.r1 {border-right-width: 1px;}&#xa;</xsl:text>
349
+ <xsl:text>.mathbook-content td.r2, .mathbook-content th.r2 {border-right-width: 2px;}&#xa;</xsl:text>
350
+ <xsl:text>.mathbook-content td.r3, .mathbook-content th.r3 {border-right-width: 3px;}&#xa;</xsl:text>
351
+ <xsl:text>.mathbook-content td.l {text-align: left;}&#xa;</xsl:text>
352
+ <xsl:text>.mathbook-content td.c {text-align: center;}&#xa;</xsl:text>
353
+ <xsl:text>.mathbook-content td.r {text-align: right;}&#xa;</xsl:text>
354
+ <xsl:text>.mathbook-content table tr.header-vertical th {writing-mode: vertical-rl; padding-left: 2em;}&#xa;</xsl:text>
355
+ <xsl:text>.mathbook-content .sbspanel img {max-width: 100%;}&#xa;</xsl:text>
356
+ <xsl:text>&lt;/style&gt;</xsl:text>
347
357
  <xsl:call-template name="end-string" />
348
358
  </xsl:with-param>
349
359
  </xsl:call-template>
@@ -352,7 +362,7 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
352
362
  <xsl:call-template name="markdown-cell">
353
363
  <xsl:with-param name="content">
354
364
  <xsl:call-template name="begin-string" />
355
- <xsl:text>**Important:** to view this notebook properly you will need to execute the cell above, which assumes you have an Internet connection. It should already be selected, or place your cursor anywhere above to select. Then press the "Run" button in the menu bar above (the right-pointing arrowhead), or press Shift-Enter on your keyboard.</xsl:text>
365
+ <xsl:text>The collapsed code cell above adds some light styling, and in a trusted notebook it takes effect automatically. If this page looks plain, run that cell once, with Shift-Enter or a Run button. Everything below is fully readable either way, and nothing here requires an Internet connection.</xsl:text>
356
366
  <xsl:call-template name="end-string" />
357
367
  </xsl:with-param>
358
368
  </xsl:call-template>
@@ -376,6 +386,12 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
376
386
  <xsl:value-of select="$latex-packages-mathjax" />
377
387
  <!-- Sequence replacements if \gt and/or \amp need to go -->
378
388
  <xsl:value-of select="str:replace($latex-macros,'\newcommand{\lt}{&lt;}&#xa;', '')"/>
389
+ <!-- "math support" macros, mirroring the base HTML template -->
390
+ <xsl:call-template name="fillin-math"/>
391
+ <!-- legacy built-in support for "slanted|beveled|nice" fractions -->
392
+ <xsl:if test="$b-has-sfrac">
393
+ <xsl:text>\newcommand{\sfrac}[2]{{#1}/{#2}}&#xa;</xsl:text>
394
+ </xsl:if>
379
395
  </xsl:with-param>
380
396
  </xsl:call-template>
381
397
  <xsl:call-template name="end-string" />
@@ -383,6 +399,33 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
383
399
  </xsl:call-template>
384
400
  </xsl:template>
385
401
 
402
+ <!-- An override of the template in pretext-common.xsl. The -->
403
+ <!-- publisher's "shade" style needs \definecolor and \colorbox, -->
404
+ <!-- from a MathJax extension a notebook's configuration does not -->
405
+ <!-- load (and we cannot touch); an unknown macro renders literally -->
406
+ <!-- as mathematics. So "shade" degrades to the visually closest -->
407
+ <!-- style, "box", which is core TeX throughout. The "underline" -->
408
+ <!-- style is also safe, and is reproduced verbatim. -->
409
+ <xsl:template name="fillin-math">
410
+ <xsl:choose>
411
+ <xsl:when test="$fillin-math-style = 'underline'">
412
+ <xsl:text>\newcommand{\fillinmath}[1]{\mathchoice</xsl:text>
413
+ <xsl:text>{\underline{\displaystyle \phantom{\ \,#1\ \,}}}</xsl:text>
414
+ <xsl:text>{\underline{\textstyle \phantom{\ \,#1\ \,}}}</xsl:text>
415
+ <xsl:text>{\underline{\scriptstyle \phantom{\ \,#1\ \,}}}</xsl:text>
416
+ <xsl:text>{\underline{\scriptscriptstyle\phantom{\ \,#1\ \,}}}}&#xa;</xsl:text>
417
+ </xsl:when>
418
+ <!-- "box", and "shade" degraded to "box" -->
419
+ <xsl:otherwise>
420
+ <xsl:text>\newcommand{\fillinmath}[1]{\mathchoice</xsl:text>
421
+ <xsl:text>{\boxed{\displaystyle \phantom{\,#1\,}}}</xsl:text>
422
+ <xsl:text>{\boxed{\textstyle \phantom{\,#1\,}}}</xsl:text>
423
+ <xsl:text>{\boxed{\scriptstyle \phantom{\,#1\,}}}</xsl:text>
424
+ <xsl:text>{\boxed{\scriptscriptstyle\phantom{\,#1\,}}}}&#xa;</xsl:text>
425
+ </xsl:otherwise>
426
+ </xsl:choose>
427
+ </xsl:template>
428
+
386
429
 
387
430
  <!-- ################# -->
388
431
  <!-- Block Level Items -->
@@ -405,19 +448,239 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
405
448
  <!-- TODO: remove filter on paragraphs once we add stack for sidebyside -->
406
449
  <xsl:template match="*[parent::*[&STRUCTURAL-FILTER; or self::paragraphs[not(ancestor::sidebyside)] or self::introduction[parent::*[&STRUCTURAL-FILTER;]] or self::conclusion[parent::*[&STRUCTURAL-FILTER;]]]]|*[parent::page]" priority="-0.5">
407
450
  <!-- <xsl:message>G:<xsl:value-of select="local-name(.)" />:G</xsl:message> -->
408
- <xsl:variable name="html-rtf">
409
- <xsl:apply-imports />
451
+ <xsl:choose>
452
+ <!-- A block with executable "sage" anywhere within it cannot -->
453
+ <!-- be one markdown cell: the code deserves genuine, -->
454
+ <!-- executable code cells. Since cells never nest, the -->
455
+ <!-- block splits into *fragments* around its code: each -->
456
+ <!-- fragment is a complete, balanced rendering, and any -->
457
+ <!-- element spanning a split (the block itself, a -->
458
+ <!-- "statement", a "task") is re-opened in the next -->
459
+ <!-- fragment. A fragment-position class lets styling draw -->
460
+ <!-- continuity (side borders) across the cells, and cell -->
461
+ <!-- metadata records the same structure durably. -->
462
+ <xsl:when test=".//sage[not(@type = 'display')]">
463
+ <!-- NB: "apply-imports" honors the current mode, so the -->
464
+ <!-- rendering must happen here, in the default mode -->
465
+ <xsl:variable name="html-rtf">
466
+ <xsl:apply-imports/>
467
+ </xsl:variable>
468
+ <xsl:call-template name="fragmented-block">
469
+ <xsl:with-param name="rendered" select="$html-rtf"/>
470
+ </xsl:call-template>
471
+ </xsl:when>
472
+ <xsl:otherwise>
473
+ <xsl:variable name="html-rtf">
474
+ <xsl:apply-imports />
475
+ </xsl:variable>
476
+ <xsl:variable name="demoted-rtf">
477
+ <xsl:apply-templates select="exsl:node-set($html-rtf)" mode="demote-headings"/>
478
+ </xsl:variable>
479
+ <xsl:variable name="html-node-set" select="exsl:node-set($demoted-rtf)" />
480
+ <xsl:call-template name="pretext-cell">
481
+ <xsl:with-param name="content">
482
+ <xsl:call-template name="begin-string" />
483
+ <xsl:apply-templates select="$html-node-set" mode="xml-to-string" />
484
+ <xsl:call-template name="end-string" />
485
+ </xsl:with-param>
486
+ </xsl:call-template>
487
+ </xsl:otherwise>
488
+ </xsl:choose>
489
+ </xsl:template>
490
+
491
+ <!-- The HTML conversion threads a heading level to every block's -->
492
+ <!-- heading, but "xsl:apply-imports" (necessary above, to reuse -->
493
+ <!-- the entire HTML conversion for a block) cannot pass -->
494
+ <!-- parameters, an XSLT 1.0 limitation. So every block heading -->
495
+ <!-- arrives at the default level, "h1". The rendered HTML -->
496
+ <!-- itself, though, records the true nesting: each block's -->
497
+ <!-- wrapper element holds its heading as a child. So the -->
498
+ <!-- accurate level is recovered from the rendering. A division -->
499
+ <!-- heading (made elsewhere) is "h2"; a block directly below it -->
500
+ <!-- is "h3"; a block within a block (a "task" of a "project", -->
501
+ <!-- say) is "h4"; and so on, capped at "h6". This is visual -->
502
+ <!-- fidelity, but more importantly it is the heading outline -->
503
+ <!-- that a screen reader's navigation presents. Headings built -->
504
+ <!-- by other routes (divisions, "paragraphs", companion pages) -->
505
+ <!-- never pass through here, and keep their explicit levels. -->
506
+ <xsl:template match="node()|@*" mode="demote-headings">
507
+ <xsl:copy>
508
+ <xsl:apply-templates select="node()|@*" mode="demote-headings"/>
509
+ </xsl:copy>
510
+ </xsl:template>
511
+
512
+ <xsl:template match="h1[contains(concat(' ', normalize-space(@class), ' '), ' heading ')]" mode="demote-headings">
513
+ <!-- each ancestor with a heading of its own is an enclosing -->
514
+ <!-- block; this heading's block is the innermost of them -->
515
+ <xsl:variable name="enclosing-blocks" select="count(ancestor::*[*[starts-with(local-name(), 'h') and (string-length(local-name()) = 2) and contains(concat(' ', normalize-space(@class), ' '), ' heading ')]])"/>
516
+ <xsl:variable name="level">
517
+ <xsl:choose>
518
+ <xsl:when test="2 + $enclosing-blocks &gt; 6">
519
+ <xsl:text>6</xsl:text>
520
+ </xsl:when>
521
+ <xsl:otherwise>
522
+ <xsl:value-of select="2 + $enclosing-blocks"/>
523
+ </xsl:otherwise>
524
+ </xsl:choose>
410
525
  </xsl:variable>
411
- <xsl:variable name="html-node-set" select="exsl:node-set($html-rtf)" />
412
- <xsl:call-template name="pretext-cell">
413
- <xsl:with-param name="content">
414
- <xsl:call-template name="begin-string" />
415
- <xsl:apply-templates select="$html-node-set" mode="xml-to-string" />
416
- <xsl:call-template name="end-string" />
417
- </xsl:with-param>
526
+ <xsl:element name="h{$level}">
527
+ <xsl:apply-templates select="node()|@*" mode="demote-headings"/>
528
+ </xsl:element>
529
+ </xsl:template>
530
+
531
+ <!-- Split a block into markdown fragments around executable code -->
532
+ <!-- cells. The block renders exactly as usual, except each -->
533
+ <!-- interior executable "sage" renders as an empty marker -->
534
+ <!-- element. The rendered tree is then *sliced* at the markers: -->
535
+ <!-- slice k keeps the nodes that follow marker k (and precede -->
536
+ <!-- marker k+1), while any element that spans a marker (the -->
537
+ <!-- block's own wrapper, a "statement", a "task") is re-opened, -->
538
+ <!-- shallowly, in every slice it spans<mdash/>so each fragment is -->
539
+ <!-- balanced HTML in the block's own dress, and the code may sit -->
540
+ <!-- at any depth. The block's heading is part of the rendering, -->
541
+ <!-- so it lands in the first slice with no special treatment. -->
542
+ <xsl:template name="fragmented-block">
543
+ <!-- the block's ordinary rendering (markers included), made -->
544
+ <!-- by the caller; the context node remains the block -->
545
+ <xsl:param name="rendered"/>
546
+ <xsl:variable name="element-name" select="local-name(.)"/>
547
+ <xsl:variable name="identifier">
548
+ <xsl:apply-templates select="." mode="html-id"/>
549
+ </xsl:variable>
550
+ <!-- settle heading levels before slicing -->
551
+ <xsl:variable name="demoted-rtf">
552
+ <xsl:apply-templates select="exsl:node-set($rendered)" mode="demote-headings"/>
553
+ </xsl:variable>
554
+ <xsl:variable name="marked" select="exsl:node-set($demoted-rtf)"/>
555
+ <!-- the code, in document order, mirroring the markers exactly -->
556
+ <xsl:variable name="code" select=".//sage[not(@type = 'display')]"/>
557
+ <xsl:variable name="code-count" select="count($code)"/>
558
+ <!-- the opening fragment always appears: it carries the heading -->
559
+ <xsl:call-template name="fragment-slice-cell">
560
+ <xsl:with-param name="marked" select="$marked"/>
561
+ <xsl:with-param name="slice-number" select="0"/>
562
+ <xsl:with-param name="element" select="$element-name"/>
563
+ <xsl:with-param name="identifier" select="$identifier"/>
564
+ <xsl:with-param name="part" select="'first'"/>
418
565
  </xsl:call-template>
566
+ <!-- alternate code cells with the slices that follow them -->
567
+ <xsl:for-each select="$code">
568
+ <xsl:variable name="place" select="position()"/>
569
+ <!-- does any actual content trail the final marker? -->
570
+ <xsl:variable name="last-slice-rtf">
571
+ <xsl:if test="$place = $code-count">
572
+ <xsl:apply-templates select="$marked/*" mode="fragment-slice">
573
+ <xsl:with-param name="slice-number" select="$code-count"/>
574
+ </xsl:apply-templates>
575
+ </xsl:if>
576
+ </xsl:variable>
577
+ <xsl:variable name="b-code-is-last" select="($place = $code-count) and not(exsl:node-set($last-slice-rtf)//text()[normalize-space(.) != ''] or exsl:node-set($last-slice-rtf)//img)"/>
578
+ <xsl:apply-templates select="." mode="sage-code-cell">
579
+ <xsl:with-param name="element" select="$element-name"/>
580
+ <xsl:with-param name="identifier" select="$identifier"/>
581
+ <xsl:with-param name="part">
582
+ <xsl:choose>
583
+ <xsl:when test="$b-code-is-last">
584
+ <xsl:text>last</xsl:text>
585
+ </xsl:when>
586
+ <xsl:otherwise>
587
+ <xsl:text>interior</xsl:text>
588
+ </xsl:otherwise>
589
+ </xsl:choose>
590
+ </xsl:with-param>
591
+ </xsl:apply-templates>
592
+ <xsl:if test="not($b-code-is-last)">
593
+ <xsl:call-template name="fragment-slice-cell">
594
+ <xsl:with-param name="marked" select="$marked"/>
595
+ <xsl:with-param name="slice-number" select="$place"/>
596
+ <xsl:with-param name="element" select="$element-name"/>
597
+ <xsl:with-param name="identifier" select="$identifier"/>
598
+ <xsl:with-param name="part">
599
+ <xsl:choose>
600
+ <xsl:when test="$place = $code-count">
601
+ <xsl:text>last</xsl:text>
602
+ </xsl:when>
603
+ <xsl:otherwise>
604
+ <xsl:text>interior</xsl:text>
605
+ </xsl:otherwise>
606
+ </xsl:choose>
607
+ </xsl:with-param>
608
+ </xsl:call-template>
609
+ </xsl:if>
610
+ </xsl:for-each>
611
+ </xsl:template>
612
+
613
+ <!-- One slice of the marked rendering becomes one markdown cell, -->
614
+ <!-- unless the slice holds no genuine content (adjacent code, or -->
615
+ <!-- code that opens its block, leave gaps that nobody misses) -->
616
+ <xsl:template name="fragment-slice-cell">
617
+ <xsl:param name="marked"/>
618
+ <xsl:param name="slice-number"/>
619
+ <xsl:param name="element"/>
620
+ <xsl:param name="identifier"/>
621
+ <xsl:param name="part"/>
622
+ <xsl:variable name="slice-rtf">
623
+ <xsl:apply-templates select="$marked/*" mode="fragment-slice">
624
+ <xsl:with-param name="slice-number" select="$slice-number"/>
625
+ <xsl:with-param name="part" select="$part"/>
626
+ </xsl:apply-templates>
627
+ </xsl:variable>
628
+ <xsl:variable name="slice" select="exsl:node-set($slice-rtf)"/>
629
+ <xsl:if test="($part = 'first') or $slice//text()[normalize-space(.) != ''] or $slice//img">
630
+ <xsl:call-template name="pretext-cell">
631
+ <xsl:with-param name="content">
632
+ <xsl:call-template name="begin-string" />
633
+ <xsl:apply-templates select="$slice" mode="xml-to-string" />
634
+ <xsl:call-template name="end-string" />
635
+ </xsl:with-param>
636
+ <xsl:with-param name="element" select="$element"/>
637
+ <xsl:with-param name="identifier" select="$identifier"/>
638
+ <xsl:with-param name="part" select="$part"/>
639
+ </xsl:call-template>
640
+ </xsl:if>
641
+ </xsl:template>
642
+
643
+ <!-- Slice a marked rendering. An element containing markers is -->
644
+ <!-- re-opened (shallow copy) in every slice it spans; any other -->
645
+ <!-- node belongs to exactly the slice numbered by the count of -->
646
+ <!-- markers preceding it. The markers themselves never survive. -->
647
+ <xsl:template match="*" mode="fragment-slice">
648
+ <xsl:param name="slice-number"/>
649
+ <xsl:param name="part" select="''"/>
650
+ <xsl:choose>
651
+ <xsl:when test=".//pi:sage-marker">
652
+ <xsl:copy>
653
+ <!-- an HTML id may appear only once, so it stays -->
654
+ <!-- with the first slice of a spanning element -->
655
+ <xsl:copy-of select="@*[not(name() = 'id') or ($slice-number = 0)]"/>
656
+ <!-- the outermost element of a slice advertises -->
657
+ <!-- its position, for styling continuity -->
658
+ <xsl:if test="not(parent::*) and not($part = '')">
659
+ <xsl:attribute name="class">
660
+ <xsl:value-of select="normalize-space(concat(@class, ' fragment-', $part))"/>
661
+ </xsl:attribute>
662
+ </xsl:if>
663
+ <xsl:apply-templates select="node()" mode="fragment-slice">
664
+ <xsl:with-param name="slice-number" select="$slice-number"/>
665
+ </xsl:apply-templates>
666
+ </xsl:copy>
667
+ </xsl:when>
668
+ <xsl:when test="count(preceding::pi:sage-marker) = $slice-number">
669
+ <xsl:copy-of select="."/>
670
+ </xsl:when>
671
+ <!-- other slices claim this node -->
672
+ </xsl:choose>
419
673
  </xsl:template>
420
674
 
675
+ <xsl:template match="text()" mode="fragment-slice">
676
+ <xsl:param name="slice-number"/>
677
+ <xsl:if test="count(preceding::pi:sage-marker) = $slice-number">
678
+ <xsl:copy-of select="."/>
679
+ </xsl:if>
680
+ </xsl:template>
681
+
682
+ <xsl:template match="pi:sage-marker" mode="fragment-slice"/>
683
+
421
684
  <!-- Kill some templates temporarily -->
422
685
  <xsl:template name="inline-warning" />
423
686
  <xsl:template name="margin-warning" />
@@ -428,7 +691,53 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
428
691
 
429
692
  <!-- Sage code -->
430
693
  <!-- Should evolve to accomodate general template -->
694
+ <!-- A "sage" element whose parent produces cells (a division or -->
695
+ <!-- pseudo-division) becomes a genuine executable code cell. But -->
696
+ <!-- cells cannot nest, so a "sage" buried within a block (say an -->
697
+ <!-- "example") that is mid-formation as a single markdown cell -->
698
+ <!-- must not fire the cell machinery: it renders as a static -->
699
+ <!-- "pre" element within the block's HTML. (Splitting such a -->
700
+ <!-- block into fragments around executable cells is the eventual -->
701
+ <!-- goal; this static form is the fallback.) -->
431
702
  <xsl:template match="sage">
703
+ <!-- formulate lines of code -->
704
+ <xsl:variable name="loc">
705
+ <xsl:call-template name="sanitize-text">
706
+ <xsl:with-param name="text">
707
+ <xsl:value-of select="input" />
708
+ </xsl:with-param>
709
+ </xsl:call-template>
710
+ </xsl:variable>
711
+ <xsl:choose>
712
+ <!-- contexts whose children each become top-level cells, -->
713
+ <!-- mirroring the block-level wildcard template -->
714
+ <xsl:when test="parent::*[&STRUCTURAL-FILTER;] or parent::paragraphs or parent::page or parent::introduction[parent::*[&STRUCTURAL-FILTER;]] or parent::conclusion[parent::*[&STRUCTURAL-FILTER;]]">
715
+ <xsl:apply-templates select="." mode="sage-code-cell"/>
716
+ </xsl:when>
717
+ <!-- a display-only listing is never executable: a static -->
718
+ <!-- rendering, as element nodes that serialize as part -->
719
+ <!-- of the enclosing cell -->
720
+ <xsl:when test="@type = 'display'">
721
+ <pre class="code-display">
722
+ <xsl:value-of select="substring($loc, 1, string-length($loc)-1)" />
723
+ </pre>
724
+ </xsl:when>
725
+ <!-- interior to a block being split into fragments: the -->
726
+ <!-- rendering carries a marker where the code belongs, -->
727
+ <!-- and the block's template slices at the markers and -->
728
+ <!-- supplies the genuine code cells -->
729
+ <xsl:otherwise>
730
+ <pi:sage-marker/>
731
+ </xsl:otherwise>
732
+ </xsl:choose>
733
+ </xsl:template>
734
+
735
+ <!-- An executable cell from a "sage" element, with optional -->
736
+ <!-- structure metadata when it sits interior to a block -->
737
+ <xsl:template match="sage" mode="sage-code-cell">
738
+ <xsl:param name="element" select="''"/>
739
+ <xsl:param name="identifier" select="''"/>
740
+ <xsl:param name="part" select="''"/>
432
741
  <!-- formulate lines of code -->
433
742
  <xsl:variable name="loc">
434
743
  <xsl:call-template name="sanitize-text">
@@ -445,6 +754,9 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
445
754
  <xsl:value-of select="substring($loc, 1, string-length($loc)-1)" />
446
755
  <xsl:call-template name="end-string" />
447
756
  </xsl:with-param>
757
+ <xsl:with-param name="element" select="$element"/>
758
+ <xsl:with-param name="identifier" select="$identifier"/>
759
+ <xsl:with-param name="part" select="$part"/>
448
760
  </xsl:call-template>
449
761
  </xsl:template>
450
762
 
@@ -482,6 +794,19 @@ along with MathBook XML. If not, see <http://www.gnu.org/licenses/>.
482
794
  <!-- For it to be effective will require the overall "ignore-math" -->
483
795
  <!-- class, and access to the MathJax configuration. -->
484
796
 
797
+ <!-- The notebook's MathJax reads mathematics from the *raw* -->
798
+ <!-- markdown source, before any HTML entity is decoded. So -->
799
+ <!-- entity-escaping inside mathematics arrives literally: an -->
800
+ <!-- alignment ampersand serialized as "&amp;amp;" renders as a -->
801
+ <!-- visible "amp;". Text within a math wrapper (the -->
802
+ <!-- "process-math" class) therefore serializes verbatim. Bare -->
803
+ <!-- markup characters are not a hazard: PreTeXt requires \lt for -->
804
+ <!-- "less than" within mathematics, and MathJax lifts the math -->
805
+ <!-- from the source before the remainder is parsed as HTML. -->
806
+ <xsl:template match="text()[ancestor::*[contains(concat(' ', normalize-space(@class), ' '), ' process-math ')]]" mode="xml-to-string">
807
+ <xsl:value-of select="."/>
808
+ </xsl:template>
809
+
485
810
  <!-- Images -->
486
811
 
487
812
  <!-- Jupyter seems to not allow an "object" tag. -->
@@ -593,34 +918,17 @@ TODO: (overall)
593
918
  <!-- ############### -->
594
919
 
595
920
  <!-- Jupyter does a very good (but incomplete) job with inline -->
596
- <!-- verbatim text, requiring little care by authors. But a -->
597
- <!-- few gotchas need adjustment. So we override. -->
921
+ <!-- verbatim text, requiring little care by authors. So we -->
922
+ <!-- override. The wrapper builds a genuine "code" element -->
923
+ <!-- whose content is plain text: the serialization step then -->
924
+ <!-- escapes any markup characters exactly once, so an author -->
925
+ <!-- may write about elements (like in the Author's Guide!) -->
926
+ <!-- and they arrive as visible text, never as interior HTML. -->
598
927
  <xsl:template name="code-wrapper">
599
928
  <xsl:param name="content"/>
600
-
601
- <!-- Jupyter notebook is careful about XML special characters, -->
602
- <!-- but if you want to write about the escaped versions, they -->
603
- <!-- just get converted to the real thing. So in these five -->
604
- <!-- very special situations we escape the leading ampersand -->
605
- <!-- and whatever conversion is happening is satiated. -->
606
- <xsl:variable name="escaped-ampersand-fixed" select="str:replace($content, '&amp;amp;', '&amp;amp;amp;' )"/>
607
- <xsl:variable name="escaped-leftbracket-fixed" select="str:replace($escaped-ampersand-fixed, '&amp;lt;', '&amp;amp;lt;' )"/>
608
- <xsl:variable name="escaped-rightbracket-fixed" select="str:replace($escaped-leftbracket-fixed, '&amp;gt;', '&amp;amp;gt;' )"/>
609
- <xsl:variable name="escaped-apostrophe-fixed" select="str:replace($escaped-rightbracket-fixed, '&amp;apos;', '&amp;amp;apos;' )"/>
610
- <xsl:variable name="escaped-quote-fixed" select="str:replace($escaped-apostrophe-fixed, '&amp;quot;', '&amp;amp;quot;' )"/>
611
-
612
- <!-- If you want to write about elements (like in the Author's -->
613
- <!-- Guide!) then you cannot have elements/tags sitting unaltered -->
614
- <!-- in verbatim text, since they now look like interior HTML. -->
615
- <!-- So we disrupt the leading left-bracket with an escaped -->
616
- <!-- version and let that convert to the character. -->
617
- <xsl:variable name="leftbracket-fixed" select="str:replace($escaped-quote-fixed, '&lt;', '&amp;lt;' )"/>
618
-
619
- <!-- We wrap verbatim inline text with an HTML "code" element. -->
620
- <!-- We enclose with PreTeXt's HTML, serializing by hand. -->
621
- <xsl:text>&lt;code class="code-inline tex2jax_ignore"&gt;</xsl:text>
622
- <xsl:value-of select="$leftbracket-fixed"/>
623
- <xsl:text>&lt;/code&gt;</xsl:text>
929
+ <code class="code-inline tex2jax_ignore">
930
+ <xsl:value-of select="$content"/>
931
+ </code>
624
932
  </xsl:template>
625
933
 
626
934
  <!-- #### -->
@@ -662,204 +970,108 @@ TODO: (overall)
662
970
  <xsl:text>"</xsl:text>
663
971
  </xsl:template>
664
972
 
665
- <!-- ########################## -->
666
- <!-- Intermediate Pseudo-Markup -->
667
- <!-- ########################## -->
668
-
669
- <!-- A Jupyter notebook is a flat sequence of cells, of type either -->
670
- <!-- "markdown" or "code." The content is primarily a list of strings. -->
671
- <!-- This presents two fundamental problems: -->
672
- <!-- -->
673
- <!-- 1. Lists of cells, or lists of strings, with n items -->
674
- <!-- need exactly n-1 commas. It is hard to predict/find -->
675
- <!-- the first or last element of a list. -->
676
- <!-- -->
677
- <!-- 2. Cells cannot be nested and content should not lie -->
678
- <!-- outside of cells. -->
679
- <!-- -->
680
- <!-- We use a sort of pseudo-markup. Adjacency of items lets -->
681
- <!-- us solve the comma problem. We are also able to effectively -->
682
- <!-- merge content into a cell, without knowing anything about -->
683
- <!-- the following cell. -->
684
- <!-- -->
685
- <!-- We pipeline a pseudo-markup as an intermediate text format. -->
686
- <!-- -->
687
- <!-- Pseudo-markup language: -->
688
- <!-- -->
689
- <!-- LB, RB: left and right brackets - should be UUIDs eventually -->
690
- <!-- -->
691
- <!-- BS, ES: begin and end string -->
692
- <!-- -->
693
- <!-- BM, EM, BC, EC: begin and end, markdown and code, cells -->
694
-
695
-
696
- <!-- ####### -->
697
- <!-- Markers -->
698
- <!-- ####### -->
699
-
700
- <!-- This is pseudo-markup, starting with delimiters -->
701
- <!-- analagous to < and > of XML. Make these delimiters -->
702
- <!-- exceedingly unique. Comment out salt while debugging -->
703
- <!-- if it helps to see intermediate structure. Or leave -->
704
- <!-- salt in, and grep final output for this "bad" string -->
705
- <!-- that should not survive. -->
706
-
707
- <!-- Random-ish output from mkpasswd utility, -->
708
- <!-- 2017-10-24 at AMS airport, Starbucks by faux gate D1 -->
709
- <xsl:variable name="salt" select="'x9rNtyUydoz3o'" />
710
-
711
- <xsl:variable name="LB">
712
- <xsl:text>[[[</xsl:text>
713
- <xsl:value-of select="$salt" />
714
- </xsl:variable>
715
-
716
- <xsl:variable name="RB">
717
- <xsl:value-of select="$salt" />
718
- <xsl:text>]]]</xsl:text>
719
- </xsl:variable>
720
-
721
- <!-- These are analagous to XML opening -->
722
- <!-- ("begin", B) and closing ("end", E) elements -->
723
-
724
- <!-- Destined to be string -->
725
- <xsl:variable name="BS">
726
- <xsl:value-of select="$LB" />
727
- <xsl:text>BS</xsl:text>
728
- <xsl:value-of select="$RB" />
729
- </xsl:variable>
730
- <xsl:variable name="ES">
731
- <xsl:value-of select="$LB" />
732
- <xsl:text>ES</xsl:text>
733
- <xsl:value-of select="$RB" />
734
- </xsl:variable>
735
-
736
- <!-- Destined to be a Jupyter markdown cell in JSON output-->
737
- <xsl:variable name="BM">
738
- <xsl:value-of select="$LB" />
739
- <xsl:text>BM</xsl:text>
740
- <xsl:value-of select="$RB" />
741
- </xsl:variable>
742
- <xsl:variable name="EM">
743
- <xsl:value-of select="$LB" />
744
- <xsl:text>EM</xsl:text>
745
- <xsl:value-of select="$RB" />
746
- </xsl:variable>
747
-
748
- <!-- Destined to be a Jupyter code cell in JSON output-->
749
- <xsl:variable name="BC">
750
- <xsl:value-of select="$LB" />
751
- <xsl:text>BC</xsl:text>
752
- <xsl:value-of select="$RB" />
753
- </xsl:variable>
754
- <xsl:variable name="EC">
755
- <xsl:value-of select="$LB" />
756
- <xsl:text>EC</xsl:text>
757
- <xsl:value-of select="$RB" />
758
- </xsl:variable>
759
-
760
- <!-- These variables describe adjacent pseudo-markup -->
761
- <!-- that will be converted to JSON equivalents. -->
762
-
763
- <xsl:variable name="ESBS">
764
- <xsl:value-of select="$ES" />
765
- <xsl:value-of select="$BS" />
766
- </xsl:variable>
767
-
768
- <xsl:variable name="RBLB">
769
- <xsl:value-of select="$RB" />
770
- <xsl:value-of select="$LB" />
771
- </xsl:variable>
772
-
773
- <!-- This is a convenience for the replacement that -->
774
- <!-- splits cells into lines within JSON file -->
775
- <xsl:variable name="RBLB-comma">
776
- <xsl:value-of select="$RB" />
777
- <xsl:text>,&#xa;</xsl:text>
778
- <xsl:value-of select="$LB" />
779
- </xsl:variable>
780
-
781
- <!-- Convenience templates -->
782
- <!-- These are primary interface to our creation -->
783
- <!-- of pseudo-markup above, but are not the whole -->
784
- <!-- story since we convert markup based on the variables -->
785
-
786
- <xsl:template name="begin-string">
787
- <xsl:value-of select="$BS" />
788
- </xsl:template>
789
-
790
- <xsl:template name="end-string">
791
- <xsl:value-of select="$ES" />
792
- </xsl:template>
793
-
794
- <xsl:template name="begin-markdown-cell">
795
- <xsl:value-of select="$BM" />
796
- </xsl:template>
797
-
798
- <xsl:template name="end-markdown-cell">
799
- <xsl:value-of select="$EM" />
800
- </xsl:template>
801
-
802
- <xsl:template name="begin-code-cell">
803
- <xsl:value-of select="$BC" />
804
- </xsl:template>
805
-
806
- <xsl:template name="end-code-cell">
807
- <xsl:value-of select="$EC" />
808
- </xsl:template>
809
-
810
-
811
973
  <!-- ################# -->
812
974
  <!-- Cell Construction -->
813
975
  <!-- ################# -->
814
976
 
815
- <xsl:variable name="begin-markdown-wrap">
816
- <xsl:text>{"cell_type":"markdown", "metadata":{}, "source":[</xsl:text>
817
- </xsl:variable>
977
+ <!-- A Jupyter notebook is a flat sequence of cells, of type either -->
978
+ <!-- "markdown" or "code", each holding one string. We describe -->
979
+ <!-- each notebook as a "notebook" element holding a flat list of -->
980
+ <!-- "cell" elements; the Python conversion routine turns each -->
981
+ <!-- description into JSON with the "nbformat" library, which owns -->
982
+ <!-- string escaping, cell boilerplate, and schema conformance. -->
983
+ <!-- Cells cannot be nested, so cell-producing templates must never -->
984
+ <!-- fire while another cell's content is under construction. -->
818
985
 
819
- <xsl:variable name="end-markdown-wrap">
820
- <xsl:text>]}</xsl:text>
821
- </xsl:variable>
986
+ <!-- The two templates immediately following are historical seams: -->
987
+ <!-- every string destined for a cell was once delimited by markers -->
988
+ <!-- these templates produced. A cell's content is now a single -->
989
+ <!-- string, so they are no-ops, retained since callers throughout -->
990
+ <!-- this stylesheet still frame content with them, and they mark -->
991
+ <!-- exactly the boundaries a future refinement (say, a markdown -->
992
+ <!-- flavor of cell content) might need to intercept. -->
822
993
 
823
- <xsl:variable name="begin-code-wrap">
824
- <xsl:text>{"cell_type": "code", "execution_count":null, "metadata":{}, "source":[</xsl:text>
825
- </xsl:variable>
994
+ <xsl:template name="begin-string"/>
826
995
 
827
- <xsl:variable name="end-code-wrap">
828
- <xsl:text>], "outputs":[]}</xsl:text>
829
- </xsl:variable>
996
+ <xsl:template name="end-string"/>
830
997
 
831
998
  <!-- A Jupyter markdown cell intended -->
832
999
  <!-- to hold markdown or unstyled HTML -->
833
1000
  <xsl:template name="markdown-cell">
834
1001
  <xsl:param name="content" />
835
- <xsl:call-template name="begin-markdown-cell" />
836
- <xsl:value-of select="$content" />
837
- <xsl:call-template name="end-markdown-cell" />
1002
+ <cell type="markdown">
1003
+ <xsl:value-of select="$content" />
1004
+ </cell>
838
1005
  </xsl:template>
839
1006
 
840
1007
  <!-- A Jupyter markdown cell intended -->
841
1008
  <!-- to hold PreTeXt styled HTML -->
842
1009
  <!-- Serialization here is "by hand" -->
1010
+ <!-- The three structure parameters describe a cell which is a -->
1011
+ <!-- *fragment* of a PreTeXt element split around code cells; -->
1012
+ <!-- they land in the notebook cell's metadata, preserving the -->
1013
+ <!-- hierarchy that the flat cell list cannot express directly. -->
843
1014
  <xsl:template name="pretext-cell">
844
1015
  <xsl:param name="content" />
845
- <xsl:call-template name="begin-markdown-cell" />
846
- <xsl:call-template name="begin-string" />
847
- <xsl:text>&lt;div class="mathbook-content"&gt;</xsl:text>
848
- <xsl:call-template name="end-string" />
849
- <xsl:value-of select="$content" />
850
- <xsl:call-template name="begin-string" />
851
- <xsl:text>&lt;/div&gt;</xsl:text>
852
- <xsl:call-template name="end-string" />
853
- <xsl:call-template name="end-markdown-cell" />
1016
+ <xsl:param name="element" select="''"/>
1017
+ <xsl:param name="identifier" select="''"/>
1018
+ <xsl:param name="part" select="''"/>
1019
+ <cell type="markdown">
1020
+ <xsl:if test="not($element = '')">
1021
+ <xsl:attribute name="element">
1022
+ <xsl:value-of select="$element"/>
1023
+ </xsl:attribute>
1024
+ </xsl:if>
1025
+ <xsl:if test="not($identifier = '')">
1026
+ <xsl:attribute name="identifier">
1027
+ <xsl:value-of select="$identifier"/>
1028
+ </xsl:attribute>
1029
+ </xsl:if>
1030
+ <xsl:if test="not($part = '')">
1031
+ <xsl:attribute name="part">
1032
+ <xsl:value-of select="$part"/>
1033
+ </xsl:attribute>
1034
+ </xsl:if>
1035
+ <xsl:text>&lt;div class="mathbook-content"&gt;</xsl:text>
1036
+ <xsl:value-of select="$content" />
1037
+ <xsl:text>&lt;/div&gt;</xsl:text>
1038
+ </cell>
854
1039
  </xsl:template>
855
1040
 
856
1041
  <!-- A Jupyter code cell intended -->
857
1042
  <!-- to hold raw text/code -->
858
1043
  <xsl:template name="code-cell">
859
1044
  <xsl:param name="content" />
860
- <xsl:call-template name="begin-code-cell" />
861
- <xsl:value-of select="$content" />
862
- <xsl:call-template name="end-code-cell" />
1045
+ <xsl:param name="element" select="''"/>
1046
+ <xsl:param name="identifier" select="''"/>
1047
+ <xsl:param name="part" select="''"/>
1048
+ <!-- "purpose" flags special cells (only 'styling' so far) for -->
1049
+ <!-- extra treatment by the Python routine: the styling cell -->
1050
+ <!-- collapses, and ships with its output pre-rendered -->
1051
+ <xsl:param name="purpose" select="''"/>
1052
+ <cell type="code">
1053
+ <xsl:if test="not($purpose = '')">
1054
+ <xsl:attribute name="purpose">
1055
+ <xsl:value-of select="$purpose"/>
1056
+ </xsl:attribute>
1057
+ </xsl:if>
1058
+ <xsl:if test="not($element = '')">
1059
+ <xsl:attribute name="element">
1060
+ <xsl:value-of select="$element"/>
1061
+ </xsl:attribute>
1062
+ </xsl:if>
1063
+ <xsl:if test="not($identifier = '')">
1064
+ <xsl:attribute name="identifier">
1065
+ <xsl:value-of select="$identifier"/>
1066
+ </xsl:attribute>
1067
+ </xsl:if>
1068
+ <xsl:if test="not($part = '')">
1069
+ <xsl:attribute name="part">
1070
+ <xsl:value-of select="$part"/>
1071
+ </xsl:attribute>
1072
+ </xsl:if>
1073
+ <xsl:value-of select="$content" />
1074
+ </cell>
863
1075
  </xsl:template>
864
1076
 
865
1077
  <!-- We don't want any permalinks -->