@pretextbook/pretext-html 0.8.0 → 0.9.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 (36) hide show
  1. package/assets/preview-html.xsl +16 -0
  2. package/assets/upstream.json +1 -1
  3. package/assets/xsl/extract-dynamic.xsl +54 -3
  4. package/assets/xsl/extract-gdscript.xsl +61 -0
  5. package/assets/xsl/extract-sageplot.xsl +3 -0
  6. package/assets/xsl/localizations/it-IT.xml +37 -41
  7. package/assets/xsl/pretext-assembly.xsl +188 -24
  8. package/assets/xsl/pretext-beamer.xsl +57 -12
  9. package/assets/xsl/pretext-common.xsl +156 -28
  10. package/assets/xsl/pretext-epub.xsl +0 -5
  11. package/assets/xsl/pretext-fo.xsl +110 -48
  12. package/assets/xsl/pretext-html.xsl +147 -54
  13. package/assets/xsl/pretext-jupyter.xsl +1 -0
  14. package/assets/xsl/pretext-latex-classic.xsl +2 -2
  15. package/assets/xsl/pretext-latex-common.xsl +268 -134
  16. package/assets/xsl/pretext-latex.xsl +16 -6
  17. package/assets/xsl/pretext-numbers.xsl +10 -0
  18. package/assets/xsl/pretext-revealjs.xsl +68 -1
  19. package/assets/xsl/pretext-runestone-fitb.xsl +179 -84
  20. package/assets/xsl/pretext-runestone-static.xsl +72 -20
  21. package/assets/xsl/pretext-runestone.xsl +27 -4
  22. package/assets/xsl/pretext-text-utilities.xsl +7 -0
  23. package/assets/xsl/pretext-text.xsl +17 -0
  24. package/assets/xsl/publisher-variables.xsl +39 -0
  25. package/assets/xsl-bundle.json +1 -1
  26. package/dist/cli-BUrNZxov.js +1098 -0
  27. package/dist/cli.js +1 -1
  28. package/dist/html-static.d.ts +2 -2
  29. package/dist/index.browser.js +595 -386
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +35 -30
  32. package/dist/renderer.d.ts +35 -0
  33. package/dist/skeleton.d.ts +41 -0
  34. package/dist/xrefs.d.ts +54 -0
  35. package/package.json +1 -1
  36. package/dist/cli-Bcn4t3B1.js +0 -871
@@ -586,7 +586,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
586
586
  <!-- section-level items in the back matter -->
587
587
  <xsl:template name="titlesec-section-style">
588
588
  <xsl:text>\titleformat{\section}[hang]&#xa;</xsl:text>
