@pretextbook/pretext-html 0.1.0 → 0.2.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 (106) hide show
  1. package/README.md +104 -4
  2. package/assets/preview-html.xsl +230 -230
  3. package/assets/upstream.json +7 -7
  4. package/assets/xsl/entities.ent +154 -154
  5. package/assets/xsl/extract-asymptote.xsl +68 -68
  6. package/assets/xsl/extract-biblio-csl.xsl +327 -327
  7. package/assets/xsl/extract-datafile.xsl +71 -71
  8. package/assets/xsl/extract-dynamic.xsl +139 -139
  9. package/assets/xsl/extract-identity.xsl +136 -136
  10. package/assets/xsl/extract-interactive.xsl +81 -81
  11. package/assets/xsl/extract-latex-image.xsl +118 -118
  12. package/assets/xsl/extract-mermaid.xsl +58 -58
  13. package/assets/xsl/extract-mom.xsl +63 -63
  14. package/assets/xsl/extract-pg.xsl +2897 -2897
  15. package/assets/xsl/extract-prefigure.xsl +157 -157
  16. package/assets/xsl/extract-qrcode.xsl +100 -100
  17. package/assets/xsl/extract-runestone-page-template.xsl +139 -139
  18. package/assets/xsl/extract-sageplot.xsl +166 -166
  19. package/assets/xsl/extract-stack.xsl +56 -56
  20. package/assets/xsl/extract-trace.xsl +134 -134
  21. package/assets/xsl/extract-youtube.xsl +63 -63
  22. package/assets/xsl/html-symbols.xsl +3344 -3344
  23. package/assets/xsl/latex/pretext-latex-AIM.xsl +56 -56
  24. package/assets/xsl/latex/pretext-latex-CLP.xsl +148 -148
  25. package/assets/xsl/latex/pretext-latex-chaos.xsl +317 -317
  26. package/assets/xsl/latex/pretext-latex-dyslexic-font.xsl +76 -76
  27. package/assets/xsl/latex/pretext-latex-guide.xsl +104 -104
  28. package/assets/xsl/latex/pretext-latex-texstyle.xsl +862 -862
  29. package/assets/xsl/localizations/af-ZA.xml +276 -276
  30. package/assets/xsl/localizations/bg-BG.xml +276 -276
  31. package/assets/xsl/localizations/ca-ES.xml +285 -285
  32. package/assets/xsl/localizations/cs-CZ.xml +280 -280
  33. package/assets/xsl/localizations/de-DE.xml +281 -281
  34. package/assets/xsl/localizations/en-US.xml +427 -427
  35. package/assets/xsl/localizations/es-ES.xml +287 -287
  36. package/assets/xsl/localizations/fi-FI.xml +275 -275
  37. package/assets/xsl/localizations/fr-CA.xml +276 -276
  38. package/assets/xsl/localizations/fr-FR.xml +280 -280
  39. package/assets/xsl/localizations/hu-HU.xml +280 -280
  40. package/assets/xsl/localizations/it-IT.xml +276 -276
  41. package/assets/xsl/localizations/ku-CKB.xml +275 -275
  42. package/assets/xsl/localizations/localizations.xml +48 -48
  43. package/assets/xsl/localizations/nl-NL.xml +275 -275
  44. package/assets/xsl/localizations/pt-BR.xml +288 -288
  45. package/assets/xsl/localizations/pt-PT.xml +292 -292
  46. package/assets/xsl/localizations/zh-HANS.xml +269 -269
  47. package/assets/xsl/pretext-assembly.xsl +4795 -4795
  48. package/assets/xsl/pretext-basic-html.xsl +86 -86
  49. package/assets/xsl/pretext-beamer.xsl +662 -662
  50. package/assets/xsl/pretext-braille-preprint.xsl +2249 -2249
  51. package/assets/xsl/pretext-common.xsl +12018 -12018
  52. package/assets/xsl/pretext-dynamic.xsl +65 -65
  53. package/assets/xsl/pretext-epub.xsl +1483 -1483
  54. package/assets/xsl/pretext-fo.xsl +4593 -4593
  55. package/assets/xsl/pretext-html.xsl +14791 -14791
  56. package/assets/xsl/pretext-json-manifest.xsl +150 -150
  57. package/assets/xsl/pretext-jupyter.xsl +868 -868
  58. package/assets/xsl/pretext-latex-classic.xsl +952 -952
  59. package/assets/xsl/pretext-latex-common.xsl +11563 -11563
  60. package/assets/xsl/pretext-latex.xsl +47 -47
  61. package/assets/xsl/pretext-litprog.xsl +105 -105
  62. package/assets/xsl/pretext-merge.xsl +41 -41
  63. package/assets/xsl/pretext-numbers.xsl +875 -875
  64. package/assets/xsl/pretext-revealjs.xsl +522 -522
  65. package/assets/xsl/pretext-runestone-fitb.xsl +1233 -1233
  66. package/assets/xsl/pretext-runestone-static.xsl +1438 -1438
  67. package/assets/xsl/pretext-runestone.xsl +3083 -3083
  68. package/assets/xsl/pretext-sage-doctest.xsl +289 -289
  69. package/assets/xsl/pretext-smc.xsl +492 -492
  70. package/assets/xsl/pretext-solution-manual-latex.xsl +323 -323
  71. package/assets/xsl/pretext-text-utilities.xsl +1217 -1217
  72. package/assets/xsl/pretext-text.xsl +392 -392
  73. package/assets/xsl/pretext-units.xsl +195 -195
  74. package/assets/xsl/pretext-view-source.xsl +157 -157
  75. package/assets/xsl/pretext-ww-problem-sets.xsl +391 -391
  76. package/assets/xsl/publisher-variables.xsl +4756 -4756
  77. package/assets/xsl/support/extract-math.xsl +256 -256
  78. package/assets/xsl/support/package-math.xsl +141 -141
  79. package/assets/xsl/support/play-button/play-button.svg +3 -3
  80. package/assets/xsl/support/pretext-pg-macros.xsl +110 -110
  81. package/assets/xsl/support/webpack_static_imports.xml +14 -14
  82. package/assets/xsl/tests/null.xml +3 -3
  83. package/assets/xsl/tests/pretext-text-utilities-test.xsl +333 -333
  84. package/assets/xsl/utilities/author-report.xsl +107 -107
  85. package/assets/xsl/utilities/deprecate-autoname.sed +18 -18
  86. package/assets/xsl/utilities/deprecate-index.sed +33 -33
  87. package/assets/xsl/utilities/fix-deprecations.xsl +663 -663
  88. package/assets/xsl/utilities/pretext-enhanced-source.xsl +112 -112
  89. package/assets/xsl/utilities/report-publisher-variables.xsl +162 -162
  90. package/assets/xsl/xml-to-json.xsl +326 -326
  91. package/assets/xsl/xml-to-string.xsl +328 -328
  92. package/assets/xsl-bundle.json +1 -0
  93. package/dist/{cli-C6tfAVvi.js → cli-B2LiKunq.js} +268 -233
  94. package/dist/cli.js +1 -1
  95. package/dist/host.browser.d.ts +29 -0
  96. package/dist/host.d.ts +41 -0
  97. package/dist/index.browser.d.ts +29 -0
  98. package/dist/index.browser.js +650 -0
  99. package/dist/index.d.ts +5 -0
  100. package/dist/index.js +16 -8
  101. package/dist/internal/posix-path.d.ts +59 -0
  102. package/dist/mounts.d.ts +18 -0
  103. package/dist/renderer.d.ts +14 -1
  104. package/dist/theme.d.ts +68 -0
  105. package/dist/theme.js +93 -0
  106. package/package.json +22 -4
