@pretextbook/pretext-html 0.1.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 (110) hide show
  1. package/README.md +160 -0
  2. package/assets/preview-html.xsl +230 -0
  3. package/assets/upstream.json +7 -0
  4. package/assets/xsl/README.md +31 -0
  5. package/assets/xsl/entities.ent +154 -0
  6. package/assets/xsl/extract-asymptote.xsl +68 -0
  7. package/assets/xsl/extract-biblio-csl.xsl +327 -0
  8. package/assets/xsl/extract-datafile.xsl +71 -0
  9. package/assets/xsl/extract-dynamic.xsl +139 -0
  10. package/assets/xsl/extract-identity.xsl +136 -0
  11. package/assets/xsl/extract-interactive.xsl +81 -0
  12. package/assets/xsl/extract-latex-image.xsl +118 -0
  13. package/assets/xsl/extract-mermaid.xsl +58 -0
  14. package/assets/xsl/extract-mom.xsl +63 -0
  15. package/assets/xsl/extract-pg.xsl +2897 -0
  16. package/assets/xsl/extract-prefigure.xsl +157 -0
  17. package/assets/xsl/extract-qrcode.xsl +100 -0
  18. package/assets/xsl/extract-runestone-page-template.xsl +139 -0
  19. package/assets/xsl/extract-sageplot.xsl +166 -0
  20. package/assets/xsl/extract-stack.xsl +56 -0
  21. package/assets/xsl/extract-trace.xsl +134 -0
  22. package/assets/xsl/extract-youtube.xsl +63 -0
  23. package/assets/xsl/html-symbols.xsl +3344 -0
  24. package/assets/xsl/latex/pretext-latex-AIM.xsl +56 -0
  25. package/assets/xsl/latex/pretext-latex-CLP.xsl +148 -0
  26. package/assets/xsl/latex/pretext-latex-chaos.xsl +317 -0
  27. package/assets/xsl/latex/pretext-latex-dyslexic-font.xsl +76 -0
  28. package/assets/xsl/latex/pretext-latex-guide.xsl +104 -0
  29. package/assets/xsl/latex/pretext-latex-texstyle.xsl +862 -0
  30. package/assets/xsl/localizations/README.md +56 -0
  31. package/assets/xsl/localizations/af-ZA.xml +276 -0
  32. package/assets/xsl/localizations/bg-BG.xml +276 -0
  33. package/assets/xsl/localizations/ca-ES.xml +285 -0
  34. package/assets/xsl/localizations/cs-CZ.xml +280 -0
  35. package/assets/xsl/localizations/de-DE.xml +281 -0
  36. package/assets/xsl/localizations/en-US.xml +427 -0
  37. package/assets/xsl/localizations/es-ES.xml +287 -0
  38. package/assets/xsl/localizations/fi-FI.xml +275 -0
  39. package/assets/xsl/localizations/fr-CA.xml +276 -0
  40. package/assets/xsl/localizations/fr-FR.xml +280 -0
  41. package/assets/xsl/localizations/hu-HU.xml +280 -0
  42. package/assets/xsl/localizations/it-IT.xml +276 -0
  43. package/assets/xsl/localizations/ku-CKB.xml +275 -0
  44. package/assets/xsl/localizations/localizations.xml +48 -0
  45. package/assets/xsl/localizations/nl-NL.xml +275 -0
  46. package/assets/xsl/localizations/pt-BR.xml +288 -0
  47. package/assets/xsl/localizations/pt-PT.xml +292 -0
  48. package/assets/xsl/localizations/zh-HANS.xml +269 -0
  49. package/assets/xsl/pretext-assembly.xsl +4795 -0
  50. package/assets/xsl/pretext-basic-html.xsl +86 -0
  51. package/assets/xsl/pretext-beamer.xsl +662 -0
  52. package/assets/xsl/pretext-braille-preprint.xsl +2249 -0
  53. package/assets/xsl/pretext-common.xsl +12018 -0
  54. package/assets/xsl/pretext-dynamic.xsl +65 -0
  55. package/assets/xsl/pretext-epub.xsl +1483 -0
  56. package/assets/xsl/pretext-fo.xsl +4593 -0
  57. package/assets/xsl/pretext-html.xsl +14791 -0
  58. package/assets/xsl/pretext-json-manifest.xsl +150 -0
  59. package/assets/xsl/pretext-jupyter.xsl +868 -0
  60. package/assets/xsl/pretext-latex-classic.xsl +952 -0
  61. package/assets/xsl/pretext-latex-common.xsl +11563 -0
  62. package/assets/xsl/pretext-latex.xsl +47 -0
  63. package/assets/xsl/pretext-litprog.xsl +105 -0
  64. package/assets/xsl/pretext-merge.xsl +41 -0
  65. package/assets/xsl/pretext-numbers.xsl +875 -0
  66. package/assets/xsl/pretext-revealjs.xsl +522 -0
  67. package/assets/xsl/pretext-runestone-fitb.xsl +1233 -0
  68. package/assets/xsl/pretext-runestone-static.xsl +1438 -0
  69. package/assets/xsl/pretext-runestone.xsl +3083 -0
  70. package/assets/xsl/pretext-sage-doctest.xsl +289 -0
  71. package/assets/xsl/pretext-smc.xsl +492 -0
  72. package/assets/xsl/pretext-solution-manual-latex.xsl +323 -0
  73. package/assets/xsl/pretext-text-utilities.xsl +1217 -0
  74. package/assets/xsl/pretext-text.xsl +392 -0
  75. package/assets/xsl/pretext-units.xsl +195 -0
  76. package/assets/xsl/pretext-view-source.xsl +157 -0
  77. package/assets/xsl/pretext-ww-problem-sets.xsl +391 -0
  78. package/assets/xsl/publisher-variables.xsl +4756 -0
  79. package/assets/xsl/support/README.md +22 -0
  80. package/assets/xsl/support/extract-math.xsl +256 -0
  81. package/assets/xsl/support/package-math.xsl +141 -0
  82. package/assets/xsl/support/play-button/README.md +46 -0
  83. package/assets/xsl/support/play-button/play-button.png +0 -0
  84. package/assets/xsl/support/play-button/play-button.svg +3 -0
  85. package/assets/xsl/support/pretext-pg-macros.xsl +110 -0
  86. package/assets/xsl/support/webpack_static_imports.xml +14 -0
  87. package/assets/xsl/tests/README.md +24 -0
  88. package/assets/xsl/tests/null.xml +4 -0
  89. package/assets/xsl/tests/pretext-text-utilities-test.xsl +334 -0
  90. package/assets/xsl/utilities/README.md +31 -0
  91. package/assets/xsl/utilities/author-report.xsl +107 -0
  92. package/assets/xsl/utilities/deprecate-autoname.sed +18 -0
  93. package/assets/xsl/utilities/deprecate-index.sed +33 -0
  94. package/assets/xsl/utilities/fix-deprecations.xsl +663 -0
  95. package/assets/xsl/utilities/pretext-enhanced-source.xsl +112 -0
  96. package/assets/xsl/utilities/report-publisher-variables.xsl +162 -0
  97. package/assets/xsl/xml-to-json.xsl +326 -0
  98. package/assets/xsl/xml-to-string.xsl +328 -0
  99. package/cli.mjs +29 -0
  100. package/dist/cli-C6tfAVvi.js +553 -0
  101. package/dist/cli.d.ts +10 -0
  102. package/dist/cli.js +7 -0
  103. package/dist/index.d.ts +6 -0
  104. package/dist/index.js +10 -0
  105. package/dist/mounts.d.ts +23 -0
  106. package/dist/publication.d.ts +16 -0
  107. package/dist/renderer.d.ts +84 -0
  108. package/dist/sourcemap.d.ts +41 -0
  109. package/dist/xinclude.d.ts +45 -0
  110. package/package.json +52 -0