589
- <xsl:text>{\divisionfont\Large\bfseries}{\thesection}{1ex}{#1}&#xa;</xsl:text>
589
+ <xsl:text>{\divisionfont\Large\bfseries}{\thesection\ptxappendixperiodsection}{1ex}{#1}&#xa;</xsl:text>
590
590
  <xsl:text>[{\large\authorsptx}]&#xa;</xsl:text>
591
591
  <xsl:text>\titleformat{name=\section,numberless}[block]&#xa;</xsl:text>
592
592
  <xsl:text>{\divisionfont\Large\bfseries}{}{0pt}{#1}&#xa;</xsl:text>
@@ -656,16 +656,24 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
656
656
  <xsl:template name="titletoc-chapter-style">
657
657
  <xsl:text>\titlecontents{chapter}%&#xa;</xsl:text>
658
658
  <xsl:text>[0pt]{\contentsmargin{0em}\addvspace{1pc}\contentsfont\bfseries}%&#xa;</xsl:text>
659
- <xsl:text>{\large\thecontentslabel\enspace}{\large}%&#xa;</xsl:text>
659
+ <xsl:text>{\large\thecontentslabel\ptxappendixperiodchapter\enspace}{\large}%&#xa;</xsl:text>
660
660
  <xsl:text>{\hfill\bfseries\thecontentspage}%&#xa;</xsl:text>
661
661
  <xsl:text>[\addvspace{.5pc}]%&#xa;</xsl:text>
662
662
  </xsl:template>
663
663
 
664
664
  <!-- The indent, and space for the number/label are straight -->
665
665
  <!-- from the titletoc documentation, which says they match -->
666
- <!-- the LaTeX book class -->
666
+ <!-- the LaTeX book class. The top-level division of an -->
667
+ <!-- article is a section, so the \dottedcontents shorthand -->
668
+ <!-- is expanded verbatim to the \titlecontents form it -->
669
+ <!-- abbreviates, exposing the label composition to carry the -->
670
+ <!-- appendix-period seam -->
667
671
  <xsl:template name="titletoc-section-style">
668
- <xsl:text>\dottedcontents{section}[3.8em]{\contentsfont}{2.3em}{1pc}%&#xa;</xsl:text>
672
+ <xsl:text>\titlecontents{section}%&#xa;</xsl:text>
673
+ <xsl:text>[3.8em]{\contentsfont}%&#xa;</xsl:text>
674
+ <xsl:text>{\contentslabel[\thecontentslabel\ptxappendixperiodsection]{2.3em}}%&#xa;</xsl:text>
675
+ <xsl:text>{\hspace*{-2.3em}}%&#xa;</xsl:text>
676
+ <xsl:text>{\titlerule*[1pc]{.}\contentspage}%&#xa;</xsl:text>
669
677
  </xsl:template>
670
678
 
671
679
  <!-- The indent, and space for the number/label are straight -->
@@ -1009,12 +1017,14 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
1009
1017
  <xsl:text>%% end: copyright-page&#xa;</xsl:text>
1010
1018
  </xsl:template>
1011
1019
 
1012
- <!-- Only for a book with a colophon, we put the statement of support at the bottom of the colophon -->
1020
+ <!-- Only for a book with a colophon, we put the statement of support -->
1021
+ <!-- at the bottom of the colophon. The schema says a "support" -->
1022
+ <!-- statement is text-level, mixed content -->
1013
1023
  <xsl:template match="bibinfo/support" mode="copyright-page">
1014
1024
  <xsl:text>%% Funding/Support statement:</xsl:text>
1015
1025
  <xsl:text>\par\medskip&#xa;</xsl:text>
1016
1026
  <xsl:text>\noindent{}</xsl:text>
1017
- <xsl:apply-templates select="*" />
1027
+ <xsl:apply-templates select="node()"/>
1018
1028
  <xsl:text>\par&#xa;</xsl:text>
1019
1029
  <xsl:text>\vspace*{\stretch{1}}&#xa;</xsl:text>
1020
1030
  </xsl:template>
@@ -810,6 +810,16 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
810
810
  <!-- Full Numbers -->
811
811
  <!-- -->
812
812
 
813
+ <!-- The "number" of a table note ("tn") is its letter: a, b, c, -->
814
+ <!-- assigned per-"tabular" in document order of the marks, which is -->
815
+ <!-- reading order (Notes to tables: CMoS 18th ed., 3.77-3.81; -->
816
+ <!-- specific notes at 3.80). One computation, consumed by every -->
817
+ <!-- conversion, and deliberately disjoint from the numbering of -->
818
+ <!-- true footnotes. -->
819
+ <xsl:template match="tabular//tn" mode="number">
820
+ <xsl:number level="any" from="tabular" format="a"/>
821
+ </xsl:template>
822
+
813
823
  <!-- Now trivial, the container structure plus the serial. -->
814
824
  <!-- We condition on empty serial number in order to create -->
815
825
  <!-- empty full numbers. This is where we add separator, -->
@@ -165,6 +165,19 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
165
165
  <xsl:text>span.mjpage { display: inline; }&#xa;</xsl:text>
166
166
  <xsl:text>span.mjpage__block { display: block; text-align: center; margin: 0.5em 0; }&#xa;</xsl:text>
167
167
  </xsl:if>
168
+ <!-- Vertical alignment of slides diverging from the -->
169
+ <!-- document-wide default. reveal.js absolutely -->
170
+ <!-- positions each slide, and when configured to center -->
171
+ <!-- ("center: true") it computes an inline "top" offset, -->
172
+ <!-- which only "!important" can overrule. A top-aligned -->
173
+ <!-- slide just pins that offset to zero. Middle and -->
174
+ <!-- bottom alignment stretch the slide to the full frame -->
175
+ <!-- and place its content by flexbox; the "display" rule -->
176
+ <!-- is confined to the "present" slide so hidden slides -->
177
+ <!-- retain "display: none". -->
178
+ <xsl:text>.reveal .slides section.valign-top { top: 0 !important; }&#xa;</xsl:text>
179
+ <xsl:text>.reveal .slides section.valign-middle.present { display: flex !important; flex-direction: column; justify-content: center; top: 0 !important; height: 100% !important; }&#xa;</xsl:text>
180
+ <xsl:text>.reveal .slides section.valign-bottom.present { display: flex !important; flex-direction: column; justify-content: flex-end; top: 0 !important; height: 100% !important; }&#xa;</xsl:text>
168
181
  </style>
169
182
  <!-- no diagcess machinery with embedded mathematics: an -->
170
183
  <!-- annotated PreFigure diagram is then a static image -->
@@ -238,7 +251,20 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
238
251
  <xsl:value-of select="$reveal-navigation-mode"/>
239
252
  <xsl:text>',&#xa;</xsl:text>
240
253
  <xsl:text> progress: false,&#xa;</xsl:text>
241
- <xsl:text> center: false,&#xa;</xsl:text>
254
+ <!-- reveal.js "center" vertically centers every slide, -->
255
+ <!-- so it realizes a document-wide "middle" default; -->
256
+ <!-- a "top" or "bottom" default disables it, and slides -->
257
+ <!-- diverging from the default get a class instead -->
258
+ <xsl:text> center: </xsl:text>
259
+ <xsl:choose>
260
+ <xsl:when test="$slides-valign-default = 'middle'">
261
+ <xsl:text>true</xsl:text>
262
+ </xsl:when>
263
+ <xsl:otherwise>
264
+ <xsl:text>false</xsl:text>
265
+ </xsl:otherwise>
266
+ </xsl:choose>
267
+ <xsl:text>,&#xa;</xsl:text>
242
268
  <xsl:text> hash: true,&#xa;</xsl:text>
243
269
  <xsl:text> transition: 'fade',&#xa;</xsl:text>
244
270
  <xsl:text> width: "100%",&#xa;</xsl:text>
@@ -283,6 +309,30 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
283
309
  </html>
284
310
  </xsl:template>
285
311
 
312
+ <!-- reveal.js natively places content at the middle of a slide -->
313
+ <!-- when centering (a "middle" default) and at the top -->
314
+ <!-- otherwise. Any other placement gets a class tied to the -->
315
+ <!-- style rules above. -->
316
+ <xsl:template name="valign-class">
317
+ <xsl:param name="valign"/>
318
+ <xsl:variable name="native">
319
+ <xsl:choose>
320
+ <xsl:when test="$slides-valign-default = 'middle'">
321
+ <xsl:text>middle</xsl:text>
322
+ </xsl:when>
323
+ <xsl:otherwise>
324
+ <xsl:text>top</xsl:text>
325
+ </xsl:otherwise>
326
+ </xsl:choose>
327
+ </xsl:variable>
328
+ <xsl:if test="not($valign = $native)">
329
+ <xsl:attribute name="class">
330
+ <xsl:text>valign-</xsl:text>
331
+ <xsl:value-of select="$valign"/>
332
+ </xsl:attribute>
333
+ </xsl:if>
334
+ </xsl:template>
335
+
286
336
  <!-- A "section" contains multiple "slide", which we process, -->
287
337
  <!-- but first we make a special slide announcing the "section" -->
288
338
  <!-- With reveal.js navigationMode set to "default" or "grid" -->
@@ -297,6 +347,9 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
297
347
  <xsl:when test="($reveal-navigation-mode = 'default') or ($reveal-navigation-mode = 'grid')">
298
348
  <section>
299
349
  <section>
350
+ <xsl:call-template name="valign-class">
351
+ <xsl:with-param name="valign" select="$slides-valign-default"/>
352
+ </xsl:call-template>
300
353
  <h2>
301
354
  <xsl:apply-templates select="." mode="title-full"/>
302
355
  </h2>
@@ -306,6 +359,9 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
306
359
  </xsl:when>
307
360
  <xsl:when test="$reveal-navigation-mode = 'linear'">
308
361
  <section>
362
+ <xsl:call-template name="valign-class">
363
+ <xsl:with-param name="valign" select="$slides-valign-default"/>
364
+ </xsl:call-template>
309
365
  <h2>
310
366
  <xsl:apply-templates select="." mode="title-full"/>
311
367
  </h2>
@@ -321,6 +377,9 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
321
377
  <xsl:template match="frontmatter">
322
378
  <section>
323
379
  <section>
380
+ <xsl:call-template name="valign-class">
381
+ <xsl:with-param name="valign" select="$slides-valign-default"/>
382
+ </xsl:call-template>
324
383
  <!-- we assume an overall title exists -->
325
384
  <h1>
326
385
  <xsl:apply-templates select="$root/slideshow" mode="title-full" />
@@ -372,6 +431,9 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
372
431
 
373
432
  <xsl:template match="abstract">
374
433
  <section>
434
+ <xsl:call-template name="valign-class">
435
+ <xsl:with-param name="valign" select="$slides-valign-default"/>
436
+ </xsl:call-template>
375
437
  <h2>Abstract</h2>
376
438
  <div align="left">
377
439
  <xsl:apply-templates select="*"/>
@@ -381,6 +443,11 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
381
443
 
382
444
  <xsl:template match="slide">
383
445
  <section>
446
+ <xsl:call-template name="valign-class">
447
+ <xsl:with-param name="valign">
448
+ <xsl:apply-templates select="." mode="valign"/>
449
+ </xsl:with-param>
450
+ </xsl:call-template>
384
451
  <xsl:variable name="slide-hN">
385
452
  <xsl:apply-templates select="." mode="hN">
386
453
  <xsl:with-param name="heading-level" select="$reveal-slide-heading-level"/>
@@ -102,7 +102,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
102
102
  </xsl:when>
103
103
  <xsl:otherwise>
104
104
  <!-- Report if a seed is not provided-->
105
- <xsl:message>PTX:FALLBACK: Dynamic exercise "<xsl:value-of select="@visible-id"/>" is missing setup @seed for static content generation.</xsl:message>
105
+ <xsl:message>PTX:FALLBACK: Dynamic exercise "<xsl:apply-templates select="." mode="unique-id" />" is missing setup @seed for static content generation.</xsl:message>
106
106
  <xsl:text>1234</xsl:text>
107
107
  </xsl:otherwise>
108
108
  </xsl:choose>
@@ -139,12 +139,20 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
139
139
  <xsl:text>,&#xa;"dyn_imports": [</xsl:text>
140
140
  <xsl:text>"BTM"</xsl:text>
141
141
  <!-- Future: add additional packages here -->
142
+ <xsl:if test="setup/jsimports">
143
+ <xsl:apply-templates select="setup/jsimports/jslibrary" mode="js-import"/>
144
+ </xsl:if>
142
145
  <xsl:text>]</xsl:text>
143
146
  </xsl:if>
144
147
  <!-- Names assigned to the blanks. -->
145
148
  <!-- Empty if none named. -->
146
149
  <xsl:text>,&#xa;"blankNames": {</xsl:text>
147
- <xsl:apply-templates select="statement//fillin" mode="declare-blanks" />
150
+ <xsl:for-each select="statement//fillin">
151
+ <xsl:if test="position() > 1">
152
+ <xsl:text>, </xsl:text>
153
+ </xsl:if>
154
+ <xsl:apply-templates select="." mode="declare-blank"/>
155
+ </xsl:for-each>
148
156
  <xsl:text>}</xsl:text>
149
157
  <xsl:if test="$b-is-dynamic">
150
158
  <!-- The actual setup code is javascript enclosed in quotes. -->
@@ -163,10 +171,21 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
163
171
  <xsl:variable name="multiAns">
164
172
  <xsl:apply-templates select="evaluation" mode="get-multianswer-check"/>
165
173
  </xsl:variable>
174
+ <!-- Verify match between fillin and evaluate -->
175
+ <xsl:if test="count(evaluation/evaluate[not(@all='yes')]) != count(statement//fillin)">
176
+ <xsl:message>PTX:ERROR: "<xsl:apply-templates select="." mode="unique-id"/>" has <xsl:value-of
177
+ select="count(statement//fillin)"/> blanks but <xsl:value-of
178
+ select="count(evaluation/evaluate[not(@all='yes')])"/> "evaluate" elements; they must correspond one-to-one.</xsl:message>
179
+ </xsl:if>
166
180
  <!-- Generate test/feedback pair for each fillin -->
167
- <xsl:apply-templates select="statement//fillin" mode="dynamic-feedback">
168
- <xsl:with-param name="multiAns" select="$multiAns" />
169
- </xsl:apply-templates>
181
+ <xsl:for-each select="statement//fillin">
182
+ <xsl:if test="position() > 1">
183
+ <xsl:text>, </xsl:text>
184
+ </xsl:if>
185
+ <xsl:apply-templates select="." mode="dynamic-feedback">
186
+ <xsl:with-param name="multiAns" select="$multiAns" />
187
+ </xsl:apply-templates>
188
+ </xsl:for-each>
170
189
  <xsl:text>]</xsl:text>