@@ -1,1233 +1,1233 @@
1
- <?xml version='1.0'?> <!-- As XML file -->
2
-
3
- <!DOCTYPE xsl:stylesheet>
4
-
5
- <xsl:stylesheet
6
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
7
- xmlns:xml="http://www.w3.org/XML/1998/namespace"
8
- xmlns:exsl="http://exslt.org/common"
9
- xmlns:date="http://exslt.org/dates-and-times"
10
- xmlns:str="http://exslt.org/strings"
11
- extension-element-prefixes="exsl date str"
12
- >
13
-
14
- <xsl:variable name="b-include-postContent" select="true()"/>
15
-
16
- <!-- ==================================================== -->
17
- <!-- Actual rules for substution when generating HTML -->
18
- <!-- ==================================================== -->
19
-
20
- <!-- Convert fillin tag to an input element on the page -->
21
- <xsl:template match="exercise[@exercise-interactive='fillin']//fillin
22
- | project[@exercise-interactive='fillin']//fillin
23
- | activity[@exercise-interactive='fillin']//fillin
24
- | exploration[@exercise-interactive='fillin']//fillin
25
- | investigation[@exercise-interactive='fillin']//fillin
26
- | task[@exercise-interactive='fillin']//fillin">
27
- <xsl:param name="b-human-readable" />
28
- <xsl:variable name="parent-id">
29
- <xsl:apply-templates select="ancestor::exercise" mode="html-id" />
30
- </xsl:variable>
31
- <xsl:element name="input">
32
- <xsl:attribute name="type">
33
- <xsl:choose>
34
- <xsl:when test="@mode = 'number'">
35
- <xsl:text>number</xsl:text>
36
- </xsl:when>
37
- <xsl:otherwise>
38
- <xsl:text>text</xsl:text>
39
- </xsl:otherwise>
40
- </xsl:choose>
41
- </xsl:attribute>
42
- <xsl:attribute name="id">
43
- <xsl:value-of select="$parent-id"/>
44
- <xsl:text>-</xsl:text>
45
- <xsl:apply-templates select="." mode="blank-name"/>
46
- </xsl:attribute>
47
- <xsl:if test="@name">
48
- <xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
49
- </xsl:if>
50
- <xsl:if test="@width">
51
- <xsl:attribute name="size">
52
- <xsl:value-of select="@width"/>
53
- </xsl:attribute>
54
- </xsl:if>
55
- </xsl:element>
56
- </xsl:template>
57
-
58
- <!-- ========================================================= -->
59
- <!-- The Runestone element is based on JSON scripts describing -->
60
- <!-- the exercise, how to set it up, and how to evaluate it. -->
61
- <!-- The HTML will contain this JSON and Runestone extracts it -->
62
- <!-- and inserts it into the HTML page via JS. -->
63
- <!-- ========================================================= -->
64
- <xsl:template match="exercise[@exercise-interactive='fillin']
65
- | project[@exercise-interactive='fillin']
66
- | activity[@exercise-interactive='fillin']
67
- | exploration[@exercise-interactive='fillin']
68
- | investigation[@exercise-interactive='fillin']
69
- | task[@exercise-interactive='fillin']" mode="runestone-to-interactive">
70
- <xsl:variable name="b-is-dynamic" select="boolean(./setup)"/>
71
- <div class="ptx-runestone-container">
72
- <div class="runestone">
73
- <div data-component="fillintheblank" class="fillintheblank" style="visibility: hidden;">
74
- <xsl:apply-templates select="." mode="runestone-id-attribute"/>
75
- <script type="application/json">
76
- <xsl:text>{&#xa;</xsl:text>
77
- <!-- A seed is provided to generate consistent static content -->
78
- <xsl:if test="setup and $b-dynamics-static-seed">
79
- <xsl:text>"static_seed": "</xsl:text>
80
- <xsl:choose>
81
- <xsl:when test="setup/@seed">
82
- <xsl:value-of select="setup/@seed"/>
83
- </xsl:when>
84
- <xsl:otherwise>
85
- <!-- Report if a seed is not provided-->
86
- <xsl:message>PTX:WARNING: Dynamic exercise "<xsl:value-of select="@visible-id"/>" is missing setup @seed for static content generation.</xsl:message>
87
- <xsl:text>1234</xsl:text>
88
- </xsl:otherwise>
89
- </xsl:choose>
90
- <xsl:text>",&#xa;</xsl:text>
91
- </xsl:if>
92
- <!-- The formatted HTML presentation of the problem, -->
93
- <!-- with escape codes for dynamic content, all of -->
94
- <!-- which is serialized and escaped to a string. -->
95
- <xsl:text>"problemHtml": </xsl:text>
96
- <xsl:call-template name="escape-quote-xml">
97
- <xsl:with-param name="xml_content">
98
- <xsl:apply-templates select="statement/*" />
99
- <xsl:if test="$b-dynamics-static-seed">
100
- <div>
101
- <xsl:attribute name="id">
102
- <xsl:apply-templates select="." mode="html-id"/>
103
- <xsl:text>-substitutions</xsl:text>
104
- </xsl:attribute>
105
- <xsl:apply-templates select="(statement|solution)//eval[@obj]|evaluation//feedback//eval[@obj]|statement//fillin[@ansobj]" mode="track-evaluation" />
106
- </div>
107
- </xsl:if>
108
- </xsl:with-param>
109
- </xsl:call-template>
110
- <!-- The formatted HTML presentation of the solution, -->
111
- <!-- similar to statement but no fillins -->
112
- <xsl:text>,&#xa;"solutionHtml": </xsl:text>
113
- <xsl:call-template name="escape-quote-xml">
114
- <xsl:with-param name="xml_content">
115
- <xsl:apply-templates select="solution/*" />
116
- </xsl:with-param>
117
- </xsl:call-template>
118
- <!-- Add packages that need to be loaded as javascript -->
119
- <xsl:if test="$b-is-dynamic">
120
- <xsl:text>,&#xa;"dyn_imports": [</xsl:text>
121
- <xsl:text>"BTM"</xsl:text>
122
- <!-- Future: add additional packages here -->
123
- <xsl:text>]</xsl:text>
124
- </xsl:if>
125
- <!-- Names assigned to the blanks. -->
126
- <!-- Empty if none named. -->
127
- <xsl:text>,&#xa;"blankNames": {</xsl:text>
128
- <xsl:apply-templates select="statement//fillin" mode="declare-blanks" />
129
- <xsl:text>}</xsl:text>
130
- <xsl:if test="$b-is-dynamic">
131
- <!-- The actual setup code is javascript enclosed in quotes. -->
132
- <!-- The declaration creates the objects that are needed. -->
133
- <!-- The script is included as an escaped string -->
134
- <xsl:text>,&#xa;"dyn_vars": </xsl:text>
135
- <xsl:call-template name="dynamic-setup" />
136
- </xsl:if>
137
- <!-- An array of tests and feedback for answer evaluation -->
138
- <!-- Each blank has a corresponding array of test/feedback -->
139
- <!-- response. The test is Javascript (stringified) that -->
140
- <!-- returns a boolean response. The first test is for -->
141
- <!-- correctness. The last response is default. -->
142
- <xsl:text>,&#xa;"feedbackArray": [</xsl:text>
143
- <!-- In case all answers are based on one test -->
144
- <xsl:variable name="multiAns">
145
- <xsl:apply-templates select="evaluation" mode="get-multianswer-check"/>
146
- </xsl:variable>
147
- <!-- Generate test/feedback pair for each fillin -->
148
- <xsl:apply-templates select="statement//fillin" mode="dynamic-feedback">
149
- <xsl:with-param name="multiAns" select="$multiAns" />
150
- </xsl:apply-templates>
151
- <xsl:text>]</xsl:text>
152
- <xsl:text>&#xa;}</xsl:text>
153
- </script>
154
- </div>
155
- <xsl:text>&#xa;</xsl:text>
156
- </div>
157
- </div>
158
- </xsl:template>
159
-
160
- <!-- Fillins can be provided a name or use a default rule -->
161
- <xsl:template match="fillin" mode="blank-name">
162
- <xsl:choose>
163
- <xsl:when test="@name">
164
- <xsl:value-of select="@name"/>
165
- </xsl:when>
166
- <xsl:otherwise>
167
- <xsl:text>blank</xsl:text>
168
- <xsl:value-of select="position()" />
169
- </xsl:otherwise>
170
- </xsl:choose>
171
- </xsl:template>
172
-
173
- <!-- Creating a list of blank names. -->
174
- <xsl:template match="fillin" mode="declare-blanks">
175
- <xsl:variable name="blankNum">
176
- <xsl:value-of select="position()" />
177
- </xsl:variable>
178
- <xsl:if test="$blankNum>1">
179
- <xsl:text>, </xsl:text>
180
- </xsl:if>
181
- <xsl:call-template name="quote-string">
182
- <xsl:with-param name="text">
183
- <xsl:apply-templates select="." mode="blank-name"/>
184
- </xsl:with-param>
185
- </xsl:call-template>
186
- <xsl:text>: </xsl:text>
187
- <xsl:value-of select="$blankNum - 1"/>
188
- </xsl:template>
189
-
190
- <!-- #eval in a dynamic exercise (has setup) is to evaluate -->
191
- <!-- an expression that has been previously generated. If -->
192
- <!-- in math-mode, we want to see if it is an object that -->
193
- <!-- knows how to formulate a LaTeX representation -->
194
- <!-- The `toTeX` javascript function is defined in BTM.js -->
195
- <!-- which is loaded by Runestone. -->
196
- <xsl:template match="eval[@obj]">
197
- <xsl:text>[%= </xsl:text>
198
- <xsl:choose>
199
- <xsl:when test="ancestor::m|ancestor::mrow">
200
- <xsl:text>toTeX(</xsl:text>
201
- <xsl:value-of select="@obj"/>
202
- <xsl:text>)</xsl:text>
203
- </xsl:when>
204
- <xsl:otherwise>
205
- <xsl:value-of select="@obj"/>
206
- </xsl:otherwise>
207
- </xsl:choose>
208
- <xsl:text> %]</xsl:text>
209
- </xsl:template>
210
-
211
-
212
- <!-- Part of static output content extraction process, -->
213
- <!-- this creates elements that will be pulled from the -->
214
- <!-- stand-alone page into a generated XML document that -->
215
- <!-- gives the actual substitution in static mode. -->
216
- <xsl:template match="eval[@obj]" mode="track-evaluation">
217
- <eval-subst>
218
- <xsl:attribute name="obj">
219
- <xsl:value-of select="@obj"/>
220
- </xsl:attribute>
221
- <xsl:apply-templates select="."/>
222
- </eval-subst>
223
- </xsl:template>
224
-
225
- <xsl:template match="fillin[@ansobj]" mode="track-evaluation">
226
- <eval-subst>
227
- <xsl:attribute name="obj">
228
- <xsl:value-of select="@ansobj"/>
229
- </xsl:attribute>
230
- <xsl:variable name="temp-eval">
231
- <xsl:choose>
232
- <xsl:when test="@mode='math' or @mode='number'">
233
- <m><eval>
234
- <xsl:attribute name="obj">
235
- <xsl:value-of select="@ansobj"/>
236
- </xsl:attribute>
237
- </eval></m>
238
- </xsl:when>
239
- <xsl:otherwise>
240
- <eval>
241
- <xsl:attribute name="obj">
242
- <xsl:value-of select="@ansobj"/>
243
- </xsl:attribute>
244
- </eval>
245
- </xsl:otherwise>
246
- </xsl:choose>
247
- </xsl:variable>
248
- <xsl:apply-templates select="exsl:node-set($temp-eval)//eval"/>
249
- </eval-subst>
250
- </xsl:template>
251
-
252
-
253
- <!-- Create the dynamic aspect of the problem. -->
254
- <!-- Define all of the mathematical elements as well as -->
255
- <!-- objects (e.g. graphs) that might depend on them -->
256
- <!-- A script in setup/setupScript is executed after -->
257
- <!-- object-based setup concludes. -->
258
- <!-- A script in setup/postRenderScript is executed -->
259
- <!-- after the dynamic creation is complete. -->
260
- <xsl:template name="dynamic-setup">
261
- <xsl:variable name="js_code">
262
- <!-- Initialize the evaluation environment -->
263
- <xsl:call-template name="setup-evaluation-environment"/>
264
- <!-- Any direct JS for environment setup -->
265
- <xsl:if test="setup/setupScript">
266
- <xsl:value-of select="setup/setupScript"/>
267
- </xsl:if>
268
- <!-- Prepare evaluation and feedback -->
269
- <xsl:call-template name="setup-fillin-parsers"/>
270
- <!-- Create the call-back for post-render tasks -->
271
- <xsl:if test="$b-include-postContent">
272
- <xsl:call-template name="setup-postContent"/>
273
- </xsl:if>
274
- </xsl:variable>
275
- <xsl:call-template name="escape-quote-string">
276
- <xsl:with-param name="text" select="$js_code"/>
277
- </xsl:call-template>
278
- </xsl:template>
279
-
280
- <!-- Parse any settings for the math environment for the problem -->
281
- <xsl:template name="setup-evaluation-environment">
282
- <xsl:text>v._mobjs = new BTM({</xsl:text>
283
- <!-- Setup seeded random number generator -->
284
- <xsl:text>'rand': rand</xsl:text>
285
- <!-- FUTURE: Pull additional settings from an environment element -->
286
- <xsl:apply-templates select="de-environment" mode="runestone-setup"/>
287
- <xsl:text>});&#xa;</xsl:text>
288
- <xsl:text>var RNG = v._mobjs.menv.rng;&#xa;</xsl:text>
289
- <!-- Generate all of the XML-declared math objects -->
290
- <xsl:apply-templates select="setup/de-object" mode="runestone-setup"/>
291
- </xsl:template>
292
-
293
- <!-- Environment Setup: Define the mathematical objects -->
294
- <xsl:template match="de-object" mode="runestone-setup">
295
- <xsl:text>v.</xsl:text><xsl:value-of select="@name"/>
296
- <xsl:text> = v._mobjs.addMathObject(</xsl:text>
297
- <xsl:call-template name="quote-string">
298
- <xsl:with-param name="text" select="@name"/>
299
- </xsl:call-template>
300
- <xsl:text>, </xsl:text>
301
- <!-- Get the context of the object here -->
302
- <xsl:call-template name="quote-string">
303
- <xsl:with-param name="text">
304
- <xsl:choose>
305
- <!-- Usually use value of @context but in case strings change. -->
306
- <xsl:when test="@context='interval' or @context='set'">
307
- <xsl:text>set</xsl:text>
308
- </xsl:when>
309
- <xsl:otherwise>
310
- <xsl:value-of select="@context"/>
311
- </xsl:otherwise>
312
- </xsl:choose>
313
- </xsl:with-param>
314
- </xsl:call-template>
315
- <xsl:text>, </xsl:text>
316
- <xsl:apply-templates select="*" mode="evaluate">
317
- <xsl:with-param name="setupMode"><xsl:text>1</xsl:text></xsl:with-param>
318
- </xsl:apply-templates>
319
- <xsl:text>);&#xa;</xsl:text>
320
- </xsl:template>
321
-
322
- <!-- Runestone requires declaring an array of answer-types for -->
323
- <!-- validating and parsing what the user enters as a response. -->
324
- <xsl:template name="setup-fillin-parsers">
325
- <xsl:text>v.types = [</xsl:text>
326
- <xsl:apply-templates select="statement//fillin" mode="setup-parsers" />
327
- <xsl:text>];&#xa;</xsl:text>
328
- </xsl:template>
329
-
330
- <!-- Create a call-back for what occurs post-rendering. -->
331
- <!-- This includes automated tasks as well as any script -->
332
- <!-- commands in setup/postRenderScript -->
333
- <xsl:template name="setup-postContent">
334
- <xsl:text>v.afterContentRender = function() {&#xa;</xsl:text>
335
- <xsl:if test="setup/postRenderScript">
336
- <xsl:value-of select="setup/postRenderScript"/>
337
- </xsl:if>
338
- <xsl:text>};&#xa;</xsl:text>
339
- </xsl:template>
340
-
341
-
342
- <!-- Each fillin defines a blank. Establish the list of parsers. -->
343
- <!-- During setup and called while creating dyn_vars. -->
344
- <xsl:template match="fillin" mode="setup-parsers">
345
- <xsl:if test="position() > 1">
346
- <xsl:text>, </xsl:text>
347
- </xsl:if>
348
- <xsl:choose>
349
- <xsl:when test="@mode='math-formula'">
350
- <xsl:text>v._mobjs.getParser()</xsl:text>
351
- </xsl:when>
352
- <xsl:when test="@mode='number'">
353
- <xsl:text>Number</xsl:text>
354
- </xsl:when>
355
- <xsl:otherwise>
356
- <xsl:text>function(val){ return val; }</xsl:text>
357
- </xsl:otherwise>
358
- </xsl:choose>
359
- </xsl:template>
360
-
361
-
362
- <!-- ========================================================== -->
363
- <!-- Evaluation and Feedback -->
364
- <!-- ========================================================== -->
365
-
366
- <!-- Default localized feedback strings. -->
367
- <xsl:template match="*" mode="get-default-feedback">
368
- <xsl:param name="b-correct" select="'no'"/>
369
- <xsl:choose>
370
- <xsl:when test="$b-correct='yes'">
371
- <xsl:apply-templates select="." mode="type-name">
372
- <xsl:with-param name="string-id" select="'correct'"/>
373
- </xsl:apply-templates>
374
- <xsl:text>!</xsl:text>
375
- </xsl:when>
376
- <xsl:otherwise>
377
- <xsl:apply-templates select="." mode="type-name">
378
- <xsl:with-param name="string-id" select="'incorrect'"/>
379
- </xsl:apply-templates>
380
- <xsl:text>.</xsl:text>
381
- </xsl:otherwise>
382
- </xsl:choose>
383
- </xsl:template>
384
-
385
- <!-- Deal with possibility of global checker for all blanks -->
386
- <xsl:template match="evaluation" mode="get-multianswer-check">
387
- <xsl:variable name="responseTree" select="../statement//fillin" />
388
- <xsl:if test="count($responseTree) > 1 and evaluate[@all='yes']/test">
389
- <xsl:apply-templates select="evaluate[@all='yes']/test" mode="create-test-feedback">
390
- <xsl:with-param name="b-correct" select="'yes'"/>
391
- <xsl:with-param name="fillin" select="$responseTree"/>
392
- </xsl:apply-templates>
393
- </xsl:if>
394
- </xsl:template>
395
-
396
- <!-- Return the index of an evaluate -->
397
- <xsl:template match="evaluation/evaluate" mode="get-index">
398
- <xsl:value-of select="position()"/>
399
- </xsl:template>
400
-
401
- <!-- Build feedback based on the #evaluate element. -->
402
- <xsl:template match="evaluate" mode="dynamic-feedback">
403
- <xsl:param name="multiAns"/>
404
- <xsl:param name="match-fillin"/>
405
-
406
- <!-- Defend against name mismatches -->
407
- <xsl:if test="@name and $match-fillin/@name and not(@name = $match-fillin/@name)">
408
- <xsl:message>PTX:ERROR: Dynamic content missing label "<xsl:value-of select="@visible-id"/>"</xsl:message>
409
- </xsl:if>
410
-
411
- <!-- First check is for correctness. -->
412
- <xsl:text>[</xsl:text>
413
- <xsl:choose>
414
- <xsl:when test="string-length($multiAns) > 0">
415
- <xsl:value-of select="$multiAns"/>
416
- </xsl:when>
417
- <xsl:when test="test[@correct='yes']">
418
- <xsl:apply-templates select="test[@correct='yes']" mode="create-test-feedback">
419
- <xsl:with-param name="fillin" select="$match-fillin" />
420
- <xsl:with-param name="b-correct" select="'yes'" />
421
- </xsl:apply-templates>
422
- </xsl:when>
423
- <!-- If no explicit test is provided, use given answer. -->
424
- <xsl:otherwise>
425
- <xsl:text>{</xsl:text>
426
- <xsl:choose>
427
- <xsl:when test="$match-fillin/@answer">
428
- <xsl:choose>
429
- <xsl:when test="$match-fillin/@mode='number'">
430
- <xsl:text>"number": [</xsl:text>
431
- <xsl:value-of select="$match-fillin/@answer"/>
432
- <xsl:text>,</xsl:text>
433
- <xsl:value-of select="$match-fillin/@answer"/>
434
- <xsl:text>]</xsl:text>
435
- </xsl:when>
436
- <xsl:when test="$match-fillin/@mode='string'">
437
- <xsl:text>"regex": "</xsl:text>
438
- <xsl:value-of select="$match-fillin/@answer"/>
439
- <xsl:text>"</xsl:text>
440
- </xsl:when>
441
- <xsl:otherwise>
442
- <xsl:message>PTX:ERROR: fillin has an @answer with invalid or missing mode (valid: number or string) in "<xsl:value-of select="@visible-id"/>. If dynamic, you should use @ansobj.".</xsl:message>
443
- </xsl:otherwise>
444
- </xsl:choose>
445
- </xsl:when>
446
- <xsl:when test="$match-fillin/@ansobj">
447
- <xsl:choose>
448
- <xsl:when test="$match-fillin/@mode='number'">
449
- <xsl:text>function() {&#xa;</xsl:text>
450
- <xsl:text> return (Math.abs(</xsl:text>
451
- <xsl:value-of select="$match-fillin/@ansobj"/>
452
- <xsl:text>- ans) &lt; 1e-10);&#xa;}</xsl:text>
453
- </xsl:when>
454
- <xsl:when test="$match-fillin/@mode='string'">
455
- <xsl:text>function() {&#xa;</xsl:text>
456
- <xsl:text> return (</xsl:text>
457
- <xsl:value-of select="$match-fillin/@ansobj"/>
458
- <xsl:text>== ans);&#xa;}</xsl:text>
459
- </xsl:when>
460
- <xsl:when test="$match-fillin/@mode='math'">
461
- <xsl:text>function() {&#xa;</xsl:text>
462
- <xsl:text> return _mobjs.compareExpressions(</xsl:text>
463
- <xsl:value-of select="$match-fillin/@ansobj"/>
464
- <xsl:text>, ans</xsl:text>
465
- <xsl:text>);&#xa;}</xsl:text>
466
- </xsl:when>
467
- <xsl:otherwise>
468
- <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>
469
- </xsl:otherwise>
470
- </xsl:choose>
471
- </xsl:when>
472
- <xsl:otherwise>
473
- <xsl:message>PTX:ERROR: No method provided to identify a correct answer for #fillin in "<xsl:value-of select="@visible-id"/>".</xsl:message>
474
- </xsl:otherwise>
475
- </xsl:choose>
476
- <xsl:text>, "feedback": "</xsl:text>
477
- <xsl:apply-templates select="." mode="get-default-feedback">
478
- <xsl:with-param name="b-correct" select="'yes'"/>
479
- </xsl:apply-templates>
480
- <xsl:text>"}</xsl:text>
481
- </xsl:otherwise>
482
- </xsl:choose>
483
-
484
- <!-- Now add additional checks for feedback. -->
485
- <xsl:for-each select="test[not(@correct='yes')]">
486
- <xsl:text>, </xsl:text>
487
- <xsl:apply-templates select="." mode="create-test-feedback">
488
- <xsl:with-param name="fillin" select="$match-fillin"/>
489
- </xsl:apply-templates>
490
- </xsl:for-each>
491
- <!-- Default feedback for the blank. Always evaluates true. -->
492
- <xsl:text>, {"feedback": "</xsl:text>
493
- <xsl:apply-templates select="." mode="get-default-feedback">
494
- <xsl:with-param name="b-correct" select="'no'"/>
495
- </xsl:apply-templates>
496
- <xsl:text>"}]</xsl:text>
497
- </xsl:template>
498
-
499
- <!-- Template for answer checking. Actual work done by specialized templates. -->
500
- <xsl:template match="fillin" mode="dynamic-feedback">
501
- <xsl:param name="multiAns"/>
502
- <xsl:variable name="curFillIn" select="."/>
503
- <xsl:variable name="fillinName">
504
- <xsl:apply-templates select="." mode="blank-name"/>
505
- </xsl:variable>
506
- <xsl:variable name="blankNum">
507
- <xsl:value-of select="position()" />
508
- </xsl:variable>
509
-
510
- <!-- we need to know which #evaluate matches the #fillin -->
511
- <xsl:variable name="evaluate-index">
512
- <xsl:choose>
513
- <xsl:when test="ancestor::statement/../evaluation/evaluate[@name = $fillinName]">
514
- <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[@name = $fillinName]" mode="get-index"/>
515
- </xsl:when>
516
- <xsl:when test="ancestor::statement/../evaluation/evaluate[position() = $blankNum]">
517
- <xsl:value-of select="$blankNum"/>
518
- </xsl:when>
519
- <!-- No check matches: Make blank default. -->
520
- <xsl:otherwise>
521
- <xsl:number value="-1"/>
522
- </xsl:otherwise>
523
- </xsl:choose>
524
- </xsl:variable>
525
-
526
- <xsl:if test="$blankNum > 1">
527
- <xsl:text>, </xsl:text>
528
- </xsl:if>
529
- <xsl:choose>
530
- <!-- First look for a matching name. -->
531
- <xsl:when test="ancestor::statement/../evaluation/evaluate[@name = $fillinName]">
532
- <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[@name = $fillinName]" mode="dynamic-feedback">
533
- <xsl:with-param name="multiAns" select="$multiAns"/>
534
- <xsl:with-param name="match-fillin" select="."/>
535
- </xsl:apply-templates>
536
- </xsl:when>
537
- <!-- Otherwise match on order. -->
538
- <xsl:when test="ancestor::statement/../evaluation/evaluate[position() = $blankNum]">
539
- <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[position() = $blankNum]" mode="dynamic-feedback">
540
- <xsl:with-param name="multiAns" select="$multiAns"/>
541
- <xsl:with-param name="match-fillin" select="."/>
542
- </xsl:apply-templates>
543
- </xsl:when>
544
- <!-- No check matches: Make blank default. -->
545
- <xsl:otherwise>
546
- <xsl:message>PTX:ERROR: No matching #evaluate for #fillin in "<xsl:value-of select="@visible-id"/>".</xsl:message>
547
- </xsl:otherwise>
548
- </xsl:choose>
549
- </xsl:template>
550
-
551
- <xsl:template match="test" mode="create-test-feedback">
552
- <xsl:param name="fillin"/>
553
- <xsl:param name="b-correct" select="'no'"/>
554
- <xsl:variable name="feedback-rtf">
555
- <xsl:apply-templates select="feedback"/>
556
- </xsl:variable>
557
- <xsl:text>{</xsl:text>
558
- <xsl:apply-templates select="." mode="create-test">
559
- <xsl:with-param name="fillin" select="$fillin" />
560
- </xsl:apply-templates>
561
- <xsl:text>, "feedback": "</xsl:text>
562
- <xsl:choose>
563
- <xsl:when test="feedback">
564
- <!-- serialize HTML as text, then escape as JSON -->
565
- <xsl:call-template name="escape-json-string">
566
- <xsl:with-param name="text">
567
- <xsl:apply-templates select="exsl:node-set($feedback-rtf)" mode="xml-to-string"/>
568
- </xsl:with-param>
569
- </xsl:call-template>
570
- </xsl:when>
571
- <xsl:otherwise>
572
- <xsl:apply-templates select="." mode="get-default-feedback">
573
- <xsl:with-param name="b-correct" select="$b-correct"/>
574
- </xsl:apply-templates>
575
- </xsl:otherwise>
576
- </xsl:choose>
577
- <xsl:text>"}</xsl:text>
578
- </xsl:template>
579
-
580
- <!-- Template for simple answer checkers: no interaction between different fillins. -->
581
- <!-- Add a post-filter to deal with additional feedback, similar to AnswerHints but allowing more complex logic. -->
582
- <!-- JSON dictionary for numerical condition -->
583
- <xsl:template match="test[numcmp]" mode="create-test">
584
- <xsl:param name="fillin"/>
585
- <xsl:choose>
586
- <xsl:when test="(numcmp/@use-answer='yes' and $fillin/@ansobj) or numcmp/@object">
587
- <xsl:apply-templates select="." mode="create-test-ansobj">
588
- <xsl:with-param name="fillin" select="$fillin"/>
589
- </xsl:apply-templates>
590
- </xsl:when>
591
- <xsl:otherwise>
592
- <xsl:variable name="answer">
593
- <xsl:choose>
594
- <xsl:when test="numcmp/@use-answer='yes' and $fillin/@answer">
595
- <xsl:value-of select="$fillin/@answer"/>
596
- </xsl:when>
597
- <xsl:otherwise>
598
- <xsl:value-of select="numcmp/@value"/>
599
- </xsl:otherwise>
600
- </xsl:choose>
601
- </xsl:variable>
602
- <xsl:variable name="tolerance">
603
- <xsl:choose>
604
- <xsl:when test="numcmp/@use-answer='yes' and $fillin/@tolerance">
605
- <xsl:value-of select="$fillin/@tolerance"/>
606
- </xsl:when>
607
- <xsl:when test="numcmp/@tolerance">
608
- <xsl:value-of select="numcmp/@tolerance"/>
609
- </xsl:when>
610
- <xsl:otherwise>
611
- <xsl:text>0</xsl:text>
612
- </xsl:otherwise>
613
- </xsl:choose>
614
- </xsl:variable>
615
- <xsl:variable name="min-val">
616
- <xsl:choose>
617
- <xsl:when test="numcmp/@min">
618
- <xsl:value-of select="numcmp/@min"/>
619
- </xsl:when>
620
- <xsl:otherwise>
621
- <xsl:value-of select="$answer - $tolerance"/>
622
- </xsl:otherwise>
623
- </xsl:choose>
624
- </xsl:variable>
625
- <xsl:variable name="max-val">
626
- <xsl:choose>
627
- <xsl:when test="numcmp/@max">
628
- <xsl:value-of select="numcmp/@max"/>
629
- </xsl:when>
630
- <xsl:otherwise>
631
- <xsl:value-of select="$answer + $tolerance"/>
632
- </xsl:otherwise>
633
- </xsl:choose>
634
- </xsl:variable>
635
- <xsl:text>"number": [</xsl:text>
636
- <xsl:value-of select="$min-val"/>
637
- <xsl:text>,</xsl:text>
638
- <xsl:value-of select="$max-val"/>
639
- <xsl:text>]</xsl:text>
640
- </xsl:otherwise>
641
- </xsl:choose>
642
- </xsl:template>
643
-
644
- <!-- When the comparison is with an object, we rely on function comparisons. -->
645
- <xsl:template match="test[numcmp]" mode="create-test-ansobj">
646
- <xsl:param name="fillin"/>
647
- <xsl:variable name="ansObject">
648
- <xsl:choose>
649
- <xsl:when test="numcmp/@use-answer='yes' and $fillin/@ansobj">
650
- <xsl:value-of select="$fillin/@ansobj"/>
651
- </xsl:when>
652
- <xsl:otherwise>
653
- <xsl:value-of select="numcmp/@object"/>
654
- </xsl:otherwise>
655
- </xsl:choose>
656
- </xsl:variable>
657
- <xsl:variable name="tolerance">
658
- <xsl:choose>
659
- <xsl:when test="numcmp/@use-answer='yes' and $fillin/@tolerance">
660
- <xsl:value-of select="$fillin/@tolerance"/>
661
- </xsl:when>
662
- <xsl:when test="numcmp/@tolerance">
663
- <xsl:value-of select="numcmp/@tolerance"/>
664
- </xsl:when>
665
- <xsl:otherwise>
666
- <xsl:text>0</xsl:text>
667
- </xsl:otherwise>
668
- </xsl:choose>
669
- </xsl:variable>
670
- <xsl:variable name="min-val">
671
- <xsl:choose>
672
- <xsl:when test="numcmp/@min">
673
- <xsl:value-of select="numcmp/@min"/>
674
- </xsl:when>
675
- <xsl:otherwise>
676
- <xsl:value-of select="$ansObject"/>
677
- <xsl:text> - </xsl:text>
678
- <xsl:value-of select="$tolerance"/>
679
- </xsl:otherwise>
680
- </xsl:choose>
681
- </xsl:variable>
682
- <xsl:variable name="max-val">
683
- <xsl:choose>
684
- <xsl:when test="numcmp/@max">
685
- <xsl:value-of select="numcmp/@max"/>
686
- </xsl:when>
687
- <xsl:otherwise>
688
- <xsl:value-of select="$ansObject"/>
689
- <xsl:text> + </xsl:text>
690
- <xsl:value-of select="$tolerance"/>
691
- </xsl:otherwise>
692
- </xsl:choose>
693
- </xsl:variable>
694
- <xsl:text>"solution_code": </xsl:text>
695
- <xsl:variable name="js_code">
696
- <xsl:text>function() { </xsl:text>
697
- <xsl:text>return (ans &gt;= </xsl:text>
698
- <xsl:value-of select="$min-val"/>
699
- <xsl:text>) &amp;&amp; (ans &lt;= </xsl:text>
700
- <xsl:value-of select="$max-val"/>
701
- <xsl:text>); }()</xsl:text>
702
- </xsl:variable>
703
- <xsl:call-template name="escape-quote-string">
704
- <xsl:with-param name="text" select="$js_code"/>
705
- </xsl:call-template>
706
- </xsl:template>
707
-
708
- <!-- JSON dictionary for string condition -->
709
- <xsl:template match="test[strcmp]" mode="create-test">
710
- <xsl:param name="fillin"/>
711
- <xsl:choose>
712
- <xsl:when test="strcmp/@use-answer='yes' and $fillin/@ansobj or strcmp//eval">
713
- <xsl:apply-templates mode="create-test-ansobj">
714
- <xsl:with-param name="fillin" select="$fillin"/>
715
- </xsl:apply-templates>
716
- </xsl:when>
717
- <xsl:otherwise>
718
- <xsl:variable name="answer">
719
- <xsl:choose>
720
- <xsl:when test="strcmp/@use-answer='yes'">
721
- <xsl:value-of select="$fillin/@answer"/>
722
- </xsl:when>
723
- <xsl:otherwise>
724
- <xsl:value-of select="strcmp"/>
725
- </xsl:otherwise>
726
- </xsl:choose>
727
- </xsl:variable>
728
- <xsl:variable name="regexFlags">
729
- <xsl:choose>
730
- <xsl:when test="strcmp/@use-answer='yes' and $fillin/@case = 'insensitive'">
731
- <xsl:text>i</xsl:text>
732
- </xsl:when>
733
- <xsl:when test="strcmp/@case = 'insensitive'">
734
- <xsl:text>i</xsl:text>
735
- </xsl:when>
736
- <!-- Otherwise nothing -->
737
- </xsl:choose>
738
- </xsl:variable>
739
- <!-- regex string match, drop -->
740
- <!-- leading/trailing whitespace -->
741
- <xsl:text>"regex": "</xsl:text>
742
- <!-- JSON escapes necessary for regular expression -->
743
- <xsl:call-template name="escape-json-string">
744
- <xsl:with-param name="text">
745
- <xsl:choose>
746
- <xsl:when test="strcmp/@strip = 'no'">
747
- <xsl:value-of select="$answer"/>
748
- </xsl:when>
749
- <xsl:otherwise>
750
- <xsl:text>^\s*</xsl:text>
751
- <xsl:value-of select="$answer"/>
752
- <xsl:text>\s*$</xsl:text>
753
- </xsl:otherwise>
754
- </xsl:choose>
755
- </xsl:with-param>
756
- </xsl:call-template>
757
- <xsl:text>"</xsl:text>
758
- <!-- flag for case-sensitive match -->
759
- <!-- default: 'sensitive' -->
760
- <xsl:text>, "regexFlags": "</xsl:text>
761
- <xsl:value-of select="$regexFlags"/>
762
- <xsl:text>"</xsl:text>
763
- </xsl:otherwise>
764
- </xsl:choose>
765
- </xsl:template>
766
-
767
- <!-- When the comparison is with an object, we rely on function comparisons. -->
768
- <xsl:template match="test[strcmp]" mode="create-test-ansobj">
769
- <xsl:variable name="regex">
770
- <xsl:choose>
771
- <xsl:when test="strcmp/@use-answer='yes' and $fillin/@ansobj">
772
- <xsl:text>`${</xsl:text>
773
- <xsl:value-of select="$fillin/@ansobj"/>
774
- <xsl:text>}`</xsl:text>
775
- </xsl:when>
776
- <xsl:otherwise>
777
- <xsl:text>`</xsl:text>
778
- <xsl:apply-templates select="strcmp/*" mode="strcmp-object-substitution"/>
779
- <xsl:text>`</xsl:text>
780
- </xsl:otherwise>
781
- </xsl:choose>
782
- </xsl:variable>
783
- <xsl:variable name="regexFlags">
784
- <xsl:choose>
785
- <xsl:when test="strcmp/@use-answer='yes' and $fillin/@case = 'insensitive'">
786
- <xsl:text>i</xsl:text>
787
- </xsl:when>
788
- <xsl:when test="strcmp/@case = 'insensitive'">
789
- <xsl:text>i</xsl:text>
790
- </xsl:when>
791
- <!-- Otherwise Nothing -->
792
- </xsl:choose>
793
- </xsl:variable>
794
- <!-- create a function to do the regex -->
795
- <xsl:text>"solution_code": </xsl:text>
796
- <xsl:variable name="js_code">
797
- <xsl:text>function(){&#xa;</xsl:text>
798
- <xsl:text> const re_str = </xsl:text><xsl:value-of select="$regex"/><xsl:text>;&#xa;</xsl:text>
799
- <xsl:text> const re = new RegExp(re_str, "</xsl:text>
800
- <xsl:value-of select="$regexFlags"/>
801
- <xsl:text>");&#xa;</xsl:text>
802
- <xsl:text> return re.test(ans);&#xa;</xsl:text>
803
- <xsl:text>}()&#xa;</xsl:text>
804
- </xsl:variable>
805
- <xsl:call-template name="escape-quote-string">
806
- <xsl:with-param name="text" select="$js_code"/>
807
- </xsl:call-template>
808
- </xsl:template>
809
-
810
- <xsl:template match="eval" mode="strcmp-object-substitution">
811
- <xsl:text>${</xsl:text>
812
- <xsl:value-of select="."/>
813
- <xsl:text>}</xsl:text>
814
- </xsl:template>
815
-
816
- <xsl:template match="*" mode="strcmp-object-substitution">
817
- <xsl:copy-of select="."/>
818
- </xsl:template>
819
-
820
- <!-- Otherwise create a function that deals with the test. -->
821
- <xsl:template match="test" mode="create-test">
822
- <xsl:param name="fillin" />
823
- <xsl:variable name="conditions" select="*[not(self::feedback)]"/>
824
- <xsl:text>"solution_code": </xsl:text>
825
- <xsl:variable name="js_code">
826
- <xsl:text>function() {&#xa;</xsl:text>
827
- <!-- Create a checker function. Initialize a stack of flag variables to track results. -->
828
- <xsl:text> var testResults = new Array();&#xa;</xsl:text>
829
- <xsl:choose>
830
- <xsl:when test="count($conditions) = 1">
831
- <xsl:call-template name="checker-simple">
832
- <xsl:with-param name="fillin" select="$fillin" />
833
- <xsl:with-param name="curTest" select="$conditions" />
834
- <xsl:with-param name="level" select="0" />
835
- </xsl:call-template>
836
- </xsl:when>
837
- <xsl:otherwise>
838
- <xsl:call-template name="checker-layer">
839
- <xsl:with-param name="fillin" select="$fillin" />
840
- <xsl:with-param name="tests" select="$conditions" />
841
- <xsl:with-param name="level" select="0" />
842
- <xsl:with-param name="logic" select="'and'" /> <!-- All tests at first layer must be true -->
843
- </xsl:call-template>
844
- </xsl:otherwise>
845
- </xsl:choose>
846
- <xsl:text> return (testResults[0]);&#xa;</xsl:text>
847
- <xsl:text>}()</xsl:text>
848
- </xsl:variable>
849
- <xsl:call-template name="escape-quote-string">
850
- <xsl:with-param name="text" select="$js_code"/>
851
- </xsl:call-template>
852
- </xsl:template>
853
-
854
-
855
- <!-- This template is called for a simple test (no compound logic). -->
856
- <xsl:template name="checker-simple">
857
- <xsl:param name="curTest" />
858
- <xsl:param name="fillin" />
859
- <xsl:param name="level" select="0" />
860
- <xsl:choose>
861
- <!-- Test might be coded directly in javascript -->
862
- <xsl:when test="name($curTest) = 'jscmp'">
863
- <xsl:text> testResults[</xsl:text>
864
- <xsl:value-of select="$level" />
865
- <xsl:text>] = </xsl:text>
866
- <xsl:value-of select="$curTest"/>
867
- </xsl:when>
868
- <!-- Test might require logic -->
869
- <xsl:when test="name($curTest)='logic'">
870
- <xsl:call-template name="checker-layer">
871
- <xsl:with-param name="fillin" select="$fillin" />
872
- <xsl:with-param name="tests" select="$curTest/*" />
873
- <xsl:with-param name="level" select="$level + 1" />
874
- <xsl:with-param name="logic" select="$curTest/@op" /> <!-- Default: All tests at first layer must be true -->
875
- </xsl:call-template>
876
- <xsl:text> testResults[</xsl:text>
877
- <xsl:value-of select="$level" />
878
- <xsl:text>] = testResults[</xsl:text>
879
- <xsl:value-of select="$level + 1" />
880
- <xsl:text>];&#xa;</xsl:text>
881
- </xsl:when>
882
- <!-- Otherwise simple test -->
883
- <xsl:otherwise>
884
- <!-- A test can have an implied equal or an explicit equal -->
885
- <!-- At root level, the test might also have a feedback. Skip that. -->
886
- <xsl:text> testResults[</xsl:text>
887
- <xsl:value-of select="$level" />
888
- <xsl:text>] = </xsl:text>
889
- <xsl:text>_mobjs.compareExpressions(</xsl:text>
890
- <xsl:choose>
891
- <!-- An equal element must have two expression children. -->
892
- <xsl:when test="name($curTest)='mathcmp' and $curTest/@use-answer='yes'">
893
- <xsl:choose>
894
- <xsl:when test="not($fillin/@ansobj)">
895
- <xsl:message>PTX:WARNING: Feedback for "<xsl:value-of select="@visible-id"/>" says to use given math answer, but @ansobj not defined. </xsl:message>
896
- <xsl:text>UNDEFINED</xsl:text>
897
- </xsl:when>
898
- <xsl:otherwise>
899
- <xsl:value-of select="$fillin/@ansobj"/>
900
- </xsl:otherwise>
901
- </xsl:choose>
902
- <xsl:text>, ans</xsl:text>
903
- </xsl:when>
904
- <xsl:when test="name($curTest)='mathcmp' and $curTest/@obj">
905
- <xsl:value-of select="$curTest/@obj"/>
906
- <xsl:text>, ans</xsl:text>
907
- </xsl:when>
908
- <xsl:when test="name($curTest)='mathcmp' and count($curTest/*)=1">
909
- <xsl:apply-templates select="$curTest/*[1]" mode="evaluate"/>
910
- <xsl:text>, ans</xsl:text>
911
- </xsl:when>
912
- <xsl:when test="name($curTest)='mathcmp' and count($curTest/*)=2">
913
- <xsl:apply-templates select="$curTest/*[1]" mode="evaluate"/>
914
- <xsl:text>, </xsl:text>
915
- <xsl:apply-templates select="$curTest/*[2]" mode="evaluate"/>
916
- </xsl:when>
917
- <!-- An implied equal compares the submitted answer to the given expression. -->
918
- <xsl:otherwise> <!-- Must be expression: #eval or #de-expression -->
919
- <xsl:apply-templates select="$curTest" mode="evaluate"/>
920
- <xsl:text>, ans</xsl:text>
921
- </xsl:otherwise>
922
- </xsl:choose>
923
- <xsl:text>)</xsl:text>
924
- </xsl:otherwise>
925
- </xsl:choose>
926
- <xsl:text>;&#xa;</xsl:text>
927
- </xsl:template>
928
-
929
- <!-- A test can also be composite involving a combination of logical tests -->
930
- <!-- This template works through one layer, recursively dealing with deeper layers as needed -->
931
- <xsl:template name="checker-layer" >
932
- <xsl:param name="fillin" />
933
- <xsl:param name="tests" /> <!-- The layer of tests (subtree) -->
934
- <xsl:param name="level" select="0" /> <!-- Level (or depth) of the layer -->
935
- <xsl:param name="logic" select="'and'" /> <!-- and = all must be true, or = at least one, not = negation -->
936
- <xsl:choose>
937
- <xsl:when test="$logic = 'and'"> <!-- Treat logic like multipication. A single false (0) makes product zero -->
938
- <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" /><xsl:text>] = 1;&#xa;</xsl:text>
939
- </xsl:when>
940
- <xsl:when test="$logic = 'or'"> <!-- Treat logic like addition. A single true makes sum positive -->
941
- <xsl:text>testResults[</xsl:text><xsl:value-of select="$level" /><xsl:text>] = 0;&#xa;</xsl:text>
942
- </xsl:when>
943
- <xsl:when test="$logic = 'not'"/> <!-- Negation has no preparation -->
944
- <xsl:otherwise>
945
- <xsl:message>PTX:ERROR: Invalid logic operator in dynamic fillin for "<xsl:value-of select="@visible-id"/>"</xsl:message>
946
- </xsl:otherwise>
947
- </xsl:choose>
948
- <xsl:for-each select="$tests"> <!-- Work through the layer of tests one at a time. -->
949
- <xsl:choose>
950
- <xsl:when test="name()='and'">
951
- <xsl:text> testResults[</xsl:text>
952
- <xsl:value-of select="$level+1" />
953
- <xsl:text>] = 1;&#xa;</xsl:text>
954
- <xsl:call-template name="checker-layer">
955
- <xsl:with-param name="fillin" select="$fillin" />
956
- <xsl:with-param name="tests" select="./*" />
957
- <xsl:with-param name="level" select="$level+1" />
958
- <xsl:with-param name="logic" select="'and'" /> <!-- Default: All tests at first layer must be true -->
959
- </xsl:call-template>
960
- </xsl:when>
961
- <xsl:when test="name()='or'">
962
- <xsl:text> testResults[</xsl:text>
963
- <xsl:value-of select="$level+1" />
964
- <xsl:text>] = 0;&#xa;</xsl:text>
965
- <xsl:call-template name="checker-layer">
966
- <xsl:with-param name="fillin" select="$fillin" />
967
- <xsl:with-param name="tests" select="./*" />
968
- <xsl:with-param name="level" select="$level+1" />
969
- <xsl:with-param name="logic" select="'or'" />
970
- </xsl:call-template>
971
- </xsl:when>
972
- <xsl:when test="name()='not'">
973
- <xsl:call-template name="checker-layer">
974
- <xsl:with-param name="fillin" select="$fillin" />
975
- <xsl:with-param name="tests" select="./*" />
976
- <xsl:with-param name="level" select="$level+1" />
977
- <xsl:with-param name="logic" select="'not'" />
978
- </xsl:call-template>
979
- </xsl:when>
980
- <xsl:otherwise>
981
- <xsl:call-template name="checker-simple">
982
- <xsl:with-param name="fillin" select="$fillin" />
983
- <xsl:with-param name="curTest" select="." />
984
- <xsl:with-param name="level" select="$level+1" />
985
- </xsl:call-template>
986
- </xsl:otherwise>
987
- </xsl:choose>
988
- <xsl:choose> <!-- Deal with the results of that recursive call to a deeper layer. -->
989
- <xsl:when test="$logic = 'and'">
990
- <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" />
991
- <xsl:text>] &amp;= testResults[</xsl:text><xsl:value-of select="$level+1" /><xsl:text>];&#xa;</xsl:text>
992
- </xsl:when>
993
- <xsl:when test="$logic = 'or'">
994
- <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" />
995
- <xsl:text>] |= testResults[</xsl:text><xsl:value-of select="$level+1" /><xsl:text>];&#xa;</xsl:text>
996
- </xsl:when>
997
- <xsl:when test="$logic = 'not'">
998
- <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" />
999
- <xsl:text>] = !(testResults[</xsl:text><xsl:value-of select="$level+1" /><xsl:text>]);&#xa;</xsl:text>
1000
- </xsl:when>
1001
- <!-- Otherwise: invalid logic call was signaled as Error earlier. -->
1002
- </xsl:choose>
1003
- </xsl:for-each>
1004
- </xsl:template>
1005
-
1006
-
1007
- <!-- ================================================ -->
1008
- <!-- Templates for working with evaluation objects -->
1009
- <!-- ================================================ -->
1010
-
1011
- <!-- Wrappers for calling appropriate javascript commands that -->
1012
- <!-- will parse the expression for a number appropriately, -->
1013
- <!-- which could be written as an expression involving symbols -->
1014
- <!-- or a random number -->
1015
- <xsl:template match="de-number" mode="evaluate">
1016
- <xsl:param name="setupMode" />
1017
- <xsl:variable name="prefix">
1018
- <xsl:if test="$setupMode">
1019
- <xsl:text>v.</xsl:text>
1020
- </xsl:if>
1021
- </xsl:variable>
1022
- <xsl:variable name="de_env">
1023
- <xsl:value-of select="$prefix"/>
1024
- <xsl:text>_mobjs</xsl:text>
1025
- </xsl:variable>
1026
- <xsl:value-of select="$de_env"/>
1027
- <xsl:text>.parseExpression(</xsl:text>
1028
- <xsl:call-template name="quote-strip-string">
1029
- <xsl:with-param name="text" select="."/>
1030
- </xsl:call-template>
1031
- <xsl:text>, "number")</xsl:text>
1032
- <xsl:text>.reduce().simplifyConstants()</xsl:text>
1033
- </xsl:template>
1034
-
1035
- <xsl:template match="de-evaluate" mode="evaluate">
1036
- <xsl:param name="setupMode" />
1037
- <xsl:variable name="prefix">
1038
- <xsl:if test="$setupMode">
1039
- <xsl:text>v.</xsl:text>
1040
- </xsl:if>
1041
- </xsl:variable>
1042
- <xsl:variable name="de_env">
1043
- <xsl:value-of select="$prefix"/>
1044
- <xsl:text>_mobjs</xsl:text>
1045
- </xsl:variable>
1046
- <xsl:value-of select="$de_env"/>
1047
- <xsl:text>.evaluateExpression(</xsl:text>
1048
- <xsl:apply-templates select="formula/*" mode="evaluate">
1049
- <xsl:with-param name="setupMode" select="$setupMode"/>
1050
- </xsl:apply-templates>
1051
- <xsl:text>, "number", {</xsl:text>
1052
- <xsl:apply-templates select="variable" mode="evaluation-binding" >
1053
- <xsl:with-param name="setupMode" select="$setupMode" />
1054
- </xsl:apply-templates>
1055
- <xsl:text>})</xsl:text>
1056
- <xsl:if test="@reduce='yes'">
1057
- <xsl:text>.reduce().simplifyConstants()</xsl:text>
1058
- </xsl:if>
1059
- </xsl:template>
1060
-
1061
- <xsl:template match="de-random[@distribution='discrete']" mode="evaluate">
1062
- <xsl:text>v._mobjs.generateRandom("discrete", { min:</xsl:text>
1063
- <xsl:choose>
1064
- <xsl:when test="@min">
1065
- <xsl:value-of select="@min"/>
1066
- </xsl:when>
1067
- <xsl:otherwise>
1068
- <xsl:text>0</xsl:text>
1069
- </xsl:otherwise>
1070
- </xsl:choose>
1071
- <xsl:text>, max:</xsl:text>
1072
- <xsl:choose>
1073
- <xsl:when test="@max">
1074
- <xsl:value-of select="@max"/>
1075
- </xsl:when>
1076
- <xsl:otherwise>
1077
- <xsl:text>1</xsl:text>
1078
- </xsl:otherwise>
1079
- </xsl:choose>
1080
- <xsl:text>, by:</xsl:text>
1081
- <xsl:choose>
1082
- <xsl:when test="@by">
1083
- <xsl:value-of select="@by"/>
1084
- </xsl:when>
1085
- <xsl:otherwise>
1086
- <xsl:text>1</xsl:text>
1087
- </xsl:otherwise>
1088
- </xsl:choose>
1089
- <xsl:text>, nonzero:</xsl:text>
1090
- <xsl:choose>
1091
- <xsl:when test="@nonzero = 'yes'">
1092
- <xsl:text>true</xsl:text>
1093
- </xsl:when>
1094
- <xsl:otherwise>
1095
- <xsl:text>false</xsl:text>
1096
- </xsl:otherwise>
1097
- </xsl:choose>
1098
- <xsl:text>})</xsl:text>
1099
- </xsl:template>
1100
-
1101
- <!-- Objects defined as formulas. Several possible modes: -->
1102
- <!-- formula (literal), substitution (composition), -->
1103
- <!-- derivative and evaluate -->
1104
- <xsl:template match="de-expression[not(@mode) or @mode='formula']" mode="evaluate">
1105
- <xsl:param name="setupMode" />
1106
- <xsl:variable name="prefix">
1107
- <xsl:if test="$setupMode">
1108
- <xsl:text>v.</xsl:text>
1109
- </xsl:if>
1110
- </xsl:variable>
1111
- <xsl:variable name="de_env">
1112
- <xsl:value-of select="$prefix"/>
1113
- <xsl:text>_mobjs</xsl:text>
1114
- </xsl:variable>
1115
- <xsl:value-of select="$de_env"/>
1116
- <xsl:text>.parseExpression(</xsl:text>
1117
- <xsl:call-template name="quote-strip-string">
1118
- <xsl:with-param name="text" select="."/>
1119
- </xsl:call-template>
1120
- <xsl:text>, "formula")</xsl:text>
1121
- <xsl:if test="@reduce='yes'">
1122
- <xsl:text>.reduce().simplifyConstants()</xsl:text>
1123
- </xsl:if>
1124
- </xsl:template>
1125
-
1126
- <xsl:template match="de-expression[@mode='substitution']" mode="evaluate">
1127
- <xsl:param name="setupMode" />
1128
- <xsl:variable name="prefix">
1129
- <xsl:if test="$setupMode">
1130
- <xsl:text>v.</xsl:text>
1131
- </xsl:if>
1132
- </xsl:variable>
1133
- <xsl:variable name="de_env">
1134
- <xsl:value-of select="$prefix"/>
1135
- <xsl:text>_mobjs</xsl:text>
1136
- </xsl:variable>
1137
- <xsl:value-of select="$de_env"/>
1138
- <xsl:text>.composeExpression(</xsl:text>
1139
- <xsl:apply-templates select="formula/*" mode="evaluate">
1140
- <xsl:with-param name="setupMode" select="$setupMode"/>
1141
- </xsl:apply-templates>
1142
- <xsl:text>, {</xsl:text>
1143
- <xsl:apply-templates select="variable" mode="evaluation-binding" >
1144
- <xsl:with-param name="setupMode" select="$setupMode" />
1145
- </xsl:apply-templates>
1146
- <xsl:text>})</xsl:text>
1147
- <xsl:if test="@reduce='yes'">
1148
- <xsl:text>.reduce().simplifyConstants()</xsl:text>
1149
- </xsl:if>
1150
- </xsl:template>
1151
-
1152
- <xsl:template match="de-expression[@mode='derivative']" mode="evaluate">
1153
- <xsl:param name="setupMode" />
1154
- <xsl:variable name="prefix">
1155
- <xsl:if test="$setupMode">
1156
- <xsl:text>v.</xsl:text>
1157
- </xsl:if>
1158
- </xsl:variable>
1159
- <xsl:variable name="de_env">
1160
- <xsl:value-of select="$prefix"/>
1161
- <xsl:text>_mobjs</xsl:text>
1162
- </xsl:variable>
1163
- <xsl:apply-templates select="formula/*" mode="evaluate">
1164
- <xsl:with-param name="setupMode" select="$setupMode" />
1165
- </xsl:apply-templates>
1166
- <xsl:text>.derivative(</xsl:text>
1167
- <xsl:call-template name="quote-string">
1168
- <xsl:with-param name="text" select="variable/@name"/>
1169
- </xsl:call-template>
1170
- <xsl:text>)</xsl:text>
1171
- <xsl:if test="@reduce='yes'">
1172
- <xsl:text>.reduce().simplifyConstants()</xsl:text>
1173
- </xsl:if>
1174
- </xsl:template>
1175
-
1176
-
1177
- <!-- Used during composition or evaluation of a variable-->
1178
- <xsl:template match="variable" mode="evaluation-binding">
1179
- <xsl:param name="setupMode" />
1180
- <xsl:variable name="prefix">
1181
- <xsl:if test="$setupMode">
1182
- <xsl:text>v.</xsl:text>
1183
- </xsl:if>
1184
- </xsl:variable>
1185
- <xsl:if test="position() > 1">
1186
- <xsl:text>, </xsl:text>
1187
- </xsl:if>
1188
- <xsl:call-template name="quote-string">
1189
- <xsl:with-param name="text" select="@name" />
1190
- </xsl:call-template>
1191
- <xsl:text>: </xsl:text>
1192
- <xsl:choose>
1193
- <xsl:when test="eval or de-number or de-expression">
1194
- <xsl:apply-templates select="eval|de-number|de-expression" mode="evaluate">
1195
- <xsl:with-param name="setupMode" select="$setupMode" />
1196
- </xsl:apply-templates>
1197
- </xsl:when>
1198
- <xsl:otherwise>
1199
- <xsl:value-of select="." />
1200
- </xsl:otherwise>
1201
- </xsl:choose>
1202
- </xsl:template>
1203
-
1204
-
1205
- <!-- var elements in expressions (evaluate) are replaced by their name -->
1206
- <!-- During setup, we need to use the context of the `v` object -->
1207
- <xsl:template match="eval" mode="evaluate">
1208
- <xsl:param name="setupMode" />
1209
- <xsl:variable name="prefix">
1210
- <xsl:if test="$setupMode">
1211
- <xsl:text>v.</xsl:text>
1212
- </xsl:if>
1213
- </xsl:variable>
1214
- <xsl:value-of select="$prefix"/>
1215
- <xsl:value-of select="@obj"/>
1216
- </xsl:template>
1217
-
1218
- <!-- Nothing else is defined for evaluation during setup -->
1219
- <xsl:template match="/" mode="evaluate"/>
1220
-
1221
- <xsl:template match="feedback" mode="serialize-feedback">
1222
- <xsl:variable name="feedback-rtf">
1223
- <xsl:apply-templates select="*" />
1224
- </xsl:variable>
1225
- <!-- serialize HTML as text, then escape as JSON -->
1226
- <xsl:call-template name="escape-json-string">
1227
- <xsl:with-param name="text">
1228
- <xsl:apply-templates select="exsl:node-set($feedback-rtf)" mode="xml-to-string"/>
1229
- </xsl:with-param>
1230
- </xsl:call-template>
1231
- </xsl:template>
1232
-
1233
- </xsl:stylesheet>
1
+ <?xml version='1.0'?> <!-- As XML file -->
2
+
3
+ <!DOCTYPE xsl:stylesheet>
4
+
5
+ <xsl:stylesheet
6
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
7
+ xmlns:xml="http://www.w3.org/XML/1998/namespace"
8
+ xmlns:exsl="http://exslt.org/common"
9
+ xmlns:date="http://exslt.org/dates-and-times"
10
+ xmlns:str="http://exslt.org/strings"
11
+ extension-element-prefixes="exsl date str"
12
+ >
13
+
14
+ <xsl:variable name="b-include-postContent" select="true()"/>
15
+
16
+ <!-- ==================================================== -->
17
+ <!-- Actual rules for substution when generating HTML -->
18
+ <!-- ==================================================== -->
19
+
20
+ <!-- Convert fillin tag to an input element on the page -->
21
+ <xsl:template match="exercise[@exercise-interactive='fillin']//fillin
22
+ | project[@exercise-interactive='fillin']//fillin
23
+ | activity[@exercise-interactive='fillin']//fillin
24
+ | exploration[@exercise-interactive='fillin']//fillin
25
+ | investigation[@exercise-interactive='fillin']//fillin
26
+ | task[@exercise-interactive='fillin']//fillin">
27
+ <xsl:param name="b-human-readable" />
28
+ <xsl:variable name="parent-id">
29
+ <xsl:apply-templates select="ancestor::exercise" mode="html-id" />
30
+ </xsl:variable>
31
+ <xsl:element name="input">
32
+ <xsl:attribute name="type">
33
+ <xsl:choose>
34
+ <xsl:when test="@mode = 'number'">
35
+ <xsl:text>number</xsl:text>
36
+ </xsl:when>
37
+ <xsl:otherwise>
38
+ <xsl:text>text</xsl:text>
39
+ </xsl:otherwise>
40
+ </xsl:choose>
41
+ </xsl:attribute>
42
+ <xsl:attribute name="id">
43
+ <xsl:value-of select="$parent-id"/>
44
+ <xsl:text>-</xsl:text>
45
+ <xsl:apply-templates select="." mode="blank-name"/>
46
+ </xsl:attribute>
47
+ <xsl:if test="@name">
48
+ <xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
49
+ </xsl:if>
50
+ <xsl:if test="@width">
51
+ <xsl:attribute name="size">
52
+ <xsl:value-of select="@width"/>
53
+ </xsl:attribute>
54
+ </xsl:if>
55
+ </xsl:element>
56
+ </xsl:template>
57
+
58
+ <!-- ========================================================= -->
59
+ <!-- The Runestone element is based on JSON scripts describing -->
60
+ <!-- the exercise, how to set it up, and how to evaluate it. -->
61
+ <!-- The HTML will contain this JSON and Runestone extracts it -->
62
+ <!-- and inserts it into the HTML page via JS. -->
63
+ <!-- ========================================================= -->
64
+ <xsl:template match="exercise[@exercise-interactive='fillin']
65
+ | project[@exercise-interactive='fillin']
66
+ | activity[@exercise-interactive='fillin']
67
+ | exploration[@exercise-interactive='fillin']
68
+ | investigation[@exercise-interactive='fillin']
69
+ | task[@exercise-interactive='fillin']" mode="runestone-to-interactive">
70
+ <xsl:variable name="b-is-dynamic" select="boolean(./setup)"/>
71
+ <div class="ptx-runestone-container">
72
+ <div class="runestone">
73
+ <div data-component="fillintheblank" class="fillintheblank" style="visibility: hidden;">
74
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
75
+ <script type="application/json">
76
+ <xsl:text>{&#xa;</xsl:text>
77
+ <!-- A seed is provided to generate consistent static content -->
78
+ <xsl:if test="setup and $b-dynamics-static-seed">
79
+ <xsl:text>"static_seed": "</xsl:text>
80
+ <xsl:choose>
81
+ <xsl:when test="setup/@seed">
82
+ <xsl:value-of select="setup/@seed"/>
83
+ </xsl:when>
84
+ <xsl:otherwise>
85
+ <!-- Report if a seed is not provided-->
86
+ <xsl:message>PTX:WARNING: Dynamic exercise "<xsl:value-of select="@visible-id"/>" is missing setup @seed for static content generation.</xsl:message>
87
+ <xsl:text>1234</xsl:text>
88
+ </xsl:otherwise>
89
+ </xsl:choose>
90
+ <xsl:text>",&#xa;</xsl:text>
91
+ </xsl:if>
92
+ <!-- The formatted HTML presentation of the problem, -->
93
+ <!-- with escape codes for dynamic content, all of -->
94
+ <!-- which is serialized and escaped to a string. -->
95
+ <xsl:text>"problemHtml": </xsl:text>
96
+ <xsl:call-template name="escape-quote-xml">
97
+ <xsl:with-param name="xml_content">
98
+ <xsl:apply-templates select="statement/*" />
99
+ <xsl:if test="$b-dynamics-static-seed">
100
+ <div>
101
+ <xsl:attribute name="id">
102
+ <xsl:apply-templates select="." mode="html-id"/>
103
+ <xsl:text>-substitutions</xsl:text>
104
+ </xsl:attribute>
105
+ <xsl:apply-templates select="(statement|solution)//eval[@obj]|evaluation//feedback//eval[@obj]|statement//fillin[@ansobj]" mode="track-evaluation" />
106
+ </div>
107
+ </xsl:if>
108
+ </xsl:with-param>
109
+ </xsl:call-template>
110
+ <!-- The formatted HTML presentation of the solution, -->
111
+ <!-- similar to statement but no fillins -->
112
+ <xsl:text>,&#xa;"solutionHtml": </xsl:text>
113
+ <xsl:call-template name="escape-quote-xml">
114
+ <xsl:with-param name="xml_content">
115
+ <xsl:apply-templates select="solution/*" />
116
+ </xsl:with-param>
117
+ </xsl:call-template>
118
+ <!-- Add packages that need to be loaded as javascript -->
119
+ <xsl:if test="$b-is-dynamic">
120
+ <xsl:text>,&#xa;"dyn_imports": [</xsl:text>
121
+ <xsl:text>"BTM"</xsl:text>
122
+ <!-- Future: add additional packages here -->
123
+ <xsl:text>]</xsl:text>
124
+ </xsl:if>
125
+ <!-- Names assigned to the blanks. -->
126
+ <!-- Empty if none named. -->
127
+ <xsl:text>,&#xa;"blankNames": {</xsl:text>
128
+ <xsl:apply-templates select="statement//fillin" mode="declare-blanks" />
129
+ <xsl:text>}</xsl:text>
130
+ <xsl:if test="$b-is-dynamic">
131
+ <!-- The actual setup code is javascript enclosed in quotes. -->
132
+ <!-- The declaration creates the objects that are needed. -->
133
+ <!-- The script is included as an escaped string -->
134
+ <xsl:text>,&#xa;"dyn_vars": </xsl:text>
135
+ <xsl:call-template name="dynamic-setup" />
136
+ </xsl:if>
137
+ <!-- An array of tests and feedback for answer evaluation -->
138
+ <!-- Each blank has a corresponding array of test/feedback -->
139
+ <!-- response. The test is Javascript (stringified) that -->
140
+ <!-- returns a boolean response. The first test is for -->
141
+ <!-- correctness. The last response is default. -->
142
+ <xsl:text>,&#xa;"feedbackArray": [</xsl:text>
143
+ <!-- In case all answers are based on one test -->
144
+ <xsl:variable name="multiAns">
145
+ <xsl:apply-templates select="evaluation" mode="get-multianswer-check"/>
146
+ </xsl:variable>
147
+ <!-- Generate test/feedback pair for each fillin -->
148
+ <xsl:apply-templates select="statement//fillin" mode="dynamic-feedback">
149
+ <xsl:with-param name="multiAns" select="$multiAns" />
150
+ </xsl:apply-templates>
151
+ <xsl:text>]</xsl:text>
152
+ <xsl:text>&#xa;}</xsl:text>
153
+ </script>
154
+ </div>
155
+ <xsl:text>&#xa;</xsl:text>
156
+ </div>
157
+ </div>
158
+ </xsl:template>
159
+
160
+ <!-- Fillins can be provided a name or use a default rule -->
161
+ <xsl:template match="fillin" mode="blank-name">
162
+ <xsl:choose>
163
+ <xsl:when test="@name">
164
+ <xsl:value-of select="@name"/>
165
+ </xsl:when>
166
+ <xsl:otherwise>
167
+ <xsl:text>blank</xsl:text>
168
+ <xsl:value-of select="position()" />
169
+ </xsl:otherwise>
170
+ </xsl:choose>
171
+ </xsl:template>
172
+
173
+ <!-- Creating a list of blank names. -->
174
+ <xsl:template match="fillin" mode="declare-blanks">
175
+ <xsl:variable name="blankNum">
176
+ <xsl:value-of select="position()" />
177
+ </xsl:variable>
178
+ <xsl:if test="$blankNum>1">
179
+ <xsl:text>, </xsl:text>
180
+ </xsl:if>
181
+ <xsl:call-template name="quote-string">
182
+ <xsl:with-param name="text">
183
+ <xsl:apply-templates select="." mode="blank-name"/>
184
+ </xsl:with-param>
185
+ </xsl:call-template>
186
+ <xsl:text>: </xsl:text>
187
+ <xsl:value-of select="$blankNum - 1"/>
188
+ </xsl:template>
189
+
190
+ <!-- #eval in a dynamic exercise (has setup) is to evaluate -->
191
+ <!-- an expression that has been previously generated. If -->
192
+ <!-- in math-mode, we want to see if it is an object that -->
193
+ <!-- knows how to formulate a LaTeX representation -->
194
+ <!-- The `toTeX` javascript function is defined in BTM.js -->
195
+ <!-- which is loaded by Runestone. -->
196
+ <xsl:template match="eval[@obj]">
197
+ <xsl:text>[%= </xsl:text>
198
+ <xsl:choose>
199
+ <xsl:when test="ancestor::m|ancestor::mrow">
200
+ <xsl:text>toTeX(</xsl:text>
201
+ <xsl:value-of select="@obj"/>
202
+ <xsl:text>)</xsl:text>
203
+ </xsl:when>
204
+ <xsl:otherwise>
205
+ <xsl:value-of select="@obj"/>
206
+ </xsl:otherwise>
207
+ </xsl:choose>
208
+ <xsl:text> %]</xsl:text>
209
+ </xsl:template>
210
+
211
+
212
+ <!-- Part of static output content extraction process, -->
213
+ <!-- this creates elements that will be pulled from the -->
214
+ <!-- stand-alone page into a generated XML document that -->
215
+ <!-- gives the actual substitution in static mode. -->
216
+ <xsl:template match="eval[@obj]" mode="track-evaluation">
217
+ <eval-subst>
218
+ <xsl:attribute name="obj">
219
+ <xsl:value-of select="@obj"/>
220
+ </xsl:attribute>
221
+ <xsl:apply-templates select="."/>
222
+ </eval-subst>
223
+ </xsl:template>
224
+
225
+ <xsl:template match="fillin[@ansobj]" mode="track-evaluation">
226
+ <eval-subst>
227
+ <xsl:attribute name="obj">
228
+ <xsl:value-of select="@ansobj"/>
229
+ </xsl:attribute>
230
+ <xsl:variable name="temp-eval">
231
+ <xsl:choose>
232
+ <xsl:when test="@mode='math' or @mode='number'">
233
+ <m><eval>
234
+ <xsl:attribute name="obj">
235
+ <xsl:value-of select="@ansobj"/>
236
+ </xsl:attribute>
237
+ </eval></m>
238
+ </xsl:when>
239
+ <xsl:otherwise>
240
+ <eval>
241
+ <xsl:attribute name="obj">
242
+ <xsl:value-of select="@ansobj"/>
243
+ </xsl:attribute>
244
+ </eval>
245
+ </xsl:otherwise>
246
+ </xsl:choose>
247
+ </xsl:variable>
248
+ <xsl:apply-templates select="exsl:node-set($temp-eval)//eval"/>
249
+ </eval-subst>
250
+ </xsl:template>
251
+
252
+
253
+ <!-- Create the dynamic aspect of the problem. -->
254
+ <!-- Define all of the mathematical elements as well as -->
255
+ <!-- objects (e.g. graphs) that might depend on them -->
256
+ <!-- A script in setup/setupScript is executed after -->
257
+ <!-- object-based setup concludes. -->
258
+ <!-- A script in setup/postRenderScript is executed -->
259
+ <!-- after the dynamic creation is complete. -->
260
+ <xsl:template name="dynamic-setup">
261
+ <xsl:variable name="js_code">
262
+ <!-- Initialize the evaluation environment -->
263
+ <xsl:call-template name="setup-evaluation-environment"/>
264
+ <!-- Any direct JS for environment setup -->
265
+ <xsl:if test="setup/setupScript">
266
+ <xsl:value-of select="setup/setupScript"/>
267
+ </xsl:if>
268
+ <!-- Prepare evaluation and feedback -->
269
+ <xsl:call-template name="setup-fillin-parsers"/>
270
+ <!-- Create the call-back for post-render tasks -->
271
+ <xsl:if test="$b-include-postContent">
272
+ <xsl:call-template name="setup-postContent"/>
273
+ </xsl:if>
274
+ </xsl:variable>
275
+ <xsl:call-template name="escape-quote-string">
276
+ <xsl:with-param name="text" select="$js_code"/>
277
+ </xsl:call-template>
278
+ </xsl:template>
279
+
280
+ <!-- Parse any settings for the math environment for the problem -->
281
+ <xsl:template name="setup-evaluation-environment">
282
+ <xsl:text>v._mobjs = new BTM({</xsl:text>
283
+ <!-- Setup seeded random number generator -->
284
+ <xsl:text>'rand': rand</xsl:text>
285
+ <!-- FUTURE: Pull additional settings from an environment element -->
286
+ <xsl:apply-templates select="de-environment" mode="runestone-setup"/>
287
+ <xsl:text>});&#xa;</xsl:text>
288
+ <xsl:text>var RNG = v._mobjs.menv.rng;&#xa;</xsl:text>
289
+ <!-- Generate all of the XML-declared math objects -->
290
+ <xsl:apply-templates select="setup/de-object" mode="runestone-setup"/>
291
+ </xsl:template>
292
+
293
+ <!-- Environment Setup: Define the mathematical objects -->
294
+ <xsl:template match="de-object" mode="runestone-setup">
295
+ <xsl:text>v.</xsl:text><xsl:value-of select="@name"/>
296
+ <xsl:text> = v._mobjs.addMathObject(</xsl:text>
297
+ <xsl:call-template name="quote-string">
298
+ <xsl:with-param name="text" select="@name"/>
299
+ </xsl:call-template>
300
+ <xsl:text>, </xsl:text>
301
+ <!-- Get the context of the object here -->
302
+ <xsl:call-template name="quote-string">
303
+ <xsl:with-param name="text">
304
+ <xsl:choose>
305
+ <!-- Usually use value of @context but in case strings change. -->
306
+ <xsl:when test="@context='interval' or @context='set'">
307
+ <xsl:text>set</xsl:text>
308
+ </xsl:when>
309
+ <xsl:otherwise>
310
+ <xsl:value-of select="@context"/>
311
+ </xsl:otherwise>
312
+ </xsl:choose>
313
+ </xsl:with-param>
314
+ </xsl:call-template>
315
+ <xsl:text>, </xsl:text>
316
+ <xsl:apply-templates select="*" mode="evaluate">
317
+ <xsl:with-param name="setupMode"><xsl:text>1</xsl:text></xsl:with-param>
318
+ </xsl:apply-templates>
319
+ <xsl:text>);&#xa;</xsl:text>
320
+ </xsl:template>
321
+
322
+ <!-- Runestone requires declaring an array of answer-types for -->
323
+ <!-- validating and parsing what the user enters as a response. -->
324
+ <xsl:template name="setup-fillin-parsers">
325
+ <xsl:text>v.types = [</xsl:text>
326
+ <xsl:apply-templates select="statement//fillin" mode="setup-parsers" />
327
+ <xsl:text>];&#xa;</xsl:text>
328
+ </xsl:template>
329
+
330
+ <!-- Create a call-back for what occurs post-rendering. -->
331
+ <!-- This includes automated tasks as well as any script -->
332
+ <!-- commands in setup/postRenderScript -->
333
+ <xsl:template name="setup-postContent">
334
+ <xsl:text>v.afterContentRender = function() {&#xa;</xsl:text>
335
+ <xsl:if test="setup/postRenderScript">
336
+ <xsl:value-of select="setup/postRenderScript"/>
337
+ </xsl:if>
338
+ <xsl:text>};&#xa;</xsl:text>
339
+ </xsl:template>
340
+
341
+
342
+ <!-- Each fillin defines a blank. Establish the list of parsers. -->
343
+ <!-- During setup and called while creating dyn_vars. -->
344
+ <xsl:template match="fillin" mode="setup-parsers">
345
+ <xsl:if test="position() > 1">
346
+ <xsl:text>, </xsl:text>
347
+ </xsl:if>
348
+ <xsl:choose>
349
+ <xsl:when test="@mode='math-formula'">
350
+ <xsl:text>v._mobjs.getParser()</xsl:text>
351
+ </xsl:when>
352
+ <xsl:when test="@mode='number'">
353
+ <xsl:text>Number</xsl:text>
354
+ </xsl:when>
355
+ <xsl:otherwise>
356
+ <xsl:text>function(val){ return val; }</xsl:text>
357
+ </xsl:otherwise>
358
+ </xsl:choose>
359
+ </xsl:template>
360
+
361
+
362
+ <!-- ========================================================== -->
363
+ <!-- Evaluation and Feedback -->
364
+ <!-- ========================================================== -->
365
+
366
+ <!-- Default localized feedback strings. -->
367
+ <xsl:template match="*" mode="get-default-feedback">
368
+ <xsl:param name="b-correct" select="'no'"/>
369
+ <xsl:choose>
370
+ <xsl:when test="$b-correct='yes'">
371
+ <xsl:apply-templates select="." mode="type-name">
372
+ <xsl:with-param name="string-id" select="'correct'"/>
373
+ </xsl:apply-templates>
374
+ <xsl:text>!</xsl:text>
375
+ </xsl:when>
376
+ <xsl:otherwise>
377
+ <xsl:apply-templates select="." mode="type-name">
378
+ <xsl:with-param name="string-id" select="'incorrect'"/>
379
+ </xsl:apply-templates>
380
+ <xsl:text>.</xsl:text>
381
+ </xsl:otherwise>
382
+ </xsl:choose>
383
+ </xsl:template>
384
+
385
+ <!-- Deal with possibility of global checker for all blanks -->
386
+ <xsl:template match="evaluation" mode="get-multianswer-check">
387
+ <xsl:variable name="responseTree" select="../statement//fillin" />
388
+ <xsl:if test="count($responseTree) > 1 and evaluate[@all='yes']/test">
389
+ <xsl:apply-templates select="evaluate[@all='yes']/test" mode="create-test-feedback">
390
+ <xsl:with-param name="b-correct" select="'yes'"/>
391
+ <xsl:with-param name="fillin" select="$responseTree"/>
392
+ </xsl:apply-templates>
393
+ </xsl:if>
394
+ </xsl:template>
395
+
396
+ <!-- Return the index of an evaluate -->
397
+ <xsl:template match="evaluation/evaluate" mode="get-index">
398
+ <xsl:value-of select="position()"/>
399
+ </xsl:template>
400
+
401
+ <!-- Build feedback based on the #evaluate element. -->
402
+ <xsl:template match="evaluate" mode="dynamic-feedback">
403
+ <xsl:param name="multiAns"/>
404
+ <xsl:param name="match-fillin"/>
405
+
406
+ <!-- Defend against name mismatches -->
407
+ <xsl:if test="@name and $match-fillin/@name and not(@name = $match-fillin/@name)">
408
+ <xsl:message>PTX:ERROR: Dynamic content missing label "<xsl:value-of select="@visible-id"/>"</xsl:message>
409
+ </xsl:if>
410
+
411
+ <!-- First check is for correctness. -->
412
+ <xsl:text>[</xsl:text>
413
+ <xsl:choose>
414
+ <xsl:when test="string-length($multiAns) > 0">
415
+ <xsl:value-of select="$multiAns"/>
416
+ </xsl:when>
417
+ <xsl:when test="test[@correct='yes']">
418
+ <xsl:apply-templates select="test[@correct='yes']" mode="create-test-feedback">
419
+ <xsl:with-param name="fillin" select="$match-fillin" />
420
+ <xsl:with-param name="b-correct" select="'yes'" />
421
+ </xsl:apply-templates>
422
+ </xsl:when>
423
+ <!-- If no explicit test is provided, use given answer. -->
424
+ <xsl:otherwise>
425
+ <xsl:text>{</xsl:text>
426
+ <xsl:choose>
427
+ <xsl:when test="$match-fillin/@answer">
428
+ <xsl:choose>
429
+ <xsl:when test="$match-fillin/@mode='number'">
430
+ <xsl:text>"number": [</xsl:text>
431
+ <xsl:value-of select="$match-fillin/@answer"/>
432
+ <xsl:text>,</xsl:text>
433
+ <xsl:value-of select="$match-fillin/@answer"/>
434
+ <xsl:text>]</xsl:text>
435
+ </xsl:when>
436
+ <xsl:when test="$match-fillin/@mode='string'">
437
+ <xsl:text>"regex": "</xsl:text>
438
+ <xsl:value-of select="$match-fillin/@answer"/>
439
+ <xsl:text>"</xsl:text>
440
+ </xsl:when>
441
+ <xsl:otherwise>
442
+ <xsl:message>PTX:ERROR: fillin has an @answer with invalid or missing mode (valid: number or string) in "<xsl:value-of select="@visible-id"/>. If dynamic, you should use @ansobj.".</xsl:message>
443
+ </xsl:otherwise>
444
+ </xsl:choose>
445
+ </xsl:when>
446
+ <xsl:when test="$match-fillin/@ansobj">
447
+ <xsl:choose>
448
+ <xsl:when test="$match-fillin/@mode='number'">
449
+ <xsl:text>function() {&#xa;</xsl:text>
450
+ <xsl:text> return (Math.abs(</xsl:text>
451
+ <xsl:value-of select="$match-fillin/@ansobj"/>
452
+ <xsl:text>- ans) &lt; 1e-10);&#xa;}</xsl:text>
453
+ </xsl:when>
454
+ <xsl:when test="$match-fillin/@mode='string'">
455
+ <xsl:text>function() {&#xa;</xsl:text>
456
+ <xsl:text> return (</xsl:text>
457
+ <xsl:value-of select="$match-fillin/@ansobj"/>
458
+ <xsl:text>== ans);&#xa;}</xsl:text>
459
+ </xsl:when>
460
+ <xsl:when test="$match-fillin/@mode='math'">
461
+ <xsl:text>function() {&#xa;</xsl:text>
462
+ <xsl:text> return _mobjs.compareExpressions(</xsl:text>
463
+ <xsl:value-of select="$match-fillin/@ansobj"/>
464
+ <xsl:text>, ans</xsl:text>
465
+ <xsl:text>);&#xa;}</xsl:text>
466
+ </xsl:when>
467
+ <xsl:otherwise>
468
+ <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>
469
+ </xsl:otherwise>
470
+ </xsl:choose>
471
+ </xsl:when>
472
+ <xsl:otherwise>
473
+ <xsl:message>PTX:ERROR: No method provided to identify a correct answer for #fillin in "<xsl:value-of select="@visible-id"/>".</xsl:message>
474
+ </xsl:otherwise>
475
+ </xsl:choose>
476
+ <xsl:text>, "feedback": "</xsl:text>
477
+ <xsl:apply-templates select="." mode="get-default-feedback">
478
+ <xsl:with-param name="b-correct" select="'yes'"/>
479
+ </xsl:apply-templates>
480
+ <xsl:text>"}</xsl:text>
481
+ </xsl:otherwise>
482
+ </xsl:choose>
483
+
484
+ <!-- Now add additional checks for feedback. -->
485
+ <xsl:for-each select="test[not(@correct='yes')]">
486
+ <xsl:text>, </xsl:text>
487
+ <xsl:apply-templates select="." mode="create-test-feedback">
488
+ <xsl:with-param name="fillin" select="$match-fillin"/>
489
+ </xsl:apply-templates>
490
+ </xsl:for-each>
491
+ <!-- Default feedback for the blank. Always evaluates true. -->
492
+ <xsl:text>, {"feedback": "</xsl:text>
493
+ <xsl:apply-templates select="." mode="get-default-feedback">
494
+ <xsl:with-param name="b-correct" select="'no'"/>
495
+ </xsl:apply-templates>
496
+ <xsl:text>"}]</xsl:text>
497
+ </xsl:template>
498
+
499
+ <!-- Template for answer checking. Actual work done by specialized templates. -->
500
+ <xsl:template match="fillin" mode="dynamic-feedback">
501
+ <xsl:param name="multiAns"/>
502
+ <xsl:variable name="curFillIn" select="."/>
503
+ <xsl:variable name="fillinName">
504
+ <xsl:apply-templates select="." mode="blank-name"/>
505
+ </xsl:variable>
506
+ <xsl:variable name="blankNum">
507
+ <xsl:value-of select="position()" />
508
+ </xsl:variable>
509
+
510
+ <!-- we need to know which #evaluate matches the #fillin -->
511
+ <xsl:variable name="evaluate-index">
512
+ <xsl:choose>
513
+ <xsl:when test="ancestor::statement/../evaluation/evaluate[@name = $fillinName]">
514
+ <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[@name = $fillinName]" mode="get-index"/>
515
+ </xsl:when>
516
+ <xsl:when test="ancestor::statement/../evaluation/evaluate[position() = $blankNum]">
517
+ <xsl:value-of select="$blankNum"/>
518
+ </xsl:when>
519
+ <!-- No check matches: Make blank default. -->
520
+ <xsl:otherwise>
521
+ <xsl:number value="-1"/>
522
+ </xsl:otherwise>
523
+ </xsl:choose>
524
+ </xsl:variable>
525
+
526
+ <xsl:if test="$blankNum > 1">
527
+ <xsl:text>, </xsl:text>
528
+ </xsl:if>
529
+ <xsl:choose>
530
+ <!-- First look for a matching name. -->
531
+ <xsl:when test="ancestor::statement/../evaluation/evaluate[@name = $fillinName]">
532
+ <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[@name = $fillinName]" mode="dynamic-feedback">
533
+ <xsl:with-param name="multiAns" select="$multiAns"/>
534
+ <xsl:with-param name="match-fillin" select="."/>
535
+ </xsl:apply-templates>
536
+ </xsl:when>
537
+ <!-- Otherwise match on order. -->
538
+ <xsl:when test="ancestor::statement/../evaluation/evaluate[position() = $blankNum]">
539
+ <xsl:apply-templates select="ancestor::statement/../evaluation/evaluate[position() = $blankNum]" mode="dynamic-feedback">
540
+ <xsl:with-param name="multiAns" select="$multiAns"/>
541
+ <xsl:with-param name="match-fillin" select="."/>
542
+ </xsl:apply-templates>
543
+ </xsl:when>
544
+ <!-- No check matches: Make blank default. -->
545
+ <xsl:otherwise>
546
+ <xsl:message>PTX:ERROR: No matching #evaluate for #fillin in "<xsl:value-of select="@visible-id"/>".</xsl:message>
547
+ </xsl:otherwise>
548
+ </xsl:choose>
549
+ </xsl:template>
550
+
551
+ <xsl:template match="test" mode="create-test-feedback">
552
+ <xsl:param name="fillin"/>
553
+ <xsl:param name="b-correct" select="'no'"/>
554
+ <xsl:variable name="feedback-rtf">
555
+ <xsl:apply-templates select="feedback"/>
556
+ </xsl:variable>
557
+ <xsl:text>{</xsl:text>
558
+ <xsl:apply-templates select="." mode="create-test">
559
+ <xsl:with-param name="fillin" select="$fillin" />
560
+ </xsl:apply-templates>
561
+ <xsl:text>, "feedback": "</xsl:text>
562
+ <xsl:choose>
563
+ <xsl:when test="feedback">
564
+ <!-- serialize HTML as text, then escape as JSON -->
565
+ <xsl:call-template name="escape-json-string">
566
+ <xsl:with-param name="text">
567
+ <xsl:apply-templates select="exsl:node-set($feedback-rtf)" mode="xml-to-string"/>
568
+ </xsl:with-param>
569
+ </xsl:call-template>
570
+ </xsl:when>
571
+ <xsl:otherwise>
572
+ <xsl:apply-templates select="." mode="get-default-feedback">
573
+ <xsl:with-param name="b-correct" select="$b-correct"/>
574
+ </xsl:apply-templates>
575
+ </xsl:otherwise>
576
+ </xsl:choose>
577
+ <xsl:text>"}</xsl:text>
578
+ </xsl:template>
579
+
580
+ <!-- Template for simple answer checkers: no interaction between different fillins. -->
581
+ <!-- Add a post-filter to deal with additional feedback, similar to AnswerHints but allowing more complex logic. -->
582
+ <!-- JSON dictionary for numerical condition -->
583
+ <xsl:template match="test[numcmp]" mode="create-test">
584
+ <xsl:param name="fillin"/>
585
+ <xsl:choose>
586
+ <xsl:when test="(numcmp/@use-answer='yes' and $fillin/@ansobj) or numcmp/@object">
587
+ <xsl:apply-templates select="." mode="create-test-ansobj">
588
+ <xsl:with-param name="fillin" select="$fillin"/>
589
+ </xsl:apply-templates>
590
+ </xsl:when>
591
+ <xsl:otherwise>
592
+ <xsl:variable name="answer">
593
+ <xsl:choose>
594
+ <xsl:when test="numcmp/@use-answer='yes' and $fillin/@answer">
595
+ <xsl:value-of select="$fillin/@answer"/>
596
+ </xsl:when>
597
+ <xsl:otherwise>
598
+ <xsl:value-of select="numcmp/@value"/>
599
+ </xsl:otherwise>
600
+ </xsl:choose>
601
+ </xsl:variable>
602
+ <xsl:variable name="tolerance">
603
+ <xsl:choose>
604
+ <xsl:when test="numcmp/@use-answer='yes' and $fillin/@tolerance">
605
+ <xsl:value-of select="$fillin/@tolerance"/>
606
+ </xsl:when>
607
+ <xsl:when test="numcmp/@tolerance">
608
+ <xsl:value-of select="numcmp/@tolerance"/>
609
+ </xsl:when>
610
+ <xsl:otherwise>
611
+ <xsl:text>0</xsl:text>
612
+ </xsl:otherwise>
613
+ </xsl:choose>
614
+ </xsl:variable>
615
+ <xsl:variable name="min-val">
616
+ <xsl:choose>
617
+ <xsl:when test="numcmp/@min">
618
+ <xsl:value-of select="numcmp/@min"/>
619
+ </xsl:when>
620
+ <xsl:otherwise>
621
+ <xsl:value-of select="$answer - $tolerance"/>
622
+ </xsl:otherwise>
623
+ </xsl:choose>
624
+ </xsl:variable>
625
+ <xsl:variable name="max-val">
626
+ <xsl:choose>
627
+ <xsl:when test="numcmp/@max">
628
+ <xsl:value-of select="numcmp/@max"/>
629
+ </xsl:when>
630
+ <xsl:otherwise>
631
+ <xsl:value-of select="$answer + $tolerance"/>
632
+ </xsl:otherwise>
633
+ </xsl:choose>
634
+ </xsl:variable>
635
+ <xsl:text>"number": [</xsl:text>
636
+ <xsl:value-of select="$min-val"/>
637
+ <xsl:text>,</xsl:text>
638
+ <xsl:value-of select="$max-val"/>
639
+ <xsl:text>]</xsl:text>
640
+ </xsl:otherwise>
641
+ </xsl:choose>
642
+ </xsl:template>
643
+
644
+ <!-- When the comparison is with an object, we rely on function comparisons. -->
645
+ <xsl:template match="test[numcmp]" mode="create-test-ansobj">
646
+ <xsl:param name="fillin"/>
647
+ <xsl:variable name="ansObject">
648
+ <xsl:choose>
649
+ <xsl:when test="numcmp/@use-answer='yes' and $fillin/@ansobj">
650
+ <xsl:value-of select="$fillin/@ansobj"/>
651
+ </xsl:when>
652
+ <xsl:otherwise>
653
+ <xsl:value-of select="numcmp/@object"/>
654
+ </xsl:otherwise>
655
+ </xsl:choose>
656
+ </xsl:variable>
657
+ <xsl:variable name="tolerance">
658
+ <xsl:choose>
659
+ <xsl:when test="numcmp/@use-answer='yes' and $fillin/@tolerance">
660
+ <xsl:value-of select="$fillin/@tolerance"/>
661
+ </xsl:when>
662
+ <xsl:when test="numcmp/@tolerance">
663
+ <xsl:value-of select="numcmp/@tolerance"/>
664
+ </xsl:when>
665
+ <xsl:otherwise>
666
+ <xsl:text>0</xsl:text>
667
+ </xsl:otherwise>
668
+ </xsl:choose>
669
+ </xsl:variable>
670
+ <xsl:variable name="min-val">
671
+ <xsl:choose>
672
+ <xsl:when test="numcmp/@min">
673
+ <xsl:value-of select="numcmp/@min"/>
674
+ </xsl:when>
675
+ <xsl:otherwise>
676
+ <xsl:value-of select="$ansObject"/>
677
+ <xsl:text> - </xsl:text>
678
+ <xsl:value-of select="$tolerance"/>
679
+ </xsl:otherwise>
680
+ </xsl:choose>
681
+ </xsl:variable>
682
+ <xsl:variable name="max-val">
683
+ <xsl:choose>
684
+ <xsl:when test="numcmp/@max">
685
+ <xsl:value-of select="numcmp/@max"/>
686
+ </xsl:when>
687
+ <xsl:otherwise>
688
+ <xsl:value-of select="$ansObject"/>
689
+ <xsl:text> + </xsl:text>
690
+ <xsl:value-of select="$tolerance"/>
691
+ </xsl:otherwise>
692
+ </xsl:choose>
693
+ </xsl:variable>
694
+ <xsl:text>"solution_code": </xsl:text>
695
+ <xsl:variable name="js_code">
696
+ <xsl:text>function() { </xsl:text>
697
+ <xsl:text>return (ans &gt;= </xsl:text>
698
+ <xsl:value-of select="$min-val"/>
699
+ <xsl:text>) &amp;&amp; (ans &lt;= </xsl:text>
700
+ <xsl:value-of select="$max-val"/>
701
+ <xsl:text>); }()</xsl:text>
702
+ </xsl:variable>
703
+ <xsl:call-template name="escape-quote-string">
704
+ <xsl:with-param name="text" select="$js_code"/>
705
+ </xsl:call-template>
706
+ </xsl:template>
707
+
708
+ <!-- JSON dictionary for string condition -->
709
+ <xsl:template match="test[strcmp]" mode="create-test">
710
+ <xsl:param name="fillin"/>
711
+ <xsl:choose>
712
+ <xsl:when test="strcmp/@use-answer='yes' and $fillin/@ansobj or strcmp//eval">
713
+ <xsl:apply-templates mode="create-test-ansobj">
714
+ <xsl:with-param name="fillin" select="$fillin"/>
715
+ </xsl:apply-templates>
716
+ </xsl:when>
717
+ <xsl:otherwise>
718
+ <xsl:variable name="answer">
719
+ <xsl:choose>
720
+ <xsl:when test="strcmp/@use-answer='yes'">
721
+ <xsl:value-of select="$fillin/@answer"/>
722
+ </xsl:when>
723
+ <xsl:otherwise>
724
+ <xsl:value-of select="strcmp"/>
725
+ </xsl:otherwise>
726
+ </xsl:choose>
727
+ </xsl:variable>
728
+ <xsl:variable name="regexFlags">
729
+ <xsl:choose>
730
+ <xsl:when test="strcmp/@use-answer='yes' and $fillin/@case = 'insensitive'">
731
+ <xsl:text>i</xsl:text>
732
+ </xsl:when>
733
+ <xsl:when test="strcmp/@case = 'insensitive'">
734
+ <xsl:text>i</xsl:text>
735
+ </xsl:when>
736
+ <!-- Otherwise nothing -->
737
+ </xsl:choose>
738
+ </xsl:variable>
739
+ <!-- regex string match, drop -->
740
+ <!-- leading/trailing whitespace -->
741
+ <xsl:text>"regex": "</xsl:text>
742
+ <!-- JSON escapes necessary for regular expression -->
743
+ <xsl:call-template name="escape-json-string">
744
+ <xsl:with-param name="text">
745
+ <xsl:choose>
746
+ <xsl:when test="strcmp/@strip = 'no'">
747
+ <xsl:value-of select="$answer"/>
748
+ </xsl:when>
749
+ <xsl:otherwise>
750
+ <xsl:text>^\s*</xsl:text>
751
+ <xsl:value-of select="$answer"/>
752
+ <xsl:text>\s*$</xsl:text>
753
+ </xsl:otherwise>
754
+ </xsl:choose>
755
+ </xsl:with-param>
756
+ </xsl:call-template>
757
+ <xsl:text>"</xsl:text>
758
+ <!-- flag for case-sensitive match -->
759
+ <!-- default: 'sensitive' -->
760
+ <xsl:text>, "regexFlags": "</xsl:text>
761
+ <xsl:value-of select="$regexFlags"/>
762
+ <xsl:text>"</xsl:text>
763
+ </xsl:otherwise>
764
+ </xsl:choose>
765
+ </xsl:template>
766
+
767
+ <!-- When the comparison is with an object, we rely on function comparisons. -->
768
+ <xsl:template match="test[strcmp]" mode="create-test-ansobj">
769
+ <xsl:variable name="regex">
770
+ <xsl:choose>
771
+ <xsl:when test="strcmp/@use-answer='yes' and $fillin/@ansobj">
772
+ <xsl:text>`${</xsl:text>
773
+ <xsl:value-of select="$fillin/@ansobj"/>
774
+ <xsl:text>}`</xsl:text>
775
+ </xsl:when>
776
+ <xsl:otherwise>
777
+ <xsl:text>`</xsl:text>
778
+ <xsl:apply-templates select="strcmp/*" mode="strcmp-object-substitution"/>
779
+ <xsl:text>`</xsl:text>
780
+ </xsl:otherwise>
781
+ </xsl:choose>
782
+ </xsl:variable>
783
+ <xsl:variable name="regexFlags">
784
+ <xsl:choose>
785
+ <xsl:when test="strcmp/@use-answer='yes' and $fillin/@case = 'insensitive'">
786
+ <xsl:text>i</xsl:text>
787
+ </xsl:when>
788
+ <xsl:when test="strcmp/@case = 'insensitive'">
789
+ <xsl:text>i</xsl:text>
790
+ </xsl:when>
791
+ <!-- Otherwise Nothing -->
792
+ </xsl:choose>
793
+ </xsl:variable>
794
+ <!-- create a function to do the regex -->
795
+ <xsl:text>"solution_code": </xsl:text>
796
+ <xsl:variable name="js_code">
797
+ <xsl:text>function(){&#xa;</xsl:text>
798
+ <xsl:text> const re_str = </xsl:text><xsl:value-of select="$regex"/><xsl:text>;&#xa;</xsl:text>
799
+ <xsl:text> const re = new RegExp(re_str, "</xsl:text>
800
+ <xsl:value-of select="$regexFlags"/>
801
+ <xsl:text>");&#xa;</xsl:text>
802
+ <xsl:text> return re.test(ans);&#xa;</xsl:text>
803
+ <xsl:text>}()&#xa;</xsl:text>
804
+ </xsl:variable>
805
+ <xsl:call-template name="escape-quote-string">
806
+ <xsl:with-param name="text" select="$js_code"/>
807
+ </xsl:call-template>
808
+ </xsl:template>
809
+
810
+ <xsl:template match="eval" mode="strcmp-object-substitution">
811
+ <xsl:text>${</xsl:text>
812
+ <xsl:value-of select="."/>
813
+ <xsl:text>}</xsl:text>
814
+ </xsl:template>
815
+
816
+ <xsl:template match="*" mode="strcmp-object-substitution">
817
+ <xsl:copy-of select="."/>
818
+ </xsl:template>
819
+
820
+ <!-- Otherwise create a function that deals with the test. -->
821
+ <xsl:template match="test" mode="create-test">
822
+ <xsl:param name="fillin" />
823
+ <xsl:variable name="conditions" select="*[not(self::feedback)]"/>
824
+ <xsl:text>"solution_code": </xsl:text>
825
+ <xsl:variable name="js_code">
826
+ <xsl:text>function() {&#xa;</xsl:text>
827
+ <!-- Create a checker function. Initialize a stack of flag variables to track results. -->
828
+ <xsl:text> var testResults = new Array();&#xa;</xsl:text>
829
+ <xsl:choose>
830
+ <xsl:when test="count($conditions) = 1">
831
+ <xsl:call-template name="checker-simple">
832
+ <xsl:with-param name="fillin" select="$fillin" />
833
+ <xsl:with-param name="curTest" select="$conditions" />
834
+ <xsl:with-param name="level" select="0" />
835
+ </xsl:call-template>
836
+ </xsl:when>
837
+ <xsl:otherwise>
838
+ <xsl:call-template name="checker-layer">
839
+ <xsl:with-param name="fillin" select="$fillin" />
840
+ <xsl:with-param name="tests" select="$conditions" />
841
+ <xsl:with-param name="level" select="0" />
842
+ <xsl:with-param name="logic" select="'and'" /> <!-- All tests at first layer must be true -->
843
+ </xsl:call-template>
844
+ </xsl:otherwise>
845
+ </xsl:choose>
846
+ <xsl:text> return (testResults[0]);&#xa;</xsl:text>
847
+ <xsl:text>}()</xsl:text>
848
+ </xsl:variable>
849
+ <xsl:call-template name="escape-quote-string">
850
+ <xsl:with-param name="text" select="$js_code"/>
851
+ </xsl:call-template>
852
+ </xsl:template>
853
+
854
+
855
+ <!-- This template is called for a simple test (no compound logic). -->
856
+ <xsl:template name="checker-simple">
857
+ <xsl:param name="curTest" />
858
+ <xsl:param name="fillin" />
859
+ <xsl:param name="level" select="0" />
860
+ <xsl:choose>
861
+ <!-- Test might be coded directly in javascript -->
862
+ <xsl:when test="name($curTest) = 'jscmp'">
863
+ <xsl:text> testResults[</xsl:text>
864
+ <xsl:value-of select="$level" />
865
+ <xsl:text>] = </xsl:text>
866
+ <xsl:value-of select="$curTest"/>
867
+ </xsl:when>
868
+ <!-- Test might require logic -->
869
+ <xsl:when test="name($curTest)='logic'">
870
+ <xsl:call-template name="checker-layer">
871
+ <xsl:with-param name="fillin" select="$fillin" />
872
+ <xsl:with-param name="tests" select="$curTest/*" />
873
+ <xsl:with-param name="level" select="$level + 1" />
874
+ <xsl:with-param name="logic" select="$curTest/@op" /> <!-- Default: All tests at first layer must be true -->
875
+ </xsl:call-template>
876
+ <xsl:text> testResults[</xsl:text>
877
+ <xsl:value-of select="$level" />
878
+ <xsl:text>] = testResults[</xsl:text>
879
+ <xsl:value-of select="$level + 1" />
880
+ <xsl:text>];&#xa;</xsl:text>
881
+ </xsl:when>
882
+ <!-- Otherwise simple test -->
883
+ <xsl:otherwise>
884
+ <!-- A test can have an implied equal or an explicit equal -->
885
+ <!-- At root level, the test might also have a feedback. Skip that. -->
886
+ <xsl:text> testResults[</xsl:text>
887
+ <xsl:value-of select="$level" />
888
+ <xsl:text>] = </xsl:text>
889
+ <xsl:text>_mobjs.compareExpressions(</xsl:text>
890
+ <xsl:choose>
891
+ <!-- An equal element must have two expression children. -->
892
+ <xsl:when test="name($curTest)='mathcmp' and $curTest/@use-answer='yes'">
893
+ <xsl:choose>
894
+ <xsl:when test="not($fillin/@ansobj)">
895
+ <xsl:message>PTX:WARNING: Feedback for "<xsl:value-of select="@visible-id"/>" says to use given math answer, but @ansobj not defined. </xsl:message>
896
+ <xsl:text>UNDEFINED</xsl:text>
897
+ </xsl:when>
898
+ <xsl:otherwise>
899
+ <xsl:value-of select="$fillin/@ansobj"/>
900
+ </xsl:otherwise>
901
+ </xsl:choose>
902
+ <xsl:text>, ans</xsl:text>
903
+ </xsl:when>
904
+ <xsl:when test="name($curTest)='mathcmp' and $curTest/@obj">
905
+ <xsl:value-of select="$curTest/@obj"/>
906
+ <xsl:text>, ans</xsl:text>
907
+ </xsl:when>
908
+ <xsl:when test="name($curTest)='mathcmp' and count($curTest/*)=1">
909
+ <xsl:apply-templates select="$curTest/*[1]" mode="evaluate"/>
910
+ <xsl:text>, ans</xsl:text>
911
+ </xsl:when>
912
+ <xsl:when test="name($curTest)='mathcmp' and count($curTest/*)=2">
913
+ <xsl:apply-templates select="$curTest/*[1]" mode="evaluate"/>
914
+ <xsl:text>, </xsl:text>
915
+ <xsl:apply-templates select="$curTest/*[2]" mode="evaluate"/>
916
+ </xsl:when>
917
+ <!-- An implied equal compares the submitted answer to the given expression. -->
918
+ <xsl:otherwise> <!-- Must be expression: #eval or #de-expression -->
919
+ <xsl:apply-templates select="$curTest" mode="evaluate"/>
920
+ <xsl:text>, ans</xsl:text>
921
+ </xsl:otherwise>
922
+ </xsl:choose>
923
+ <xsl:text>)</xsl:text>
924
+ </xsl:otherwise>
925
+ </xsl:choose>
926
+ <xsl:text>;&#xa;</xsl:text>
927
+ </xsl:template>
928
+
929
+ <!-- A test can also be composite involving a combination of logical tests -->
930
+ <!-- This template works through one layer, recursively dealing with deeper layers as needed -->
931
+ <xsl:template name="checker-layer" >
932
+ <xsl:param name="fillin" />
933
+ <xsl:param name="tests" /> <!-- The layer of tests (subtree) -->
934
+ <xsl:param name="level" select="0" /> <!-- Level (or depth) of the layer -->
935
+ <xsl:param name="logic" select="'and'" /> <!-- and = all must be true, or = at least one, not = negation -->
936
+ <xsl:choose>
937
+ <xsl:when test="$logic = 'and'"> <!-- Treat logic like multipication. A single false (0) makes product zero -->
938
+ <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" /><xsl:text>] = 1;&#xa;</xsl:text>
939
+ </xsl:when>
940
+ <xsl:when test="$logic = 'or'"> <!-- Treat logic like addition. A single true makes sum positive -->
941
+ <xsl:text>testResults[</xsl:text><xsl:value-of select="$level" /><xsl:text>] = 0;&#xa;</xsl:text>
942
+ </xsl:when>
943
+ <xsl:when test="$logic = 'not'"/> <!-- Negation has no preparation -->
944
+ <xsl:otherwise>
945
+ <xsl:message>PTX:ERROR: Invalid logic operator in dynamic fillin for "<xsl:value-of select="@visible-id"/>"</xsl:message>
946
+ </xsl:otherwise>
947
+ </xsl:choose>
948
+ <xsl:for-each select="$tests"> <!-- Work through the layer of tests one at a time. -->
949
+ <xsl:choose>
950
+ <xsl:when test="name()='and'">
951
+ <xsl:text> testResults[</xsl:text>
952
+ <xsl:value-of select="$level+1" />
953
+ <xsl:text>] = 1;&#xa;</xsl:text>
954
+ <xsl:call-template name="checker-layer">
955
+ <xsl:with-param name="fillin" select="$fillin" />
956
+ <xsl:with-param name="tests" select="./*" />
957
+ <xsl:with-param name="level" select="$level+1" />
958
+ <xsl:with-param name="logic" select="'and'" /> <!-- Default: All tests at first layer must be true -->
959
+ </xsl:call-template>
960
+ </xsl:when>
961
+ <xsl:when test="name()='or'">
962
+ <xsl:text> testResults[</xsl:text>
963
+ <xsl:value-of select="$level+1" />
964
+ <xsl:text>] = 0;&#xa;</xsl:text>
965
+ <xsl:call-template name="checker-layer">
966
+ <xsl:with-param name="fillin" select="$fillin" />
967
+ <xsl:with-param name="tests" select="./*" />
968
+ <xsl:with-param name="level" select="$level+1" />
969
+ <xsl:with-param name="logic" select="'or'" />
970
+ </xsl:call-template>
971
+ </xsl:when>
972
+ <xsl:when test="name()='not'">
973
+ <xsl:call-template name="checker-layer">
974
+ <xsl:with-param name="fillin" select="$fillin" />
975
+ <xsl:with-param name="tests" select="./*" />
976
+ <xsl:with-param name="level" select="$level+1" />
977
+ <xsl:with-param name="logic" select="'not'" />
978
+ </xsl:call-template>
979
+ </xsl:when>
980
+ <xsl:otherwise>
981
+ <xsl:call-template name="checker-simple">
982
+ <xsl:with-param name="fillin" select="$fillin" />
983
+ <xsl:with-param name="curTest" select="." />
984
+ <xsl:with-param name="level" select="$level+1" />
985
+ </xsl:call-template>
986
+ </xsl:otherwise>
987
+ </xsl:choose>
988
+ <xsl:choose> <!-- Deal with the results of that recursive call to a deeper layer. -->
989
+ <xsl:when test="$logic = 'and'">
990
+ <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" />
991
+ <xsl:text>] &amp;= testResults[</xsl:text><xsl:value-of select="$level+1" /><xsl:text>];&#xa;</xsl:text>
992
+ </xsl:when>
993
+ <xsl:when test="$logic = 'or'">
994
+ <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" />
995
+ <xsl:text>] |= testResults[</xsl:text><xsl:value-of select="$level+1" /><xsl:text>];&#xa;</xsl:text>
996
+ </xsl:when>
997
+ <xsl:when test="$logic = 'not'">
998
+ <xsl:text> testResults[</xsl:text><xsl:value-of select="$level" />
999
+ <xsl:text>] = !(testResults[</xsl:text><xsl:value-of select="$level+1" /><xsl:text>]);&#xa;</xsl:text>
1000
+ </xsl:when>
1001
+ <!-- Otherwise: invalid logic call was signaled as Error earlier. -->
1002
+ </xsl:choose>
1003
+ </xsl:for-each>
1004
+ </xsl:template>
1005
+
1006
+
1007
+ <!-- ================================================ -->
1008
+ <!-- Templates for working with evaluation objects -->
1009
+ <!-- ================================================ -->
1010
+
1011
+ <!-- Wrappers for calling appropriate javascript commands that -->
1012
+ <!-- will parse the expression for a number appropriately, -->
1013
+ <!-- which could be written as an expression involving symbols -->
1014
+ <!-- or a random number -->
1015
+ <xsl:template match="de-number" mode="evaluate">
1016
+ <xsl:param name="setupMode" />
1017
+ <xsl:variable name="prefix">
1018
+ <xsl:if test="$setupMode">
1019
+ <xsl:text>v.</xsl:text>
1020
+ </xsl:if>
1021
+ </xsl:variable>
1022
+ <xsl:variable name="de_env">
1023
+ <xsl:value-of select="$prefix"/>
1024
+ <xsl:text>_mobjs</xsl:text>
1025
+ </xsl:variable>
1026
+ <xsl:value-of select="$de_env"/>
1027
+ <xsl:text>.parseExpression(</xsl:text>
1028
+ <xsl:call-template name="quote-strip-string">
1029
+ <xsl:with-param name="text" select="."/>
1030
+ </xsl:call-template>
1031
+ <xsl:text>, "number")</xsl:text>
1032
+ <xsl:text>.reduce().simplifyConstants()</xsl:text>
1033
+ </xsl:template>
1034
+
1035
+ <xsl:template match="de-evaluate" mode="evaluate">
1036
+ <xsl:param name="setupMode" />
1037
+ <xsl:variable name="prefix">
1038
+ <xsl:if test="$setupMode">
1039
+ <xsl:text>v.</xsl:text>
1040
+ </xsl:if>
1041
+ </xsl:variable>
1042
+ <xsl:variable name="de_env">
1043
+ <xsl:value-of select="$prefix"/>
1044
+ <xsl:text>_mobjs</xsl:text>
1045
+ </xsl:variable>
1046
+ <xsl:value-of select="$de_env"/>
1047
+ <xsl:text>.evaluateExpression(</xsl:text>
1048
+ <xsl:apply-templates select="formula/*" mode="evaluate">
1049
+ <xsl:with-param name="setupMode" select="$setupMode"/>
1050
+ </xsl:apply-templates>
1051
+ <xsl:text>, "number", {</xsl:text>
1052
+ <xsl:apply-templates select="variable" mode="evaluation-binding" >
1053
+ <xsl:with-param name="setupMode" select="$setupMode" />
1054
+ </xsl:apply-templates>
1055
+ <xsl:text>})</xsl:text>
1056
+ <xsl:if test="@reduce='yes'">
1057
+ <xsl:text>.reduce().simplifyConstants()</xsl:text>
1058
+ </xsl:if>
1059
+ </xsl:template>
1060
+
1061
+ <xsl:template match="de-random[@distribution='discrete']" mode="evaluate">
1062
+ <xsl:text>v._mobjs.generateRandom("discrete", { min:</xsl:text>
1063
+ <xsl:choose>
1064
+ <xsl:when test="@min">
1065
+ <xsl:value-of select="@min"/>
1066
+ </xsl:when>
1067
+ <xsl:otherwise>
1068
+ <xsl:text>0</xsl:text>
1069
+ </xsl:otherwise>
1070
+ </xsl:choose>
1071
+ <xsl:text>, max:</xsl:text>
1072
+ <xsl:choose>
1073
+ <xsl:when test="@max">
1074
+ <xsl:value-of select="@max"/>
1075
+ </xsl:when>
1076
+ <xsl:otherwise>
1077
+ <xsl:text>1</xsl:text>
1078
+ </xsl:otherwise>
1079
+ </xsl:choose>
1080
+ <xsl:text>, by:</xsl:text>
1081
+ <xsl:choose>
1082
+ <xsl:when test="@by">
1083
+ <xsl:value-of select="@by"/>
1084
+ </xsl:when>
1085
+ <xsl:otherwise>
1086
+ <xsl:text>1</xsl:text>
1087
+ </xsl:otherwise>
1088
+ </xsl:choose>
1089
+ <xsl:text>, nonzero:</xsl:text>
1090
+ <xsl:choose>
1091
+ <xsl:when test="@nonzero = 'yes'">
1092
+ <xsl:text>true</xsl:text>
1093
+ </xsl:when>
1094
+ <xsl:otherwise>
1095
+ <xsl:text>false</xsl:text>
1096
+ </xsl:otherwise>
1097
+ </xsl:choose>
1098
+ <xsl:text>})</xsl:text>
1099
+ </xsl:template>
1100
+
1101
+ <!-- Objects defined as formulas. Several possible modes: -->
1102
+ <!-- formula (literal), substitution (composition), -->
1103
+ <!-- derivative and evaluate -->
1104
+ <xsl:template match="de-expression[not(@mode) or @mode='formula']" mode="evaluate">
1105
+ <xsl:param name="setupMode" />
1106
+ <xsl:variable name="prefix">
1107
+ <xsl:if test="$setupMode">
1108
+ <xsl:text>v.</xsl:text>
1109
+ </xsl:if>
1110
+ </xsl:variable>
1111
+ <xsl:variable name="de_env">
1112
+ <xsl:value-of select="$prefix"/>
1113
+ <xsl:text>_mobjs</xsl:text>
1114
+ </xsl:variable>
1115
+ <xsl:value-of select="$de_env"/>
1116
+ <xsl:text>.parseExpression(</xsl:text>
1117
+ <xsl:call-template name="quote-strip-string">
1118
+ <xsl:with-param name="text" select="."/>
1119
+ </xsl:call-template>
1120
+ <xsl:text>, "formula")</xsl:text>
1121
+ <xsl:if test="@reduce='yes'">
1122
+ <xsl:text>.reduce().simplifyConstants()</xsl:text>
1123
+ </xsl:if>
1124
+ </xsl:template>
1125
+
1126
+ <xsl:template match="de-expression[@mode='substitution']" mode="evaluate">
1127
+ <xsl:param name="setupMode" />
1128
+ <xsl:variable name="prefix">
1129
+ <xsl:if test="$setupMode">
1130
+ <xsl:text>v.</xsl:text>
1131
+ </xsl:if>
1132
+ </xsl:variable>
1133
+ <xsl:variable name="de_env">
1134
+ <xsl:value-of select="$prefix"/>
1135
+ <xsl:text>_mobjs</xsl:text>
1136
+ </xsl:variable>
1137
+ <xsl:value-of select="$de_env"/>
1138
+ <xsl:text>.composeExpression(</xsl:text>
1139
+ <xsl:apply-templates select="formula/*" mode="evaluate">
1140
+ <xsl:with-param name="setupMode" select="$setupMode"/>
1141
+ </xsl:apply-templates>
1142
+ <xsl:text>, {</xsl:text>
1143
+ <xsl:apply-templates select="variable" mode="evaluation-binding" >
1144
+ <xsl:with-param name="setupMode" select="$setupMode" />
1145
+ </xsl:apply-templates>
1146
+ <xsl:text>})</xsl:text>
1147
+ <xsl:if test="@reduce='yes'">
1148
+ <xsl:text>.reduce().simplifyConstants()</xsl:text>
1149
+ </xsl:if>
1150
+ </xsl:template>
1151
+
1152
+ <xsl:template match="de-expression[@mode='derivative']" mode="evaluate">
1153
+ <xsl:param name="setupMode" />
1154
+ <xsl:variable name="prefix">
1155
+ <xsl:if test="$setupMode">
1156
+ <xsl:text>v.</xsl:text>
1157
+ </xsl:if>
1158
+ </xsl:variable>
1159
+ <xsl:variable name="de_env">
1160
+ <xsl:value-of select="$prefix"/>
1161
+ <xsl:text>_mobjs</xsl:text>
1162
+ </xsl:variable>
1163
+ <xsl:apply-templates select="formula/*" mode="evaluate">
1164
+ <xsl:with-param name="setupMode" select="$setupMode" />
1165
+ </xsl:apply-templates>
1166
+ <xsl:text>.derivative(</xsl:text>
1167
+ <xsl:call-template name="quote-string">
1168
+ <xsl:with-param name="text" select="variable/@name"/>
1169
+ </xsl:call-template>
1170
+ <xsl:text>)</xsl:text>
1171
+ <xsl:if test="@reduce='yes'">
1172
+ <xsl:text>.reduce().simplifyConstants()</xsl:text>
1173
+ </xsl:if>
1174
+ </xsl:template>
1175
+
1176
+
1177
+ <!-- Used during composition or evaluation of a variable-->
1178
+ <xsl:template match="variable" mode="evaluation-binding">
1179
+ <xsl:param name="setupMode" />
1180
+ <xsl:variable name="prefix">
1181
+ <xsl:if test="$setupMode">
1182
+ <xsl:text>v.</xsl:text>
1183
+ </xsl:if>
1184
+ </xsl:variable>
1185
+ <xsl:if test="position() > 1">
1186
+ <xsl:text>, </xsl:text>
1187
+ </xsl:if>
1188
+ <xsl:call-template name="quote-string">
1189
+ <xsl:with-param name="text" select="@name" />
1190
+ </xsl:call-template>
1191
+ <xsl:text>: </xsl:text>
1192
+ <xsl:choose>
1193
+ <xsl:when test="eval or de-number or de-expression">
1194
+ <xsl:apply-templates select="eval|de-number|de-expression" mode="evaluate">
1195
+ <xsl:with-param name="setupMode" select="$setupMode" />
1196
+ </xsl:apply-templates>
1197
+ </xsl:when>
1198
+ <xsl:otherwise>
1199
+ <xsl:value-of select="." />
1200
+ </xsl:otherwise>
1201
+ </xsl:choose>
1202
+ </xsl:template>
1203
+
1204
+
1205
+ <!-- var elements in expressions (evaluate) are replaced by their name -->
1206
+ <!-- During setup, we need to use the context of the `v` object -->
1207
+ <xsl:template match="eval" mode="evaluate">
1208
+ <xsl:param name="setupMode" />
1209
+ <xsl:variable name="prefix">
1210
+ <xsl:if test="$setupMode">
1211
+ <xsl:text>v.</xsl:text>
1212
+ </xsl:if>
1213
+ </xsl:variable>
1214
+ <xsl:value-of select="$prefix"/>
1215
+ <xsl:value-of select="@obj"/>
1216
+ </xsl:template>
1217
+
1218
+ <!-- Nothing else is defined for evaluation during setup -->
1219
+ <xsl:template match="/" mode="evaluate"/>
1220
+
1221
+ <xsl:template match="feedback" mode="serialize-feedback">
1222
+ <xsl:variable name="feedback-rtf">
1223
+ <xsl:apply-templates select="*" />
1224
+ </xsl:variable>
1225
+ <!-- serialize HTML as text, then escape as JSON -->
1226
+ <xsl:call-template name="escape-json-string">
1227
+ <xsl:with-param name="text">
1228
+ <xsl:apply-templates select="exsl:node-set($feedback-rtf)" mode="xml-to-string"/>
1229
+ </xsl:with-param>
1230
+ </xsl:call-template>
1231
+ </xsl:template>
1232
+
1233
+ </xsl:stylesheet>