@@ -0,0 +1,3083 @@
1
+ <?xml version='1.0'?> <!-- As XML file -->
2
+
3
+ <!--********************************************************************
4
+ Copyright 2022 Robert A. Beezer
5
+
6
+ This file is part of PreTeXt.
7
+
8
+ PreTeXt is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation, either version 2 or version 3 of the
11
+ License (at your option).
12
+
13
+ PreTeXt is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
20
+ *********************************************************************-->
21
+
22
+ <!-- http://pimpmyxslt.com/articles/entity-tricks-part2/ -->
23
+ <!DOCTYPE xsl:stylesheet [
24
+ <!ENTITY % entities SYSTEM "entities.ent">
25
+ %entities;
26
+ ]>
27
+
28
+ <!-- exsl: necessary to write out Runestone manifest -->
29
+ <!-- str: necessary to tokenize alternate Runestone Services -->
30
+
31
+ <xsl:stylesheet
32
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
33
+ xmlns:xml="http://www.w3.org/XML/1998/namespace"
34
+ xmlns:pi="http://pretextbook.org/2020/pretext/internal"
35
+ xmlns:exsl="http://exslt.org/common"
36
+ xmlns:str="http://exslt.org/strings"
37
+ extension-element-prefixes="exsl str pi"
38
+ >
39
+
40
+ <!-- ######################## -->
41
+ <!-- Runestone Infrastructure -->
42
+ <!-- ######################## -->
43
+
44
+ <!-- Runestone Services -->
45
+ <!-- Runestone provides universally-applicable Javascript, and since Brad Miller -->
46
+ <!-- is "such a nice guy" he provides an XML version of the necessary files. -->
47
+ <!-- These are obtained/created/analyzed in the Python script. The structure -->
48
+ <!-- of this file is pretty simple, and should be apparent to the cognescenti. -->
49
+
50
+ <!-- The content of the services file is provided via -->
51
+ <!-- string parameters passed into this stylesheet. The purpose is to -->
52
+ <!-- allow the core Python routines to query the Runestone server for -->
53
+ <!-- the *very latest* services file available online. This is meant -->
54
+ <!-- to be a totally automated operation, so parameter names are not -->
55
+ <!-- always human-friendly. Sometimes these parameters are provided -->
56
+ <!-- from alternate sources due to some debugging mode being employed. -->
57
+ <!-- See the Python script for more detail. -->
58
+ <xsl:param name="rs-js" select="''"/>
59
+ <xsl:param name="rs-css" select="''"/>
60
+ <xsl:param name="rs-version" select="''"/>
61
+
62
+ <!-- The Runestone Services version actually in use is -->
63
+ <!-- needed several places, so we compute it once now. -->
64
+ <!-- Manifest, two "ebookConfig". -->
65
+ <xsl:variable name="runestone-version">
66
+ <xsl:value-of select="$rs-version"/>
67
+ </xsl:variable>
68
+
69
+ <!-- The Runestone platform option requires output that can be used -->
70
+ <!-- on the server with a templating language/tool. For books -->
71
+ <!-- originating from PreTeXt we use a non-default pair of strings. -->
72
+ <!-- This is because the default is {{, }} and these behave poorly -->
73
+ <!-- in a/@href output, since the outer pair looks like an XSL AVT -->
74
+ <!-- and then the inner pair gets escaped as a reserved character in -->
75
+ <!-- a URI. (We can turn off URI-escaping with XSLT 2.0, but the -->
76
+ <!-- AVT confusion may still be a problem.) -->
77
+
78
+ <!-- These are used two places, so defined globally, not -->
79
+ <!-- conditionally. Due to their ubiquity in these two -->
80
+ <!-- concentrated locations, the variable names are -->
81
+ <!-- intentionally cryptic, contrary to usual practice. -->
82
+ <!-- rs = Runestone, o = open, c = close. -->
83
+
84
+ <xsl:variable name="rso" select="'~._'"/>
85
+ <xsl:variable name="rsc" select="'_.~'"/>
86
+
87
+ <!-- Determines the base url of a book served on Runestone -->
88
+ <!-- If there is an authored base url, use it as the author -->
89
+ <!-- may be targetting a non-public server. Otherwise, -->
90
+ <!-- assume book is hosted on Runestone Academy -->
91
+ <xsl:template name="runestone-server-baseurl">
92
+ <xsl:choose>
93
+ <xsl:when test="$b-has-baseurl">
94
+ <xsl:value-of select="$baseurl"/>
95
+ </xsl:when>
96
+ <xsl:otherwise>
97
+ <xsl:text>https://runestone.academy/ns/books/published/</xsl:text>
98
+ <xsl:value-of select="$document-id"/>
99
+ <xsl:text>/</xsl:text>
100
+ </xsl:otherwise>
101
+ </xsl:choose>
102
+ </xsl:template>
103
+
104
+ <!-- Runestone Services (Javascript) -->
105
+ <!-- OR, Hosting at Runestone Academy -->
106
+ <xsl:template name="runestone-header">
107
+ <!-- Runestone templating for customizing hosted books -->
108
+ <!-- no Sphinx {% %} templating for build system at all, -->
109
+ <!-- conditional on $b-host-runestone -->
110
+ <!-- 'true' - hosted on a Runestone server, -->
111
+ <!-- {{ }} templating replaced by $rso, $rsc -->
112
+ <!-- 'false' - local viewing, no server, -->
113
+ <!-- just Runestone Services, so dummy values -->
114
+ <xsl:choose>
115
+ <!-- Runestone Server build -->
116
+ <xsl:when test="$b-host-runestone">
117
+ <script type="text/javascript">
118
+ <xsl:text>&#xa;</xsl:text>
119
+ <xsl:text>eBookConfig = {};&#xa;</xsl:text>
120
+ <xsl:text>eBookConfig.useRunestoneServices = true;&#xa;</xsl:text>
121
+ <xsl:text>eBookConfig.host = '';&#xa;</xsl:text>
122
+ <xsl:text>eBookConfig.app = eBookConfig.host + '/runestone';&#xa;</xsl:text>
123
+ <xsl:text>eBookConfig.course = '</xsl:text><xsl:value-of select="$rso"/><xsl:text> course_name </xsl:text><xsl:value-of select="$rsc"/><xsl:text>';&#xa;</xsl:text>
124
+ <xsl:text>eBookConfig.termStartDate = '</xsl:text><xsl:value-of select="$rso"/><xsl:text> term_start_date </xsl:text><xsl:value-of select="$rsc"/><xsl:text>';&#xa;</xsl:text>
125
+ <xsl:text>eBookConfig.basecourse = '</xsl:text><xsl:value-of select="$rso"/><xsl:text> base_course </xsl:text><xsl:value-of select="$rsc"/><xsl:text>';&#xa;</xsl:text>
126
+ <xsl:text>eBookConfig.isLoggedIn = </xsl:text><xsl:value-of select="$rso"/><xsl:text> is_logged_in|default('false') </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
127
+ <xsl:text>eBookConfig.email = '</xsl:text><xsl:value-of select="$rso"/><xsl:text> user_email </xsl:text><xsl:value-of select="$rsc"/><xsl:text>';&#xa;</xsl:text>
128
+ <xsl:text>eBookConfig.isInstructor = </xsl:text><xsl:value-of select="$rso"/><xsl:text> is_instructor|default('false') </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
129
+ <xsl:text>eBookConfig.logLevel = 10;&#xa;</xsl:text>
130
+ <xsl:text>eBookConfig.ajaxURL = eBookConfig.app + "/ajax/";&#xa;</xsl:text>
131
+ <!-- no .loglevel -->
132
+ <xsl:text>eBookConfig.username = '</xsl:text><xsl:value-of select="$rso"/><xsl:text> user_id </xsl:text><xsl:value-of select="$rsc"/><xsl:text>';&#xa;</xsl:text>
133
+ <xsl:text>eBookConfig.readings = </xsl:text><xsl:value-of select="$rso"/><xsl:text> readings|default([])|safe </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
134
+ <xsl:text>eBookConfig.activities = </xsl:text><xsl:value-of select="$rso"/><xsl:text> activity_info|default({})|safe </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
135
+ <xsl:text>eBookConfig.downloadsEnabled = </xsl:text><xsl:value-of select="$rso"/><xsl:text> downloads_enabled|default('false') </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
136
+ <xsl:text>eBookConfig.allow_pairs = </xsl:text><xsl:value-of select="$rso"/><xsl:text> allow_pairs|default('false') </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
137
+ <xsl:text>eBookConfig.course_attrs = </xsl:text><xsl:value-of select="$rso"/><xsl:text> course_attrs|default({})|tojson|safe </xsl:text><xsl:value-of select="$rsc"/><xsl:text>;&#xa;</xsl:text>
138
+ <!-- Scratch ActiveCode windows are a publisher option -->
139
+ <xsl:text>eBookConfig.enableScratchAC = </xsl:text>
140
+ <xsl:choose>
141
+ <xsl:when test="$b-has-scratch-activecode">
142
+ <xsl:text>true</xsl:text>
143
+ </xsl:when>
144
+ <xsl:otherwise>
145
+ <xsl:text>false</xsl:text>
146
+ </xsl:otherwise>
147
+ </xsl:choose>
148
+ <xsl:text>;&#xa;</xsl:text>
149
+ <!-- And set the language when scratch ActiveCode is enabled -->
150
+ <xsl:if test="$b-has-scratch-activecode">
151
+ <xsl:text>eBookConfig.acDefaultLanguage = '</xsl:text>
152
+ <xsl:value-of select="$html-scratch-activecode-language"/>
153
+ <xsl:text>';&#xa;</xsl:text>
154
+ </xsl:if>
155
+ <!-- end Scratch ActiveCode windows -->
156
+ <xsl:text>eBookConfig.new_server_prefix = "/ns";&#xa;</xsl:text>
157
+ <xsl:text>eBookConfig.runestone_version = '</xsl:text>
158
+ <xsl:value-of select="$runestone-version"/>
159
+ <xsl:text>';&#xa;</xsl:text>
160
+ <!-- no .build_info -->
161
+ <!-- no .python3 -->
162
+ <!-- no .jobehost -->
163
+ <!-- no .proxyuri_runs -->
164
+ <!-- no .proxyuri_files -->
165
+ <!-- no .enable_chatcodes -->
166
+ </script>
167
+ <xsl:text>&#xa;</xsl:text>
168
+
169
+ <!-- Ethical Ads: only on Runestone server, only visible in -->
170
+ <!-- *non-login* versions of books hosted at Runestone -->
171
+ <script src="https://media.ethicalads.io/media/client/ethicalads.min.js"/>
172
+
173
+ </xsl:when>
174
+ <!-- Runestone for All build -->
175
+ <!-- Hosted without a Runestone Server, just using Javascript -->
176
+ <xsl:when test="not($b-host-runestone)">
177
+ <xsl:comment>** eBookCongig is necessary to configure interactive **</xsl:comment><xsl:text>&#xa;</xsl:text>
178
+ <xsl:comment>** Runestone components to run locally in reader's browser **</xsl:comment><xsl:text>&#xa;</xsl:text>
179
+ <xsl:comment>** No external communication: **</xsl:comment><xsl:text>&#xa;</xsl:text>
180
+ <xsl:comment>** log level is 0, Runestone Services are disabled **</xsl:comment><xsl:text>&#xa;</xsl:text>
181
+ <script type="text/javascript">
182
+ <xsl:text>&#xa;</xsl:text>
183
+ <xsl:text>eBookConfig = {};&#xa;</xsl:text>
184
+ <xsl:text>eBookConfig.useRunestoneServices = false;&#xa;</xsl:text>
185
+ <xsl:text>eBookConfig.host = 'http://127.0.0.1:8000';&#xa;</xsl:text>
186
+ <!-- no .app -->
187
+ <!-- scratch Active Code fails if these faux strings have spaces or colons -->
188
+ <xsl:text>eBookConfig.course = 'PTX_Course_Title_Here';&#xa;</xsl:text>
189
+ <xsl:text>eBookConfig.basecourse = 'PTX_Base_Course';&#xa;</xsl:text>
190
+ <xsl:text>eBookConfig.isLoggedIn = false;&#xa;</xsl:text>
191
+ <xsl:text>eBookConfig.email = '';&#xa;</xsl:text>
192
+ <xsl:text>eBookConfig.isInstructor = false;&#xa;</xsl:text>
193
+ <!-- no .ajaxURL since no .app -->
194
+ <xsl:text>eBookConfig.logLevel = 0;&#xa;</xsl:text>
195
+ <xsl:text>eBookConfig.username = '';&#xa;</xsl:text>
196
+ <xsl:text>eBookConfig.readings = null;&#xa;</xsl:text>
197
+ <xsl:text>eBookConfig.activities = null;&#xa;</xsl:text>
198
+ <xsl:text>eBookConfig.downloadsEnabled = false;&#xa;</xsl:text>
199
+ <xsl:text>eBookConfig.allow_pairs = false;&#xa;</xsl:text>
200
+ <!-- Scratch ActiveCode windows are a publisher option -->
201
+ <xsl:text>eBookConfig.enableScratchAC = </xsl:text>
202
+ <xsl:choose>
203
+ <xsl:when test="$b-has-scratch-activecode">
204
+ <xsl:text>true</xsl:text>
205
+ </xsl:when>
206
+ <xsl:otherwise>
207
+ <xsl:text>false</xsl:text>
208
+ </xsl:otherwise>
209
+ </xsl:choose>
210
+ <xsl:text>;&#xa;</xsl:text>
211
+ <!-- And set the language when scratch ActiveCode is enabled -->
212
+ <xsl:if test="$b-has-scratch-activecode">
213
+ <xsl:text>eBookConfig.acDefaultLanguage = '</xsl:text>
214
+ <xsl:value-of select="$html-scratch-activecode-language"/>
215
+ <xsl:text>';&#xa;</xsl:text>
216
+ </xsl:if>
217
+ <!-- end Scratch ActiveCode windows -->
218
+ <xsl:text>eBookConfig.build_info = "";&#xa;</xsl:text>
219
+ <xsl:text>eBookConfig.python3 = null;&#xa;</xsl:text>
220
+ <xsl:text>eBookConfig.runestone_version = '</xsl:text>
221
+ <xsl:value-of select="$runestone-version"/>
222
+ <xsl:text>';&#xa;</xsl:text>
223
+ <xsl:text>eBookConfig.jobehost = '';&#xa;</xsl:text>
224
+ <xsl:text>eBookConfig.proxyuri_runs = '';&#xa;</xsl:text>
225
+ <xsl:text>eBookConfig.proxyuri_files = '';&#xa;</xsl:text>
226
+ <xsl:text>eBookConfig.enable_chatcodes = false;&#xa;</xsl:text>
227
+ </script>
228
+ <xsl:text>&#xa;</xsl:text>
229
+ </xsl:when>
230
+ </xsl:choose>
231
+
232
+ <!-- The Runestone Services file typically contains multiple filenames -->
233
+ <!-- for Javascript and CSS (like two or three). -->
234
+ <!-- We expect the two string parameters to be lists delimited by a colon -->
235
+ <!-- (':'), so this character should not ever appear in the filenames. -->
236
+ <!-- Note: these variables will be vacuous when the string parameters are -->
237
+ <!-- empty strings, and then will not ever be employed -->
238
+ <xsl:variable name="rs-js-tokens" select="str:tokenize($rs-js, ':')"/>
239
+ <xsl:variable name="rs-css-tokens" select="str:tokenize($rs-css, ':')"/>
240
+
241
+ <!-- Javascript and CSS "master" links/pointers into _static -->
242
+ <xsl:comment>*** Runestone Services ***</xsl:comment>
243
+ <xsl:text>&#xa;</xsl:text>
244
+ <xsl:for-each select="$rs-js-tokens">
245
+ <script>
246
+ <xsl:attribute name="src">
247
+ <xsl:value-of select="$cdn-prefix"/>
248
+ <xsl:text>_static/</xsl:text>
249
+ <xsl:value-of select="."/>
250
+ </xsl:attribute>
251
+ </script>
252
+ </xsl:for-each>
253
+ <xsl:for-each select="$rs-css-tokens">
254
+ <link rel="stylesheet" type="text/css">
255
+ <xsl:attribute name="href">
256
+ <xsl:value-of select="$cdn-prefix"/>
257
+ <xsl:text>_static/</xsl:text>
258
+ <xsl:value-of select="."/>
259
+ </xsl:attribute>
260
+ </link>
261
+ </xsl:for-each>
262
+ </xsl:template>
263
+
264
+ <!-- User Menu (aka Bust Menu) -->
265
+ <!-- Conditional on a build for Runestone hosting -->
266
+ <!-- "template" elements are not visible, ever, -->
267
+ <!-- but rather serve as a model for Javascript -->
268
+
269
+ <xsl:template name="runestone-bust-menu">
270
+ <!-- "Bust w/ Silhoutte" is U+1F464, used as menu icon -->
271
+ <xsl:if test="$b-host-runestone">
272
+ <xsl:variable name="profile-localization">
273
+ <xsl:apply-templates select="." mode="type-name">
274
+ <xsl:with-param name="string-id" select="'profile'"/>
275
+ </xsl:apply-templates>
276
+ </xsl:variable>
277
+ <!-- note: as of 6/20/2026, anchor positioning is not widely supported -->
278
+ <!-- when it becomes supported, would make sense to use popover API -->
279
+ <button type="button" id="ptx-user-dropdown-button" class="runestone-profile button ptx-dropdown-button" aria-haspopup="menu" aria-controls="ptx-user-dropdown-content" aria-expanded="false" title="{$profile-localization}">
280
+ <xsl:call-template name="insert-symbol">
281
+ <xsl:with-param name="name" select="'person'"/>
282
+ </xsl:call-template>
283
+ <span class="name">
284
+ <xsl:value-of select="$profile-localization"/>
285
+ </span>
286
+ </button>
287
+ <div id="ptx-user-dropdown-content" class="ptx-dropdown-content" aria-labelledby="ptx-user-dropdown-button" role="menu">
288
+ <ul id="ptx-user-dropdown_rs-content" role="group" aria-label="Runestone"></ul>
289
+ </div>
290
+ <!-- clone template, modify href and inner text, and append to ptx-user-dropdown_rs-content -->
291
+ <template id="ptx-user-dropdown-content_item-template">
292
+ <li role="presentation" class="ptx-dropdown-item">
293
+ <a href="url here" role="menuitem">title here</a>
294
+ </li>
295
+ </template>
296
+ <!-- clone and append to dropdown content area and add a separator within the menu -->
297
+ <template id="ptx-user-dropdown-content_separator-template">
298
+ <li role="presentation" class="ptx-dropdown-separator"></li>
299
+ </template>
300
+ </xsl:if>
301
+ </xsl:template>
302
+
303
+ <!-- Scratch ActiveCode window, for all builds (powered by Runestone -->
304
+ <!-- Javascript). But more languages available on a Runestone server. -->
305
+ <!-- Only if requested, explicitly or implicitly, via publisher file. -->
306
+ <!-- Unicode Character 'PENCIL' (U+270F) -->
307
+ <xsl:template name="runestone-scratch-activecode">
308
+ <xsl:if test="$b-has-scratch-activecode">
309
+ <xsl:variable name="scratch-localization">
310
+ <xsl:apply-templates select="." mode="type-name">
311
+ <xsl:with-param name="string-id" select="'scratch-activecode'"/>
312
+ </xsl:apply-templates>
313
+ </xsl:variable>
314
+ <button onclick="runestoneComponents.popupScratchAC()" class="activecode-toggle button" title="{$scratch-localization}">
315
+ <xsl:call-template name="insert-symbol">
316
+ <xsl:with-param name="name" select="'edit'"/>
317
+ </xsl:call-template>
318
+ <span class="name">
319
+ <xsl:value-of select="$scratch-localization"/>
320
+ </span>
321
+ </button>
322
+ </xsl:if>
323
+ </xsl:template>
324
+
325
+ <!-- A convenience for attaching a Runestone id -->
326
+ <!-- NB: we attempt to only use this template in this stylesheet. -->
327
+ <!-- To enforce this, we *could* make a no-op, plus warning, -->
328
+ <!-- template in the "pretext-html" stylesheet, with this -->
329
+ <!-- implementation doing an overide. -->
330
+ <xsl:template match="exercise|program|datafile|query|&PROJECT-LIKE;|task|video[@youtube]|exercises|worksheet" mode="runestone-id-attribute">
331
+ <xsl:variable name="id">
332
+ <xsl:apply-templates select="." mode="runestone-id"/>
333
+ </xsl:variable>
334
+ <xsl:if test="$id != ''">
335
+ <xsl:attribute name="id">
336
+ <xsl:value-of select="$id"/>
337
+ </xsl:attribute>
338
+ </xsl:if>
339
+ </xsl:template>
340
+
341
+ <!-- ############### -->
342
+ <!-- Runestone Hooks -->
343
+ <!-- ############### -->
344
+
345
+ <!-- Various additions/modifications to the HTML output, but which -->
346
+ <!-- we isolate here in this stylesheet for organizational reasons. -->
347
+
348
+ <!-- A textual and visual progress indicator of completed activities -->
349
+ <!-- for a book hosted on a RS server, only. At the RS "subchapter" -->
350
+ <!-- level, which we shortcut by checking for a chapter-level parent. -->
351
+ <xsl:template match="&STRUCTURAL;" mode="runestone-progress-indicator">
352
+ <xsl:if test="$b-host-runestone and (parent::chapter or parent::appendix)">
353
+ <div id="scprogresscontainer">You have attempted <span id="scprogresstotal"/> of <span id="scprogressposs"/> activities on this page.<div id="subchapterprogress" aria-label="Page progress"/></div>
354
+ </xsl:if>
355
+ </xsl:template>
356
+
357
+ <!-- A timed exam requires markup that wraps an entire collection -->
358
+ <!-- of exercises, so we pass the exercises in as a parameter -->
359
+ <xsl:template match="exercises" mode="runestone-timed-exam">
360
+ <xsl:param name="the-exercises"/>
361
+ <xsl:param name="b-manifest" select="false()"/>
362
+
363
+ <!-- Since the component wraps the exercises, we do not need any -->
364
+ <!-- restriction about being at teh Runestone "subchapter" level. -->
365
+ <xsl:if test="$b-host-runestone">
366
+ <div class="timedAssessment">
367
+ <!-- for the manifest only, report information about -->
368
+ <!-- the "exercises" division being re-purposed -->
369
+ <xsl:if test="$b-manifest">
370
+ <title>
371
+ <xsl:apply-templates select="." mode="title-full"/>
372
+ </title>
373
+ <number>
374
+ <xsl:apply-templates select="." mode="number"/>
375
+ </number>
376
+ </xsl:if>
377
+ <ul data-component="timedAssessment" data-question_label="">
378
+ <!-- a Runestone id -->
379
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
380
+ <xsl:apply-templates select="." mode="runestone-timed-exam-attributes"/>
381
+ <!-- the actual list of exercises -->
382
+ <xsl:copy-of select="$the-exercises"/>
383
+ <xsl:if test="not($b-manifest)">
384
+ <!-- only at "section" level. only when building for a Runestone server -->
385
+ <xsl:apply-templates select="." mode="runestone-progress-indicator"/>
386
+ </xsl:if>
387
+ </ul>
388
+ </div>
389
+ </xsl:if>
390
+ </xsl:template>
391
+
392
+ <!-- These are the possible attributes the Runestone component -->
393
+ <!-- expects for a timed exam, in a style that the Runestone -->
394
+ <!-- HTML uses. We repeat them *verbatim* when the Runestone -->
395
+ <!-- manifect reports an "exercises" as a timed exam, as the -->
396
+ <!-- best way to endure some consistency in defaults, etc. -->
397
+ <xsl:template match="exercises" mode="runestone-timed-exam-attributes">
398
+ <!-- one mandatory attribute -->
399
+ <xsl:attribute name="data-time">
400
+ <xsl:value-of select="@time-limit"/>
401
+ </xsl:attribute>
402
+ <!-- result, timer, feedback, pause are *on* by -->
403
+ <!-- default if a PreTeXt attribute is "no" then -->
404
+ <!-- issue empty "data-no-*" Runestone attribute -->
405
+ <xsl:if test="@results = 'no'">
406
+ <xsl:attribute name="data-no-result"/>
407
+ </xsl:if>
408
+ <xsl:if test="@timer = 'no'">
409
+ <xsl:attribute name="data-no-timer"/>
410
+ </xsl:if>
411
+ <xsl:if test="@feedback = 'no'">
412
+ <xsl:attribute name="data-no-feedback"/>
413
+ </xsl:if>
414
+ <xsl:if test="@pause = 'no'">
415
+ <xsl:attribute name="data-no-pause"/>
416
+ </xsl:if>
417
+ </xsl:template>
418
+
419
+ <!-- An "exercises" division can be a group work exercise, by virtue -->
420
+ <!-- of selection and submission features at the bottom of the page. -->
421
+ <xsl:template match="exercises|worksheet" mode="runestone-groupwork">
422
+ <div class="runestone">
423
+ <div data-component="groupsub">
424
+ <!-- the Runestone id -->
425
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
426
+ <xsl:attribute name="data-size_limit">
427
+ <xsl:choose>
428
+ <xsl:when test="@groupsize">
429
+ <xsl:value-of select="@groupsize"/>
430
+ </xsl:when>
431
+ <xsl:otherwise>
432
+ <xsl:text>3</xsl:text>
433
+ </xsl:otherwise>
434
+ </xsl:choose>
435
+ </xsl:attribute>
436
+ <!-- students select their partners in their group -->
437
+ <div>
438
+ <select multiple="" class="assignment_partner_select" style="width: 100%"/>
439
+ </div>
440
+ <!-- and a submit button once done -->
441
+ <div class="groupsub_button"/>
442
+ <div class="para">The Submit Group button will submit the answer for each each question on this page for each member of your group. It also logs you as the official group submitter.</div>
443
+ </div>
444
+ </div>
445
+ </xsl:template>
446
+
447
+
448
+ <!-- ################## -->
449
+ <!-- Runestone Manifest -->
450
+ <!-- ################## -->
451
+
452
+ <!-- HTML ID and real title for each chapter and section -->
453
+ <!-- A PTX "section" is a Runestone "subchapter" -->
454
+ <!-- Document hierarchy is preserved in XML structure -->
455
+ <!-- TODO: add exercises as "question" -->
456
+
457
+ <!-- Conditional run-in -->
458
+ <xsl:template name="runestone-manifest">
459
+ <xsl:if test="$b-host-runestone and ($b-is-book or $b-is-article)">
460
+ <!-- $document-root *will* be a book -->
461
+ <xsl:apply-templates select="$document-root" mode="runestone-manifest"/>
462
+ </xsl:if>
463
+ </xsl:template>
464
+
465
+ <!-- Compute list of any programs that are linked to from other programs, -->
466
+ <!-- they will need to be rendered into the source. Result is a node-set -->
467
+ <!-- containing <token>id</token> -->
468
+ <xsl:variable name="linked-programs-list-rtf">
469
+ <xsl:variable name="linked-id-string">
470
+ <xsl:for-each select="//program[boolean(@add-files) or boolean(@include) or boolean(@compile-also)]">
471
+ <xsl:value-of select="@add-files"/>
472
+ <xsl:text>,</xsl:text>
473
+ <xsl:value-of select="@include"/>
474
+ <xsl:text>,</xsl:text>
475
+ <xsl:value-of select="@compile-also"/>
476
+ <xsl:text>,</xsl:text>
477
+ </xsl:for-each>
478
+ </xsl:variable>
479
+ <xsl:call-template name="unique-token-set">
480
+ <xsl:with-param name="s" select="$linked-id-string"/>
481
+ </xsl:call-template>
482
+ </xsl:variable>
483
+ <xsl:variable name="linked-programs-list" select="exsl:node-set($linked-programs-list-rtf)/token"/>
484
+
485
+ <xsl:template match="book|article" mode="runestone-manifest">
486
+ <exsl:document href="runestone-manifest.xml" method="xml" indent="yes" encoding="UTF-8">
487
+ <manifest>
488
+ <!-- version of Runestone Services used for this build -->
489
+ <runestone-services>
490
+ <xsl:attribute name="version">
491
+ <xsl:value-of select="$runestone-version"/>
492
+ </xsl:attribute>
493
+ </runestone-services>
494
+ <!-- default programming language for book as specified in docinfo -->
495
+ <!-- if not specified, assume python -->
496
+ <default-language>
497
+ <xsl:choose>
498
+ <xsl:when test="$default-active-programming-language != ''">
499
+ <xsl:value-of select="$default-active-programming-language"/>
500
+ </xsl:when>
501
+ <xsl:otherwise>
502
+ <xsl:text>python</xsl:text>
503
+ </xsl:otherwise>
504
+ </xsl:choose>
505
+ </default-language>
506
+ <!-- mine various bits and pieces of the source for RS metadata -->
507
+ <!-- collection, which is technically a "conf.py" file, per-book -->
508
+ <library-metadata publisher="pretext">
509
+ <!-- sanitizes footnotes, quotes, math for overall title-->
510
+ <title>
511
+ <xsl:apply-templates select="$document-root" mode="title-plain"/>
512
+ </title>
513
+ <subtitle>
514
+ <xsl:apply-templates select="$document-root" mode="subtitle"/>
515
+ </subtitle>
516
+ <xsl:for-each select="$document-root/frontmatter/bibinfo/author">
517
+ <author>
518
+ <xsl:apply-templates select="personname"/>
519
+ </author>
520
+ </xsl:for-each>
521
+ <!-- edition, too? -->
522
+ <document-id>
523
+ <!-- global variables defined in -common -->
524
+ <xsl:attribute name="edition">
525
+ <xsl:value-of select="$edition"/>
526
+ </xsl:attribute>
527
+ <xsl:value-of select="$document-id"/>
528
+ </document-id>
529
+ <!-- duplicate blurb, blurb/@shelf for Runestone's convenience -->
530
+ <!-- use "value-of" to enforce assumption there is no markup -->
531
+ <!-- NB: if absent in PTX source, these are empty in manifest -->
532
+ <shelf>
533
+ <xsl:value-of select="$docinfo/blurb/@shelf"/>
534
+ </shelf>
535
+ <blurb>
536
+ <xsl:value-of select="$docinfo/blurb"/>
537
+ </blurb>
538
+ </library-metadata>
539
+ <!-- LaTeX packages and macros first -->
540
+ <latex-macros>
541
+ <xsl:text>&#xa;</xsl:text>
542
+ <xsl:value-of select="$latex-packages-mathjax"/>
543
+ <xsl:value-of select="$latex-macros"/>
544
+ </latex-macros>
545
+ <!-- Report major and minor versions from WW so Runestone knows what's up. -->
546
+ <!-- These variables are formed in -html and could be empty/blank. Hoever, -->
547
+ <!-- they should always be defined as global variables. -->
548
+ <webwork-version major="{$webwork-major-version}" minor="{$webwork-minor-version}"/>
549
+ <xsl:choose>
550
+ <xsl:when test="self::book">
551
+ <!-- Now recurse into chapters, appendix -->
552
+ <xsl:apply-templates select="*" mode="runestone-manifest"/>
553
+ </xsl:when>
554
+ <xsl:when test="self::article">
555
+ <!-- Now recurse into sections, appendix -->
556
+ <!-- with a faux chapter, using "article" -->
557
+ <chapter>
558
+ <id>
559
+ <xsl:apply-templates select="." mode="html-id"/>
560
+ </id>
561
+ <title>
562
+ <xsl:apply-templates select="." mode="title-full"/>
563
+ </title>
564
+ <number>
565
+ <xsl:apply-templates select="." mode="number"/>
566
+ </number>
567
+ <division>
568
+ <xsl:value-of select="local-name()"/>
569
+ </division>
570
+ <xsl:apply-templates select="*" mode="runestone-manifest"/>
571
+ </chapter>
572
+ </xsl:when>
573
+ </xsl:choose>
574
+ </manifest>
575
+ </exsl:document>
576
+ </xsl:template>
577
+
578
+ <xsl:template match="chapter" mode="runestone-manifest">
579
+ <chapter>
580
+ <id>
581
+ <xsl:apply-templates select="." mode="html-id"/>
582
+ </id>
583
+ <title>
584
+ <xsl:apply-templates select="." mode="title-full"/>
585
+ </title>
586
+ <number>
587
+ <xsl:apply-templates select="." mode="number"/>
588
+ </number>
589
+ <division>
590
+ <xsl:value-of select="local-name()"/>
591
+ </division>
592
+ <!-- Recurse into PTX sections, or if the chapter is not structured, -->
593
+ <!-- then pick up inline exercises directly within a chapter -->
594
+ <xsl:apply-templates select="*" mode="runestone-manifest"/>
595
+ </chapter>
596
+ </xsl:template>
597
+
598
+ <!-- Every division at PreTeXt "section" level, potentially containing -->
599
+ <!-- an "exercise", e.g. a "worksheet", but not a "references", is a -->
600
+ <!-- Runestone "subchapter". Runestone "timed exams" (on a per-chapter -->
601
+ <!-- basis here), which are an "exercises" with a @time-limit, are -->
602
+ <!-- exceptional and have templates elsewhere. -->
603
+ <xsl:template match="section|chapter/exercises[not(@time-limit)]|chapter/worksheet|chapter/handout|chapter/reading-questions" mode="runestone-manifest">
604
+ <subchapter>
605
+ <!-- some properties of the division -->
606
+ <xsl:apply-templates select="." mode="runestone-division-properties"/>
607
+ <!-- nearly a dead end, recurse into "exercise" and PROJECT-LIKE at *any* PTX -->
608
+ <!-- depth, for example within a "subsection" (which Runestone does not have) -->
609
+ <!-- If any of the next select are containers (full of "task") they will not -->
610
+ <!-- meet the dead-end monster match below, and the default template will -->
611
+ <!-- recurse into non-container "task" eventually, so "task" do get -->
612
+ <!-- processed, even if they seem to be missing from this select. -->
613
+ <!-- Exceptions (early in the select): we explicitly recurse into a -->
614
+ <!-- "exercises" division at the PreTeXt "subsection" level that is a -->
615
+ <!-- Runestone timed exam associated with some "section". We also avoid -->
616
+ <!-- collecting "exercise" at arbitrary depth for the timed exams, as the -->
617
+ <!-- template for those divisions will do that collection. -->
618
+ <xsl:apply-templates select="exercises[@time-limit]|.//exercise[not(ancestor::exercises[@time-limit])]|.//project|.//activity|.//exploration|.//investigation|.//video[@youtube]|.//program[(@interactive = 'codelens') and not(parent::exercise)]|.//program[(@interactive = 'activecode') and not(parent::exercise)]|.//datafile" mode="runestone-manifest"/>
619
+
620
+ <!-- Now check for programs that have been included elsewhere. They need -->
621
+ <!-- to be rendered into <source> elements. -->
622
+ <xsl:apply-templates select=".//program[@xml:id = $linked-programs-list]" mode="runestone-manifest-source"/>
623
+ </subchapter>
624
+ <!-- dead end structurally, no more recursion, even if "subsection", etc. -->
625
+ </xsl:template>
626
+
627
+ <!-- "exercises" divisions with @time-limit are Runestone "timed-exams". -->
628
+ <!-- They can be at the PreTeXt "section" level (as a Runestone "subchapter") -->
629
+ <!-- or they are at the PreTeXt "subsection" level. Their contained -->
630
+ <!-- "exercise" are excluded previously, and are now collected here. -->
631
+ <xsl:template match="chapter/exercises[@time-limit]|chapter/section/exercises[@time-limit]" mode="runestone-manifest">
632
+ <!-- Duplicate the division, ehnhanced with indicators of a timed -->
633
+ <!-- exam. Include the contained exercises as manifest items. -->
634
+ <!-- Strictly speaking this not exactly correct, for example, -->
635
+ <!-- we get a progress indicator we do not want. -->
636
+ <xsl:apply-templates select="." mode="runestone-timed-exam">
637
+ <xsl:with-param name="the-exercises">
638
+ <xsl:apply-templates select=".//exercise" mode="runestone-manifest"/>
639
+ </xsl:with-param>
640
+ <xsl:with-param name="b-manifest" select="true()"/>
641
+ </xsl:apply-templates>
642
+ </xsl:template>
643
+
644
+ <!-- Properties to report for each division -->
645
+ <xsl:template match="*" mode="runestone-division-properties">
646
+ <id>
647
+ <xsl:apply-templates select="." mode="html-id"/>
648
+ </id>
649
+ <title>
650
+ <xsl:apply-templates select="." mode="title-full"/>
651
+ </title>
652
+ <number>
653
+ <xsl:apply-templates select="." mode="number"/>
654
+ </number>
655
+ <!-- report the division type -->
656
+ <division>
657
+ <xsl:value-of select="local-name()"/>
658
+ </division>
659
+ </xsl:template>
660
+
661
+ <!-- A Runestone exercise needs to identify itself when an instructor wants -->
662
+ <!-- to select it for assignment, so we want to provide enough identification -->
663
+ <!-- in the manifest, via a "label" element full of raw text. -->
664
+ <xsl:template match="exercise|project|activity|exploration|investigation|task" mode="runestone-manifest-label">
665
+ <label>
666
+ <xsl:apply-templates select="." mode="type-name"/>
667
+ <xsl:text> </xsl:text>
668
+ <xsl:apply-templates select="." mode="number"/>
669
+ <xsl:text> </xsl:text>
670
+ <xsl:apply-templates select="." mode="title-full"/>
671
+ </label>
672
+ </xsl:template>
673
+
674
+ <!-- Minimal label. In database for obvious reasons, -->
675
+ <!-- this is best (only?) thing to use as a label. -->
676
+ <xsl:template match="datafile" mode="runestone-manifest-label">
677
+ <label>
678
+ <xsl:value-of select="@filename"/>
679
+ </label>
680
+ </xsl:template>
681
+
682
+ <!-- Runestone tracks engagement with YouTube videos and "stray" -->
683
+ <!-- ActiveCode and CodeLens (i.e. an "inline" "program", not as -->
684
+ <!-- a portion of an "exercise". As these are atomic elements, -->
685
+ <!-- there is little to grab onto for a label in a report for an -->
686
+ <!-- instructor. So we provide examination of an enclosing -->
687
+ <!-- figure ("video") or listing ("program"). -->
688
+
689
+ <xsl:template match="video[@youtube]|program[((@interactive = 'codelens') or (@interactive = 'activecode')) and not(parent::exercise)]" mode="runestone-manifest-label">
690
+ <label>
691
+ <!-- three ways to get a type-name -->
692
+ <xsl:choose>
693
+ <xsl:when test="self::video[@youtube]">
694
+ <xsl:apply-templates select="." mode="type-name"/>
695
+ </xsl:when>
696
+ <xsl:when test="self::program[@interactive = 'codelens']">
697
+ <xsl:apply-templates select="." mode="type-name">
698
+ <xsl:with-param name="string-id" select="'program-codelens'"/>
699
+ </xsl:apply-templates>
700
+ </xsl:when>
701
+ <xsl:when test="self::program[@interactive = 'activecode']">
702
+ <xsl:apply-templates select="." mode="type-name">
703
+ <xsl:with-param name="string-id" select="'program-activecode'"/>
704
+ </xsl:apply-templates>
705
+ </xsl:when>
706
+ </xsl:choose>
707
+ <!-- "video" can be a "figure", and "program" can be in a -->
708
+ <!-- "listing", but the two situations are similar enough -->
709
+ <!-- to combine and work with a generic parent/enclosure. -->
710
+ <!-- This could well be empty if inline objects. -->
711
+ <xsl:variable name="enclosure" select="parent::figure|parent::listing"/>
712
+ <xsl:variable name="b-title" select="boolean(title)"/>
713
+ <xsl:variable name="b-enclosure" select="boolean($enclosure)"/>
714
+ <!-- more coming, use a separator -->
715
+ <xsl:if test="$b-title or $b-enclosure">
716
+ <xsl:text>: </xsl:text>
717
+ </xsl:if>
718
+ <!-- object's title is "closer" (or may be only -->
719
+ <!-- possible identification if standalone) -->
720
+ <xsl:if test="$b-title">
721
+ <xsl:apply-templates select="." mode="title-full"/>
722
+ </xsl:if>
723
+ <!-- if an abundance, use a separator -->
724
+ <xsl:if test="$b-title and $b-enclosure">
725
+ <xsl:text>; </xsl:text>
726
+ </xsl:if>
727
+ <xsl:if test="$b-enclosure">
728
+ <xsl:apply-templates select="$enclosure" mode="type-name"/>
729
+ <xsl:text> </xsl:text>
730
+ <xsl:apply-templates select="$enclosure" mode="number"/>
731
+ <xsl:if test="$enclosure/title">
732
+ <xsl:text> </xsl:text>
733
+ <xsl:apply-templates select="$enclosure" mode="title-full"/>
734
+ </xsl:if>
735
+ </xsl:if>
736
+ <!-- last ditch effor for a YouTube video -->
737
+ <xsl:if test="self::video[@youtube] and not($b-title) and not($b-enclosure)">
738
+ <xsl:text>: </xsl:text>
739
+ <!-- need to know Runestone CSS to make this code/monospace -->
740
+ <xsl:value-of select="@youtube"/>
741
+ </xsl:if>
742
+ </label>
743
+ </xsl:template>
744
+
745
+ <!-- Exercises to the Runestone manifest -->
746
+ <!-- This is includes some things that can't exist. e.g. webwork in task -->
747
+ <!-- but standardized treatment is easier to maintain than explicit -->
748
+ <!-- listing of expected combinations. -->
749
+ <!-- Intentionally matching all containers and then filtering with if on -->
750
+ <!-- attribute. Explicit listing in template match might be slightly more-->
751
+ <!-- efficient but has proven to be a popular spot for bugs. -->
752
+ <xsl:template match="exercise|&PROJECT-LIKE;|task" mode="runestone-manifest">
753
+ <xsl:variable name="manifestable-interactives-fenced">|truefalse|multiplechoice|parson|parson-horizontal|cardsort|matching|clickablearea|select|fillin-basic|fillin|coding|dual|shortanswer|webwork|</xsl:variable>
754
+ <xsl:if test="contains($manifestable-interactives-fenced, concat('|', @exercise-interactive, '|'))">
755
+ <question>
756
+ <!-- A divisional exercise ("exercises/../exercise") is not really -->
757
+ <!-- a reading activity in the Runestone model, so we flag these -->
758
+ <!-- exercises as such. Also, interactive "task" come through here, -->
759
+ <!-- so we need to look to an ancestor to see if the containing -->
760
+ <!-- "exercise" is divisional. The @optional attribute matches the -->
761
+ <!-- "optional" flag in the Runestone database. We simply set the -->
762
+ <!-- value to "yes" and never bother to say "no". The only -->
763
+ <!-- consumer is the import into the Runestone database, so any -->
764
+ <!-- change needs only coordinate there. -->
765
+ <xsl:if test="(@exercise-customization = 'divisional') or
766
+ (self::task and ancestor::exercise[@exercise-customization = 'divisional'])">
767
+ <xsl:attribute name="optional">
768
+ <xsl:text>yes</xsl:text>
769
+ </xsl:attribute>
770
+ </xsl:if>
771
+ <!-- label is from the "exercise" -->
772
+ <xsl:apply-templates select="." mode="runestone-manifest-label"/>
773
+ <!-- Duplicate, but still should look like original (ID, etc.), -->
774
+ <!-- not knowled. Solutions are available in the originals, via -->
775
+ <!-- an "in context" link off the Assignment page -->
776
+ <htmlsrc>
777
+ <!-- next template produces nothing, unless the -->
778
+ <!-- "exercise" is in an "exercisegroup" ("eg") -->
779
+ <xsl:apply-templates select="." mode="eg-introduction"/>
780
+ <xsl:choose>
781
+ <!-- with "webwork" guts, the HTML is exceptional -->
782
+ <xsl:when test="@exercise-interactive = 'webwork'">
783
+ <xsl:apply-templates select="." mode="webwork-core">
784
+ <xsl:with-param name="b-original" select="true()"/>
785
+ <xsl:with-param name="heading-level" select="3"/>
786
+ </xsl:apply-templates>
787
+ </xsl:when>
788
+ <xsl:otherwise>
789
+ <!-- next template collects "introduction" preceding a "task" -->
790
+ <!-- Note: we are explicitly dodging webwork/task -->
791
+ <xsl:apply-templates select="." mode="task-introductions"/>
792
+
793
+ <!-- determine which components publisher wants rendered-->
794
+ <xsl:variable name="ex-components-rtf">
795
+ <xsl:apply-templates select="." mode="exercise-components-report"/>
796
+ </xsl:variable>
797
+ <xsl:variable name="ex-components-report" select="exsl:node-set($ex-components-rtf)/exercise-component-report"/>
798
+
799
+ <xsl:apply-templates select="." mode="exercise-components">
800
+ <xsl:with-param name="b-original" select="true()"/>
801
+ <xsl:with-param name="block-type" select="'visible'"/>
802
+ <xsl:with-param name="heading-level" select="3"/>
803
+ <xsl:with-param name="b-has-statement" select="true()"/>
804
+ <xsl:with-param name="b-has-hint" select="$ex-components-report/@has-hint = 'true'"/>
805
+ <xsl:with-param name="b-has-answer" select="$ex-components-report/@has-answer = 'true'"/>
806
+ <xsl:with-param name="b-has-solution" select="$ex-components-report/@has-solution = 'true'"/>
807
+ </xsl:apply-templates>
808
+
809
+ <!-- next template collects "conclusion" preceding a "task" -->
810
+ <!-- Note: we are explicitly dodging webwork/task -->
811
+ <xsl:apply-templates select="." mode="task-conclusions"/>
812
+ </xsl:otherwise>
813
+ </xsl:choose>
814
+ </htmlsrc>
815
+ </question>
816
+ </xsl:if>
817
+ <!-- The match for this template will include "exercise" and &PROJECT-LIKE -->
818
+ <!-- that are just containers for a bunch of "task". In other words, they -->
819
+ <!-- will not be marked with the "@exercise-interactive" attribute. So the -->
820
+ <!-- "xsl:if" above will fail. And right here is a dead-end. We need to -->
821
+ <!-- recurse into "task" for the possibility they are marked with -->
822
+ <!-- "@exercise-interactive" so they can potentially get placed properly in -->
823
+ <!-- the manifest. -->
824
+ <xsl:apply-templates select="task" mode="runestone-manifest"/>
825
+ </xsl:template>
826
+
827
+ <!-- For an "exercise" divorced from it's "exercisegroup" "introduction", -->
828
+ <!-- we pick it up to be part of what is shown to the student on the -->
829
+ <!-- Runestone Assignment page. -->
830
+
831
+ <xsl:template match="*" mode="eg-introduction"/>
832
+
833
+ <xsl:template match="exercisegroup/exercise" mode="eg-introduction">
834
+ <xsl:apply-templates select="parent::exercisegroup/introduction"/>
835
+ </xsl:template>
836
+
837
+ <!-- Next two templates replicate all ancestral "introduction" -->
838
+ <!-- and "conclusion" on a per-task basis so that when a "task" -->
839
+ <!-- is viewed in the Runestone assignment builder, any overall -->
840
+ <!-- explanatory text is available. -->
841
+
842
+ <xsl:template match="task" mode="task-introductions">
843
+ <!-- collect in a variable, so processing is subsequently in document order -->
844
+ <xsl:variable name="introductions" select="ancestor-or-self::task/preceding-sibling::introduction"/>
845
+ <xsl:apply-templates select="$introductions"/>
846
+ </xsl:template>
847
+
848
+ <xsl:template match="*" mode="task-introductions"/>
849
+
850
+ <xsl:template match="task" mode="task-conclusions">
851
+ <!-- collect in a variable, so processing is subsequently in document order -->
852
+ <xsl:variable name="conclusions" select="ancestor-or-self::task/following-sibling::conclusion"/>
853
+ <xsl:apply-templates select="$conclusions"/>
854
+ </xsl:template>
855
+
856
+ <xsl:template match="*" mode="task-conclusions"/>
857
+
858
+ <!-- TODO: by renaming/refactoring the templates inside of -->
859
+ <!-- "htmlsrc" then perhaps several of these templates with -->
860
+ <!-- similar structure can be combined via one larger match. -->
861
+
862
+ <xsl:template match="video[@youtube]" mode="runestone-manifest">
863
+ <question>
864
+ <!-- label is from the "video", or enclosing "figure" -->
865
+ <xsl:apply-templates select="." mode="runestone-manifest-label"/>
866
+ <htmlsrc>
867
+ <xsl:apply-templates select="." mode="runestone-youtube-embed"/>
868
+ </htmlsrc>
869
+ </question>
870
+ </xsl:template>
871
+
872
+ <xsl:template match="program[(@interactive = 'codelens') and not(parent::*[self::exercise or &PROJECT-FILTER;])]" mode="runestone-manifest">
873
+ <question>
874
+ <!-- label is from the "program", or enclosing "listing" -->
875
+ <xsl:apply-templates select="." mode="runestone-manifest-label"/>
876
+ <htmlsrc>
877
+ <xsl:apply-templates select="." mode="runestone-codelens"/>
878
+ </htmlsrc>
879
+ </question>
880
+ </xsl:template>
881
+
882
+ <xsl:template match="program[(@interactive = 'activecode') and not(parent::*[self::exercise or &PROJECT-FILTER;])]" mode="runestone-manifest">
883
+ <!-- verify that program is in a language that can be made active -->
884
+ <xsl:variable name="active-language">
885
+ <xsl:apply-templates select="." mode="active-language"/>
886
+ </xsl:variable>
887
+ <xsl:if test="$active-language != ''">
888
+ <question>
889
+ <!-- label is from the "program", or enclosing "listing" -->
890
+ <xsl:apply-templates select="." mode="runestone-manifest-label"/>
891
+ <htmlsrc>
892
+ <xsl:apply-templates select="." mode="runestone-activecode"/>
893
+ </htmlsrc>
894
+ </question>
895
+ </xsl:if>
896
+ </xsl:template>
897
+
898
+ <!-- Source for every program that is linked to goes in as a <source> -->
899
+ <!-- For activecode/codelens, source will also appear in a <question> element -->
900
+ <!-- but in that location it will be buried in a pile of html. -->
901
+ <xsl:template match="program" mode="runestone-manifest-source">
902
+ <xsl:variable name="filename">
903
+ <xsl:apply-templates select="." mode="runestone-filename"/>
904
+ </xsl:variable>
905
+ <source>
906
+ <xsl:attribute name="filename">
907
+ <xsl:value-of select="$filename"/>
908
+ </xsl:attribute>
909
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
910
+ <xsl:call-template name="sanitize-text">
911
+ <xsl:with-param name="text" select="code"/>
912
+ </xsl:call-template>
913
+ </source>
914
+ </xsl:template>
915
+
916
+ <!-- Get filename to use for an element, fallback on label if filename is missing -->
917
+ <xsl:template match="program|datafile" mode="runestone-filename">
918
+ <xsl:choose>
919
+ <xsl:when test="@filename">
920
+ <xsl:value-of select="@filename"/>
921
+ </xsl:when>
922
+ <xsl:when test="@label">
923
+ <xsl:value-of select="@label"/>
924
+ </xsl:when>
925
+ <xsl:otherwise>
926
+ <xsl:text>Missing filename attribute on </xsl:text>
927
+ <xsl:value-of select="name()"/>
928
+ <xsl:text> with xml:id </xsl:text>
929
+ <xsl:value-of select="@xml:id"/>
930
+ </xsl:otherwise>
931
+ </xsl:choose>
932
+ </xsl:template>
933
+
934
+ <!-- In database with the same structure as an exercise/question. -->
935
+ <!-- When datafile is in appendix, we want to wrap it with <datafile> -->
936
+ <!-- and not write it as a question, which are expected to be in -->
937
+ <!-- numbered chapters. -->
938
+ <xsl:template match="datafile" mode="runestone-manifest">
939
+ <xsl:variable name="container-name">
940
+ <xsl:choose>
941
+ <xsl:when test="ancestor::appendix">
942
+ <xsl:text>datafile</xsl:text>
943
+ </xsl:when>
944
+ <xsl:otherwise>
945
+ <xsl:text>question</xsl:text>
946
+ </xsl:otherwise>
947
+ </xsl:choose>
948
+ </xsl:variable>
949
+ <xsl:element name="{$container-name}">
950
+ <xsl:attribute name="optional">
951
+ <xsl:text>yes</xsl:text>
952
+ </xsl:attribute>
953
+ <!-- label is from the "program", or enclosing "listing" -->
954
+ <xsl:apply-templates select="." mode="runestone-manifest-label"/>
955
+ <htmlsrc>
956
+ <xsl:apply-templates select="." mode="runestone-to-interactive"/>
957
+ </htmlsrc>
958
+ </xsl:element>
959
+ </xsl:template>
960
+
961
+ <!-- Appendix is currently not allowed to have "questions" as Runestone -->
962
+ <!-- requires those to have a chapter with an integer number -->
963
+ <!-- But look for code and datafiles that should go in source_code table -->
964
+ <xsl:template match="appendix[.//program or .//datafile]" mode="runestone-manifest">
965
+ <appendix>
966
+ <!-- Check for programs that have been included elsewhere. -->
967
+ <xsl:apply-templates select=".//program[@xml:id = $linked-programs-list]" mode="runestone-manifest-source"/>
968
+ <!-- Check for datafiles, and write them as "<datafile>"s -->
969
+ <xsl:apply-templates select=".//datafile" mode="runestone-manifest"/>
970
+ </appendix>
971
+ </xsl:template>
972
+ <!-- If no programs or datafiles, do nothing -->
973
+ <xsl:template match="appendix" mode="runestone-manifest"/>
974
+
975
+ <!-- Traverse the tree,looking for things to do -->
976
+ <!-- http://stackoverflow.com/questions/3776333/ -->
977
+ <!-- stripping-all-elements-except-one-in-xml-using-xslt -->
978
+ <xsl:template match="*" mode="runestone-manifest">
979
+ <xsl:apply-templates select="*" mode="runestone-manifest"/>
980
+ </xsl:template>
981
+
982
+ <!-- ########## -->
983
+ <!-- Components -->
984
+ <!-- ########## -->
985
+
986
+ <!-- The "runestone-to-interactive" templates will combine a -->
987
+ <!-- "regular" PreTeXt statement together with some additional -->
988
+ <!-- interactive material to make a hybrid "statement" -->
989
+
990
+ <!-- The application of the "runestone-to-interactive" template is -->
991
+ <!-- controlled by a surrounding "match" that limits elements -->
992
+ <!-- to "exercise", PROJECT-LIKE, and soon "task". So the -->
993
+ <!-- matches here are fine with a *[@exercise-interactive='foo'], -->
994
+ <!-- as a convenience. -->
995
+
996
+ <!-- True/False -->
997
+
998
+ <xsl:template match="*[@exercise-interactive = 'truefalse']" mode="runestone-to-interactive">
999
+ <div class="ptx-runestone-container">
1000
+ <div class="runestone multiplechoice_section">
1001
+ <!-- ul can have multiple answer attribute -->
1002
+ <ul data-component="multiplechoice" data-multipleanswers="false">
1003
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1004
+ <!-- Q: the statement is not a list item, but appears *inside* the list? -->
1005
+ <!-- overall statement, not per-choice -->
1006
+ <xsl:apply-templates select="statement"/>
1007
+ <!-- radio button for True -->
1008
+ <li data-component="answer">
1009
+ <xsl:attribute name="id">
1010
+ <xsl:apply-templates select="." mode="runestone-id"/>
1011
+ <xsl:text>_opt_t</xsl:text>
1012
+ </xsl:attribute>
1013
+ <!-- Correct answer if problem statement is correct/True -->
1014
+ <xsl:if test="statement/@correct = 'yes'">
1015
+ <xsl:attribute name="data-correct"/>
1016
+ </xsl:if>
1017
+ <p>True.</p>
1018
+ </li>
1019
+ <li data-component="feedback">
1020
+ <xsl:attribute name="id">
1021
+ <xsl:apply-templates select="." mode="runestone-id"/>
1022
+ <xsl:text>_opt_t</xsl:text>
1023
+ </xsl:attribute>
1024
+ <!-- identical feedback for each reader responses -->
1025
+ <xsl:apply-templates select="feedback"/>
1026
+ </li>
1027
+ <!-- radio button for False -->
1028
+ <li data-component="answer">
1029
+ <xsl:attribute name="id">
1030
+ <xsl:apply-templates select="." mode="runestone-id"/>
1031
+ <xsl:text>_opt_f</xsl:text>
1032
+ </xsl:attribute>
1033
+ <!-- Correct answer if problem statement is incorrect/False -->
1034
+ <xsl:if test="statement/@correct = 'no'">
1035
+ <xsl:attribute name="data-correct"/>
1036
+ </xsl:if>
1037
+ <p>False.</p>
1038
+ </li>
1039
+ <li data-component="feedback">
1040
+ <xsl:attribute name="id">
1041
+ <xsl:apply-templates select="." mode="runestone-id"/>
1042
+ <xsl:text>_opt_f</xsl:text>
1043
+ </xsl:attribute>
1044
+ <!-- identical feedback for each reader responses -->
1045
+ <xsl:apply-templates select="feedback"/>
1046
+ </li>
1047
+ </ul>
1048
+ </div>
1049
+ </div>
1050
+ </xsl:template>
1051
+
1052
+ <!-- Multiple Choice -->
1053
+
1054
+ <xsl:template match="*[@exercise-interactive = 'multiplechoice']" mode="runestone-to-interactive">
1055
+ <div class="ptx-runestone-container">
1056
+ <div class="runestone multiplechoice_section">
1057
+ <!-- overall statement, not per-choice -->
1058
+ <div class="exercise-statement">
1059
+ <xsl:apply-templates select="statement"/>
1060
+ </div>
1061
+ <!-- ul can have multiple answer attribute -->
1062
+ <ul data-component="multiplechoice" class="exercise-interactives">
1063
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1064
+ <xsl:variable name="ncorrect" select="count(choices/choice[@correct = 'yes'])"/>
1065
+ <xsl:attribute name="data-multipleanswers">
1066
+ <xsl:choose>
1067
+ <xsl:when test="choices/@multiple-correct = 'yes'">
1068
+ <xsl:text>true</xsl:text>
1069
+ </xsl:when>
1070
+ <xsl:when test="choices/@multiple-correct = 'no'">
1071
+ <xsl:text>false</xsl:text>
1072
+ </xsl:when>
1073
+ <xsl:when test="$ncorrect > 1">
1074
+ <xsl:text>true</xsl:text>
1075
+ </xsl:when>
1076
+ <xsl:otherwise>
1077
+ <xsl:text>false</xsl:text>
1078
+ </xsl:otherwise>
1079
+ </xsl:choose>
1080
+ </xsl:attribute>
1081
+ <!-- bare attribute, iff requested -->
1082
+ <xsl:if test="choices/@randomize = 'yes'">
1083
+ <xsl:attribute name="data-random"/>
1084
+ </xsl:if>
1085
+ <xsl:apply-templates select="choices/choice">
1086
+ <xsl:with-param name="the-id">
1087
+ <xsl:apply-templates select="." mode="runestone-id"/>
1088
+ </xsl:with-param>
1089
+ </xsl:apply-templates>
1090
+ </ul>
1091
+ </div>
1092
+ </div>
1093
+ </xsl:template>
1094
+
1095
+ <xsl:template match="choices/choice">
1096
+ <xsl:param name="the-id"/>
1097
+
1098
+ <!-- id for each "choice" -->
1099
+ <!-- with common base, then a, b, c suffix -->
1100
+ <!-- Used *twice* on adjacent "li"? -->
1101
+ <xsl:variable name="choice-id">
1102
+ <xsl:value-of select="$the-id"/>
1103
+ <xsl:text>_opt_</xsl:text>
1104
+ <!-- will count preceding "choice" only -->
1105
+ <xsl:number format="a"/>
1106
+ </xsl:variable>
1107
+ <li data-component="answer">
1108
+ <xsl:attribute name="id">
1109
+ <xsl:value-of select="$choice-id"/>
1110
+ </xsl:attribute>
1111
+ <!-- mark correct answers (empty attribute value) -->
1112
+ <xsl:if test="@correct = 'yes'">
1113
+ <xsl:attribute name="data-correct"/>
1114
+ </xsl:if>
1115
+ <!-- per-choice statement -->
1116
+ <xsl:apply-templates select="statement"/>
1117
+ </li>
1118
+ <li data-component="feedback">
1119
+ <xsl:attribute name="id">
1120
+ <xsl:value-of select="$choice-id"/>
1121
+ </xsl:attribute>
1122
+ <!-- per-choice explanation -->
1123
+ <xsl:apply-templates select="feedback"/>
1124
+ </li>
1125
+ </xsl:template>
1126
+
1127
+ <!-- Parsons Problem -->
1128
+
1129
+ <xsl:template match="*[@exercise-interactive = 'parson']" mode="runestone-to-interactive">
1130
+ <!-- active-language only used if runnable but needed multiple places -->
1131
+ <xsl:variable name="active-language">
1132
+ <xsl:apply-templates select="." mode="active-language"/>
1133
+ </xsl:variable>
1134
+ <!-- determine this option before context switches -->
1135
+ <xsl:variable name="b-natural" select="($active-language = '') or ($active-language = 'natural')"/>
1136
+ <div class="ptx-runestone-container">
1137
+ <div class="runestone parsons_section" style="max-width: none;">
1138
+ <div data-component="parsons" class="parsons">
1139
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1140
+ <div class="parsons_question parsons-text" >
1141
+ <!-- the prompt -->
1142
+ <xsl:apply-templates select="statement"/>
1143
+ </div>
1144
+ <pre class="parsonsblocks" data-question_label="" style="visibility: hidden;">
1145
+ <!-- presence of a program implies runnable when completed -->
1146
+ <xsl:if test="program">
1147
+ <xsl:choose>
1148
+ <xsl:when test="$active-language != ''">
1149
+ <xsl:attribute name="data-runnable">
1150
+ <xsl:text>true</xsl:text>
1151
+ </xsl:attribute>
1152
+ </xsl:when>
1153
+ <xsl:otherwise>
1154
+ <xsl:variable name="id">
1155
+ <xsl:apply-templates select="." mode="unique-id"/>
1156
+ </xsl:variable>
1157
+ <xsl:message>PTX:WARNING: Parsons problems need a @langauge that is a valid activecode language to be runnable</xsl:message>
1158
+ <xsl:message> id: <xsl:value-of select="$id"></xsl:value-of></xsl:message>
1159
+ </xsl:otherwise>
1160
+ </xsl:choose>
1161
+ </xsl:if>
1162
+ <!-- author opts-in to adaptive problems -->
1163
+ <xsl:attribute name="data-language">
1164
+ <xsl:choose>
1165
+ <xsl:when test="$b-natural">
1166
+ <xsl:text>natural</xsl:text>
1167
+ </xsl:when>
1168
+ <xsl:otherwise>
1169
+ <!-- must now have a language -->
1170
+ <xsl:value-of select="$active-language"/>
1171
+ </xsl:otherwise>
1172
+ </xsl:choose>
1173
+ </xsl:attribute>
1174
+ <!-- do not confuse this attribute with the @pi:numbered -->
1175
+ <!-- attribute for numbered equations -->
1176
+ <xsl:variable name="numbered" select="blocks/@numbered"/>
1177
+ <xsl:choose>
1178
+ <xsl:when test="($numbered = 'left') or ($numbered = 'right')">
1179
+ <xsl:attribute name="data-numbered">
1180
+ <xsl:value-of select="$numbered"/>
1181
+ </xsl:attribute>
1182
+ </xsl:when>
1183
+ <!-- default is un-numbered, so no attribute at all -->
1184
+ <xsl:when test="$numbered = 'no'"/>
1185
+ <xsl:otherwise/>
1186
+ </xsl:choose>
1187
+ <xsl:if test="@adaptive = 'yes'">
1188
+ <xsl:attribute name="data-adaptive">
1189
+ <xsl:text>true</xsl:text>
1190
+ </xsl:attribute>
1191
+ </xsl:if>
1192
+ <xsl:if test="blocks/block/@depends">
1193
+ <xsl:attribute name="data-grader">
1194
+ <xsl:text>dag</xsl:text>
1195
+ </xsl:attribute>
1196
+ </xsl:if>
1197
+ <!-- Fixed-order rendering, gated by blocks/@randomize='no' -->
1198
+ <xsl:if test="blocks/@randomize = 'no'">
1199
+ <xsl:attribute name="data-order">
1200
+ <xsl:apply-templates select="blocks" mode="parsons-data-order"/>
1201
+ </xsl:attribute>
1202
+ </xsl:if>
1203
+ <!-- author asks student to provide indentation via -->
1204
+ <!-- the indentation-enabled "drop" text window -->
1205
+ <!-- (not relevant for natural language) -->
1206
+ <xsl:attribute name="data-noindent">
1207
+ <xsl:choose>
1208
+ <xsl:when test="@indentation = 'hide'">
1209
+ <xsl:text>false</xsl:text>
1210
+ </xsl:when>
1211
+ <!-- default is 'show' -->
1212
+ <xsl:otherwise>
1213
+ <xsl:text>true</xsl:text>
1214
+ </xsl:otherwise>
1215
+ </xsl:choose>
1216
+ </xsl:attribute>
1217
+ <!-- the blocks -->
1218
+ <xsl:apply-templates select="blocks/block" mode="vertical-blocks">
1219
+ <xsl:with-param name="b-natural" select="$b-natural"/>
1220
+ </xsl:apply-templates>
1221
+ </pre>
1222
+ </div>
1223
+ <xsl:if test="program">
1224
+ <!-- Parsons is executable when finished -->
1225
+ <div style="display: none">
1226
+ <xsl:attribute name="id">
1227
+ <xsl:apply-templates select="." mode="runestone-id"/>
1228
+ <xsl:text>-runnable</xsl:text>
1229
+ </xsl:attribute>
1230
+ <div data-component="parsons-runnable">
1231
+ <xsl:attribute name="id">
1232
+ <xsl:apply-templates select="." mode="runestone-id"/>
1233
+ <xsl:text>-runnable-ac</xsl:text>
1234
+ </xsl:attribute>
1235
+ <textarea data-lang="{$active-language}" data-audio="" data-coach="true" style="visibility: hidden;">
1236
+ <xsl:variable name="hosting">
1237
+ <xsl:apply-templates select="." mode="activecode-host"/>
1238
+ </xsl:variable>
1239
+ <!-- loop just to set context. program should be single -->
1240
+ <xsl:for-each select="program">
1241
+ <xsl:call-template name="runestone-activecode-editor-attributes">
1242
+ <xsl:with-param name="active-language" select="$active-language"/>
1243
+ <xsl:with-param name="hosting" select="$hosting"/>
1244
+ </xsl:call-template>
1245
+ </xsl:for-each>
1246
+ <!-- the content -->
1247
+ <xsl:text>&#xa;</xsl:text>
1248
+ <xsl:call-template name="add-indentation">
1249
+ <xsl:with-param name="text">
1250
+ <xsl:call-template name="sanitize-text">
1251
+ <xsl:with-param name="text" select="program-preamble"/>
1252
+ <xsl:with-param name="preserve-end" select="true()"/>
1253
+ </xsl:call-template>
1254
+ </xsl:with-param>
1255
+ <xsl:with-param name="indent"><xsl:value-of select="program-preamble/@indent"/></xsl:with-param>
1256
+ </xsl:call-template>
1257
+ <!-- placeholder for user code -->
1258
+ <xsl:text>==PARSONSCODE==&#xa;</xsl:text>
1259
+ <xsl:call-template name="add-indentation">
1260
+ <xsl:with-param name="text">
1261
+ <xsl:call-template name="sanitize-text">
1262
+ <xsl:with-param name="text" select="program-postamble"/>
1263
+ <xsl:with-param name="preserve-start" select="true()"/>
1264
+ </xsl:call-template>
1265
+ </xsl:with-param>
1266
+ <xsl:with-param name="indent"><xsl:value-of select="program-postamble/@indent"/></xsl:with-param>
1267
+ </xsl:call-template>
1268
+ </textarea>
1269
+ </div>
1270
+ </div>
1271
+ </xsl:if>
1272
+ </div>
1273
+ </div>
1274
+ </xsl:template>
1275
+
1276
+ <!-- Helper: build the @data-order list (comma-separated, -->
1277
+ <!-- 0-based) in the order specified by @order on each -->
1278
+ <!-- block. A block containing "choice" children does -->
1279
+ <!-- not get an index; each choice gets one in its place. -->
1280
+ <xsl:template match="blocks" mode="parsons-data-order">
1281
+ <xsl:for-each select="block">
1282
+ <xsl:sort select="@order" data-type="number"/>
1283
+ <xsl:if test="position() &gt; 1">
1284
+ <xsl:text>,</xsl:text>
1285
+ </xsl:if>
1286
+ <xsl:choose>
1287
+ <xsl:when test="choice">
1288
+ <xsl:for-each select="choice">
1289
+ <xsl:if test="position() &gt; 1">
1290
+ <xsl:text>,</xsl:text>
1291
+ </xsl:if>
1292
+ <!-- This choice is a leaf. Its 0-based index is the count -->
1293
+ <!-- of leaves preceding it: choice-less blocks and choices -->
1294
+ <!-- of blocks before its parent, plus its earlier siblings. -->
1295
+ <xsl:value-of select="count(../preceding-sibling::block[not(choice)])
1296
+ + count(../preceding-sibling::block/choice)
1297
+ + count(preceding-sibling::choice)"/>
1298
+ </xsl:for-each>
1299
+ </xsl:when>
1300
+ <xsl:otherwise>
1301
+ <!-- This choice-less block is a leaf. Its 0-based index -->
1302
+ <!-- is the count of leaves preceding it: choice-less -->
1303
+ <!-- blocks, plus choices belonging to blocks-with-choices. -->
1304
+ <xsl:value-of select="count(preceding-sibling::block[not(choice)])
1305
+ + count(preceding-sibling::block/choice)"/>
1306
+ </xsl:otherwise>
1307
+ </xsl:choose>
1308
+ </xsl:for-each>
1309
+ </xsl:template>
1310
+
1311
+ <xsl:template match="blocks/block" mode="vertical-blocks">
1312
+ <xsl:param name="b-natural"/>
1313
+ <xsl:variable name="name">
1314
+ <xsl:choose>
1315
+ <xsl:when test="@name|@depends">
1316
+ <xsl:text> #tag:</xsl:text>
1317
+ <xsl:value-of select="@name"/>
1318
+ <xsl:text>; depends:</xsl:text>
1319
+ <xsl:value-of select="str:replace(@depends, ' ', ',')"/>
1320
+ <xsl:text>;</xsl:text>
1321
+ </xsl:when>
1322
+ </xsl:choose>
1323
+ </xsl:variable>
1324
+
1325
+ <xsl:choose>
1326
+ <xsl:when test="choice">
1327
+ <!-- put single correct choice first -->
1328
+ <!-- default on "choice" is correct="no" -->
1329
+ <xsl:apply-templates select="choice[@correct = 'yes']">
1330
+ <xsl:with-param name="b-natural" select="$b-natural"/>
1331
+ <xsl:with-param name="name" select="$name"/>
1332
+ </xsl:apply-templates>
1333
+ <xsl:apply-templates select="choice[not(@correct = 'yes')]">
1334
+ <xsl:with-param name="b-natural" select="$b-natural"/>
1335
+ <xsl:with-param name="name"/>
1336
+ </xsl:apply-templates>
1337
+ </xsl:when>
1338
+ <xsl:otherwise>
1339
+ <xsl:choose>
1340
+ <xsl:when test="$b-natural">
1341
+ <xsl:apply-templates/>
1342
+ </xsl:when>
1343
+ <xsl:otherwise>
1344
+ <xsl:for-each select="cline">
1345
+ <xsl:value-of select="."/>
1346
+ <xsl:if test="following-sibling::cline">
1347
+ <xsl:text>&#xa;</xsl:text>
1348
+ </xsl:if>
1349
+ </xsl:for-each>
1350
+ </xsl:otherwise>
1351
+ </xsl:choose>
1352
+ <!-- default on "block" is correct="yes" -->
1353
+ <xsl:if test="@correct = 'no'">
1354
+ <xsl:text> #distractor</xsl:text>
1355
+ </xsl:if>
1356
+ <xsl:value-of select="$name"/>
1357
+ </xsl:otherwise>
1358
+ </xsl:choose>
1359
+ <xsl:if test="following-sibling::block">
1360
+ <xsl:text>&#xa;---&#xa;</xsl:text>
1361
+ </xsl:if>
1362
+ </xsl:template>
1363
+
1364
+ <xsl:template match="blocks/block/choice">
1365
+ <xsl:param name="b-natural"/>
1366
+ <xsl:param name="name"/>
1367
+
1368
+ <!-- Exactly one choice is correct, it is placed first. -->
1369
+ <!-- Then the n - 1 separators can be placed on all -->
1370
+ <!-- the "wrong" choices. -->
1371
+ <xsl:if test="not(@correct = 'yes')">
1372
+ <xsl:text>&#xa;---&#xa;</xsl:text>
1373
+ </xsl:if>
1374
+ <xsl:choose>
1375
+ <xsl:when test="$b-natural">
1376
+ <xsl:apply-templates select="*"/>
1377
+ </xsl:when>
1378
+ <xsl:otherwise>
1379
+ <xsl:for-each select="cline">
1380
+ <xsl:value-of select="."/>
1381
+ <xsl:if test="following-sibling::cline">
1382
+ <xsl:text>&#xa;</xsl:text>
1383
+ </xsl:if>
1384
+ </xsl:for-each>
1385
+ </xsl:otherwise>
1386
+ </xsl:choose>
1387
+ <xsl:if test="not(@correct = 'yes')">
1388
+ <xsl:text> #paired</xsl:text>
1389
+ </xsl:if>
1390
+ <xsl:value-of select="$name"/>
1391
+ </xsl:template>
1392
+
1393
+ <!-- Parsons Problem (Horizontal)-->
1394
+
1395
+ <xsl:template match="*[@exercise-interactive = 'parson-horizontal']" mode="runestone-to-interactive">
1396
+ <!-- determine these options before context switches -->
1397
+ <xsl:variable name="active-language">
1398
+ <xsl:apply-templates select="." mode="active-language"/>
1399
+ </xsl:variable>
1400
+ <xsl:variable name="b-natural" select="($active-language = '') or ($active-language = 'natural')"/>
1401
+ <!-- randomize by default, so must explicitly turn off -->
1402
+ <xsl:variable name="b-randomize" select="not(blocks/@randomize = 'no')"/>
1403
+ <!-- A @ref is automatic indicator, else reuse has been requested on blocks -->
1404
+ <xsl:variable name="b-reuse" select="blocks/block[@ref] or (blocks/@reuse = 'yes')"/>
1405
+ <!-- We loop over blocks, as authored, so this is just a convenience -->
1406
+ <xsl:variable name="authored-blocks" select="blocks/block"/>
1407
+ <!-- A block with @ref indicates reuse of some other block, for answer -->
1408
+ <!-- specifications we want to treat them as duplicates. And we do not -->
1409
+ <!-- write them into the HTML either. So we make a subset of *unique* blocks. -->
1410
+ <xsl:variable name="unique-blocks" select="blocks/block[not(@ref)]"/>
1411
+
1412
+ <div class="ptx-runestone-container">
1413
+ <div class="runestone hparsons_section">
1414
+ <div data-component="hparsons" class="hparsons_section">
1415
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1416
+ <div class="hp_question">
1417
+ <!-- the prompt -->
1418
+ <xsl:apply-templates select="statement"/>
1419
+ </div>
1420
+ <!-- empty div seems necessary? -->
1421
+ <div class="hparsons"/>
1422
+ <textarea style="visibility: hidden">
1423
+ <!-- A SQL database can be provided for automated -->
1424
+ <!-- testing of correct answers via unit tests. -->
1425
+ <!-- This is a location in the external directory. -->
1426
+ <!-- NB: sample had paths with a leading backslash -->
1427
+ <xsl:if test="@database">
1428
+ <xsl:attribute name="data-dburl">
1429
+ <xsl:choose>
1430
+ <xsl:when test="$b-managed-directories">
1431
+ <xsl:value-of select="$external-directory"/>
1432
+ <xsl:value-of select="@database"/>
1433
+ </xsl:when>
1434
+ <xsl:otherwise>
1435
+ <xsl:value-of select="@database"/>
1436
+ </xsl:otherwise>
1437
+ </xsl:choose>
1438
+ </xsl:attribute>
1439
+ </xsl:if>
1440
+ <!-- for natural language, just skip attribute -->
1441
+ <xsl:if test="not($b-natural)">
1442
+ <xsl:attribute name="data-language">
1443
+ <xsl:value-of select="$active-language"/>
1444
+ </xsl:attribute>
1445
+ </xsl:if>
1446
+ <!-- default is to randomize, so only set -->
1447
+ <!-- to "false" when explicitly requested -->
1448
+ <xsl:attribute name="data-randomize">
1449
+ <xsl:choose>
1450
+ <xsl:when test="$b-randomize">
1451
+ <xsl:text>true</xsl:text>
1452
+ </xsl:when>
1453
+ <xsl:otherwise>
1454
+ <xsl:text>false</xsl:text>
1455
+ </xsl:otherwise>
1456
+ </xsl:choose>
1457
+ </xsl:attribute>
1458
+ <!-- default is to not allow reuse, so only -->
1459
+ <!-- set to "true" when explicitly requested -->
1460
+ <xsl:attribute name="data-reuse">
1461
+ <xsl:choose>
1462
+ <xsl:when test="$b-reuse">
1463
+ <xsl:text>true</xsl:text>
1464
+ </xsl:when>
1465
+ <xsl:otherwise>
1466
+ <xsl:text>false</xsl:text>
1467
+ </xsl:otherwise>
1468
+ </xsl:choose>
1469
+ </xsl:attribute>
1470
+ <!-- the @blockanswer attribute is a space-separated -->
1471
+ <!-- list of the block numbers, *in the order listed -->
1472
+ <!-- in the HTML* (see below), starting at zero, that -->
1473
+ <!-- consitutes a correct answer. But the authored -->
1474
+ <!-- order is the corrrect answer, so we loop over -->
1475
+ <!-- those blocks as we determine positions/locations -->
1476
+ <!-- in the HTML list. -->
1477
+ <!-- NB: the "blockanswer" appears to be parsed based -->
1478
+ <!-- on *exactly* one space between separating indices -->
1479
+ <xsl:variable name="blockanswer">
1480
+ <!-- answer is list as long as authored blocks, -->
1481
+ <!-- so loop over authored in every case -->
1482
+ <xsl:for-each select="$authored-blocks">
1483
+ <!-- save off the block in question before context shifts below -->
1484
+ <xsl:variable name="the-block" select="."/>
1485
+ <xsl:choose>
1486
+ <!-- Randomized or not (below), a distractor is a distractor -->
1487
+ <!-- and does not go in the "blockanswer" attribute. -->
1488
+ <xsl:when test="@correct = 'no'"/>
1489
+ <!-- For the randomized case the answer is list of -->
1490
+ <!-- non-negative integers in order, but interrupted -->
1491
+ <!-- by duplicates authored as references as references. -->
1492
+ <xsl:when test="$b-randomize">
1493
+ <xsl:choose>
1494
+ <!-- placeholder/pointer/resuse, locate origin -->
1495
+ <xsl:when test="@ref">
1496
+ <xsl:variable name="target" select="id(@ref)"/>
1497
+ <!-- error-check $target here? -->
1498
+ <xsl:for-each select="$unique-blocks">
1499
+ <xsl:if test="count(.|$target) = 1">
1500
+ <xsl:value-of select="position() - 1"/>
1501
+ </xsl:if>
1502
+ </xsl:for-each>
1503
+ </xsl:when>
1504
+ <!-- "original" block, count place among originals -->
1505
+ <xsl:otherwise>
1506
+ <xsl:for-each select="$unique-blocks">
1507
+ <xsl:if test="count(.|$the-block) = 1">
1508
+ <xsl:value-of select="position() - 1"/>
1509
+ </xsl:if>
1510
+ </xsl:for-each>
1511
+ </xsl:otherwise>
1512
+ </xsl:choose>
1513
+ </xsl:when>
1514
+ <!-- For the fixed rearrangement case, the blocks are -->
1515
+ <!-- rearranged in the HTML, so we re-create that -->
1516
+ <!-- order when determining the location/number of any -->
1517
+ <!-- given block. The "choose" stanza here is identical -->
1518
+ <!-- to the above, except the unique blocks are sorted -->
1519
+ <!-- into their HTML order. (Can't see how to vary an -->
1520
+ <!-- unsorted list versus a sorted list as a parameter -->
1521
+ <!-- to remove duplication.) -->
1522
+ <xsl:otherwise>
1523
+ <xsl:choose>
1524
+ <!-- placeholder/pointer/resuse, locate origin -->
1525
+ <xsl:when test="@ref">
1526
+ <xsl:variable name="target" select="id(@ref)"/>
1527
+ <!-- error-check $target here? -->
1528
+ <xsl:for-each select="$unique-blocks">
1529
+ <xsl:sort select="@order"/>
1530
+ <xsl:if test="count(.|$target) = 1">
1531
+ <xsl:value-of select="position() - 1"/>
1532
+ </xsl:if>
1533
+ </xsl:for-each>
1534
+ </xsl:when>
1535
+ <!-- "original" block, count place among originals -->
1536
+ <xsl:otherwise>
1537
+ <xsl:for-each select="$unique-blocks">
1538
+ <xsl:sort select="@order"/>
1539
+ <xsl:if test="count(.|$the-block) = 1">
1540
+ <xsl:value-of select="position() - 1"/>
1541
+ </xsl:if>
1542
+ </xsl:for-each>
1543
+ </xsl:otherwise>
1544
+ </xsl:choose>
1545
+ </xsl:otherwise>
1546
+ </xsl:choose>
1547
+ <!-- space-separated list, per block, one extra at the very end -->
1548
+ <!-- when a distractor is skipped, do not place a separator -->
1549
+ <xsl:if test="not(@correct= 'no')">
1550
+ <xsl:text> </xsl:text>
1551
+ </xsl:if>
1552
+ </xsl:for-each>
1553
+ </xsl:variable>
1554
+ <!-- strip an extra space separator created just above -->
1555
+ <xsl:attribute name="data-blockanswer">
1556
+ <xsl:value-of select="substring($blockanswer, 1, string-length($blockanswer) - 1)"/>
1557
+ </xsl:attribute>
1558
+ <!-- blocks themselves, left justified on margin -->
1559
+ <!-- reused blocks are not presented, distractors are -->
1560
+ <!-- leading newline is just cosmetic -->
1561
+ <xsl:text>&#xa;--blocks--&#xa;</xsl:text>
1562
+ <xsl:choose>
1563
+ <!-- just go with authored order as canonical -->
1564
+ <xsl:when test="$b-randomize">
1565
+ <xsl:apply-templates select="$unique-blocks" mode="horizontal-blocks">
1566
+ <xsl:with-param name="b-natural" select="$b-natural"/>
1567
+ </xsl:apply-templates>
1568
+ </xsl:when>
1569
+ <!-- sort by the order provided by author -->
1570
+ <xsl:otherwise>
1571
+ <xsl:for-each select="$unique-blocks">
1572
+ <xsl:sort select="@order"/>
1573
+ <xsl:apply-templates select="." mode="horizontal-blocks">
1574
+ <xsl:with-param name="b-natural" select="$b-natural"/>
1575
+ </xsl:apply-templates>
1576
+ </xsl:for-each>
1577
+ </xsl:otherwise>
1578
+ </xsl:choose>
1579
+ <!-- a block of unit tests for automatic feedback (with, say, an SQL database) -->
1580
+ <!-- NB: could mimic "program/tests" to avoid empty "tests" elements -->
1581
+ <xsl:if test="tests">
1582
+ <xsl:text>--unittest--&#xa;</xsl:text>
1583
+ <xsl:call-template name="sanitize-text">
1584
+ <xsl:with-param name="text" select="tests" />
1585
+ </xsl:call-template>
1586
+ </xsl:if>
1587
+ </textarea>
1588
+ </div>
1589
+ </div>
1590
+ </div>
1591
+ </xsl:template>
1592
+
1593
+ <!-- Assumes a "tight" run of text, or a "c", no newlines authored. -->
1594
+ <!-- Non-reused block. Perhaps text should be massaged here? -->
1595
+ <!-- We do not ever dump duplicates into HTML or to the reader -->
1596
+ <xsl:template match="blocks/block[not(@ref)]" mode="horizontal-blocks">
1597
+ <xsl:param name="b-natural"/>
1598
+
1599
+ <xsl:choose>
1600
+ <xsl:when test="$b-natural">
1601
+ <xsl:apply-templates select="."/>
1602
+ </xsl:when>
1603
+ <!-- Now this a problem with code, requiring a "c" wrapper -->
1604
+ <!-- 2023-03-07: can move to warnings or validation-plus, semi-deprecated -->
1605
+ <xsl:when test="not(c)">
1606
+ <xsl:message>PTX:WARNING: a block of a horizontal Parson problem with</xsl:message>
1607
+ <xsl:message> code needs to be enclosed in a "c" element</xsl:message>
1608
+ <xsl:apply-templates select="."/>
1609
+ <xsl:text> (NEEDS "c" ELEMENT)</xsl:text>
1610
+ </xsl:when>
1611
+ <xsl:otherwise>
1612
+ <xsl:value-of select="c"/>
1613
+ </xsl:otherwise>
1614
+ </xsl:choose>
1615
+ <xsl:text>&#xa;</xsl:text>
1616
+ </xsl:template>
1617
+
1618
+ <!-- Carsort Problem -->
1619
+
1620
+ <xsl:template match="*[@exercise-interactive = 'cardsort']" mode="runestone-to-interactive">
1621
+ <div class="ptx-runestone-container">
1622
+ <div class="runestone cardsort_section">
1623
+ <ul data-component="dragndrop" data-question_label="" style="visibility: hidden;">
1624
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1625
+ <span data-subcomponent="question">
1626
+ <xsl:apply-templates select="statement"/>
1627
+ </span>
1628
+ <xsl:if test="feedback">
1629
+ <span data-subcomponent="feedback">
1630
+ <xsl:apply-templates select="feedback"/>
1631
+ </span>
1632
+ </xsl:if>
1633
+ <!-- NB: need to compute Runestone ID in current context -->
1634
+ <!-- and save off before changing context with for-each -->
1635
+ <xsl:variable name="rsid">
1636
+ <xsl:apply-templates select="." mode="runestone-id"/>
1637
+ </xsl:variable>
1638
+ <xsl:for-each select="cardsort/match">
1639
+
1640
+ <!-- PTX premise = RS draggable -->
1641
+ <!-- may be multiple premise or none -->
1642
+ <xsl:for-each select="premise">
1643
+ <li data-subcomponent="draggable">
1644
+ <xsl:attribute name="id">
1645
+ <xsl:value-of select="$rsid"/>
1646
+ <xsl:text>_drag</xsl:text>
1647
+ <xsl:number count="premise" from="cardsort" level="any"/>
1648
+ </xsl:attribute>
1649
+ <xsl:apply-templates select="parent::match" mode="category-attribute"/>
1650
+ <xsl:apply-templates select="."/>
1651
+ </li>
1652
+ </xsl:for-each>
1653
+ <!-- PTX response = RS dropzone -->
1654
+ <!-- one response, or none -->
1655
+ <xsl:for-each select="response">
1656
+ <li data-subcomponent="dropzone">
1657
+ <xsl:attribute name="for">
1658
+ <xsl:value-of select="$rsid"/>
1659
+ <xsl:text>_drag</xsl:text>
1660
+ <xsl:number count="response" from="cardsort" level="any"/>
1661
+ </xsl:attribute>
1662
+ <xsl:apply-templates select="parent::match" mode="category-attribute"/>
1663
+ <xsl:apply-templates select="."/>
1664
+ </li>
1665
+ </xsl:for-each>
1666
+ </xsl:for-each>
1667
+ </ul>
1668
+ </div>
1669
+ </div>
1670
+ </xsl:template>
1671
+
1672
+ <!-- A "category" is simply the sequence number of an -->
1673
+ <!-- enclosing "match" element, which serves to group -->
1674
+ <!-- any number of "premise" with zero or one "response", -->
1675
+ <!-- indicating they have a relationship (they match!). -->
1676
+ <xsl:template match="match" mode="category-attribute">
1677
+ <xsl:attribute name="data-category">
1678
+ <!-- no @count, then implies count="match" (peers), -->
1679
+ <!-- from="cardsort", while @level="single" is default -->
1680
+ <xsl:number/>
1681
+ </xsl:attribute>
1682
+ </xsl:template>
1683
+
1684
+ <!-- Matching Problem -->
1685
+
1686
+ <xsl:template match="*[@exercise-interactive = 'matching']" mode="runestone-to-interactive">
1687
+ <div class="ptx-runestone-container">
1688
+ <div data-component="matching" class="runestone">
1689
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1690
+ <!-- Note: Runestone will condition on script/@type as -->
1691
+ <!-- it handles this (mostly to differentiate between -->
1692
+ <!-- JSON blobs. Note below describes how this is -->
1693
+ <!-- really HTML, not XML. So any adjustment here -->
1694
+ <!-- must be coordinated with the Runestone consumer. -->
1695
+ <script type="text/xml">
1696
+ <!-- one-off XML for Runestone JS to consume -->
1697
+ <matching>
1698
+ <!-- Provide authored statement of the exercise. -->
1699
+ <!-- Here, and elsewhere, we need to be careful. -->
1700
+ <!-- We want to produce XML, but a rendered -->
1701
+ <!-- "statement" will be HTML and have some -->
1702
+ <!-- undesirable things, like unclosed tags -->
1703
+ <!-- (e.g. "img"). We really want what was once -->
1704
+ <!-- called XHTML. Our made-up XML below seems -->
1705
+ <!-- to be OK when parsed as if it were HTML. -->
1706
+ <statement>
1707
+ <xsl:apply-templates select="statement"/>
1708
+ </statement>
1709
+ <!-- "feedback" is for the interactive interface -->
1710
+ <!-- to show to the reader at the right time -->
1711
+ <feedback>
1712
+ <xsl:apply-templates select="feedback"/>
1713
+ </feedback>
1714
+ <!-- sequence of "premise" -->
1715
+ <xsl:apply-templates select="matching/premise" mode="node-info"/>
1716
+ <!-- sequence of "response" -->
1717
+ <xsl:apply-templates select="matching/response" mode="node-info"/>
1718
+ <!-- sequence of "edge" -->
1719
+ <xsl:apply-templates select="matching" mode="matching-adjacencies"/>
1720
+ </matching>
1721
+ </script>
1722
+ </div>
1723
+ </div>
1724
+ </xsl:template>
1725
+
1726
+ <!-- utility template to create unique ID for each premise and -->
1727
+ <!-- response for use in the user interface, invisible to the reader -->
1728
+ <xsl:template match="matching/premise|matching/response" mode="matching-identification">
1729
+ <xsl:value-of select="parent::matching/parent::*/@label"/>
1730
+ <xsl:text>-</xsl:text>
1731
+ <xsl:choose>
1732
+ <xsl:when test="self::premise">
1733
+ <xsl:text>p</xsl:text>
1734
+ </xsl:when>
1735
+ <xsl:when test="self::response">
1736
+ <xsl:text>r</xsl:text>
1737
+ </xsl:when>
1738
+ </xsl:choose>
1739
+ <!-- counts among elements of current type -->
1740
+ <xsl:number/>
1741
+ </xsl:template>
1742
+
1743
+ <xsl:template match="premise|response" mode="node-info">
1744
+ <xsl:variable name="element-name" select="local-name(.)"/>
1745
+ <xsl:element name="{$element-name}">
1746
+ <id>
1747
+ <xsl:apply-templates select="." mode="matching-identification"/>
1748
+ </id>
1749
+ <label>
1750
+ <xsl:apply-templates select="."/>
1751
+ </label>
1752
+ </xsl:element>
1753
+ </xsl:template>
1754
+
1755
+
1756
+ <xsl:template match="matching" mode="matching-adjacencies">
1757
+ <!-- Run over all response, to see if their single @xml:id -->
1758
+ <!-- is present in the premise's multi-valued @ref -->
1759
+ <!-- array of pairs -->
1760
+ <xsl:for-each select="premise">
1761
+ <!-- save off some "premise" info before context change -->
1762
+ <xsl:variable name="the-ref-fenced" select="concat('|', translate(@ref, ' ', '|'), '|')"/>
1763
+ <xsl:variable name="the-premise-identification">
1764
+ <xsl:apply-templates select="." mode="matching-identification"/>
1765
+ </xsl:variable>
1766
+ <!-- note context is no longer "matching", so step up -->
1767
+ <xsl:for-each select="../response">
1768
+ <xsl:variable name="response-xmlid-fenced" select="concat('|', @xml:id, '|')"/>
1769
+ <!-- finally, the test for adjacency -->
1770
+ <xsl:if test="contains($the-ref-fenced, $response-xmlid-fenced)">
1771
+ <!-- a pair, connecting "premise" to "response"-->
1772
+ <edge>
1773
+ <label>
1774
+ <!-- "matching-identification", saved off above -->
1775
+ <xsl:value-of select="$the-premise-identification"/>
1776
+ </label>
1777
+ <label>
1778
+ <xsl:apply-templates select="." mode="matching-identification"/>
1779
+ </label>
1780
+ </edge>
1781
+ </xsl:if>
1782
+ </xsl:for-each>
1783
+ </xsl:for-each>
1784
+ </xsl:template>
1785
+
1786
+
1787
+ <!-- Clickable Area Problem -->
1788
+
1789
+ <xsl:template match="*[@exercise-interactive = 'clickablearea']" mode="runestone-to-interactive">
1790
+ <div class="ptx-runestone-container">
1791
+ <div class="runestone clickablearea_section">
1792
+ <div data-component="clickablearea" data-question_label="" style="visibility: hidden;">
1793
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1794
+ <span data-question="">
1795
+ <xsl:apply-templates select="statement"/>
1796
+ </span>
1797
+ <span data-feedback="">
1798
+ <xsl:apply-templates select="feedback"/>
1799
+ </span>
1800
+ <xsl:apply-templates select="areas" mode="clickable-html"/>
1801
+ </div>
1802
+ </div>
1803
+ </div>
1804
+ </xsl:template>
1805
+
1806
+ <!-- We use modal templates, primarily for the case of code, -->
1807
+ <!-- so we do not mangle text() using routines in -common -->
1808
+ <!-- and to preserve the structure of the program. -->
1809
+
1810
+ <xsl:template match="areas" mode="clickable-html">
1811
+ <xsl:choose>
1812
+ <xsl:when test="cline">
1813
+ <!-- code, so make the "pre" structure -->
1814
+ <pre>
1815
+ <xsl:apply-templates select="cline" mode="clickable-html"/>
1816
+ </pre>
1817
+ </xsl:when>
1818
+ <xsl:otherwise>
1819
+ <!-- regular text, this will match a default template later -->
1820
+ <xsl:apply-templates select="*" mode="clickable-html"/>
1821
+ </xsl:otherwise>
1822
+ </xsl:choose>
1823
+ </xsl:template>
1824
+
1825
+ <!-- A mix of text and "area", and needs a newline -->
1826
+ <xsl:template match="areas/cline" mode="clickable-html">
1827
+ <xsl:apply-templates select="text()|area" mode="clickable-html"/>
1828
+ <xsl:text>&#xa;</xsl:text>
1829
+ </xsl:template>
1830
+
1831
+ <!-- Avoid falling into low-level manipulations to remain verbatim -->
1832
+ <xsl:template match="areas/cline/text()" mode="clickable-html">
1833
+ <xsl:value-of select="."/>
1834
+ </xsl:template>
1835
+
1836
+ <!-- Constructions for regular text ("p", "ul", etc) should -->
1837
+ <!-- drop out to default templates, as will an "area" element -->
1838
+ <!-- (both code and regular text). -->
1839
+ <xsl:template match="*" mode="clickable-html">
1840
+ <xsl:apply-templates select="."/>
1841
+ </xsl:template>
1842
+
1843
+ <!-- NB: we want a generic template (not modal) for use within -->
1844
+ <!-- sentences, etc. As such it will then be available in every -->
1845
+ <!-- derived conversion, *but* the "area" element should not -->
1846
+ <!-- survive a conversion to a static form, so will not be present. -->
1847
+ <xsl:template match="area">
1848
+ <span>
1849
+ <xsl:choose>
1850
+ <xsl:when test="@correct = 'no'">
1851
+ <xsl:attribute name="data-incorrect"/>
1852
+ </xsl:when>
1853
+ <xsl:otherwise>
1854
+ <xsl:attribute name="data-correct"/>
1855
+ </xsl:otherwise>
1856
+ </xsl:choose>
1857
+ <xsl:apply-templates/>
1858
+ </span>
1859
+ </xsl:template>
1860
+
1861
+ <!-- Select Questions -->
1862
+
1863
+ <!-- "Select" questions come in three types -->
1864
+ <!-- * Question List - several equivalent problems, -->
1865
+ <!-- reader is assigned just one (good for exams) -->
1866
+ <!-- * A/B Experiment - two choices, experiment managed by Runestone -->
1867
+ <xsl:template match="*[@exercise-interactive = 'select']" mode="runestone-to-interactive">
1868
+ <!-- identify the type of "select" -->
1869
+ <!-- duplicated in "pretext-runestone-static.xsl" -->
1870
+ <xsl:variable name="select-variant">
1871
+ <xsl:choose>
1872
+ <!-- Runestone JS picks a random problem from many-->
1873
+ <xsl:when test="select/@grade='random'">
1874
+ <xsl:text>random</xsl:text>
1875
+ </xsl:when>
1876
+ <!-- Two questions, split across roster into A, B groups. -->
1877
+ <xsl:when test="select/@grade='ab-experiment'">
1878
+ <xsl:text>ab-experiment</xsl:text>
1879
+ </xsl:when>
1880
+ <!-- Two questions (usually), the first will always be graded, -->
1881
+ <!-- while any others may be worked and may be helpful. -->
1882
+ <!-- Colloquially known as a "toggle lock" question. -->
1883
+ <xsl:when test="select/@grade='first'">
1884
+ <xsl:text>first</xsl:text>
1885
+ </xsl:when>
1886
+ <!-- Two questions (usually), any one may be -->
1887
+ <!-- chosen by the reader to be graded. -->
1888
+ <!-- Colloquially known as a "toggle " question. -->
1889
+ <xsl:when test="select/@grade='any'">
1890
+ <xsl:text>any</xsl:text>
1891
+ </xsl:when>
1892
+ <!-- default to "random" selection -->
1893
+ <xsl:otherwise>
1894
+ <xsl:text>random</xsl:text>
1895
+ </xsl:otherwise>
1896
+ </xsl:choose>
1897
+ </xsl:variable>
1898
+ <!-- -->
1899
+ <div class="ptx-runestone-container">
1900
+ <div class="runestone sqcontainer selectquestion_section">
1901
+ <div data-component="selectquestion" data-points="1" data-limit-basecourse="false">
1902
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1903
+ <!-- attributes vary by type of select question -->
1904
+ <xsl:choose>
1905
+ <!-- no attributes for generic selection -->
1906
+ <xsl:when test="$select-variant='random'"/>
1907
+ <!-- @experiment name is required (and is a signal) -->
1908
+ <xsl:when test="$select-variant='ab-experiment'">
1909
+ <xsl:attribute name="data-ab">
1910
+ <xsl:value-of select="select/@experiment-name"/>
1911
+ </xsl:attribute>
1912
+ </xsl:when>
1913
+ <!-- set lock on first question -->
1914
+ <xsl:when test="$select-variant='first'">
1915
+ <xsl:attribute name="data-toggleoptions">
1916
+ <xsl:text>toggle, lock</xsl:text>
1917
+ </xsl:attribute>
1918
+ <xsl:attribute name="data-togglelabels">
1919
+ <xsl:text>togglelabels:</xsl:text>
1920
+ </xsl:attribute>
1921
+ </xsl:when>
1922
+ <!-- allow any question to be chosen by reader -->
1923
+ <xsl:when test="$select-variant='any'">
1924
+ <xsl:attribute name="data-toggleoptions">
1925
+ <xsl:text>toggle</xsl:text>
1926
+ </xsl:attribute>
1927
+ <xsl:attribute name="data-togglelabels">
1928
+ <xsl:text>togglelabels:</xsl:text>
1929
+ </xsl:attribute>
1930
+ </xsl:when>
1931
+ </xsl:choose>
1932
+ <!-- always form the list of ids for the questions -->
1933
+ <xsl:attribute name="data-questionlist">
1934
+ <xsl:call-template name="runestone-targets">
1935
+ <xsl:with-param name="id-list" select="select/@questions"/>
1936
+ <xsl:with-param name="separator" select="', '"/>
1937
+ </xsl:call-template>
1938
+ </xsl:attribute>
1939
+ <p>Loading a select question (grading type: <xsl:value-of select="$select-variant"/>)...</p>
1940
+ </div>
1941
+ </div>
1942
+ </div>
1943
+ </xsl:template>
1944
+
1945
+
1946
+ <!-- Fill-in-the-Blanks problem -->
1947
+
1948
+ <!-- Runestone structure -->
1949
+ <xsl:template match="*[@exercise-interactive = 'fillin-basic']" mode="runestone-to-interactive">
1950
+ <div class="ptx-runestone-container">
1951
+ <div class="runestone fillintheblank_section">
1952
+ <!-- dropped "visibility: hidden" on next div -->
1953
+ <div data-component="fillintheblank" data-question_label="">
1954
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
1955
+ <xsl:apply-templates select="statement"/>
1956
+ <script type="application/json">
1957
+ <xsl:apply-templates select="setup" mode="json-conditions"/>
1958
+ </script>
1959
+ </div>
1960
+ </div>
1961
+ </div>
1962
+ </xsl:template>
1963
+
1964
+ <!-- simple substitution in output -->
1965
+ <xsl:template match="exercise/statement//var">
1966
+ <!-- NB: this code is used in formulating static representations -->
1967
+ <!-- count location of (context) "var" in problem statement -->
1968
+ <xsl:variable name="location">
1969
+ <xsl:number from="statement"/>
1970
+ </xsl:variable>
1971
+ <!-- locate corresponding "var" in "setup" -->
1972
+ <xsl:variable name="setup-var" select="ancestor::exercise/setup/var[position() = $location]"/>
1973
+
1974
+ <!-- Know can tell what sort of data goes into the form entry -->
1975
+ <xsl:variable name="placeholder-hint">
1976
+ <xsl:choose>
1977
+ <xsl:when test="$setup-var/condition[1]/@number">
1978
+ <xsl:text>Number</xsl:text>
1979
+ </xsl:when>
1980
+ <xsl:when test="$setup-var/condition[1]/@string">
1981
+ <xsl:text>Text</xsl:text>
1982
+ </xsl:when>
1983
+ </xsl:choose>
1984
+ </xsl:variable>
1985
+ <!-- the actual form fill-in, with hint and width -->
1986
+ <input type="text" placeholder="{$placeholder-hint}">
1987
+ <xsl:if test="@width">
1988
+ <xsl:attribute name="size">
1989
+ <xsl:value-of select="@width"/>
1990
+ </xsl:attribute>
1991
+ </xsl:if>
1992
+ </input>
1993
+ </xsl:template>
1994
+
1995
+ <!-- JSON list-of-list structure -->
1996
+ <xsl:template match="exercise/setup" mode="json-conditions">
1997
+ <!-- outermost list begin -->
1998
+ <xsl:text>[</xsl:text>
1999
+ <xsl:for-each select="var">
2000
+ <!-- per-var list begin -->
2001
+ <xsl:text>[</xsl:text>
2002
+ <xsl:for-each select="condition">
2003
+ <!-- where the real content originates -->
2004
+ <xsl:apply-templates select="." mode="condition-to-json"/>
2005
+ <!-- separate dictionaries for conditions -->
2006
+ <xsl:if test="following-sibling::condition">
2007
+ <xsl:text>, </xsl:text>
2008
+ </xsl:if>
2009
+ </xsl:for-each>
2010
+ <!-- per-var list end -->
2011
+ <xsl:text>]</xsl:text>
2012
+ <!-- separate vars -->
2013
+ <xsl:if test="following-sibling::var">
2014
+ <xsl:text>, </xsl:text>
2015
+ </xsl:if>
2016
+ </xsl:for-each>
2017
+ <!-- outermost list end -->
2018
+ <xsl:text>]</xsl:text>
2019
+ </xsl:template>
2020
+
2021
+ <!-- JSON dictionary for numerical condition -->
2022
+ <xsl:template match="setup/var/condition[@number]" mode="condition-to-json">
2023
+ <!-- per-condition dictionary begin -->
2024
+ <xsl:text>{</xsl:text>
2025
+ <xsl:text>"number": [</xsl:text>
2026
+ <xsl:choose>
2027
+ <xsl:when test="@tolerance">
2028
+ <xsl:value-of select="@number - @tolerance"/>
2029
+ <xsl:text>,</xsl:text>
2030
+ <xsl:value-of select="@number + @tolerance"/>
2031
+ </xsl:when>
2032
+ <xsl:otherwise>
2033
+ <xsl:value-of select="@number"/>
2034
+ <xsl:text>, </xsl:text>
2035
+ <xsl:value-of select="@number"/>
2036
+ </xsl:otherwise>
2037
+ </xsl:choose>
2038
+ <xsl:text>]</xsl:text>
2039
+ <xsl:if test="feedback">
2040
+ <xsl:text>, "feedback": "</xsl:text>
2041
+ <xsl:apply-templates select="feedback" mode="serialize-feedback"/>
2042
+ <xsl:text>"</xsl:text>
2043
+ </xsl:if>
2044
+ <!-- per-condition dictionary end -->
2045
+ <xsl:text>}</xsl:text>
2046
+ </xsl:template>
2047
+
2048
+ <!-- JSON dictionary for string condition -->
2049
+ <xsl:template match="setup/var/condition[@string]" mode="condition-to-json">
2050
+ <!-- per-condition dictionary begin -->
2051
+ <xsl:text>{</xsl:text>
2052
+ <!-- regex string match, drop -->
2053
+ <!-- leading/trailing whitespace -->
2054
+ <xsl:text>"regex": "</xsl:text>
2055
+ <!-- JSON escapes necessary for regular expression -->
2056
+ <xsl:call-template name="escape-json-string">
2057
+ <xsl:with-param name="text">
2058
+ <xsl:text>^\s*</xsl:text>
2059
+ <xsl:value-of select="@string"/>
2060
+ <xsl:text>\s*$</xsl:text>
2061
+ </xsl:with-param>
2062
+ </xsl:call-template>
2063
+ <xsl:text>"</xsl:text>
2064
+ <!-- flag for case-sensitive match -->
2065
+ <!-- default: 'sensitive' -->
2066
+ <xsl:text>, "regexFlags": "</xsl:text>
2067
+ <xsl:if test="parent::var/@case = 'insensitive'">
2068
+ <xsl:text>i</xsl:text>
2069
+ </xsl:if>
2070
+ <xsl:text>"</xsl:text>
2071
+ <!-- optional feedback -->
2072
+ <xsl:if test="feedback">
2073
+ <xsl:text>, "feedback": "</xsl:text>
2074
+ <xsl:apply-templates select="feedback" mode="serialize-feedback"/>
2075
+ <xsl:text>"</xsl:text>
2076
+ </xsl:if>
2077
+ <!-- per-condition dictionary end -->
2078
+ <xsl:text>}</xsl:text>
2079
+ </xsl:template>
2080
+
2081
+ <xsl:template match="setup/var/condition/feedback" mode="serialize-feedback">
2082
+ <xsl:variable name="feedback-rtf">
2083
+ <xsl:apply-templates select="."/>
2084
+ </xsl:variable>
2085
+ <!-- serialize HTML as text, then escape as JSON -->
2086
+ <xsl:call-template name="escape-json-string">
2087
+ <xsl:with-param name="text">
2088
+ <xsl:apply-templates select="exsl:node-set($feedback-rtf)" mode="xml-to-string"/>
2089
+ </xsl:with-param>
2090
+ </xsl:call-template>
2091
+ </xsl:template>
2092
+
2093
+
2094
+ <!-- Coding exercise -->
2095
+
2096
+ <xsl:template match="*[@exercise-interactive = 'coding']" mode="runestone-to-interactive">
2097
+ <!-- We don't have a 'coding' attribute value -->
2098
+ <!-- unless one of the two tests below is true -->
2099
+ <xsl:choose>
2100
+ <xsl:when test="program/@interactive = 'codelens'">
2101
+ <xsl:apply-templates select="program" mode="runestone-codelens">
2102
+ <xsl:with-param name="exercise-statement" select="statement"/>
2103
+ </xsl:apply-templates>
2104
+ </xsl:when>
2105
+ <xsl:when test="program/@interactive = 'activecode'">
2106
+ <xsl:apply-templates select="program" mode="runestone-activecode">
2107
+ <xsl:with-param name="exercise-statement" select="statement"/>
2108
+ </xsl:apply-templates>
2109
+ </xsl:when>
2110
+ </xsl:choose>
2111
+ </xsl:template>
2112
+
2113
+
2114
+ <!-- Dual Form exercise (dynamic/static)-->
2115
+
2116
+ <!-- 2025-11-11: Runestone HTML is speculative -->
2117
+ <xsl:template match="*[@exercise-interactive = 'dual']" mode="runestone-to-interactive">
2118
+ <div class="ptx-runestone-container">
2119
+ <div class="runestone dualquestion_section">
2120
+ <div data-component="dual">
2121
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2122
+ <!-- Assume only "p" and a single "interactive". The -->
2123
+ <!-- "p" could be expanded to include some options. -->
2124
+ <xsl:apply-templates select="statement/p|statement/interactive[1]"/>
2125
+ </div>
2126
+ </div>
2127
+ </div>
2128
+ </xsl:template>
2129
+
2130
+
2131
+ <!-- Short Answer problem -->
2132
+
2133
+ <!-- Traditional form, but not converted like other interactive exercises -->
2134
+ <!-- Won't match indiscriminately, there is some control over when to be -->
2135
+ <!-- interactive, see "static" v. "dynamic" publisher variables. -->
2136
+ <!-- NB - not currently applying to short-form with no "statement" -->
2137
+ <!-- NB: match is recycled in manifest formation -->
2138
+ <xsl:template match="*[@exercise-interactive = 'shortanswer']" mode="runestone-to-interactive">
2139
+ <xsl:choose>
2140
+ <xsl:when test="$b-host-runestone or ($short-answer-responses = 'always')">
2141
+ <!-- when "response" has attributes, perhaps they get interpreted here -->
2142
+ <div class="ptx-runestone-container">
2143
+ <div class="runestone shortanswer_section">
2144
+ <div data-component="shortanswer" data-question_label="" class="journal" data-mathjax="">
2145
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2146
+ <!-- showing a box, but it can't be graded, so warn reader -->
2147
+ <xsl:if test="not($b-host-runestone)">
2148
+ <xsl:attribute name="data-placeholder">
2149
+ <xsl:text>You can write here, and it will be saved on this device, but your response will not be graded.</xsl:text>
2150
+ </xsl:attribute>
2151
+ </xsl:if>
2152
+ <!-- Author can indicate problem expects an -->
2153
+ <!-- attachment. Effectively, default is no. -->
2154
+ <xsl:if test="(@attachment = 'yes') and $b-host-runestone">
2155
+ <xsl:attribute name="data-attachment"/>
2156
+ </xsl:if>
2157
+ <xsl:apply-templates select="statement"/>
2158
+ </div>
2159
+ </div>
2160
+ </div>
2161
+ </xsl:when>
2162
+ <xsl:otherwise>
2163
+ <!-- pointless to do fancy outside of a server/LMS situation -->
2164
+ <xsl:apply-templates select="statement"/>
2165
+ </xsl:otherwise>
2166
+ </xsl:choose>
2167
+ </xsl:template>
2168
+
2169
+ <!-- YouTube Video -->
2170
+ <!-- When hosted on a Runestone server, we use a different embedding -->
2171
+ <!-- for a YouTube video (only), which allows using a YouTube API for -->
2172
+ <!-- monitoring events from readers. We have to pass in an actual -->
2173
+ <!-- height and width (in pixels) for semi-custom attributes here. -->
2174
+ <!-- Many PreTeXt video features (like posters) are lost. -->
2175
+ <!-- The Runestone JavaScript will automatically include the -->
2176
+ <!-- player_api JavaScript after it has set up the appropriate -->
2177
+ <!-- events such as API loaded. -->
2178
+
2179
+ <!-- TODO: are start/end attributes useful? -->
2180
+ <xsl:template match="video[@youtube]" mode="runestone-youtube-embed">
2181
+ <xsl:param name="width"/>
2182
+ <xsl:param name="height"/>
2183
+
2184
+ <xsl:variable name="hid">
2185
+ <xsl:apply-templates select="." mode="runestone-id"/>
2186
+ </xsl:variable>
2187
+
2188
+ <div class="ptx-runestone-container">
2189
+ <div class="runestone yt_section">
2190
+ <div id="{$hid}" data-component="youtube" class="align-left youtube-video"
2191
+ data-video-height="{$height}" data-video-width="{$width}"
2192
+ data-video-videoid="{@youtube}" data-video-divid="{$hid}"
2193
+ data-video-start="0" data-video-end="-1"/>
2194
+ </div>
2195
+ </div>
2196
+ </xsl:template>
2197
+
2198
+ <!-- ########### -->
2199
+ <!-- Active Code -->
2200
+ <!-- ########### -->
2201
+
2202
+ <!-- Runestone has support for various languages. Some -->
2203
+ <!-- are "in-browser" while others are backed by "real" -->
2204
+ <!-- compilers as part of a Runestone server. For an -->
2205
+ <!-- exercise, we pass in some lead-in text, which is -->
2206
+ <!-- the directions for using the ActiveCode component. -->
2207
+ <!-- This template does the best job possible: -->
2208
+ <!-- 1. Unsupported language, static rendering. -->
2209
+ <!-- 2. Supported in-browser, always interactive. -->
2210
+ <!-- 3. On a Runestone server, always interactive. -->
2211
+ <!-- -->
2212
+ <!-- When a "program" is part of an exercise/project-like, -->
2213
+ <!-- then we need to get the problem "statement" absorbed -->
2214
+ <!-- into the ActiveCode. This is an authored "statement" -->
2215
+ <!-- passed in via the $exercise-statement parameter. -->
2216
+ <!-- Other times a "program" is an atomic item -->
2217
+ <!-- and surrounding text explains its purpose, hence -->
2218
+ <!-- "exercise-statment" appropriately defaults to an -->
2219
+ <!-- empty node-set. -->
2220
+ <xsl:template match="program" mode="runestone-activecode">
2221
+ <xsl:param name="exercise-statement" select="/.."/>
2222
+ <xsl:variable name="active-language">
2223
+ <xsl:apply-templates select="." mode="active-language"/>
2224
+ </xsl:variable>
2225
+ <xsl:variable name="hosting">
2226
+ <xsl:apply-templates select="." mode="activecode-host"/>
2227
+ </xsl:variable>
2228
+ <!-- Use an id from the "program" element, unless employed -->
2229
+ <!-- inside an exercise/project-like, which is up a level -->
2230
+ <!-- (and could be many different types of project-like). -->
2231
+ <xsl:variable name="rsid">
2232
+ <xsl:choose>
2233
+ <xsl:when test="$exercise-statement">
2234
+ <xsl:apply-templates select="parent::*" mode="runestone-id"/>
2235
+ </xsl:when>
2236
+ <xsl:otherwise>
2237
+ <xsl:apply-templates select="." mode="runestone-id"/>
2238
+ </xsl:otherwise>
2239
+ </xsl:choose>
2240
+ </xsl:variable>
2241
+ <xsl:choose>
2242
+ <!-- unsupported on Runestone, period -->
2243
+ <xsl:when test="$active-language = ''">
2244
+ <xsl:apply-templates select="$exercise-statement"/>
2245
+ <xsl:apply-templates select="." mode="code-inclusion"/>
2246
+ </xsl:when>
2247
+ <!-- needs server, and we aren't there -->
2248
+ <xsl:when test="($hosting = 'jobeserver') and not($b-host-runestone)">
2249
+ <xsl:apply-templates select="$exercise-statement"/>
2250
+ <xsl:apply-templates select="." mode="code-inclusion"/>
2251
+ </xsl:when>
2252
+ <!-- this is the logical negation of the previous, so could be "otherwise" -->
2253
+ <xsl:when test="($hosting = 'browser') or $b-host-runestone">
2254
+ <div class="ptx-runestone-container">
2255
+ <div class="runestone explainer ac_section ">
2256
+ <div data-component="activecode">
2257
+ <xsl:attribute name="id">
2258
+ <xsl:value-of select="$rsid"/>
2259
+ </xsl:attribute>
2260
+ <!-- filename used for this data if another program makes use of it -->
2261
+ <!-- via add-files -->
2262
+ <xsl:if test="@filename">
2263
+ <xsl:attribute name="data-filename">
2264
+ <xsl:value-of select="@filename"/>
2265
+ </xsl:attribute>
2266
+ </xsl:if>
2267
+ <!-- add some lead-in text to the window -->
2268
+ <xsl:if test="$exercise-statement">
2269
+ <div class="ac_question exercise-statement">
2270
+ <xsl:attribute name="id">
2271
+ <xsl:value-of select="$rsid"/>
2272
+ <xsl:text>_question</xsl:text>
2273
+ </xsl:attribute>
2274
+ <xsl:apply-templates select="$exercise-statement"/>
2275
+ </div>
2276
+ </xsl:if>
2277
+ <textarea data-lang="{$active-language}" data-audio="" data-coach="true" style="visibility: hidden;">
2278
+ <xsl:if test="stdin">
2279
+ <xsl:attribute name="data-stdin">
2280
+ <xsl:call-template name="sanitize-text">
2281
+ <xsl:with-param name="text" select="stdin" />
2282
+ </xsl:call-template>
2283
+ </xsl:attribute>
2284
+ </xsl:if>
2285
+ <xsl:attribute name="id">
2286
+ <xsl:value-of select="$rsid"/>
2287
+ <xsl:text>_editor</xsl:text>
2288
+ </xsl:attribute>
2289
+ <!-- conditional attributes shared with parsons activecodes -->
2290
+ <xsl:call-template name="runestone-activecode-editor-attributes">
2291
+ <xsl:with-param name="active-language" select="$active-language"/>
2292
+ <xsl:with-param name="hosting" select="$hosting"/>
2293
+ </xsl:call-template>
2294
+ <!-- CodeTailor: when an author requests it, emit the data -->
2295
+ <!-- attributes that have Runestone offer the reader an -->
2296
+ <!-- automatically-personalized Parsons puzzle as scaffolding -->
2297
+ <!-- (a "Get Help" button) when they are stuck on the exercise. -->
2298
+ <xsl:if test="@codetailor = 'yes'">
2299
+ <xsl:attribute name="data-parsonspersonalize">movable</xsl:attribute>
2300
+ <xsl:attribute name="data-parsonsexample">LLM-example</xsl:attribute>
2301
+ </xsl:if>
2302
+ <!-- This is a bit awful, but we need to figure out how much margin -->
2303
+ <!-- to add to runestone dividers. It must match indentation used for -->
2304
+ <!-- the overall program. -->
2305
+ <!-- To avoid as much as possible of the duplicate work, clean each -->
2306
+ <!-- part and store those for reuse later. -->
2307
+ <xsl:variable name="program-preamble-trimmed">
2308
+ <xsl:apply-templates select="preamble" mode="program-part-processing"/>
2309
+ </xsl:variable>
2310
+ <xsl:variable name="program-code-trimmed">
2311
+ <xsl:apply-templates select="code" mode="program-part-processing"/>
2312
+ </xsl:variable>
2313
+ <xsl:variable name="program-postamble-trimmed">
2314
+ <xsl:apply-templates select="postamble" mode="program-part-processing"/>
2315
+ </xsl:variable>
2316
+ <!-- Now temporarily assemble to count left margin spaces -->
2317
+ <xsl:variable name="program-left-margin">
2318
+ <xsl:call-template name="count-pad-length">
2319
+ <xsl:with-param name="text">
2320
+ <!-- need to clear any blank line left by empty preamble and/or -->
2321
+ <!-- leading blank lines at the start of code if no preamble -->
2322
+ <xsl:call-template name="trim-start-lines">
2323
+ <xsl:with-param name="text">
2324
+ <xsl:value-of select="$program-preamble-trimmed"/>
2325
+ <xsl:value-of select="$program-code-trimmed"/>
2326
+ <xsl:value-of select="$program-postamble-trimmed"/>
2327
+ </xsl:with-param>
2328
+ </xsl:call-template>
2329
+ </xsl:with-param>
2330
+ </xsl:call-template>
2331
+ </xsl:variable>
2332
+ <!-- Build actual string we will prepend to magic dividers -->
2333
+ <xsl:variable name="left-margin-string">
2334
+ <xsl:value-of select="str:padding($program-left-margin, ' ')" />
2335
+ </xsl:variable>
2336
+ <!-- Now we are ready to build the real program text. -->
2337
+ <!-- For historical reasons, this does not include potential <tests> child. -->
2338
+ <xsl:variable name="program-text">
2339
+ <xsl:for-each select="preamble">
2340
+ <!-- only expect one, for-each just for binding -->
2341
+ <xsl:value-of select="$program-preamble-trimmed"/>
2342
+ <xsl:value-of select="$left-margin-string"/>
2343
+ <xsl:choose>
2344
+ <xsl:when test='@visible = "no"'>
2345
+ <xsl:text>^^^^&#xa;</xsl:text>
2346
+ </xsl:when>
2347
+ <xsl:otherwise>
2348
+ <xsl:text>^^^!&#xa;</xsl:text>
2349
+ </xsl:otherwise>
2350
+ </xsl:choose>
2351
+ </xsl:for-each>
2352
+ <!-- code - clean up start and end -->
2353
+ <xsl:value-of select="$program-code-trimmed"/>
2354
+ <!-- postamble - clean up start -->
2355
+ <xsl:for-each select="postamble">
2356
+ <!-- only expect one, for-each just for binding -->
2357
+ <xsl:value-of select="$left-margin-string"/>
2358
+ <xsl:choose>
2359
+ <xsl:when test='@visible = "no"'>
2360
+ <xsl:text>====&#xa;</xsl:text>
2361
+ </xsl:when>
2362
+ <xsl:otherwise>
2363
+ <xsl:text>===!&#xa;</xsl:text>
2364
+ </xsl:otherwise>
2365
+ </xsl:choose>
2366
+ <xsl:value-of select="$program-postamble-trimmed"/>
2367
+ </xsl:for-each>
2368
+ </xsl:variable>
2369
+ <!-- now sanitize the whole blob -->
2370
+ <xsl:call-template name="sanitize-text">
2371
+ <xsl:with-param name="text" select="$program-text" />
2372
+ </xsl:call-template>
2373
+ <!-- optional unit testing -->
2374
+ <xsl:if test="tests">
2375
+ <!-- Be wary of empty "test" elements which lead to -->
2376
+ <!-- empty files, which are possibly not legal -->
2377
+ <!-- programs for their target languages, and hence -->
2378
+ <!-- raise errors due to the Runestone back-end -->
2379
+ <!-- trying to process them. -->
2380
+ <!-- NB: static versions never show "tests" anyway -->
2381
+ <xsl:variable name="tests-content">
2382
+ <xsl:variable name="trimmed-text">
2383
+ <xsl:apply-templates select="tests" mode="program-part-processing"/>
2384
+ </xsl:variable>
2385
+ <xsl:call-template name="left-align-text">
2386
+ <xsl:with-param name="text" select="$trimmed-text" />
2387
+ </xsl:call-template>
2388
+ </xsl:variable>
2389
+ <!-- guard against whitespace-only tests, could be tests/iotests -->
2390
+ <xsl:if test="not(normalize-space($tests-content) = '')">
2391
+ <xsl:choose>
2392
+ <xsl:when test="tests[@visible = 'yes']">
2393
+ <xsl:choose>
2394
+ <xsl:when test="postamble[@visible = 'no']">
2395
+ <xsl:message>PTX:WARNING: There is no support for visible tests after an invisible postamble. (Issue in <xsl:value-of select="$rsid"/>).</xsl:message>
2396
+ </xsl:when>
2397
+ <xsl:when test="not(postamble)">
2398
+ <!-- need to add header -->
2399
+ <xsl:text>===!&#xa;</xsl:text>
2400
+ </xsl:when>
2401
+ <!-- otherwise header created by postamble -->
2402
+ </xsl:choose>
2403
+ </xsl:when>
2404
+ <xsl:otherwise>
2405
+ <!-- invisible tests, need header if not after invisible postamble -->
2406
+ <xsl:if test="not(postamble[@visible = 'no'])">
2407
+ <xsl:text>====&#xa;</xsl:text>
2408
+ </xsl:if>
2409
+ </xsl:otherwise>
2410
+ </xsl:choose>
2411
+ <xsl:value-of select="$tests-content"/>
2412
+ </xsl:if>
2413
+ <xsl:if test="tests/iotest">
2414
+ <xsl:choose>
2415
+ <xsl:when test="not(normalize-space(tests/text()) = '')">
2416
+ <xsl:message>WARNING: You can either write text based tests or use iotests, but not both. iotests ignored in <xsl:value-of select="$rsid"/>.</xsl:message>
2417
+ </xsl:when>
2418
+ <xsl:otherwise>
2419
+ <xsl:text>===iotests===&#x0a;</xsl:text>
2420
+ <xsl:text>[</xsl:text>
2421
+ <xsl:for-each select="tests/iotest">
2422
+ <xsl:text>{"input":"</xsl:text>
2423
+ <xsl:call-template name="escape-json-string">
2424
+ <xsl:with-param name="text">
2425
+ <xsl:call-template name="sanitize-text">
2426
+ <xsl:with-param name="text">
2427
+ <xsl:value-of select="input"/>
2428
+ </xsl:with-param>
2429
+ </xsl:call-template>
2430
+ </xsl:with-param>
2431
+ </xsl:call-template>
2432
+ <xsl:text>","out":"</xsl:text>
2433
+ <xsl:call-template name="escape-json-string">
2434
+ <xsl:with-param name="text">
2435
+ <xsl:call-template name="sanitize-text">
2436
+ <xsl:with-param name="text">
2437
+ <xsl:value-of select="output"/>
2438
+ </xsl:with-param>
2439
+ </xsl:call-template>
2440
+ </xsl:with-param>
2441
+ </xsl:call-template>
2442
+ <xsl:text>"}</xsl:text>
2443
+ <xsl:if test="position() != last()">
2444
+ <xsl:text>,</xsl:text>
2445
+ </xsl:if>
2446
+ </xsl:for-each>
2447
+ <xsl:text>]&#x0a;</xsl:text>
2448
+ </xsl:otherwise>
2449
+ </xsl:choose>
2450
+ </xsl:if>
2451
+ </xsl:if>
2452
+ </textarea>
2453
+ </div>
2454
+ </div>
2455
+ </div>
2456
+ </xsl:when>
2457
+ </xsl:choose>
2458
+ </xsl:template>
2459
+
2460
+ <xsl:template name="runestone-activecode-editor-attributes">
2461
+ <xsl:param name="active-language"/>
2462
+ <xsl:param name="hosting"/>
2463
+ <xsl:attribute name="data-question_label"/>
2464
+ <!-- Code Lens only for certain languages -->
2465
+ <xsl:attribute name="data-codelens">
2466
+ <xsl:choose>
2467
+ <xsl:when test="@codelens = 'no'">
2468
+ <xsl:text>false</xsl:text>
2469
+ </xsl:when>
2470
+ <xsl:when test="($active-language = 'python') or ($active-language = 'python2') or ($active-language = 'python3')">
2471
+ <xsl:text>true</xsl:text>
2472
+ </xsl:when>
2473
+ <xsl:when test="($active-language = 'c') or ($active-language = 'cpp') or ($active-language = 'java')">
2474
+ <xsl:text>true</xsl:text>
2475
+ </xsl:when>
2476
+ <xsl:otherwise>
2477
+ <xsl:text>false</xsl:text>
2478
+ </xsl:otherwise>
2479
+ </xsl:choose>
2480
+ </xsl:attribute>
2481
+ <!-- allow @datafile attribute on <program> -->
2482
+ <xsl:if test="@datafile">
2483
+ <!-- multiple files, coma- or space- separated -->
2484
+ <xsl:variable name="tokens" select="str:tokenize(@datafile, ', ')"/>
2485
+ <xsl:attribute name="data-datafile">
2486
+ <xsl:for-each select="$tokens">
2487
+ <xsl:value-of select="."/>
2488
+ <!-- n - 1 separators, required by receiving Javascript -->
2489
+ <!-- comma-separated this time -->
2490
+ <xsl:if test="following-sibling::token">
2491
+ <xsl:text>,</xsl:text>
2492
+ </xsl:if>
2493
+ </xsl:for-each>
2494
+ </xsl:attribute>
2495
+ </xsl:if>
2496
+
2497
+ <!-- Merge add-files and compile-also, get unique items -->
2498
+ <!-- This will be the list that we use as add-files for the manifest -->
2499
+ <xsl:variable name="all-extra-files">
2500
+ <xsl:variable name="id-list">
2501
+ <xsl:value-of select="@add-files"/>
2502
+ <xsl:if test="@add-files and @compile-also">
2503
+ <xsl:text>, </xsl:text>
2504
+ </xsl:if>
2505
+ <xsl:value-of select="@compile-also"/>
2506
+ </xsl:variable>
2507
+ <xsl:variable name="unique-tokens">
2508
+ <xsl:call-template name="unique-token-set">
2509
+ <xsl:with-param name="s" select="$id-list"/>
2510
+ </xsl:call-template>
2511
+ </xsl:variable>
2512
+ <xsl:for-each select="exsl:node-set($unique-tokens)/token">
2513
+ <xsl:value-of select="."/>
2514
+ <xsl:if test="following-sibling::token">
2515
+ <xsl:text>,</xsl:text>
2516
+ </xsl:if>
2517
+ </xsl:for-each>
2518
+ </xsl:variable>
2519
+
2520
+ <!-- allow @add-files attribute on <program> -->
2521
+ <xsl:if test="$all-extra-files != ''">
2522
+ <xsl:attribute name="data-add-files">
2523
+ <xsl:call-template name="runestone-targets">
2524
+ <xsl:with-param name="id-list" select="$all-extra-files"/>
2525
+ <xsl:with-param name="separator" select="','"/>
2526
+ </xsl:call-template>
2527
+ </xsl:attribute>
2528
+ </xsl:if>
2529
+ <!-- allow @compile-with attribute on <program> -->
2530
+ <xsl:if test="@compile-also">
2531
+ <xsl:attribute name="data-compile-also">
2532
+ <xsl:call-template name="runestone-targets">
2533
+ <xsl:with-param name="id-list" select="@compile-also"/>
2534
+ <xsl:with-param name="separator" select="','"/>
2535
+ <xsl:with-param name="output-field" select="'filename'"/>
2536
+ </xsl:call-template>
2537
+ </xsl:attribute>
2538
+ </xsl:if>
2539
+ <!-- allow @include attribute on <program> -->
2540
+ <xsl:if test="@include">
2541
+ <!-- space-separated this time -->
2542
+ <xsl:attribute name="data-include">
2543
+ <xsl:call-template name="runestone-targets">
2544
+ <xsl:with-param name="id-list" select="@include"/>
2545
+ <xsl:with-param name="separator" select="' '"/>
2546
+ </xsl:call-template>
2547
+ </xsl:attribute>
2548
+ </xsl:if>
2549
+ <!-- SQL (only) needs an attribute so it can find some code -->
2550
+ <xsl:if test="$active-language = 'sql'">
2551
+ <xsl:attribute name="data-wasm">
2552
+ <xsl:text>/_static</xsl:text>
2553
+ </xsl:attribute>
2554
+ <!-- A SQL database can be provided for automated -->
2555
+ <!-- testing of correct answers via unit tests. -->
2556
+ <!-- This is a location in the external directory. -->
2557
+ <xsl:if test="@database">
2558
+ <xsl:attribute name="data-dburl">
2559
+ <xsl:choose>
2560
+ <xsl:when test="$b-managed-directories">
2561
+ <xsl:value-of select="$external-directory"/>
2562
+ <xsl:value-of select="@database"/>
2563
+ </xsl:when>
2564
+ <xsl:otherwise>
2565
+ <xsl:value-of select="@database"/>
2566
+ </xsl:otherwise>
2567
+ </xsl:choose>
2568
+ </xsl:attribute>
2569
+ </xsl:if>
2570
+ </xsl:if>
2571
+ <!-- interpreter arguments for hosted languages -->
2572
+ <xsl:variable name="interpreter-args">
2573
+ <xsl:call-template name="get-program-attr-or-default">
2574
+ <xsl:with-param name="attr" select="'interpreter-args'"/>
2575
+ </xsl:call-template>
2576
+ </xsl:variable>
2577
+ <xsl:if test="$interpreter-args != '' and ($hosting = 'jobeserver')">
2578
+ <xsl:attribute name="data-interpreterargs">
2579
+ <xsl:call-template name="comma-list-to-json-array">
2580
+ <xsl:with-param name="list" select="$interpreter-args"/>
2581
+ </xsl:call-template>
2582
+ </xsl:attribute>
2583
+ </xsl:if>
2584
+ <!-- compiler arguments for hosted languages -->
2585
+ <xsl:variable name="compiler-args">
2586
+ <xsl:call-template name="get-program-attr-or-default">
2587
+ <xsl:with-param name="attr" select="'compiler-args'"/>
2588
+ </xsl:call-template>
2589
+ </xsl:variable>
2590
+ <!-- extra compiler args, appended to compiler args -->
2591
+ <xsl:variable name="extra-compiler-args" select="@extra-compiler-args"/>
2592
+ <xsl:if test="($compiler-args != '' or $extra-compiler-args != '') and ($hosting = 'jobeserver')">
2593
+ <xsl:variable name="compiler-args-full">
2594
+ <xsl:value-of select="$compiler-args"/>
2595
+ <xsl:if test="$compiler-args != '' and $extra-compiler-args != ''">
2596
+ <xsl:text>,</xsl:text>
2597
+ </xsl:if>
2598
+ <xsl:value-of select="$extra-compiler-args"/>
2599
+ </xsl:variable>
2600
+ <xsl:attribute name="data-compileargs">
2601
+ <xsl:call-template name="comma-list-to-json-array">
2602
+ <xsl:with-param name="list" select="$compiler-args-full"/>
2603
+ </xsl:call-template>
2604
+ </xsl:attribute>
2605
+ </xsl:if>
2606
+ <!-- linker arguments for hosted languages -->
2607
+ <xsl:variable name="linker-args">
2608
+ <xsl:call-template name="get-program-attr-or-default">
2609
+ <xsl:with-param name="attr" select="'linker-args'"/>
2610
+ </xsl:call-template>
2611
+ </xsl:variable>
2612
+ <xsl:if test="$linker-args != '' and ($hosting = 'jobeserver')">
2613
+ <xsl:attribute name="data-linkargs">
2614
+ <xsl:call-template name="comma-list-to-json-array">
2615
+ <xsl:with-param name="list" select="$linker-args"/>
2616
+ </xsl:call-template>
2617
+ </xsl:attribute>
2618
+ </xsl:if>
2619
+ <!-- timelimit -->
2620
+ <xsl:variable name="timelimit">
2621
+ <xsl:call-template name="get-program-attr-or-default">
2622
+ <xsl:with-param name="attr" select="'timelimit'"/>
2623
+ <xsl:with-param name="default" select="'25000'"/>
2624
+ </xsl:call-template>
2625
+ </xsl:variable>
2626
+ <xsl:if test="$timelimit != ''">
2627
+ <xsl:attribute name="data-timelimit">
2628
+ <xsl:value-of select="$timelimit"/>
2629
+ </xsl:attribute>
2630
+ </xsl:if>
2631
+ <!-- assorted boolean flags -->
2632
+ <xsl:variable name="download">
2633
+ <xsl:call-template name="get-program-attr-or-default">
2634
+ <xsl:with-param name="attr" select="'download'"/>
2635
+ <xsl:with-param name="default" select="''"/>
2636
+ </xsl:call-template>
2637
+ </xsl:variable>
2638
+ <xsl:if test="$download = 'yes'">
2639
+ <xsl:attribute name="data-enabledownload">
2640
+ <xsl:text>yes</xsl:text>
2641
+ </xsl:attribute>
2642
+ </xsl:if>
2643
+ <xsl:if test="@autorun = 'yes'">
2644
+ <xsl:attribute name="data-autorun">
2645
+ <xsl:text>yes</xsl:text>
2646
+ </xsl:attribute>
2647
+ </xsl:if>
2648
+ <xsl:if test="@hidecode = 'yes'">
2649
+ <xsl:attribute name="data-hidecode">
2650
+ <xsl:text>yes</xsl:text>
2651
+ </xsl:attribute>
2652
+ </xsl:if>
2653
+ <xsl:if test="@chatcodes = 'yes'">
2654
+ <xsl:attribute name="data-chatcodes">
2655
+ <xsl:text>yes</xsl:text>
2656
+ </xsl:attribute>
2657
+ </xsl:if>
2658
+ <!-- Pass on highlighted line numbering -->
2659
+ <xsl:if test="@highlight-lines != ''">
2660
+ <xsl:attribute name="data-highlight-lines">
2661
+ <!-- force comma-, or space-separated, list to commas -->
2662
+ <xsl:value-of select="translate(normalize-space(translate(@highlight-lines, ',', ' ')), ' ', ',')"/>
2663
+ </xsl:attribute>
2664
+ </xsl:if>
2665
+ </xsl:template>
2666
+
2667
+
2668
+ <!-- ######## -->
2669
+ <!-- CodeLens -->
2670
+ <!-- ######## -->
2671
+
2672
+ <xsl:template match="program[@interactive = 'codelens']" mode="runestone-codelens">
2673
+ <xsl:param name="exercise-statement" select="/.."/>
2674
+ <xsl:variable name="active-language">
2675
+ <xsl:apply-templates select="." mode="active-language"/>
2676
+ </xsl:variable>
2677
+ <!-- as a variable so it does not look like an AVT -->
2678
+ <xsl:variable name="parameter-dictionary">
2679
+ <xsl:text>{</xsl:text>
2680
+ <xsl:text>"embeddedMode": true, </xsl:text>
2681
+ <xsl:text>"lang": "</xsl:text>
2682
+ <xsl:value-of select="$active-language"/>
2683
+ <xsl:text>", </xsl:text>
2684
+ <xsl:text>"jumpToEnd": false</xsl:text>
2685
+ <xsl:text>}</xsl:text>
2686
+ </xsl:variable>
2687
+ <!-- locate trace data via a *.js file, managed or not -->
2688
+ <xsl:variable name="trace-file">
2689
+ <xsl:value-of select="$generated-directory"/>
2690
+ <xsl:if test="$b-managed-directories">
2691
+ <xsl:text>trace/</xsl:text>
2692
+ </xsl:if>
2693
+ <xsl:apply-templates select="." mode="runestone-codelens-trace-filename"/>
2694
+ </xsl:variable>
2695
+ <!-- the Runestone HTML -->
2696
+ <div class="ptx-runestone-container">
2697
+ <div class="runestone codelens">
2698
+ <div class="cd_section" data-component="codelens" data-question_label="">
2699
+ <xsl:if test="$exercise-statement">
2700
+ <div class="exercise-statement">
2701
+ <xsl:apply-templates select="$exercise-statement"/>
2702
+ </div>
2703
+ </xsl:if>
2704
+ <div class="pytutorVisualizer exercise-interactive">
2705
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2706
+ <xsl:attribute name="data-params">
2707
+ <xsl:value-of select="$parameter-dictionary"/>
2708
+ </xsl:attribute>
2709
+ </div>
2710
+ <!-- no caption, should go inside a listing? -->
2711
+ </div>
2712
+ <!-- access simple script with variable set to -->
2713
+ <!-- the trace data via the @src attribute -->
2714
+ <script>
2715
+ <xsl:attribute name="src">
2716
+ <xsl:value-of select="$trace-file"/>
2717
+ </xsl:attribute>
2718
+ <!-- script tag MUST not be self closing. force content to prevent -->
2719
+ <xsl:text> </xsl:text>
2720
+ </script>
2721
+ </div>
2722
+ </div>
2723
+ </xsl:template>
2724
+
2725
+ <!-- Some Runestone languages are supported within a browser, -->
2726
+ <!-- so can be used as part of Runestone for All, while others -->
2727
+ <!-- require a JOBE server on the Runestone server. This template -->
2728
+ <!-- simply returns the necessary hosting capability. -->
2729
+ <!-- N.B. This match could be simply on "program", but a runnable -->
2730
+ <!-- Parsons problem may have a @language on it as part of being -->
2731
+ <!-- runnable, thus a more liberal match (which could be -->
2732
+ <!-- tightened, most likely). -->
2733
+ <xsl:template match="*" mode="activecode-host">
2734
+ <xsl:variable name="language">
2735
+ <xsl:apply-templates select="." mode="active-language"/>
2736
+ </xsl:variable>
2737
+ <xsl:choose>
2738
+ <xsl:when test="$language = 'python'"> <xsl:text>browser</xsl:text></xsl:when>
2739
+ <xsl:when test="$language = 'javascript'"> <xsl:text>browser</xsl:text></xsl:when>
2740
+ <xsl:when test="$language = 'html'"> <xsl:text>browser</xsl:text></xsl:when>
2741
+ <xsl:when test="$language = 'sql'"> <xsl:text>browser</xsl:text></xsl:when>
2742
+ <xsl:when test="$language = 'c'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2743
+ <xsl:when test="$language = 'cpp'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2744
+ <xsl:when test="$language = 'java'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2745
+ <xsl:when test="$language = 'kotlin'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2746
+ <xsl:when test="$language = 'python2'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2747
+ <xsl:when test="$language = 'python3'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2748
+ <xsl:when test="$language = 'octave'"> <xsl:text>jobeserver</xsl:text></xsl:when>
2749
+ <xsl:otherwise/>
2750
+ </xsl:choose>
2751
+ </xsl:template>
2752
+
2753
+ <!-- Data Files -->
2754
+ <xsl:template match="datafile" mode="runestone-to-interactive">
2755
+ <!-- Possibly annotate with the source -->
2756
+ <xsl:apply-templates select="." mode="view-source-widget"/>
2757
+ <!-- Some templates and variables are defined in -common for consistency -->
2758
+
2759
+ <!-- If there is a child "pre" element, then we build an un-editable -->
2760
+ <!-- HTML "pre" in the uneditable case and an editable HTML -->
2761
+ <!-- "textarea" in the editable case. This discintion is mirrored in -->
2762
+ <!-- this variable and a subsequent boolean. -->
2763
+ <xsl:variable name="pre-element">
2764
+ <xsl:choose>
2765
+ <!-- only a clear "yes" will yield editable, -->
2766
+ <!-- default is "no" (or anything but "yes") -->
2767
+ <xsl:when test="pre and not(@editable = 'yes')">
2768
+ <xsl:text>pre</xsl:text>
2769
+ </xsl:when>
2770
+ <xsl:when test="pre and (@editable = 'yes')">
2771
+ <xsl:text>textarea</xsl:text>
2772
+ </xsl:when>
2773
+ <!-- safeguard, should not ever be queried -->
2774
+ <!-- outside of "pre" in source -->
2775
+ <xsl:otherwise/>
2776
+ </xsl:choose>
2777
+ </xsl:variable>
2778
+ <xsl:variable name="b-is-editable" select="$pre-element = 'textarea'"/>
2779
+
2780
+ <!-- The HTML that Runestone expects -->
2781
+ <div class="runestone datafile">
2782
+ <div class="datafile_caption">
2783
+ <code class="code-inline tex2jax_ignore">
2784
+ <!-- Internationalize? See comments in static conversion -->
2785
+ <xsl:text>Data: </xsl:text>
2786
+ <xsl:value-of select="@filename" />
2787
+ </code>
2788
+ </div>
2789
+ <xsl:choose>
2790
+ <xsl:when test="image">
2791
+ <!-- filename is relative to author's source -->
2792
+ <xsl:variable name="data-filename">
2793
+ <xsl:apply-templates select="." mode="datafile-filename"/>
2794
+ </xsl:variable>
2795
+ <xsl:variable name="image-b64-elt" select="document($data-filename, $original)/pi:image-b64"/>
2796
+ <img data-component="datafile" data-isimage="true">
2797
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2798
+ <xsl:attribute name="data-filename">
2799
+ <xsl:value-of select="@filename"/>
2800
+ </xsl:attribute>
2801
+ <xsl:attribute name="src">
2802
+ <xsl:text>data:</xsl:text>
2803
+ <xsl:value-of select="$image-b64-elt/@pi:mime-type"/>
2804
+ <xsl:text>;base64,</xsl:text>
2805
+ <xsl:value-of select="$image-b64-elt/@pi:base64"/>
2806
+ </xsl:attribute>
2807
+ </img>
2808
+ </xsl:when>
2809
+ <!-- text, an authored toy example, or a serious external file -->
2810
+ <xsl:when test="pre">
2811
+ <xsl:element name="{$pre-element}">
2812
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2813
+ <xsl:attribute name="data-component">
2814
+ <xsl:text>datafile</xsl:text>
2815
+ </xsl:attribute>
2816
+ <xsl:attribute name="data-filename">
2817
+ <xsl:value-of select="@filename"/>
2818
+ </xsl:attribute>
2819
+ <xsl:attribute name="data-edit">
2820
+ <!-- conveniently, value is true/false -->
2821
+ <xsl:value-of select="$b-is-editable"/>
2822
+ </xsl:attribute>
2823
+ <!-- Runestone can only hide non-editable text -->
2824
+ <xsl:if test="(not(@editable = 'yes')) and (@hide = 'yes')">
2825
+ <xsl:attribute name="data-hidden"/>
2826
+ </xsl:if>
2827
+ <xsl:attribute name="data-rows">
2828
+ <xsl:choose>
2829
+ <xsl:when test="@rows">
2830
+ <xsl:value-of select="@rows"/>
2831
+ </xsl:when>
2832
+ <!-- default is 20 rows -->
2833
+ <xsl:otherwise>
2834
+ <xsl:value-of select="$datafile-default-rows"/>
2835
+ </xsl:otherwise>
2836
+ </xsl:choose>
2837
+ </xsl:attribute>
2838
+ <xsl:attribute name="data-cols">
2839
+ <xsl:choose>
2840
+ <xsl:when test="@cols">
2841
+ <xsl:value-of select="@cols"/>
2842
+ </xsl:when>
2843
+ <!-- default is 40 columns -->
2844
+ <xsl:otherwise>
2845
+ <xsl:value-of select="$datafile-default-cols"/>
2846
+ </xsl:otherwise>
2847
+ </xsl:choose>
2848
+ </xsl:attribute>
2849
+ <xsl:apply-templates select="." mode="datafile-text-contents"/>
2850
+ </xsl:element>
2851
+ </xsl:when>
2852
+ <!-- no other source/PTX element is supported , bail out-->
2853
+ <xsl:otherwise/>
2854
+ </xsl:choose>
2855
+ </div>
2856
+ </xsl:template>
2857
+
2858
+ <!-- ############# -->
2859
+ <!-- Tabbed Viewer -->
2860
+ <!-- ############# -->
2861
+
2862
+ <!-- Strictly a presentational device, but useful in certain situations -->
2863
+ <!-- as a pedogogical device. But we start with presentation. An -->
2864
+ <!-- "exercise" or PROJECT-LIKE, structured by "task" can have each -->
2865
+ <!-- top-level task go into a tab, along with tabs for "introduction" -->
2866
+ <!-- and "conclusion". We *never* do this for worksheets, to avoid -->
2867
+ <!-- gumming up printing and spacing. And WeBWorK problems manage -->
2868
+ <!-- their own tasks. The "match" here is pretty selective for -->
2869
+ <!-- "exercise", should perhaps be tighter for PROJECT-LIKE. Of course, -->
2870
+ <!-- Runestone Components play nicely with this device, along with more -->
2871
+ <!-- boring exercises. -->
2872
+
2873
+ <xsl:template match="exercise[task and not(@exercise-customization = 'worksheet')]|&PROJECT-LIKE;" mode="tabbed-tasks">
2874
+ <xsl:param name="heading-level"/>
2875
+ <div class="ptx-runestone-container">
2876
+ <div>
2877
+ <xsl:attribute name="class">
2878
+ <xsl:text>runestone tabbed_section</xsl:text>
2879
+ <!-- need to know if it contains an element that wants to be wide in wide layout -->
2880
+ <xsl:if test=".//program[@interactive = 'activecode'] or .//program[@interactive = 'codelens'] or .//exercise/blocks">
2881
+ <xsl:text> contains-wide</xsl:text>
2882
+ </xsl:if>
2883
+ </xsl:attribute>
2884
+ <!-- "introduction" first as lead-in, not as a tab -->
2885
+ <xsl:if test="introduction">
2886
+ <div class="tabs-introduction exercise-leadin">
2887
+ <xsl:apply-templates select="introduction">
2888
+ <xsl:with-param name="heading-level" select="$heading-level"/>
2889
+ </xsl:apply-templates>
2890
+ </div>
2891
+ </xsl:if>
2892
+ <!-- @data-component="tab" do not need an HTML @id for any -->
2893
+ <!-- purpose in Runestone (such as, say, tracking activity -->
2894
+ <!-- as a reader clicks from tab to tab) -->
2895
+ <div data-component="tabbedStuff">
2896
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2897
+ <xsl:for-each select="task">
2898
+ <xsl:variable name="the-task-number">
2899
+ <xsl:text>(</xsl:text>
2900
+ <xsl:apply-templates select="." mode="serial-number"/>
2901
+ <xsl:text>)</xsl:text>
2902
+ </xsl:variable>
2903
+ <div data-component="tab" data-tabname="{$the-task-number}">
2904
+ <xsl:apply-templates select=".">
2905
+ <xsl:with-param name="heading-level" select="$heading-level"/>
2906
+ </xsl:apply-templates>
2907
+ </div>
2908
+ </xsl:for-each>
2909
+ </div>
2910
+ <!-- -->
2911
+ <!-- "conclusion" last as lead-out, not as a tab -->
2912
+ <xsl:if test="conclusion">
2913
+ <div class="tabs-conclusion exercise-leadout">
2914
+ <xsl:apply-templates select="conclusion">
2915
+ <xsl:with-param name="heading-level" select="$heading-level"/>
2916
+ </xsl:apply-templates>
2917
+ </div>
2918
+ </xsl:if>
2919
+ </div>
2920
+ </div>
2921
+ </xsl:template>
2922
+
2923
+ <!-- ####### -->
2924
+ <!-- Queries -->
2925
+ <!-- ####### -->
2926
+
2927
+ <xsl:template match="query" mode="runestone-to-interactive">
2928
+ <!-- <xsl:text>FOO</xsl:text> -->
2929
+ <xsl:variable name="the-choices" select="choices/choice"/>
2930
+ <div class="ptx-runestone-container">
2931
+ <div class="runestone">
2932
+ <ul data-component="poll">
2933
+ <xsl:apply-templates select="." mode="runestone-id-attribute"/>
2934
+ <xsl:attribute name="data-results">
2935
+ <xsl:choose>
2936
+ <xsl:when test="(@visibility = 'instructor') or (@visibility = 'all')">
2937
+ <xsl:value-of select="@visibility"/>
2938
+ </xsl:when>
2939
+ <xsl:otherwise>
2940
+ <xsl:text>instructor</xsl:text>
2941
+ </xsl:otherwise>
2942
+ </xsl:choose>
2943
+ </xsl:attribute>
2944
+ <xsl:apply-templates select="statement"/>
2945
+ <!-- infrastructure to here is common to a query with -->
2946
+ <!-- explicit distinct choices, versus a query with a -->
2947
+ <!-- simple scale for responses -->
2948
+ <!-- NB: could define a variable early on indicating -->
2949
+ <!-- the nature of the poll, should there need to be -->
2950
+ <!-- more differentiation -->
2951
+ <xsl:choose>
2952
+ <xsl:when test="choices">
2953
+ <xsl:apply-templates select="choices/choice"/>
2954
+ </xsl:when>
2955
+ <xsl:when test="foome">
2956
+ <!-- context switch will allow "position()" to behave -->
2957
+ <xsl:for-each select="$the-choices">
2958
+ <li>
2959
+ <span class="poll-choice">
2960
+ <xsl:value-of select="position()"/>
2961
+ </span>
2962
+ <xsl:apply-templates/>
2963
+ </li>
2964
+ </xsl:for-each>
2965
+ </xsl:when>
2966
+ <xsl:when test="@scale">
2967
+ <!-- generate list items with numbers recursively -->
2968
+ <xsl:call-template name="numbered-list-items">
2969
+ <xsl:with-param name="max" select="@scale"/>
2970
+ </xsl:call-template>
2971
+ </xsl:when>
2972
+ <!-- could add error warning here? Or rely on schema? -->
2973
+ <xsl:otherwise/>
2974
+ </xsl:choose>
2975
+ </ul>
2976
+ </div>
2977
+ </div>
2978
+ </xsl:template>
2979
+
2980
+ <xsl:template match="query/choices/choice">
2981
+ <li>
2982
+ <!-- <span class="poll-choice">
2983
+ <xsl:number/>
2984
+ </span> -->
2985
+ <xsl:number/>
2986
+ <xsl:text>. </xsl:text>
2987
+ <xsl:apply-templates/>
2988
+ </li>
2989
+ </xsl:template>
2990
+
2991
+ <xsl:template name="numbered-list-items">
2992
+ <!-- always initialize with 1 to start -->
2993
+ <xsl:param name="current" select="'1'"/>
2994
+ <xsl:param name="max"/>
2995
+
2996
+ <xsl:choose>
2997
+ <!-- $current is too big, done with recursion -->
2998
+ <xsl:when test="$current > $max"/>
2999
+ <xsl:otherwise>
3000
+ <!-- make numbered list-item -->
3001
+ <li>
3002
+ <xsl:value-of select="$current"/>
3003
+ </li>
3004
+ <!-- recurse with next integer -->
3005
+ <xsl:call-template name="numbered-list-items">
3006
+ <xsl:with-param name="current" select="$current + 1"/>
3007
+ <xsl:with-param name="max" select="$max"/>
3008
+ </xsl:call-template>
3009
+ </xsl:otherwise>
3010
+ </xsl:choose>
3011
+ </xsl:template>
3012
+
3013
+ <!-- ######### -->
3014
+ <!-- Utilities -->
3015
+ <!-- ######### -->
3016
+
3017
+ <!-- Interpreter/compiler/linker args all start as comma separated lists "-Wall, -std=c++17" -->
3018
+ <!-- and need to end up a JSON array of strings: "['-Wall', '-std=c++17']" -->
3019
+ <xsl:template name="comma-list-to-json-array">
3020
+ <xsl:param name="list"/>
3021
+ <!-- comma separated in PreTeXt source -->
3022
+ <xsl:variable name="tokens" select="str:tokenize($list, ',')"/>
3023
+ <xsl:text>[</xsl:text>
3024
+ <xsl:for-each select="$tokens">
3025
+ <xsl:text>'</xsl:text>
3026
+ <!-- prune leading/trailing spaces but leave ones in middle -->
3027
+ <xsl:value-of select="normalize-space(.)"/>
3028
+ <xsl:text>'</xsl:text>
3029
+ <xsl:if test="following-sibling::token">
3030
+ <xsl:text>, </xsl:text>
3031
+ </xsl:if>
3032
+ </xsl:for-each>
3033
+ <xsl:text>]</xsl:text>
3034
+ </xsl:template>
3035
+
3036
+ <!-- Runestone components, such as data files and select questions, -->
3037
+ <!-- frequently point to other Runestone components in the database. -->
3038
+ <!-- * Authors point in their source with @xml:id -->
3039
+ <!-- values in a space- or comma- separated list -->
3040
+ <!-- * We locate the targets in the orginal source -->
3041
+ <!-- * Compute the Runestone database id -->
3042
+ <!-- * Return a list (varying separator) to use in Runestone HTML. -->
3043
+ <xsl:template name="runestone-targets">
3044
+ <xsl:param name="id-list"/>
3045
+ <xsl:param name="separator" select="'MISSING SEPARATOR'"/>
3046
+ <xsl:param name="output-field" select="'runestone-id'"/>
3047
+
3048
+ <!-- comma or space separated in PreTeXt source -->
3049
+ <xsl:variable name="tokens" select="str:tokenize($id-list, ', ')"/>
3050
+ <xsl:for-each select="$tokens">
3051
+ <!-- attribute value is an xml:id, get target interactive -->
3052
+ <xsl:variable name="the-id">
3053
+ <xsl:value-of select="."/>
3054
+ </xsl:variable>
3055
+ <!-- context shift so id() functions properly -->
3056
+ <xsl:for-each select="$original">
3057
+ <xsl:variable name="target" select="id($the-id)"/>
3058
+ <xsl:if test="not($target)">
3059
+ <xsl:message>PTX:ERROR: an @xml:id value "<xsl:value-of select="$the-id"/>" was used to specify a runestone component but no item with that id exists.</xsl:message>
3060
+ </xsl:if>
3061
+ <!-- build Runestone database id of the target -->
3062
+ <xsl:choose>
3063
+ <xsl:when test="$output-field = 'runestone-id'">
3064
+ <xsl:apply-templates select="$target" mode="runestone-id"/>
3065
+ </xsl:when>
3066
+ <xsl:when test="$output-field = 'filename'">
3067
+ <xsl:value-of select="$target/@filename"/>
3068
+ </xsl:when>
3069
+ <xsl:otherwise>
3070
+ <xsl:apply-templates select="$target" mode="runestone-id"/>
3071
+ <xsl:message>PTX:ERROR: runestone-targets template was called with an invalid @output-field value "<xsl:value-of select="$output-field"/>"</xsl:message>
3072
+ </xsl:otherwise>
3073
+ </xsl:choose>
3074
+ <!-- n - 1 separators, required by receiving Javascript -->
3075
+ </xsl:for-each>
3076
+ <xsl:if test="following-sibling::token">
3077
+ <xsl:value-of select="$separator"/>
3078
+ </xsl:if>
3079
+ </xsl:for-each>
3080
+ </xsl:template>
3081
+
3082
+
3083
+ </xsl:stylesheet>