171
190
  <xsl:text>&#xa;}</xsl:text>
172
191
  </script>
@@ -176,6 +195,16 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
176
195
  </div>
177
196
  </xsl:template>
178
197
 
198
+ <!-- Find the sequential position of the fillin relative to its ancestor statement -->
199
+ <xsl:template match="fillin" mode="blank-number">
200
+ <xsl:variable name="the-fillin" select="."/>
201
+ <xsl:for-each select="ancestor::statement[1]//fillin">
202
+ <xsl:if test="generate-id() = generate-id($the-fillin)">
203
+ <xsl:value-of select="position()"/>
204
+ </xsl:if>
205
+ </xsl:for-each>
206
+ </xsl:template>
207
+
179
208
  <!-- Fillins can be provided a name or use a default rule -->
180
209
  <xsl:template match="fillin" mode="blank-name">
181
210
  <xsl:choose>
@@ -184,26 +213,58 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
184
213
  </xsl:when>
185
214
  <xsl:otherwise>
186
215
  <xsl:text>blank</xsl:text>
187
- <xsl:value-of select="position()" />
216
+ <xsl:apply-templates select="." mode="blank-number" />
188
217
  </xsl:otherwise>
189
218
  </xsl:choose>
190
219
  </xsl:template>
191
220
 
192
221
  <!-- Creating a list of blank names. -->
193
- <xsl:template match="fillin" mode="declare-blanks">
222
+ <xsl:template match="fillin" mode="declare-blank">
223
+ <xsl:if test="not(@mode)">
224
+ <xsl:message>PTX:FALLBACK: a "fillin" of interactive exercise "<xsl:apply-templates
225
+ select="ancestor::statement[1]/parent::*" mode="unique-id"/>" has no @mode, so "string" will be used. A response is then compared as plain text, and an @ansobj answer is rendered with its "toString" and not as mathematics. Set @mode to "math", "number", or "string" to say which is wanted.</xsl:message>
226
+ </xsl:if>
194
227
  <xsl:variable name="blankNum">
195
- <xsl:value-of select="position()" />
228
+ <xsl:apply-templates select="." mode="blank-number" />
196
229
  </xsl:variable>
197
- <xsl:if test="$blankNum>1">
198
- <xsl:text>, </xsl:text>
199
- </xsl:if>
200
230
  <xsl:call-template name="quote-string">
201
231
  <xsl:with-param name="text">
202
232
  <xsl:apply-templates select="." mode="blank-name"/>
203
233
  </xsl:with-param>
204
234
  </xsl:call-template>
205
235
  <xsl:text>: </xsl:text>
206
- <xsl:value-of select="$blankNum - 1"/>
236
+ <xsl:value-of select="number($blankNum) - 1"/>
237
+ </xsl:template>
238
+
239
+ <!-- Identify external javascript libraries, either as asset or by url -->
240
+ <xsl:template match="jslibrary" mode="js-import-path">
241
+ <xsl:choose>
242
+ <xsl:when test="@source">
243
+ <xsl:value-of select="$external-directory"/>
244
+ <xsl:value-of select="@source"/>
245
+ </xsl:when>
246
+ <xsl:when test="@url">
247
+ <xsl:value-of select="@url"/>
248
+ </xsl:when>
249
+ <xsl:otherwise>
250
+ <xsl:message>PTX:WARNING: a "jslibrary" element needs either a @source (project-local) or a @url (remote) attribute, and this one has neither, so it will be ignored.</xsl:message>
251
+ </xsl:otherwise>
252
+ </xsl:choose>
253
+ </xsl:template>
254
+
255
+ <!-- A "jslibrary" as an entry of the HTML "dyn_imports" array. The -->
256
+ <!-- array always leads with "BTM", so every entry here is preceded -->
257
+ <!-- by a comma. -->
258
+ <xsl:template match="jslibrary" mode="js-import">
259
+ <xsl:variable name="import-path">
260
+ <xsl:apply-templates select="." mode="js-import-path"/>
261
+ </xsl:variable>
262
+ <xsl:if test="string-length($import-path) > 0">
263
+ <xsl:text>, </xsl:text>
264
+ <xsl:call-template name="escape-quote-string">
265
+ <xsl:with-param name="text" select="$import-path"/>
266
+ </xsl:call-template>
267
+ </xsl:if>
207
268
  </xsl:template>
208
269
 
209
270
  <!-- #eval in a dynamic exercise (has setup) is to evaluate -->
@@ -215,7 +276,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
215
276
  <xsl:template match="eval[@obj]">
216
277
  <xsl:text>[%= </xsl:text>
217
278
  <xsl:choose>
218
- <xsl:when test="ancestor::m|ancestor::mrow">
279
+ <xsl:when test="ancestor::m|ancestor::md|ancestor::mrow">
219
280
  <xsl:text>toTeX(</xsl:text>
220
281
  <xsl:value-of select="@obj"/>
221
282
  <xsl:text>)</xsl:text>
@@ -227,7 +288,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
227
288
  <xsl:text> %]</xsl:text>
228
289
  </xsl:template>
229
290
 
230
-
231
291
  <!-- Part of static output content extraction process, -->
232
292
  <!-- this creates elements that will be pulled from the -->
233
293
  <!-- stand-alone page into a generated XML document that -->
@@ -305,6 +365,16 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
305
365
  <xsl:apply-templates select="de-environment" mode="runestone-setup"/>
306
366
  <xsl:text>});&#xa;</xsl:text>
307
367
  <xsl:text>var RNG = v._mobjs.menv.rng;&#xa;</xsl:text>
368
+ <!-- Option to define v._config for any data purposes -->
369
+ <xsl:if test="setup/config-json">
370
+ <xsl:text>const _config = v._config = JSON.parse("</xsl:text>
371
+ <xsl:call-template name="escape-json-string">
372
+ <xsl:with-param name="text">
373
+ <xsl:value-of select="setup/config-json"/>
374
+ </xsl:with-param>
375
+ </xsl:call-template>
376
+ <xsl:text>");&#xa;</xsl:text>
377
+ </xsl:if>
308
378
  <!-- Generate all of the XML-declared math objects -->
309
379
  <xsl:apply-templates select="setup/de-object" mode="runestone-setup"/>
310
380
  </xsl:template>
@@ -342,30 +412,23 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
342
412
  <!-- validating and parsing what the user enters as a response. -->
343
413
  <xsl:template name="setup-fillin-parsers">
344
414
  <xsl:text>v.types = [</xsl:text>
345
- <xsl:apply-templates select="statement//fillin" mode="setup-parsers" />
346
- <xsl:text>];&#xa;</xsl:text>
347
- </xsl:template>
348
-
349
- <!-- Create a call-back for what occurs post-rendering. -->
350
- <!-- This includes automated tasks as well as any script -->
351
- <!-- commands in setup/postRenderScript -->
352
- <xsl:template name="setup-postContent">
353
- <xsl:text>v.afterContentRender = function() {&#xa;</xsl:text>
354
- <xsl:if test="setup/postRenderScript">
355
- <xsl:value-of select="setup/postRenderScript"/>
415
+ <xsl:for-each select="statement//fillin">
416
+ <xsl:if test="position() > 1">
417
+ <xsl:text>, </xsl:text>
356
418
  </xsl:if>
357
- <xsl:text>};&#xa;</xsl:text>
419
+ <xsl:apply-templates select="." mode="setup-parsers"/>
420
+ </xsl:for-each>
421
+ <xsl:text>];&#xa;</xsl:text>
358
422
  </xsl:template>
359
423
 
360
-
361
424
  <!-- Each fillin defines a blank. Establish the list of parsers. -->
362
425
  <!-- During setup and called while creating dyn_vars. -->
363
426
  <xsl:template match="fillin" mode="setup-parsers">
364
- <xsl:if test="position() > 1">
365
- <xsl:text>, </xsl:text>
366
- </xsl:if>
367
427
  <xsl:choose>
368
- <xsl:when test="@mode='math-formula'">
428
+ <xsl:when test="@parser">
429
+ <xsl:value-of select="@parser"/>
430
+ </xsl:when>
431
+ <xsl:when test="@mode='math'">
369
432
  <xsl:text>v._mobjs.getParser()</xsl:text>
370
433
  </xsl:when>
371
434
  <xsl:when test="@mode='number'">
@@ -377,6 +440,17 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
377
440
  </xsl:choose>
378
441
  </xsl:template>
379
442
 
443
+ <!-- Create a call-back for what occurs post-rendering. -->
444
+ <!-- This includes automated tasks as well as any script -->
445
+ <!-- commands in setup/postRenderScript -->
446
+ <xsl:template name="setup-postContent">
447
+ <xsl:text>v.afterContentRender = function() {&#xa;</xsl:text>
448
+ <xsl:if test="setup/postRenderScript">
449
+ <xsl:value-of select="setup/postRenderScript"/>
450
+ </xsl:if>
451
+ <xsl:text>};&#xa;</xsl:text>
452
+ </xsl:template>
453
+
380
454
 
381
455
  <!-- ========================================================== -->
382
456
  <!-- Evaluation and Feedback -->
@@ -412,20 +486,12 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
412
486
  </xsl:if>
413
487
  </xsl:template>
414
488
 
415
- <!-- Return the index of an evaluate -->
416
- <xsl:template match="evaluation/evaluate" mode="get-index">
417
- <xsl:value-of select="position()"/>
418
- </xsl:template>
419
-
420
489
  <!-- Build feedback based on the #evaluate element. -->
421
490
  <xsl:template match="evaluate" mode="dynamic-feedback">
422
491
  <xsl:param name="multiAns"/>
423
492
  <xsl:param name="match-fillin"/>
424
493
 
425
- <!-- Defend against name mismatches -->
426
- <xsl:if test="@name and $match-fillin/@name and not(@name = $match-fillin/@name)">
427
- <xsl:message>PTX:ERROR: Dynamic content missing label "<xsl:value-of select="@visible-id"/>"</xsl:message>
428
- </xsl:if>
494
+ <!-- Catching name mismatch is detected by the caller. See "fillin"/"dynamic-feedback". -->
429
495
 
430
496
  <!-- First check is for correctness. -->
431
497
  <xsl:text>[</xsl:text>
@@ -474,12 +540,12 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
474
540
  <xsl:text>);&#xa;}</xsl:text>
475
541
  </xsl:when>
476
542
  <xsl:otherwise>
477
- <xsl:message>PTX:ERROR: fillin has an @ansobj with invalid or missing mode (valid: number|string|math) in "<xsl:value-of select="@visible-id"/>".</xsl:message>
543
+ <xsl:message>PTX:ERROR: fillin has an @ansobj with invalid or missing mode (valid: number|string|math) in "<xsl:apply-templates select="." mode="unique-id" />".</xsl:message>
478
544
  </xsl:otherwise>
479
545
  </xsl:choose>
480
546
  </xsl:when>
481
547
  <xsl:otherwise>
482
- <xsl:message>PTX:ERROR: No method provided to identify a correct answer for #fillin in "<xsl:value-of select="@visible-id"/>".</xsl:message>
548
+ <xsl:message>PTX:ERROR: No method provided to identify a correct answer for #fillin in "<xsl:apply-templates select="." mode="unique-id" />".</xsl:message>
483
549
  </xsl:otherwise>
484
550
  </xsl:choose>
485
551
  <xsl:text>, "feedback": "</xsl:text>
@@ -508,51 +574,73 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
508
574
  <!-- Template for answer checking. Actual work done by specialized templates. -->
509
575
  <xsl:template match="fillin" mode="dynamic-feedback">
510
576
  <xsl:param name="multiAns"/>
511
- <xsl:variable name="curFillIn" select="."/>
577
+
512
578
  <xsl:variable name="fillinName">
513
579
  <xsl:apply-templates select="." mode="blank-name"/>
514
580
  </xsl:variable>
515
581
  <xsl:variable name="blankNum">
516
- <xsl:value-of select="position()" />
582
+ <xsl:apply-templates select="." mode="blank-number"/>
517
583
  </xsl:variable>
518
584
 
519
- <!-- we need to know which #evaluate matches the #fillin -->
520
- <xsl:variable name="evaluate-index">
521
- <xsl:choose>
522
- <xsl:when test="ancestor::statement/../evaluation/evaluate[@name = $fillinName]">
523
- <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[@name = $fillinName]" mode="get-index"/>
524
- </xsl:when>
525
- <xsl:when test="ancestor::statement/../evaluation/evaluate[position() = $blankNum]">
526
- <xsl:value-of select="$blankNum"/>
527
- </xsl:when>
528
- <!-- No check matches: Make blank default. -->
529
- <xsl:otherwise>
530
- <xsl:number value="-1"/>
531
- </xsl:otherwise>
532
- </xsl:choose>
533
- </xsl:variable>
585
+ <!-- The multi-answer checker applies to the exercise as a whole, so -->
586
+ <!-- it is excluded from the numbering. Every other "evaluate" holds -->
587
+ <!-- a slot, named or not: the pairing with blanks is one-to-one. -->
588
+ <xsl:variable name="the-evaluation" select="ancestor::statement[1]/../evaluation"/>
589
+ <xsl:variable name="ordered-evaluates" select="$the-evaluation/evaluate[not(@all='yes')]"/>
534
590
 
535
- <xsl:if test="$blankNum > 1">
536
- <xsl:text>, </xsl:text>
537
- </xsl:if>
591
+ <!-- Pair by name when possible, else by position. -->
592
+ <xsl:variable name="named-match" select="$the-evaluation/evaluate[@name = $fillinName]"/>
593
+ <xsl:variable name="ordered-match"
594
+ select="$ordered-evaluates[position() = number($blankNum)]"/>
595
+ <xsl:variable name="the-evaluate"
596
+ select="$named-match | $ordered-match[count($named-match) = 0]"/>
597
+
598
+ <!-- The two error branches below still emit a well-formed array, so -->
599
+ <!-- that one authoring mistake costs the exercise its feedback and -->
600
+ <!-- not its JSON. The caller has already written the separator that -->
601
+ <!-- precedes this blank; emitting nothing here would leave ", ," and -->
602
+ <!-- the whole script would fail to parse. -->
538
603
  <xsl:choose>
539
- <!-- First look for a matching name. -->
540
- <xsl:when test="ancestor::statement/../evaluation/evaluate[@name = $fillinName]">
541
- <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[@name = $fillinName]" mode="dynamic-feedback">
542
- <xsl:with-param name="multiAns" select="$multiAns"/>
543
- <xsl:with-param name="match-fillin" select="."/>
544
- </xsl:apply-templates>
604
+ <!-- Author reused one @name on several "evaluate" elements. -->
605
+ <xsl:when test="count($the-evaluate) > 1">
606
+ <xsl:message>PTX:ERROR: several "evaluate" share the name "<xsl:value-of
607
+ select="$fillinName"/>" in "<xsl:apply-templates select="." mode="unique-id"/>".</xsl:message>
608
+ <xsl:text>[{"feedback": "[Error: Several evaluate elements share this blank's name]"}]</xsl:text>
545
609
  </xsl:when>
546
- <!-- Otherwise match on order. -->
547
- <xsl:when test="ancestor::statement/../evaluation/evaluate[position() = $blankNum]">
548
- <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[position() = $blankNum]" mode="dynamic-feedback">
610
+ <xsl:when test="$the-evaluate">
611
+ <!-- Only reachable on the positional path: a name match makes -->
612
+ <!-- @name and $fillinName equal by construction. -->
613
+ <!-- -->
614
+ <!-- @name on an "evaluate" exists to pair it with a "fillin" of -->
615
+ <!-- the same name, so a positional match whose names disagree -->
616
+ <!-- is reported here. -->
617
+ <xsl:if test="$the-evaluate/@name and not($the-evaluate/@name = $fillinName)">
618
+ <xsl:choose>
619
+ <!-- Names don't agree -->
620
+ <xsl:when test="@name">
621
+ <xsl:message>PTX:ERROR: blank named "<xsl:value-of select="@name"/>" of "<xsl:apply-templates
622
+ select="." mode="unique-id"/>" was matched by position to an "evaluate" named "<xsl:value-of
623
+ select="$the-evaluate/@name"/>". The names must agree, or the blank must be left unnamed.</xsl:message>
624
+ </xsl:when>
625
+ <!-- Name present for evaluate but not fillin -->
626
+ <xsl:otherwise>
627
+ <xsl:message>PTX:WARNING: the "evaluate" named "<xsl:value-of select="$the-evaluate/@name"/>" in "<xsl:apply-templates
628
+ select="." mode="unique-id"/>" was matched to blank <xsl:value-of select="$blankNum"/> by position,
629
+ because that blank has no @name. A name on an "evaluate" is only used to pair it with a "fillin"
630
+ carrying the same name; give the blank @name="<xsl:value-of select="$the-evaluate/@name"/>" to make
631
+ the pairing explicit, or drop the name from the "evaluate".</xsl:message>
632
+ </xsl:otherwise>
633
+ </xsl:choose>
634
+ </xsl:if>
635
+ <xsl:apply-templates select="$the-evaluate" mode="dynamic-feedback">
549
636
  <xsl:with-param name="multiAns" select="$multiAns"/>
550
637
  <xsl:with-param name="match-fillin" select="."/>
551
638
  </xsl:apply-templates>
552
639
  </xsl:when>
553
- <!-- No check matches: Make blank default. -->
554
640
  <xsl:otherwise>
555
- <xsl:message>PTX:ERROR: No matching #evaluate for #fillin in "<xsl:value-of select="@visible-id"/>".</xsl:message>
641
+ <xsl:message>PTX:ERROR: No matching #evaluate for #fillin in "<xsl:apply-templates
642
+ select="." mode="unique-id"/>".</xsl:message>
643
+ <xsl:text>[{"feedback": "[Error: No evaluate to match this blank]"}]</xsl:text>
556
644
  </xsl:otherwise>
557
645
  </xsl:choose>
558
646
  </xsl:template>
@@ -909,7 +997,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
909
997
  <xsl:when test="name($curTest)='mathcmp' and $curTest/@use-answer='yes'">
910
998
  <xsl:choose>
911
999
  <xsl:when test="not($fillin/@ansobj)">
912
- <xsl:message>PTX:ERROR: Feedback for "<xsl:value-of select="@visible-id"/>" says to use given math answer, but @ansobj not defined. </xsl:message>
1000
+ <xsl:message>PTX:ERROR: Feedback for "<xsl:apply-templates select="." mode="unique-id" />" says to use given math answer, but @ansobj not defined. </xsl:message>
913
1001
  <xsl:text>UNDEFINED</xsl:text>
914
1002
  </xsl:when>
915
1003
  <xsl:otherwise>
@@ -959,7 +1047,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
959
1047
  </xsl:when>
960
1048
  <xsl:when test="$logic = 'not'"/> <!-- Negation has no preparation -->
961
1049
  <xsl:otherwise>
962
- <xsl:message>PTX:ERROR: Invalid logic operator in dynamic fillin for "<xsl:value-of select="@visible-id"/>"</xsl:message>
1050
+ <xsl:message>PTX:ERROR: Invalid logic operator in dynamic fillin for "<xsl:apply-templates select="." mode="unique-id" />"</xsl:message>
963
1051
  </xsl:otherwise>
964
1052
  </xsl:choose>
965
1053
  <xsl:for-each select="$tests"> <!-- Work through the layer of tests one at a time. -->
@@ -1066,9 +1154,14 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
1066
1154
  <xsl:with-param name="setupMode" select="$setupMode"/>
1067
1155
  </xsl:apply-templates>
1068
1156
  <xsl:text>, "number", {</xsl:text>
1069
- <xsl:apply-templates select="variable" mode="evaluation-binding" >
1070
- <xsl:with-param name="setupMode" select="$setupMode" />
1071
- </xsl:apply-templates>
1157
+ <xsl:for-each select="variable">
1158
+ <xsl:if test="position() > 1">
1159
+ <xsl:text>, </xsl:text>
1160
+ </xsl:if>
1161
+ <xsl:apply-templates select="." mode="evaluation-binding" >
1162
+ <xsl:with-param name="setupMode" select="$setupMode" />
1163
+ </xsl:apply-templates>
1164
+ </xsl:for-each>
1072
1165
  <xsl:text>})</xsl:text>
1073
1166
  <xsl:if test="@reduce='yes'">
1074
1167
  <xsl:text>.reduce().simplifyConstants()</xsl:text>
@@ -1157,9 +1250,14 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
1157
1250
  <xsl:with-param name="setupMode" select="$setupMode"/>
1158
1251
  </xsl:apply-templates>
1159
1252
  <xsl:text>, {</xsl:text>
1160
- <xsl:apply-templates select="variable" mode="evaluation-binding" >
1161
- <xsl:with-param name="setupMode" select="$setupMode" />
1162
- </xsl:apply-templates>
1253
+ <xsl:for-each select="variable">
1254
+ <xsl:if test="position() > 1">
1255
+ <xsl:text>, </xsl:text>
1256
+ </xsl:if>
1257
+ <xsl:apply-templates select="." mode="evaluation-binding" >
1258
+ <xsl:with-param name="setupMode" select="$setupMode" />
1259
+ </xsl:apply-templates>
1260
+ </xsl:for-each>
1163
1261
  <xsl:text>})</xsl:text>
1164
1262
  <xsl:if test="@reduce='yes'">
1165
1263
  <xsl:text>.reduce().simplifyConstants()</xsl:text>
@@ -1199,9 +1297,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
1199
1297
  <xsl:text>v.</xsl:text>
1200
1298
  </xsl:if>
1201
1299
  </xsl:variable>
1202
- <xsl:if test="position() > 1">
1203
- <xsl:text>, </xsl:text>
1204
- </xsl:if>
1205
1300
  <xsl:call-template name="quote-string">
1206
1301
  <xsl:with-param name="text" select="@name" />
1207
1302
  </xsl:call-template>