@citolab/qti-components 7.28.1 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/cdn/index.global.js +1 -1
  2. package/cdn/index.js +6316 -2710
  3. package/dist/base.d.ts +100 -15
  4. package/dist/base.js +27 -6
  5. package/dist/chunk-3AJOQCDU.js +11 -0
  6. package/dist/chunk-3AJOQCDU.js.map +1 -0
  7. package/dist/chunk-64XSIBGO.js +241 -0
  8. package/dist/chunk-64XSIBGO.js.map +1 -0
  9. package/dist/chunk-7HC5SMCU.js +102 -0
  10. package/dist/chunk-7HC5SMCU.js.map +1 -0
  11. package/dist/{chunk-SP6S2WMG.js → chunk-7V4HJFHW.js} +3 -3
  12. package/dist/{chunk-SP6S2WMG.js.map → chunk-7V4HJFHW.js.map} +1 -1
  13. package/dist/chunk-CAO4JRLY.js +11 -0
  14. package/dist/chunk-CAO4JRLY.js.map +1 -0
  15. package/dist/{chunk-MPC7LIPC.js → chunk-F4BNJWYN.js} +4264 -3647
  16. package/dist/chunk-F4BNJWYN.js.map +1 -0
  17. package/dist/{chunk-3XV26R5M.js → chunk-H3J7LFRO.js} +829 -1005
  18. package/dist/chunk-H3J7LFRO.js.map +1 -0
  19. package/dist/{chunk-3WBG257S.js → chunk-IJD53WY6.js} +96 -74
  20. package/dist/chunk-IJD53WY6.js.map +1 -0
  21. package/dist/chunk-LVBIQDPM.js +1374 -0
  22. package/dist/chunk-LVBIQDPM.js.map +1 -0
  23. package/dist/{chunk-REI3AMCZ.js → chunk-NA2N2WSG.js} +1303 -1193
  24. package/dist/chunk-NA2N2WSG.js.map +1 -0
  25. package/dist/chunk-QECOPVPO.js +294 -0
  26. package/dist/chunk-QECOPVPO.js.map +1 -0
  27. package/dist/{chunk-HFCZFWSH.js → chunk-RXGDQDOV.js} +1993 -1764
  28. package/dist/chunk-RXGDQDOV.js.map +1 -0
  29. package/dist/chunk-V5G7X53Y.js +11 -0
  30. package/dist/chunk-V5G7X53Y.js.map +1 -0
  31. package/dist/{chunk-WDO2TRFM.js → chunk-WVVMNF74.js} +851 -925
  32. package/dist/chunk-WVVMNF74.js.map +1 -0
  33. package/dist/{chunk-FLJAKWXL.js → chunk-X3E5HXG2.js} +2 -2
  34. package/dist/chunk-XU7HNT4F.js +1608 -0
  35. package/dist/chunk-XU7HNT4F.js.map +1 -0
  36. package/dist/{computed-item.context-DfsW_96S.d.ts → computed-item.types.d-BKZdSLLJ.d.ts} +2 -5
  37. package/dist/{computed.context-DHbxad0N.d.ts → computed.types.d-rKLSPvVG.d.ts} +2 -5
  38. package/dist/config.context.d-BvU9r-Mg.d.ts +91 -0
  39. package/dist/corrections.d.ts +432 -0
  40. package/dist/corrections.js +84 -0
  41. package/dist/elements.d.ts +8 -14
  42. package/dist/elements.js +6 -5
  43. package/dist/index.d.ts +51 -16
  44. package/dist/index.js +193 -76
  45. package/dist/interaction.d-D4MstYRz.d.ts +115 -0
  46. package/dist/interaction.interface.d-CvjHM2bi.d.ts +46 -0
  47. package/dist/interactions.d.ts +1206 -552
  48. package/dist/interactions.js +19 -15
  49. package/dist/item.css +4228 -1656
  50. package/dist/item.d.ts +13 -55
  51. package/dist/item.js +6 -11
  52. package/dist/{variables-CMYcDbyW.d.ts → item.types.d-QU7LljEK.d.ts} +13 -1
  53. package/dist/loader.d.ts +4 -4
  54. package/dist/loader.js +2 -3
  55. package/dist/processing.d.ts +6 -7
  56. package/dist/processing.js +2 -3
  57. package/dist/qti-components-jsx.d.ts +5348 -2709
  58. package/dist/qti-components-jsx.js +1 -0
  59. package/dist/qti-condition-expression.d-aMvBvpc6.d.ts +8 -0
  60. package/dist/{qti-condition-expression-Dam-RER6.d.ts → qti-expression.d-BietT_8b.d.ts} +14 -9
  61. package/dist/{qti-feedback-ChmXhZuf.d.ts → qti-feedback.d-DZbad0RN.d.ts} +1 -1
  62. package/dist/{qti-transform-test-DoZaWItm.d.ts → qti-transform-test.d-BL7GGFJj.d.ts} +2 -2
  63. package/dist/response.d-_8y4kjGP.d.ts +106 -0
  64. package/dist/test.d.ts +199 -39
  65. package/dist/test.js +34 -11
  66. package/dist/transformers.d.ts +1 -1
  67. package/dist/transformers.js +1 -2
  68. package/package.json +24 -17
  69. package/custom-elements.json +0 -43251
  70. package/dist/chunk-3WBG257S.js.map +0 -1
  71. package/dist/chunk-3XV26R5M.js.map +0 -1
  72. package/dist/chunk-AQ5RJFM6.js +0 -3673
  73. package/dist/chunk-AQ5RJFM6.js.map +0 -1
  74. package/dist/chunk-HFCZFWSH.js.map +0 -1
  75. package/dist/chunk-MPC7LIPC.js.map +0 -1
  76. package/dist/chunk-QXBXORM3.js +0 -27
  77. package/dist/chunk-REI3AMCZ.js.map +0 -1
  78. package/dist/chunk-RLTYVS2I.js +0 -530
  79. package/dist/chunk-RLTYVS2I.js.map +0 -1
  80. package/dist/chunk-WDO2TRFM.js.map +0 -1
  81. package/dist/config.context-BjZ2bZSY.d.ts +0 -42
  82. package/dist/interaction-Bgzlw7Iu.d.ts +0 -110
  83. package/dist/item.context-CkUeDcaO.d.ts +0 -15
  84. package/dist/test.context-CByZZjkR.d.ts +0 -15
  85. /package/dist/{chunk-FLJAKWXL.js.map → chunk-X3E5HXG2.js.map} +0 -0
  86. /package/dist/{chunk-QXBXORM3.js.map → corrections.js.map} +0 -0
  87. /package/dist/{qti-rule-base-ajOnfGXY.d.ts → qti-rule-base.d-ajOnfGXY.d.ts} +0 -0
  88. /package/dist/{qti.context-CejJMlfZ.d.ts → qti.context.d-CejJMlfZ.d.ts} +0 -0
@@ -0,0 +1,1608 @@
1
+ import {
2
+ QtiTest,
3
+ TestCheckItem,
4
+ TestNavigation
5
+ } from "./chunk-RXGDQDOV.js";
6
+ import {
7
+ QtiAssessmentItem
8
+ } from "./chunk-WVVMNF74.js";
9
+ import {
10
+ QtiAssociateInteraction,
11
+ QtiChoiceInteraction,
12
+ QtiExtendedTextInteraction,
13
+ QtiGapMatchInteraction,
14
+ QtiGapText,
15
+ QtiGraphicAssociateInteraction,
16
+ QtiGraphicGapMatchInteraction,
17
+ QtiGraphicOrderInteraction,
18
+ QtiHotspotChoice,
19
+ QtiHotspotInteraction,
20
+ QtiHottext,
21
+ QtiHottextInteraction,
22
+ QtiInlineChoiceInteraction,
23
+ QtiMatchInteraction,
24
+ QtiOrderInteraction,
25
+ QtiPortableCustomInteraction,
26
+ QtiSelectPointInteraction,
27
+ QtiSimpleAssociableChoice,
28
+ QtiSimpleChoice,
29
+ QtiSliderInteraction,
30
+ QtiTextEntryInteraction,
31
+ c as c2,
32
+ o
33
+ } from "./chunk-F4BNJWYN.js";
34
+ import {
35
+ QtiItem
36
+ } from "./chunk-QECOPVPO.js";
37
+ import {
38
+ watch
39
+ } from "./chunk-64XSIBGO.js";
40
+ import {
41
+ A,
42
+ ScoringHelper,
43
+ T,
44
+ b,
45
+ c,
46
+ computedContext,
47
+ computedItemContext,
48
+ configContext,
49
+ i,
50
+ i2,
51
+ n2 as n,
52
+ r,
53
+ responseAttributeConverter
54
+ } from "./chunk-H3J7LFRO.js";
55
+
56
+ // ../qti-corrections/dist/mixins/correct-response.mixin.js
57
+ var __decorate = function(decorators, target, key, desc) {
58
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
59
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
60
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
61
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
62
+ };
63
+ var Correctness;
64
+ (function(Correctness2) {
65
+ Correctness2["Correct"] = "correct";
66
+ Correctness2["PartiallyCorrect"] = "partially-correct";
67
+ Correctness2["Incorrect"] = "incorrect";
68
+ })(Correctness || (Correctness = {}));
69
+ var CorrectResponseMixin = (superClass) => {
70
+ class CorrectResponseMixinClass extends superClass {
71
+ constructor() {
72
+ super(...arguments);
73
+ this.showCorrectResponse = false;
74
+ this._handleShowCorrectResponseChange = (_, show) => {
75
+ this.toggleInternalCorrectResponse(show);
76
+ };
77
+ this.showFullCorrectResponse = false;
78
+ this._handleShowFullCorrectResponseChange = (_, show) => {
79
+ this.toggleFullCorrectResponse(show);
80
+ };
81
+ this.showCandidateCorrection = false;
82
+ this._handleShowCandidateCorrectionChange = (_, show) => {
83
+ this.toggleCandidateCorrection(show);
84
+ };
85
+ this._correctResponse = null;
86
+ }
87
+ /**
88
+ * The correct response for this interaction. Can be set as a comma-separated
89
+ * string via attribute or as string/string[] via property.
90
+ *
91
+ * @example
92
+ * ```html
93
+ * <qti-choice-interaction correct-response="A,B">
94
+ * ```
95
+ */
96
+ set correctResponseAttr(val) {
97
+ this._correctResponse = val;
98
+ }
99
+ get correctResponseAttr() {
100
+ return this._correctResponse;
101
+ }
102
+ get isFullCorrectResponse() {
103
+ return this._isFullCorrectResponse;
104
+ }
105
+ get registersWithItem() {
106
+ return !this.isFullCorrectResponse && super.registersWithItem;
107
+ }
108
+ set isFullCorrectResponse(val) {
109
+ this._isFullCorrectResponse = val;
110
+ if (val) {
111
+ this.setAttribute("inert", "");
112
+ this.setAttribute("response-identifier", this.responseIdentifier + "_cr");
113
+ this.setAttribute("answer-key", "");
114
+ }
115
+ }
116
+ get correctResponse() {
117
+ const responseVariable = this.responseVariable;
118
+ if (responseVariable?.correctResponse) {
119
+ return responseVariable.correctResponse;
120
+ }
121
+ return this._correctResponse;
122
+ }
123
+ set correctResponse(val) {
124
+ const oldVal = this._correctResponse;
125
+ this._correctResponse = val;
126
+ if (val === null || val === void 0) {
127
+ this.removeAttribute("correct-response");
128
+ } else {
129
+ this.setAttribute("correct-response", Array.isArray(val) ? val.join(",") : val);
130
+ }
131
+ this.requestUpdate("_correctResponse", oldVal);
132
+ }
133
+ // ─── correctness getter ───────────────────────────────────────────────────
134
+ get correctness() {
135
+ const correctResponse = this.correctResponse;
136
+ if (!correctResponse)
137
+ return null;
138
+ const currentResponse = this.response;
139
+ if (currentResponse === null || currentResponse === void 0 || currentResponse === "" || Array.isArray(currentResponse) && currentResponse.length === 0) {
140
+ return null;
141
+ }
142
+ const correctArr = Array.isArray(correctResponse) ? correctResponse : [correctResponse];
143
+ const responseArr = Array.isArray(currentResponse) ? currentResponse : [currentResponse];
144
+ const filteredResponseArr = responseArr.filter((r2) => r2 !== "");
145
+ if (filteredResponseArr.length === 0)
146
+ return null;
147
+ const correctCount = filteredResponseArr.filter((r2) => correctArr.includes(r2)).length;
148
+ const incorrectCount = filteredResponseArr.filter((r2) => !correctArr.includes(r2)).length;
149
+ if (correctCount === correctArr.length && incorrectCount === 0 && filteredResponseArr.length === correctArr.length) {
150
+ return Correctness.Correct;
151
+ }
152
+ if (correctCount > 0 && (correctCount < correctArr.length || incorrectCount > 0)) {
153
+ return Correctness.PartiallyCorrect;
154
+ }
155
+ return Correctness.Incorrect;
156
+ }
157
+ // ─── correctionPart ───────────────────────────────────────────────────────
158
+ get correctionPart() {
159
+ return "correction";
160
+ }
161
+ // ─── toggle methods ───────────────────────────────────────────────────────
162
+ toggleCorrectResponse(show) {
163
+ const correctionConfig = this.configContext;
164
+ const correctResponseMode = correctionConfig?.correctResponseMode || "internal";
165
+ if (correctResponseMode === "full") {
166
+ this.toggleFullCorrectResponse(show);
167
+ } else {
168
+ this.toggleInternalCorrectResponse(show);
169
+ }
170
+ }
171
+ /**
172
+ * Whether the full correct response is withheld from a candidate who is already correct.
173
+ *
174
+ * Withheld by default, unless the item configures `fullCorrectResponseOnlyWhenIncorrect: false`
175
+ * — the behaviour `show-full-correct-response` has always had, on the reasoning that a correct
176
+ * answer already *is* the key.
177
+ *
178
+ * An interaction that routes its internal mode here overrides this to false: there, the caller
179
+ * asked to show the correct response, and silently showing nothing is worse than showing the
180
+ * candidate the answer they already gave.
181
+ */
182
+ get withholdsFullCorrectResponseWhenCorrect() {
183
+ return this.configContext?.fullCorrectResponseOnlyWhenIncorrect !== false;
184
+ }
185
+ async toggleFullCorrectResponse(show) {
186
+ const nextSibling = this.nextSibling;
187
+ const nextSiblingIsFullCorrectResponse = nextSibling instanceof HTMLDivElement && nextSibling?.classList.contains("full-correct-response");
188
+ const correctResponse = this.correctResponse;
189
+ if (!correctResponse) {
190
+ return;
191
+ }
192
+ const showFullCorrectResponse = show && (!this.withholdsFullCorrectResponseWhenCorrect || this.correctness !== Correctness.Correct);
193
+ if (!showFullCorrectResponse) {
194
+ if (!nextSiblingIsFullCorrectResponse) {
195
+ return;
196
+ }
197
+ this.parentElement?.removeChild(nextSibling);
198
+ return;
199
+ }
200
+ if (nextSiblingIsFullCorrectResponse) {
201
+ return;
202
+ }
203
+ const clone = this.cloneNode(true);
204
+ clone.removeAttribute("show-full-correct-response");
205
+ clone.removeAttribute("show-correct-response");
206
+ clone.removeAttribute("show-candidate-correction");
207
+ const containerDiv = document.createElement("div");
208
+ containerDiv.classList.add("full-correct-response");
209
+ if (this.isInline) {
210
+ containerDiv.classList.add("full-correct-response-inline");
211
+ } else {
212
+ containerDiv.classList.add("full-correct-response-block");
213
+ }
214
+ containerDiv.role = "full-correct-response";
215
+ containerDiv.appendChild(clone);
216
+ clone.isFullCorrectResponse = true;
217
+ this.parentElement?.insertBefore(containerDiv, this.nextSibling);
218
+ await clone.updateComplete;
219
+ clone.response = Array.isArray(correctResponse) ? [...correctResponse] : correctResponse;
220
+ }
221
+ toggleInternalCorrectResponse(show) {
222
+ this._internals.states.delete("show-correct-response");
223
+ if (show && this.correctResponse) {
224
+ this._internals.states.add("show-correct-response");
225
+ }
226
+ }
227
+ toggleCandidateCorrection(show) {
228
+ if (!show) {
229
+ return;
230
+ }
231
+ }
232
+ // ─── Lifecycle ────────────────────────────────────────────────────────────
233
+ firstUpdated() {
234
+ super.firstUpdated();
235
+ if (this.isFullCorrectResponse) {
236
+ return;
237
+ }
238
+ if (this.showCorrectResponse) {
239
+ this.toggleInternalCorrectResponse(true);
240
+ }
241
+ if (this.showFullCorrectResponse) {
242
+ this.toggleFullCorrectResponse(true);
243
+ }
244
+ }
245
+ }
246
+ __decorate([
247
+ n({
248
+ attribute: "correct-response",
249
+ reflect: true,
250
+ converter: responseAttributeConverter({ emptyAs: null })
251
+ })
252
+ ], CorrectResponseMixinClass.prototype, "correctResponseAttr", null);
253
+ __decorate([
254
+ n({ type: Boolean, attribute: "show-correct-response", reflect: true })
255
+ ], CorrectResponseMixinClass.prototype, "showCorrectResponse", void 0);
256
+ __decorate([
257
+ watch("showCorrectResponse", { waitUntilFirstUpdate: true })
258
+ ], CorrectResponseMixinClass.prototype, "_handleShowCorrectResponseChange", void 0);
259
+ __decorate([
260
+ n({ type: Boolean, attribute: "show-full-correct-response", reflect: true })
261
+ ], CorrectResponseMixinClass.prototype, "showFullCorrectResponse", void 0);
262
+ __decorate([
263
+ watch("showFullCorrectResponse", { waitUntilFirstUpdate: true })
264
+ ], CorrectResponseMixinClass.prototype, "_handleShowFullCorrectResponseChange", void 0);
265
+ __decorate([
266
+ n({ type: Boolean, attribute: "show-candidate-correction", reflect: true })
267
+ ], CorrectResponseMixinClass.prototype, "showCandidateCorrection", void 0);
268
+ __decorate([
269
+ watch("showCandidateCorrection", { waitUntilFirstUpdate: true })
270
+ ], CorrectResponseMixinClass.prototype, "_handleShowCandidateCorrectionChange", void 0);
271
+ __decorate([
272
+ r()
273
+ ], CorrectResponseMixinClass.prototype, "_isFullCorrectResponse", void 0);
274
+ __decorate([
275
+ r()
276
+ ], CorrectResponseMixinClass.prototype, "_correctResponse", void 0);
277
+ return CorrectResponseMixinClass;
278
+ };
279
+
280
+ // ../qti-corrections/dist/styles/correction.styles.js
281
+ var correctionPart = i`
282
+ /* Nothing at all until the item judges this element. */
283
+ [part~='correction'] {
284
+ display: none;
285
+ }
286
+
287
+ [part~='correction-correct'],
288
+ [part~='correction-incorrect'],
289
+ [part~='correction-partially-correct'] {
290
+ display: inline-flex;
291
+ flex: 0 0 auto;
292
+
293
+ /*
294
+ * The badge aligns ITSELF, and that is the whole of the vertical story.
295
+ *
296
+ * It is a flex item with a definite height, so left alone its cross-axis position is decided by
297
+ * whatever its host says — and the hosts disagree: qti-simple-associable-choice defaults to
298
+ * align-items: normal, and qti-gap-text gates center behind :not([part~='drag']), so the
299
+ * same chip centred its badge in the bank and top-aligned it once placed. align-self settles it
300
+ * from the badge's side, in one line, without touching a single container.
301
+ *
302
+ * This replaces an align-items/justify-content: center pair that had never done anything: the
303
+ * badge is empty — a mask painted over a background — so centring its CONTENTS centres nothing.
304
+ */
305
+ align-self: center;
306
+
307
+ box-sizing: border-box;
308
+ width: var(--qti-correction-size, 1em);
309
+ height: var(--qti-correction-size, 1em);
310
+ margin-left: var(--qti-glyph-gap, 0.5em);
311
+
312
+ pointer-events: none;
313
+ }
314
+ `;
315
+
316
+ // ../qti-corrections/dist/mixins/candidate-correction.mixin.js
317
+ var __decorate2 = function(decorators, target, key, desc) {
318
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
319
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
320
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
321
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
322
+ };
323
+ var CandidateCorrectionMixin = (superClass) => {
324
+ class CandidateCorrectionMixinClass extends CorrectResponseMixin(superClass) {
325
+ constructor() {
326
+ super(...arguments);
327
+ this._candidateCorrection = null;
328
+ }
329
+ get correctionPart() {
330
+ return this._candidateCorrection ? `correction correction-${this._candidateCorrection}` : "correction";
331
+ }
332
+ toggleCandidateCorrection(show) {
333
+ if (this.showCandidateCorrection !== show) {
334
+ this.showCandidateCorrection = show;
335
+ }
336
+ super.toggleCandidateCorrection(show);
337
+ const internals = this._internals;
338
+ internals.states.delete("candidate-correct");
339
+ internals.states.delete("candidate-partially-correct");
340
+ internals.states.delete("candidate-incorrect");
341
+ let verdict = null;
342
+ if (show) {
343
+ if (this.correctness === Correctness.Correct) {
344
+ internals.states.add("candidate-correct");
345
+ verdict = "correct";
346
+ }
347
+ if (this.correctness === Correctness.PartiallyCorrect) {
348
+ internals.states.add("candidate-partially-correct");
349
+ verdict = "partially-correct";
350
+ }
351
+ if (this.correctness === Correctness.Incorrect) {
352
+ internals.states.add("candidate-incorrect");
353
+ verdict = "incorrect";
354
+ }
355
+ }
356
+ if (this._candidateCorrection !== verdict) {
357
+ this._candidateCorrection = verdict;
358
+ }
359
+ }
360
+ connectedCallback() {
361
+ super.connectedCallback?.();
362
+ queueMicrotask(() => {
363
+ if (!this.isConnected || this.isFullCorrectResponse) {
364
+ return;
365
+ }
366
+ if (this.showCandidateCorrection) {
367
+ this.toggleCandidateCorrection(true);
368
+ }
369
+ });
370
+ }
371
+ }
372
+ __decorate2([
373
+ r()
374
+ ], CandidateCorrectionMixinClass.prototype, "_candidateCorrection", void 0);
375
+ return CandidateCorrectionMixinClass;
376
+ };
377
+
378
+ // ../qti-corrections/dist/mixins/choice-correction.mixin.js
379
+ var ChoiceCorrectionMixin = (superClass) => {
380
+ class ChoiceCorrectionMixinClass extends CandidateCorrectionMixin(superClass) {
381
+ toggleInternalCorrectResponse(show) {
382
+ super.toggleInternalCorrectResponse(show);
383
+ const correctResponse = this.correctResponse;
384
+ const correctResponses = correctResponse ? Array.isArray(correctResponse) ? correctResponse : [correctResponse] : [];
385
+ for (const choice of this._choiceElements) {
386
+ choice.internals.states.delete("correct-response");
387
+ choice.internals.states.delete("incorrect-response");
388
+ if (show && correctResponses.length > 0) {
389
+ choice.internals.states.add(correctResponses.includes(choice.identifier) ? "correct-response" : "incorrect-response");
390
+ }
391
+ }
392
+ }
393
+ toggleCandidateCorrection(show) {
394
+ super.toggleCandidateCorrection(show);
395
+ for (const choice of this._choiceElements) {
396
+ choice.candidateCorrection = null;
397
+ }
398
+ if (!show || !this.correctResponse) {
399
+ return;
400
+ }
401
+ const correctResponses = Array.isArray(this.correctResponse) ? this.correctResponse : [this.correctResponse];
402
+ const candidateResponses = Array.isArray(this.response) ? this.response : this.response ? [this.response] : [];
403
+ for (const choice of this._choiceElements) {
404
+ if (!candidateResponses.includes(choice.identifier)) {
405
+ continue;
406
+ }
407
+ choice.candidateCorrection = correctResponses.includes(choice.identifier) ? "correct" : "incorrect";
408
+ }
409
+ }
410
+ }
411
+ return ChoiceCorrectionMixinClass;
412
+ };
413
+
414
+ // ../qti-corrections/dist/mixins/active-element-correction.mixin.js
415
+ var ActiveElementCorrectionMixin = (superClass) => {
416
+ class ActiveElementCorrectionMixinClass extends superClass {
417
+ #candidateCorrection = null;
418
+ get candidateCorrection() {
419
+ return this.#candidateCorrection;
420
+ }
421
+ set candidateCorrection(value) {
422
+ if (this.#candidateCorrection === value)
423
+ return;
424
+ const previous = this.#candidateCorrection;
425
+ this.#candidateCorrection = value;
426
+ const states = this.internals.states;
427
+ states.delete("candidate-correct");
428
+ states.delete("candidate-incorrect");
429
+ states.delete("candidate-partially-correct");
430
+ if (value)
431
+ states.add(`candidate-${value}`);
432
+ this.requestUpdate("candidateCorrection", previous);
433
+ }
434
+ get correctionPart() {
435
+ return this.#candidateCorrection ? `correction correction-${this.#candidateCorrection}` : "correction";
436
+ }
437
+ }
438
+ return ActiveElementCorrectionMixinClass;
439
+ };
440
+
441
+ // ../qti-corrections/dist/elements/qti-gap-text-correction.js
442
+ var QtiGapTextCorrection = class extends ActiveElementCorrectionMixin(QtiGapText) {
443
+ static {
444
+ this.styles = [QtiGapText.styles, correctionPart];
445
+ }
446
+ render() {
447
+ return T`<div part="control"></div>
448
+ <slot part="label"></slot>
449
+ <span part=${this.correctionPart} aria-hidden="true"></span>`;
450
+ }
451
+ };
452
+
453
+ // ../qti-corrections/dist/elements/qti-hotspot-choice-correction.js
454
+ var __decorate3 = function(decorators, target, key, desc) {
455
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
456
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
457
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
458
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
459
+ };
460
+ var QtiHotspotChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiHotspotChoice) {
461
+ constructor() {
462
+ super(...arguments);
463
+ this.orderCorrect = null;
464
+ }
465
+ };
466
+ __decorate3([
467
+ n({ attribute: "aria-ordercorrectvalue", type: Number, reflect: true })
468
+ ], QtiHotspotChoiceCorrection.prototype, "orderCorrect", void 0);
469
+
470
+ // ../qti-corrections/dist/elements/qti-hottext-correction.js
471
+ var QtiHottextCorrection = class extends ActiveElementCorrectionMixin(QtiHottext) {
472
+ static {
473
+ this.styles = [QtiHottext.styles, correctionPart];
474
+ }
475
+ render() {
476
+ return T`<div part="control"><div part="control-mark"></div></div>
477
+ <slot part="label"></slot>
478
+ <span part=${this.correctionPart} aria-hidden="true"></span>`;
479
+ }
480
+ };
481
+
482
+ // ../qti-corrections/dist/elements/qti-simple-associable-choice-correction.js
483
+ var QtiSimpleAssociableChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiSimpleAssociableChoice) {
484
+ static {
485
+ this.styles = [QtiSimpleAssociableChoice.styles, correctionPart];
486
+ }
487
+ render() {
488
+ return T`
489
+ <div part="control"></div>
490
+ <slot part="label"></slot>
491
+ <span part=${this.correctionPart} aria-hidden="true"></span>
492
+ <div part="drop">
493
+ ${c2(this.drags, (node) => node.getAttribute("identifier"), (node) => node)}
494
+ </div>
495
+ `;
496
+ }
497
+ };
498
+
499
+ // ../qti-corrections/dist/elements/qti-simple-choice-correction.js
500
+ var QtiSimpleChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiSimpleChoice) {
501
+ static {
502
+ this.styles = [QtiSimpleChoice.styles, correctionPart];
503
+ }
504
+ render() {
505
+ return T`<div part="control" tabindex="0"><div part="control-mark"></div></div>
506
+ ${this.marker ? T`<div id="label" part="marker">${this.marker}</div>` : A}
507
+ <slot part="label"></slot>
508
+ <span part=${this.correctionPart} aria-hidden="true"></span>`;
509
+ }
510
+ };
511
+
512
+ // ../qti-corrections/dist/components/qti-assessment-item-correction.js
513
+ var QtiAssessmentItemCorrection = class extends QtiAssessmentItem {
514
+ showCorrectResponse(show) {
515
+ for (const interaction of this.interactionElements) {
516
+ interaction.toggleCorrectResponse?.(show);
517
+ }
518
+ this.updateControlState("item-show-correct-response", show);
519
+ }
520
+ showCandidateCorrection(show) {
521
+ for (const interaction of this.interactionElements) {
522
+ interaction.toggleCandidateCorrection?.(show);
523
+ }
524
+ this.updateControlState("item-show-candidate-correction", show);
525
+ }
526
+ afterResponseVariableUpdated() {
527
+ this.showCandidateCorrection(false);
528
+ }
529
+ updateControlState(selector, shown) {
530
+ let root = this.getRootNode();
531
+ while (true) {
532
+ root.querySelectorAll(selector).forEach((control) => control.shown = shown);
533
+ if (!(root instanceof ShadowRoot))
534
+ return;
535
+ root = root.host.getRootNode();
536
+ }
537
+ }
538
+ };
539
+
540
+ // ../qti-corrections/dist/components/qti-item-correction.js
541
+ var QtiItemCorrection = class extends QtiItem {
542
+ #onShowCorrectResponse = (event) => {
543
+ const show = event.detail;
544
+ this.assessmentItem?.showCorrectResponse?.(show);
545
+ this.updateControlState("item-show-correct-response", show);
546
+ };
547
+ #onShowCandidateCorrection = (event) => {
548
+ const show = event.detail;
549
+ this.assessmentItem?.showCandidateCorrection?.(show);
550
+ this.updateControlState("item-show-candidate-correction", show);
551
+ };
552
+ #onSwitchCorrectResponseMode = (event) => {
553
+ this.assessmentItem?.showCorrectResponse?.(false);
554
+ this.configContext = {
555
+ ...this.configContext,
556
+ correctResponseMode: event.detail
557
+ };
558
+ };
559
+ constructor() {
560
+ super();
561
+ this.addEventListener("item-show-correct-response", this.#onShowCorrectResponse);
562
+ this.addEventListener("item-show-candidate-correction", this.#onShowCandidateCorrection);
563
+ this.addEventListener("item-switch-correct-response-mode", this.#onSwitchCorrectResponseMode);
564
+ }
565
+ updateControlState(selector, shown) {
566
+ this.querySelectorAll(selector).forEach((control) => control.shown = shown);
567
+ }
568
+ disconnectedCallback() {
569
+ this.removeEventListener("item-show-correct-response", this.#onShowCorrectResponse);
570
+ this.removeEventListener("item-show-candidate-correction", this.#onShowCandidateCorrection);
571
+ this.removeEventListener("item-switch-correct-response-mode", this.#onSwitchCorrectResponseMode);
572
+ super.disconnectedCallback();
573
+ }
574
+ };
575
+
576
+ // ../qti-corrections/dist/components/test-correction-elements.js
577
+ var TestNavigationCorrection = class extends TestNavigation {
578
+ #onShowCorrectResponse = (event) => {
579
+ this.activeAssessmentItem?.showCorrectResponse?.(event.detail);
580
+ };
581
+ #onShowCandidateCorrection = (event) => {
582
+ this.activeAssessmentItem?.showCandidateCorrection?.(event.detail);
583
+ };
584
+ constructor() {
585
+ super();
586
+ this.addEventListener("test-show-correct-response", this.#onShowCorrectResponse);
587
+ this.addEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
588
+ }
589
+ disconnectedCallback() {
590
+ this.removeEventListener("test-show-correct-response", this.#onShowCorrectResponse);
591
+ this.removeEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
592
+ super.disconnectedCallback();
593
+ }
594
+ };
595
+ var QtiTestCorrection = class extends QtiTest {
596
+ updateAssessmentItemView(assessmentItem, view) {
597
+ assessmentItem.showCorrectResponse?.(view === "scorer");
598
+ }
599
+ };
600
+ var TestCheckItemCorrection = class extends TestCheckItem {
601
+ constructor() {
602
+ super();
603
+ this.addEventListener("click", () => {
604
+ this.dispatchEvent(new CustomEvent("test-show-correct-response", { detail: true, bubbles: true }));
605
+ });
606
+ }
607
+ };
608
+
609
+ // ../qti-corrections/dist/components/item-correct-response-mode/item-correct-response-mode.js
610
+ var __decorate4 = function(decorators, target, key, desc) {
611
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
612
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
613
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
614
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
615
+ };
616
+ var ItemCorrectResponseMode = class extends i2 {
617
+ constructor() {
618
+ super(...arguments);
619
+ this.label = "Correct response mode";
620
+ this._options = {
621
+ internal: "Internal",
622
+ full: "Full (copy)"
623
+ };
624
+ }
625
+ #switchMode(view) {
626
+ this.dispatchEvent(new CustomEvent("item-switch-correct-response-mode", {
627
+ detail: view,
628
+ bubbles: true
629
+ }));
630
+ }
631
+ render() {
632
+ return T`
633
+ <label part="label" for="modeSelect">${this.label}</label>
634
+ <select
635
+ part="select"
636
+ id="modeSelect"
637
+ @change=${(e) => {
638
+ const el = e.target;
639
+ this.#switchMode(el.value);
640
+ }}
641
+ >
642
+ ${Object.keys(this._options).map((value) => T`<option value="${value}" ?selected=${value === this.itemContext?.correctResponseMode}>
643
+ ${this._options[value]}
644
+ </option>`)}
645
+ </select>
646
+ `;
647
+ }
648
+ };
649
+ __decorate4([
650
+ c({ context: configContext, subscribe: true })
651
+ ], ItemCorrectResponseMode.prototype, "itemContext", void 0);
652
+ __decorate4([
653
+ n({ type: String })
654
+ ], ItemCorrectResponseMode.prototype, "label", void 0);
655
+ __decorate4([
656
+ r()
657
+ ], ItemCorrectResponseMode.prototype, "_options", void 0);
658
+
659
+ // ../qti-corrections/dist/components/styles.js
660
+ var form = i`
661
+ display: inline-flex;
662
+ align-items: center;
663
+ cursor: pointer;
664
+ padding: 0.5rem 1rem;
665
+ border-radius: 0.25rem;
666
+ user-select: none;
667
+ `;
668
+ var btn = i`
669
+ background-color: lightgray;
670
+ ${form};
671
+ `;
672
+ var dis = i`
673
+ cursor: not-allowed;
674
+ opacity: 0.8;
675
+ `;
676
+ var ind = i`
677
+ ${form};
678
+ border: 1px solid gray;
679
+ `;
680
+
681
+ // ../qti-corrections/dist/components/item-show-candidate-correction/item-show-candidate-correction.js
682
+ var __decorate5 = function(decorators, target, key, desc) {
683
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
684
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
685
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
686
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
687
+ };
688
+ var ItemShowCandidateCorrection = class extends i2 {
689
+ static {
690
+ this.styles = i`
691
+ :host {
692
+ ${btn};
693
+ }
694
+ :host([disabled]) {
695
+ ${dis};
696
+ }
697
+ `;
698
+ }
699
+ #hasCorrectResponse;
700
+ // correct response is removed on certain point
701
+ #previousActiveItem;
702
+ // Store previous active item reference
703
+ constructor() {
704
+ super();
705
+ this.shown = false;
706
+ this.disabled = false;
707
+ this.showCandidateCorrectionText = "Show candidate correction";
708
+ this.hideCandidateCorrectionText = "Hide candidate correction";
709
+ this.noCorrectResponseText = "No correct response specified";
710
+ this.#hasCorrectResponse = false;
711
+ this.#previousActiveItem = "";
712
+ }
713
+ updated() {
714
+ if (!this.#hasCorrectResponse || this.#previousActiveItem !== this.computedContext?.identifier) {
715
+ this.#previousActiveItem = this.computedContext?.identifier;
716
+ const containsCorrectResponse = !!this.computedContext?.variables.some((v) => v["correctResponse"]);
717
+ const containsMapping = !!this.computedContext?.variables.some((v) => {
718
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
719
+ });
720
+ this.#hasCorrectResponse = containsCorrectResponse || containsMapping;
721
+ }
722
+ this.disabled = !this.#hasCorrectResponse;
723
+ }
724
+ #toggleState() {
725
+ if (this.disabled)
726
+ return;
727
+ this.dispatchEvent(new CustomEvent("item-show-candidate-correction", {
728
+ detail: !this.shown,
729
+ bubbles: true
730
+ }));
731
+ }
732
+ #getDisplayedText() {
733
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCandidateCorrectionText : this.showCandidateCorrectionText;
734
+ }
735
+ render() {
736
+ return T` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
737
+ }
738
+ };
739
+ __decorate5([
740
+ c({ context: computedItemContext, subscribe: true })
741
+ ], ItemShowCandidateCorrection.prototype, "computedContext", void 0);
742
+ __decorate5([
743
+ n({ type: Boolean, reflect: true })
744
+ ], ItemShowCandidateCorrection.prototype, "shown", void 0);
745
+ __decorate5([
746
+ n({ type: Boolean, reflect: true })
747
+ ], ItemShowCandidateCorrection.prototype, "disabled", void 0);
748
+ __decorate5([
749
+ n({ type: String })
750
+ ], ItemShowCandidateCorrection.prototype, "showCandidateCorrectionText", void 0);
751
+ __decorate5([
752
+ n({ type: String })
753
+ ], ItemShowCandidateCorrection.prototype, "hideCandidateCorrectionText", void 0);
754
+ __decorate5([
755
+ n({ type: String })
756
+ ], ItemShowCandidateCorrection.prototype, "noCorrectResponseText", void 0);
757
+
758
+ // ../qti-corrections/dist/components/item-show-correct-response/item-show-correct-response.js
759
+ var __decorate6 = function(decorators, target, key, desc) {
760
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
761
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
762
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
763
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
764
+ };
765
+ var ItemShowCorrectResponse = class extends i2 {
766
+ constructor() {
767
+ super(...arguments);
768
+ this.shown = false;
769
+ this.disabled = false;
770
+ this.showCorrectText = "Show correct response";
771
+ this.hideCorrectText = "Hide correct response";
772
+ this.noCorrectResponseText = "No correct response specified";
773
+ this.#hasCorrectResponse = false;
774
+ this.#previousActiveItem = "";
775
+ }
776
+ static {
777
+ this.styles = i`
778
+ :host {
779
+ ${btn};
780
+ }
781
+ :host([disabled]) {
782
+ ${dis};
783
+ }
784
+ `;
785
+ }
786
+ #hasCorrectResponse;
787
+ // correct response is removed on certain point
788
+ #previousActiveItem;
789
+ // Store previous active item reference
790
+ updated() {
791
+ if (!this.#hasCorrectResponse || this.#previousActiveItem !== this.computedContext?.identifier) {
792
+ this.#previousActiveItem = this.computedContext?.identifier;
793
+ const containsCorrectResponse = !!this.computedContext?.variables.some((v) => v["correctResponse"]);
794
+ const containsMapping = !!this.computedContext?.variables.some((v) => {
795
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
796
+ });
797
+ this.#hasCorrectResponse = containsCorrectResponse || containsMapping;
798
+ }
799
+ this.disabled = !this.#hasCorrectResponse;
800
+ }
801
+ #toggleState() {
802
+ if (this.disabled)
803
+ return;
804
+ this.dispatchEvent(new CustomEvent("item-show-correct-response", {
805
+ detail: !this.shown,
806
+ bubbles: true
807
+ }));
808
+ }
809
+ #getDisplayedText() {
810
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
811
+ }
812
+ render() {
813
+ return T` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
814
+ }
815
+ };
816
+ __decorate6([
817
+ c({ context: computedItemContext, subscribe: true })
818
+ ], ItemShowCorrectResponse.prototype, "computedContext", void 0);
819
+ __decorate6([
820
+ n({ type: Boolean, reflect: true })
821
+ ], ItemShowCorrectResponse.prototype, "shown", void 0);
822
+ __decorate6([
823
+ n({ type: Boolean, reflect: true })
824
+ ], ItemShowCorrectResponse.prototype, "disabled", void 0);
825
+ __decorate6([
826
+ n({ type: String })
827
+ ], ItemShowCorrectResponse.prototype, "showCorrectText", void 0);
828
+ __decorate6([
829
+ n({ type: String })
830
+ ], ItemShowCorrectResponse.prototype, "hideCorrectText", void 0);
831
+ __decorate6([
832
+ n({ type: String })
833
+ ], ItemShowCorrectResponse.prototype, "noCorrectResponseText", void 0);
834
+
835
+ // ../qti-corrections/dist/components/test-show-correct-response/test-show-correct-response.js
836
+ var __decorate7 = function(decorators, target, key, desc) {
837
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
838
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
839
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
840
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
841
+ };
842
+ var TestShowCorrectResponse = class extends i2 {
843
+ constructor() {
844
+ super(...arguments);
845
+ this.shown = false;
846
+ this.disabled = false;
847
+ this.showCorrectText = "Show correct response";
848
+ this.hideCorrectText = "Hide correct response";
849
+ this.noCorrectResponseText = "No correct response specified";
850
+ }
851
+ static {
852
+ this.styles = i`
853
+ :host {
854
+ ${btn};
855
+ }
856
+ :host([disabled]) {
857
+ ${dis};
858
+ }
859
+ `;
860
+ }
861
+ #previousActiveItem;
862
+ // Store previous active item reference
863
+ willUpdate(_changedProperties) {
864
+ const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
865
+ if (this.#previousActiveItem !== activeItem) {
866
+ this.shown = false;
867
+ this.#previousActiveItem = activeItem;
868
+ }
869
+ if (activeItem) {
870
+ const containsCorrectResponse = !!activeItem?.variables?.some((v) => v["correctResponse"]);
871
+ const containsMapping = !!activeItem?.variables?.some((v) => {
872
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
873
+ });
874
+ this.disabled = !containsCorrectResponse && !containsMapping;
875
+ } else {
876
+ this.disabled = true;
877
+ }
878
+ }
879
+ #toggleState() {
880
+ if (this.disabled)
881
+ return;
882
+ this.shown = !this.shown;
883
+ this.dispatchEvent(new CustomEvent("test-show-correct-response", {
884
+ detail: this.shown,
885
+ bubbles: true
886
+ }));
887
+ }
888
+ #getDisplayedText() {
889
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
890
+ }
891
+ render() {
892
+ return T` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
893
+ }
894
+ };
895
+ __decorate7([
896
+ c({ context: computedContext, subscribe: true })
897
+ ], TestShowCorrectResponse.prototype, "computedContext", void 0);
898
+ __decorate7([
899
+ n({ type: Boolean, reflect: true })
900
+ ], TestShowCorrectResponse.prototype, "shown", void 0);
901
+ __decorate7([
902
+ n({ type: Boolean, reflect: true })
903
+ ], TestShowCorrectResponse.prototype, "disabled", void 0);
904
+ __decorate7([
905
+ n({ type: String })
906
+ ], TestShowCorrectResponse.prototype, "showCorrectText", void 0);
907
+ __decorate7([
908
+ n({ type: String })
909
+ ], TestShowCorrectResponse.prototype, "hideCorrectText", void 0);
910
+ __decorate7([
911
+ n({ type: String })
912
+ ], TestShowCorrectResponse.prototype, "noCorrectResponseText", void 0);
913
+
914
+ // ../qti-corrections/dist/mixins/drag-drop-correction.mixin.js
915
+ var DragDropCorrectionMixin = (superClass) => {
916
+ class DragDropCorrectionMixinClass extends CandidateCorrectionMixin(superClass) {
917
+ toggleInternalCorrectResponse(show) {
918
+ if (!this.showFullCorrectResponse) {
919
+ this.toggleFullCorrectResponse(show);
920
+ }
921
+ }
922
+ afterCache() {
923
+ super.afterCache();
924
+ if (this.showCorrectResponse)
925
+ this.toggleInternalCorrectResponse(true);
926
+ if (this.showCandidateCorrection)
927
+ this.toggleCandidateCorrection(true);
928
+ if (this.showFullCorrectResponse)
929
+ this.toggleFullCorrectResponse(true);
930
+ }
931
+ }
932
+ return DragDropCorrectionMixinClass;
933
+ };
934
+
935
+ // ../qti-corrections/dist/interactions/qti-associate-interaction-correction.js
936
+ var QtiAssociateInteractionCorrection = class extends DragDropCorrectionMixin(QtiAssociateInteraction) {
937
+ };
938
+
939
+ // ../qti-corrections/dist/interactions/qti-choice-interaction-correction.js
940
+ var QtiChoiceInteractionCorrection = class extends ChoiceCorrectionMixin(QtiChoiceInteraction) {
941
+ };
942
+
943
+ // ../qti-corrections/dist/interactions/qti-extended-text-interaction-correction.js
944
+ var __decorate8 = function(decorators, target, key, desc) {
945
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
946
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
947
+ else for (var i3 = decorators.length - 1; i3 >= 0; i3--) if (d = decorators[i3]) r2 = (c3 < 3 ? d(r2) : c3 > 3 ? d(target, key, r2) : d(target, key)) || r2;
948
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
949
+ };
950
+ var QtiExtendedTextInteractionCorrection = class extends ActiveElementCorrectionMixin(QtiExtendedTextInteraction) {
951
+ static {
952
+ this.styles = [
953
+ QtiExtendedTextInteraction.styles,
954
+ correctionPart,
955
+ i`
956
+ /*
957
+ * Placement only — the glyph itself is the theme's, keyed on ::part(correction-*). That split
958
+ * is the contract in correction.styles.ts, which deliberately leaves the shared badge in flow
959
+ * and invites a component to lift it out if it wants a corner badge.
960
+ *
961
+ * A textarea is a box the candidate writes in, so the badge goes over its top-left corner
962
+ * rather than trailing the text the way a chip's does, and the theme reserves the gutter for it
963
+ * (padding-left on ::part(textarea)).
964
+ *
965
+ * Anchored to the TEXTAREA, not to the host. The badge's containing block is :host, and the
966
+ * host also contains the prompt slot — so a plain top: 0.85rem measured from the host lands on
967
+ * the prompt and the badge floats above the field, which is exactly what it did. Only the
968
+ * vertical offset needs anchoring: the textarea spans the host's full width, so the left
969
+ * offset is the same measured from either.
970
+ */
971
+ [part~='textarea'] {
972
+ anchor-name: --qti-extended-text-field;
973
+ }
974
+
975
+ [part~='correction'] {
976
+ position: absolute;
977
+ position-anchor: --qti-extended-text-field;
978
+ margin: 0;
979
+ width: var(--qti-correction-size);
980
+
981
+ /*
982
+ * One FIELD line tall, not square — this is what puts the glyph on the text rather than near
983
+ * it. The mask is drawn center / contain, so in a box exactly one line high it centres itself
984
+ * on that line while the width still governs the glyph's size.
985
+ *
986
+ * Not 1lh: that unit resolves against the BADGE's own inherited line-height (measured 18.4px),
987
+ * not the textarea's (24px), so it landed a third of a line low. --qti-field-line-height is
988
+ * the same number @mixin field-typography gives the field, so the two cannot drift.
989
+ */
990
+ height: calc(var(--qti-correction-size) * var(--qti-field-line-height));
991
+
992
+ /* Inset exactly as the text is: the same trailing token the two field-shaped interactions
993
+ use, read here as a LEADING inset because this badge lives in a left gutter. */
994
+ left: var(--qti-correction-inset);
995
+
996
+ /* Anchored to the field's first line: its top edge plus the field's own padding-top. */
997
+ top: calc(anchor(top) + var(--qti-spacing));
998
+ }
999
+
1000
+ /*
1001
+ * Fallback for engines without anchor positioning, where the top above does not parse and the
1002
+ * badge would fall to its static position below the field. The bottom of the gutter needs no
1003
+ * knowledge of where the field starts, and the gutter runs the full height, so the badge still
1004
+ * clears the text.
1005
+ *
1006
+ * Scoped by @supports rather than declared alongside. Both were set at first, on the assumption
1007
+ * that top + height over-constrains the box and bottom is ignored — it is not: measured, the
1008
+ * bottom won and put the badge on the third line.
1009
+ */
1010
+ @supports not (anchor-name: --probe) {
1011
+ [part~='correction'] {
1012
+ top: auto;
1013
+ bottom: var(--qti-correction-inset);
1014
+ }
1015
+ }
1016
+ `
1017
+ ];
1018
+ }
1019
+ /** The badge goes between the field and the validation message; everything else is the base's. */
1020
+ render() {
1021
+ return T`${this.renderPrompt()}${this.renderTextarea()}
1022
+ <span part=${this.correctionPart} aria-hidden="true"></span>
1023
+ ${this.renderValidationMessage()}`;
1024
+ }
1025
+ };
1026
+ __decorate8([
1027
+ n({ attribute: "candidate-correction", reflect: true, noAccessor: true })
1028
+ ], QtiExtendedTextInteractionCorrection.prototype, "candidateCorrection", void 0);
1029
+
1030
+ // ../qti-corrections/dist/interactions/shared.js
1031
+ var parsePairs = (response) => {
1032
+ if (!response)
1033
+ return [];
1034
+ const entries = Array.isArray(response) ? response : [response];
1035
+ return entries.map((entry) => {
1036
+ const [source, target] = entry.split(" ");
1037
+ return { source, target };
1038
+ });
1039
+ };
1040
+
1041
+ // ../qti-corrections/dist/interactions/qti-gap-match-interaction-correction.js
1042
+ var QtiGapMatchInteractionCorrection = class extends DragDropCorrectionMixin(QtiGapMatchInteraction) {
1043
+ toggleCandidateCorrection(show) {
1044
+ super.toggleCandidateCorrection(show);
1045
+ const matches = parsePairs(this.correctResponse);
1046
+ for (const target of this.querySelectorAll("qti-gap")) {
1047
+ const targetId = target.getAttribute("identifier");
1048
+ const selectedChoices = target.drags;
1049
+ for (const choice of selectedChoices) {
1050
+ choice.candidateCorrection = null;
1051
+ if (show && matches.some((match) => match.source === choice.identifier && match.target === targetId)) {
1052
+ choice.candidateCorrection = "correct";
1053
+ } else if (show) {
1054
+ choice.candidateCorrection = "incorrect";
1055
+ }
1056
+ }
1057
+ }
1058
+ }
1059
+ };
1060
+
1061
+ // ../qti-corrections/dist/interactions/qti-graphic-associate-interaction-correction.js
1062
+ var QtiGraphicAssociateInteractionCorrection = class extends CandidateCorrectionMixin(QtiGraphicAssociateInteraction) {
1063
+ #correctLines = [];
1064
+ toggleInternalCorrectResponse(show) {
1065
+ super.toggleInternalCorrectResponse(show);
1066
+ const response = this.correctResponse;
1067
+ const previous = this.#correctLines;
1068
+ this.#correctLines = show && response ? Array.isArray(response) ? [...response] : [response] : [];
1069
+ this.requestUpdate("correctLines", previous);
1070
+ }
1071
+ renderSupplementalLines() {
1072
+ return c2(this.#correctLines, (line) => line, (line) => b`
1073
+ <line
1074
+ part="correct-line"
1075
+ x1=${parseInt(this.querySelector(`[identifier=${line.split(" ")[0]}]`).style.left)}
1076
+ y1=${parseInt(this.querySelector(`[identifier=${line.split(" ")[0]}]`).style.top)}
1077
+ x2=${parseInt(this.querySelector(`[identifier=${line.split(" ")[1]}]`).style.left)}
1078
+ y2=${parseInt(this.querySelector(`[identifier=${line.split(" ")[1]}]`).style.top)}
1079
+ stroke="var(--qti-correct)"
1080
+ stroke-width="3"
1081
+ stroke-dasharray="5,5"
1082
+ />
1083
+ `);
1084
+ }
1085
+ };
1086
+
1087
+ // ../qti-corrections/dist/interactions/qti-graphic-gap-match-interaction-correction.js
1088
+ var QtiGraphicGapMatchInteractionCorrection = class extends DragDropCorrectionMixin(QtiGraphicGapMatchInteraction) {
1089
+ };
1090
+
1091
+ // ../qti-corrections/dist/interactions/qti-graphic-order-interaction-correction.js
1092
+ var QtiGraphicOrderInteractionCorrection = class extends CandidateCorrectionMixin(QtiGraphicOrderInteraction) {
1093
+ resolvePinColor(choice) {
1094
+ const correctableChoice = choice;
1095
+ if (correctableChoice.matches(":state(candidate-incorrect)"))
1096
+ return "var(--qti-incorrect)";
1097
+ if (correctableChoice.matches(":state(candidate-correct)"))
1098
+ return "var(--qti-correct)";
1099
+ if (correctableChoice.matches(":state(correct-response)"))
1100
+ return "var(--qti-answer-border)";
1101
+ return super.resolvePinColor(choice);
1102
+ }
1103
+ toggleInternalCorrectResponse(show) {
1104
+ super.toggleInternalCorrectResponse(show);
1105
+ const response = this.correctResponse;
1106
+ const correctOrder = response ? Array.isArray(response) ? response : [response] : [];
1107
+ for (const hotspot of this._choiceElements) {
1108
+ const index = correctOrder.indexOf(hotspot.identifier);
1109
+ hotspot.orderCorrect = show && index >= 0 ? index + 1 : null;
1110
+ }
1111
+ this.refreshLocatorPins();
1112
+ }
1113
+ toggleCandidateCorrection(show) {
1114
+ super.toggleCandidateCorrection(show);
1115
+ const response = this.correctResponse;
1116
+ const correctOrder = response ? Array.isArray(response) ? response : [response] : [];
1117
+ for (const hotspot of this._choiceElements) {
1118
+ hotspot.candidateCorrection = null;
1119
+ if (show && hotspot.order != null) {
1120
+ hotspot.candidateCorrection = correctOrder[hotspot.order - 1] === hotspot.identifier ? "correct" : "incorrect";
1121
+ }
1122
+ }
1123
+ this.refreshLocatorPins();
1124
+ }
1125
+ };
1126
+
1127
+ // ../qti-corrections/dist/interactions/qti-hotspot-interaction-correction.js
1128
+ var QtiHotspotInteractionCorrection = class extends ChoiceCorrectionMixin(QtiHotspotInteraction) {
1129
+ };
1130
+
1131
+ // ../qti-corrections/dist/interactions/qti-hottext-interaction-correction.js
1132
+ var QtiHottextInteractionCorrection = class extends ChoiceCorrectionMixin(QtiHottextInteraction) {
1133
+ };
1134
+
1135
+ // ../qti-corrections/dist/interactions/qti-inline-choice-interaction-correction.js
1136
+ var QtiInlineChoiceInteractionCorrection = class extends CandidateCorrectionMixin(QtiInlineChoiceInteraction) {
1137
+ static get styles() {
1138
+ return [
1139
+ QtiInlineChoiceInteraction.styles,
1140
+ correctionPart,
1141
+ i`
1142
+ /*
1143
+ * Only the TRAILING inset — the leading gap is the shared --qti-glyph-gap, like every other
1144
+ * badge. This used to set both margins to --qti-gap (1rem), twice everyone else's 0.5em, on
1145
+ * the one token qti-variables.css calls "the page's rhythm … an order of magnitude too wide
1146
+ * for a glyph sitting inside a line".
1147
+ *
1148
+ * The trailing inset is needed here, and in text-entry, because these two put the field's box
1149
+ * on the HOST while its padding sits on an inner element (::part(trigger), ::part(input)) —
1150
+ * so a badge that is a child of the host falls outside that padding and lands on the border.
1151
+ * Chips, choices and hottext carry their padding on the host, so their badge is already inset
1152
+ * by it and needs nothing.
1153
+ */
1154
+ [part~='correction'] {
1155
+ margin-inline-end: var(--qti-correction-inset);
1156
+ }
1157
+ `
1158
+ ];
1159
+ }
1160
+ /**
1161
+ * Internal mode defers to the full correct response, exactly as text-entry does.
1162
+ *
1163
+ * There is no room beside a dropdown for an answer key: the interaction sits inside a line of
1164
+ * running text, so a marker next to the field competes with the sentence around it, and when the
1165
+ * candidate happened to pick the correct option it printed the same word twice. The full variant
1166
+ * puts the key in a `div.full-correct-response-inline` after the field instead — one already-themed
1167
+ * presentation for both inline interactions rather than a second one only these two use.
1168
+ *
1169
+ * `super` is deliberately not called: its `show-correct-response` host state paints the dashed
1170
+ * outline in qti-states.css, and this element is no longer the thing presenting the key — the
1171
+ * wrapper beside it is. Leaving the state off keeps internal mode looking identical to an item
1172
+ * authored with `show-full-correct-response`.
1173
+ *
1174
+ * The guard stops the two entry points rendering two wrappers: `showFullCorrectResponse` has its
1175
+ * own watcher, so with both flags set this would otherwise run a second time.
1176
+ */
1177
+ toggleInternalCorrectResponse(show) {
1178
+ if (!this.showFullCorrectResponse)
1179
+ this.toggleFullCorrectResponse(show);
1180
+ }
1181
+ /**
1182
+ * The key is shown whether or not the candidate was right.
1183
+ *
1184
+ * The base withholds it from a correct candidate, on the reasoning that their answer already is
1185
+ * the key. That reasoning does not survive contact with this element: asking for the correct
1186
+ * response and being shown nothing is indistinguishable from the feature being broken, and it is
1187
+ * the only inline interaction where the answer and the key are the same short phrase, so there is
1188
+ * no visual clue either. Show it, and let the correctness badge say who was right.
1189
+ */
1190
+ get withholdsFullCorrectResponseWhenCorrect() {
1191
+ return false;
1192
+ }
1193
+ /** The badge trails the validation message; everything else is the base's. */
1194
+ render() {
1195
+ return T`
1196
+ ${this.renderTrigger()} ${this.renderMenu()} ${this.renderValidationMessage()}
1197
+ <span part=${this.correctionPart} aria-hidden="true"></span>
1198
+ `;
1199
+ }
1200
+ };
1201
+
1202
+ // ../qti-corrections/dist/interactions/qti-match-interaction-correction.js
1203
+ var QtiMatchInteractionCorrection = class extends DragDropCorrectionMixin(QtiMatchInteraction) {
1204
+ #correctPairs = /* @__PURE__ */ new Set();
1205
+ toggleCandidateCorrection(show) {
1206
+ super.toggleCandidateCorrection(show);
1207
+ const matches = parsePairs(this.correctResponse);
1208
+ this.#correctPairs = show ? new Set(matches.map((match) => `${match.source} ${match.target}`)) : /* @__PURE__ */ new Set();
1209
+ for (const target of this.targetChoices) {
1210
+ const targetId = target.getAttribute("identifier");
1211
+ const selectedChoices = target.drags;
1212
+ for (const choice of selectedChoices) {
1213
+ choice.candidateCorrection = null;
1214
+ if (show) {
1215
+ choice.candidateCorrection = this.#correctPairs.has(`${choice.identifier} ${targetId}`) ? "correct" : "incorrect";
1216
+ }
1217
+ }
1218
+ }
1219
+ this.requestUpdate();
1220
+ }
1221
+ getCellStateVariant(value, checked) {
1222
+ if (!this.showCandidateCorrection || !checked)
1223
+ return "";
1224
+ return this.#correctPairs.has(value) ? "correct" : "incorrect";
1225
+ }
1226
+ };
1227
+
1228
+ // ../qti-corrections/dist/utils/longest-increasing-subsequence.js
1229
+ function findLongestIncreasingSubsequenceIndices(sequence) {
1230
+ if (sequence.length === 0) {
1231
+ return /* @__PURE__ */ new Set();
1232
+ }
1233
+ const lengthEndingAt = new Array(sequence.length).fill(1);
1234
+ const predecessorIndex = new Array(sequence.length).fill(-1);
1235
+ for (let currentIndex = 0; currentIndex < sequence.length; currentIndex++) {
1236
+ for (let previousIndex = 0; previousIndex < currentIndex; previousIndex++) {
1237
+ if (sequence[previousIndex] < sequence[currentIndex] && lengthEndingAt[previousIndex] + 1 > lengthEndingAt[currentIndex]) {
1238
+ lengthEndingAt[currentIndex] = lengthEndingAt[previousIndex] + 1;
1239
+ predecessorIndex[currentIndex] = previousIndex;
1240
+ }
1241
+ }
1242
+ }
1243
+ let bestEndIndex = 0;
1244
+ for (let index = 1; index < sequence.length; index++) {
1245
+ if (lengthEndingAt[index] > lengthEndingAt[bestEndIndex]) {
1246
+ bestEndIndex = index;
1247
+ }
1248
+ }
1249
+ const resultIndices = /* @__PURE__ */ new Set();
1250
+ for (let index = bestEndIndex; index !== -1; index = predecessorIndex[index]) {
1251
+ resultIndices.add(index);
1252
+ }
1253
+ return resultIndices;
1254
+ }
1255
+ function findCorrectlyPlacedIdentifiers(candidateOrder, correctOrder) {
1256
+ const rankByIdentifier = new Map(correctOrder.map((identifier, position) => [identifier, position]));
1257
+ const knownIdentifiers = candidateOrder.filter((identifier) => rankByIdentifier.has(identifier));
1258
+ const rankSequence = knownIdentifiers.map((identifier) => rankByIdentifier.get(identifier));
1259
+ const winningIndices = findLongestIncreasingSubsequenceIndices(rankSequence);
1260
+ const result = /* @__PURE__ */ new Set();
1261
+ winningIndices.forEach((index) => result.add(knownIdentifiers[index]));
1262
+ return result;
1263
+ }
1264
+
1265
+ // ../qti-corrections/dist/interactions/qti-order-interaction-correction.js
1266
+ var QtiOrderInteractionCorrection = class extends DragDropCorrectionMixin(QtiOrderInteraction) {
1267
+ toggleCandidateCorrection(show) {
1268
+ super.toggleCandidateCorrection(show);
1269
+ const dropTargets = Array.from(this.shadowRoot.querySelectorAll(`[part~='drop']`));
1270
+ const placedChoices = dropTargets.flatMap((drop) => this.chipsIn(drop));
1271
+ for (const choice of placedChoices)
1272
+ choice.candidateCorrection = null;
1273
+ if (!show)
1274
+ return;
1275
+ const response = this.correctResponse;
1276
+ const entries = response ? Array.isArray(response) ? response : [response] : [];
1277
+ const correctOrder = entries.map((entry, index) => {
1278
+ const [identifier, dropId] = entry.split(" ");
1279
+ const parsed = dropId?.startsWith("droplist") ? parseInt(dropId.slice("droplist".length), 10) : index;
1280
+ return { identifier, dropIndex: Number.isNaN(parsed) ? index : parsed };
1281
+ }).filter((entry) => entry.identifier).sort((a, b2) => a.dropIndex - b2.dropIndex).map((entry) => entry.identifier);
1282
+ if (correctOrder.length === 0)
1283
+ return;
1284
+ const placedEntries = placedChoices.map((choice) => ({ choice, identifier: choice.getAttribute("identifier") })).filter((entry) => Boolean(entry.identifier));
1285
+ const correctlyPlaced = findCorrectlyPlacedIdentifiers(placedEntries.map((entry) => entry.identifier), correctOrder);
1286
+ for (const { choice, identifier } of placedEntries) {
1287
+ choice.candidateCorrection = correctlyPlaced.has(identifier) ? "correct" : "incorrect";
1288
+ }
1289
+ }
1290
+ };
1291
+
1292
+ // ../qti-corrections/dist/interactions/qti-portable-custom-interaction-correction.js
1293
+ var QtiPortableCustomInteractionCorrection = class extends CandidateCorrectionMixin(QtiPortableCustomInteraction) {
1294
+ toggleInternalCorrectResponse(show) {
1295
+ super.toggleInternalCorrectResponse(show);
1296
+ const responseVariable = this.responseVariable;
1297
+ const sourceResponse = responseVariable?.correctResponse ?? this.correctResponse;
1298
+ this.correctResponse = show ? sourceResponse : responseVariable?.cardinality === "single" ? "" : [];
1299
+ const containerId = `correct-response-container-${this.responseIdentifier}`;
1300
+ this.parentElement?.querySelectorAll(`#${containerId}`).forEach((element) => element.remove());
1301
+ if (!show) {
1302
+ this.enable();
1303
+ return;
1304
+ }
1305
+ this.disable();
1306
+ if (!this.correctResponse)
1307
+ return;
1308
+ const container = document.createElement("div");
1309
+ container.id = containerId;
1310
+ container.className = "pci-correct-response-container";
1311
+ Object.assign(container.style, {
1312
+ position: "relative",
1313
+ marginTop: "20px",
1314
+ border: "2px solid green",
1315
+ padding: "16px",
1316
+ borderRadius: "4px",
1317
+ backgroundColor: "rgba(0, 128, 0, 0.05)"
1318
+ });
1319
+ const label = document.createElement("div");
1320
+ label.textContent = "Correct Response:";
1321
+ Object.assign(label.style, { fontWeight: "bold", marginBottom: "10px", color: "green" });
1322
+ container.append(label);
1323
+ const viewer = this.cloneNode(false);
1324
+ viewer.removeAttribute("id");
1325
+ viewer.removeAttribute("show-correct-response");
1326
+ viewer.removeAttribute("show-full-correct-response");
1327
+ viewer.removeAttribute("show-candidate-correction");
1328
+ viewer.isFullCorrectResponse = true;
1329
+ const originalResponseId = this.responseIdentifier;
1330
+ viewer.responseIdentifier = `${originalResponseId}-correct`;
1331
+ for (const child of Array.from(this.children)) {
1332
+ if (child.tagName.startsWith("QTI-"))
1333
+ viewer.append(child.cloneNode(true));
1334
+ }
1335
+ const correctResponse = this.correctResponse;
1336
+ const cardinality = responseVariable?.cardinality ?? (Array.isArray(correctResponse) ? "multiple" : "single");
1337
+ const baseType = responseVariable?.baseType ?? "string";
1338
+ viewer.status = "solution";
1339
+ viewer.responseDeclaration = {
1340
+ baseType,
1341
+ cardinality,
1342
+ correctResponse: { value: correctResponse }
1343
+ };
1344
+ viewer.addEventListener("qti-portable-custom-interaction-loaded", () => {
1345
+ const value = viewer.responseVariablesToQtiVariableJSON(correctResponse, cardinality, baseType);
1346
+ viewer.sendMessageToIframe("setBoundTo", { [originalResponseId]: value });
1347
+ viewer.sendMessageToIframe("setState", { state: "review" });
1348
+ }, { once: true });
1349
+ viewer.style.pointerEvents = "none";
1350
+ container.append(viewer);
1351
+ this.after(container);
1352
+ }
1353
+ };
1354
+
1355
+ // ../qti-corrections/dist/interactions/qti-select-point-interaction-correction.js
1356
+ var QtiSelectPointInteractionCorrection = class extends CandidateCorrectionMixin(QtiSelectPointInteraction) {
1357
+ #correctAreas = [];
1358
+ #responseCorrection = [];
1359
+ static {
1360
+ this.styles = [
1361
+ QtiSelectPointInteraction.styles,
1362
+ i`
1363
+ [part~='correct'] {
1364
+ --qti-select-point-marker-color: var(--qti-correct);
1365
+ }
1366
+ [part~='incorrect'] {
1367
+ --qti-select-point-marker-color: var(--qti-incorrect);
1368
+ }
1369
+ `
1370
+ ];
1371
+ }
1372
+ toggleCandidateCorrection(show) {
1373
+ super.toggleCandidateCorrection(show);
1374
+ const previous = this.#responseCorrection;
1375
+ this.#responseCorrection = [];
1376
+ if (!show) {
1377
+ this.requestUpdate("responseCorrection", previous);
1378
+ return;
1379
+ }
1380
+ const areaEntries = this._effectiveAreaEntries;
1381
+ const baseType = this.responseVariable?.baseType;
1382
+ const response = this.correctResponse;
1383
+ for (const point of this.responsePoints) {
1384
+ let correct = areaEntries.some((area) => ScoringHelper.isPointInArea(`${point.x} ${point.y}`, `${area.shape},${area.coords}`, baseType));
1385
+ if (!areaEntries.length && response) {
1386
+ const expected = Array.isArray(response) ? response : [response];
1387
+ correct = expected.some((entry) => {
1388
+ const [x, y] = entry.split(" ").map(Number);
1389
+ return Number.isFinite(x) && Number.isFinite(y) && Math.hypot(point.x - x, point.y - y) <= 10;
1390
+ });
1391
+ }
1392
+ this.#responseCorrection.push(correct);
1393
+ }
1394
+ this.requestUpdate("responseCorrection", previous);
1395
+ }
1396
+ toggleInternalCorrectResponse(show) {
1397
+ super.toggleInternalCorrectResponse(show);
1398
+ if (!show) {
1399
+ const previous2 = this.#correctAreas;
1400
+ this.#correctAreas = [];
1401
+ this.requestUpdate("correctAreas", previous2);
1402
+ return;
1403
+ }
1404
+ let entries = this._effectiveAreaEntries;
1405
+ if (!entries.length) {
1406
+ const response = this.correctResponse;
1407
+ const values = response ? Array.isArray(response) ? response : [response] : [];
1408
+ if (values.some((value) => value.split(" ").length < 2)) {
1409
+ console.error("No valid correct responses found.");
1410
+ return;
1411
+ }
1412
+ entries = values.map((value) => ({
1413
+ shape: "circle",
1414
+ coords: `${value.split(" ").join(",")},10`,
1415
+ defaultValue: 1,
1416
+ mappedValue: 1
1417
+ }));
1418
+ }
1419
+ const previous = this.#correctAreas;
1420
+ this.#correctAreas = entries.map((entry) => ({ shape: entry.shape, coords: entry.coords }));
1421
+ this.requestUpdate("correctAreas", previous);
1422
+ }
1423
+ pointPart(_point, index) {
1424
+ const verdict = this.#responseCorrection[index];
1425
+ return verdict === void 0 ? "point" : `point ${verdict ? "correct" : "incorrect"}`;
1426
+ }
1427
+ renderSupplementalLayer() {
1428
+ return c2(this.#correctAreas, (area) => `${area.shape}:${area.coords}`, (area, index) => T`<div
1429
+ style=${o({
1430
+ position: "absolute",
1431
+ pointerEvents: "none",
1432
+ backgroundColor: "var(--qti-correct)",
1433
+ opacity: "0.5"
1434
+ })}
1435
+ data-correction-area
1436
+ data-coord=${area.coords}
1437
+ data-shape=${area.shape}
1438
+ alt=${`correct-response-${index + 1}`}
1439
+ ></div>`);
1440
+ }
1441
+ updated(changedProperties) {
1442
+ super.updated(changedProperties);
1443
+ if (changedProperties.has("correctAreas")) {
1444
+ this.positionOverlayElements(this.shadowRoot.querySelectorAll("[data-correction-area]"));
1445
+ }
1446
+ }
1447
+ };
1448
+
1449
+ // ../qti-corrections/dist/interactions/qti-slider-interaction-correction.js
1450
+ var QtiSliderInteractionCorrection = class extends CandidateCorrectionMixin(QtiSliderInteraction) {
1451
+ #correctResponseNumber = null;
1452
+ static {
1453
+ this.styles = [
1454
+ QtiSliderInteraction.styles,
1455
+ i`
1456
+ [part='knob-correct'] {
1457
+ background-color: var(--qti-correct-light);
1458
+ border: 2px solid var(--qti-correct);
1459
+ position: relative;
1460
+ height: 1rem;
1461
+ width: 1rem;
1462
+ transform-origin: center;
1463
+ transform: translateX(-50%);
1464
+ cursor: pointer;
1465
+ border-radius: 9999px;
1466
+ left: var(--value-percentage-correct);
1467
+ }
1468
+ `
1469
+ ];
1470
+ }
1471
+ toggleInternalCorrectResponse(show) {
1472
+ super.toggleInternalCorrectResponse(show);
1473
+ const response = this.correctResponse;
1474
+ const raw = Array.isArray(response) ? response[0] : response;
1475
+ const value = show && raw ? parseFloat(raw) : NaN;
1476
+ const previous = this.#correctResponseNumber;
1477
+ this.#correctResponseNumber = Number.isFinite(value) ? value : null;
1478
+ if (this.#correctResponseNumber !== null) {
1479
+ const percentage = (this.#correctResponseNumber - this.min) / (this.max - this.min) * 100;
1480
+ this.style.setProperty("--value-percentage-correct", `${percentage}%`);
1481
+ } else {
1482
+ this.style.removeProperty("--value-percentage-correct");
1483
+ }
1484
+ this.requestUpdate("correctResponseNumber", previous);
1485
+ }
1486
+ renderRailSupplement() {
1487
+ return this.#correctResponseNumber === null ? A : T`<div id="knob-correct" part="knob-correct">
1488
+ <div id="value" part="value">${this.#correctResponseNumber}</div>
1489
+ </div>`;
1490
+ }
1491
+ };
1492
+
1493
+ // ../qti-corrections/dist/interactions/qti-text-entry-interaction-correction.js
1494
+ var QtiTextEntryInteractionCorrection = class extends CandidateCorrectionMixin(QtiTextEntryInteraction) {
1495
+ static {
1496
+ this.styles = [
1497
+ QtiTextEntryInteraction.styles,
1498
+ correctionPart,
1499
+ i`
1500
+ /*
1501
+ * The trailing inset only — size and leading gap come from the shared sheet.
1502
+ *
1503
+ * This block used to also set position: static; translate: none (dead: nothing positions this
1504
+ * badge) and size it with --qti-control-size, the radio/checkbox token — 1rem, absolute, where
1505
+ * every other badge is 1em and scales with the text it annotates. The two agreed only while the
1506
+ * item's font-size happened to equal the root's.
1507
+ *
1508
+ * The inset is needed because the field's box is the HOST while its padding is on ::part(input),
1509
+ * so the badge — a child of the host — falls outside that padding and lands flush on the border.
1510
+ * Measured at 0px from the border, against 16px for a choice and 12.8px for a chip.
1511
+ */
1512
+ [part~='correction'] {
1513
+ margin-inline-end: var(--qti-correction-inset);
1514
+ }
1515
+ :host {
1516
+ position: relative;
1517
+ anchor-name: --text-entry-host, --qti-correction-anchor;
1518
+ }
1519
+ [part='correct'] {
1520
+ position: absolute;
1521
+ position-anchor: --text-entry-host;
1522
+ position-area: top span-right;
1523
+ margin-bottom: 0.25rem;
1524
+ padding: var(--qti-padding-box);
1525
+ background-color: var(--qti-bg);
1526
+ white-space: nowrap;
1527
+ }
1528
+ `
1529
+ ];
1530
+ }
1531
+ get correctness() {
1532
+ const variable = this.responseVariable;
1533
+ if (!variable)
1534
+ return super.correctness;
1535
+ if (variable.value === null)
1536
+ return Correctness.Incorrect;
1537
+ if (variable.mapping) {
1538
+ const maxScore = variable.mapping.mapEntries.reduce((maximum, entry) => Math.max(entry.mappedValue, maximum), 0);
1539
+ for (const entry of variable.mapping.mapEntries) {
1540
+ let expected = entry.mapKey;
1541
+ let actual = variable.value;
1542
+ if (!entry.caseSensitive) {
1543
+ expected = expected.toLowerCase();
1544
+ actual = actual.toLowerCase();
1545
+ }
1546
+ if (expected !== actual)
1547
+ continue;
1548
+ if (entry.mappedValue === maxScore)
1549
+ return Correctness.Correct;
1550
+ if (entry.mappedValue <= (variable.mapping.defaultValue || 0))
1551
+ return Correctness.Incorrect;
1552
+ return Correctness.PartiallyCorrect;
1553
+ }
1554
+ }
1555
+ return variable.correctResponse === variable.value ? Correctness.Correct : Correctness.Incorrect;
1556
+ }
1557
+ toggleInternalCorrectResponse(show) {
1558
+ if (!this.showFullCorrectResponse)
1559
+ this.toggleFullCorrectResponse(show);
1560
+ }
1561
+ /** The badge goes between the field and the validation message; everything else is the base's. */
1562
+ render() {
1563
+ return T`
1564
+ ${this.renderAnswer()} ${this.renderInput()}
1565
+ <span part=${this.correctionPart} aria-hidden="true"></span>
1566
+ ${this.renderValidationMessage()}
1567
+ `;
1568
+ }
1569
+ };
1570
+
1571
+ export {
1572
+ Correctness,
1573
+ CorrectResponseMixin,
1574
+ correctionPart,
1575
+ CandidateCorrectionMixin,
1576
+ ChoiceCorrectionMixin,
1577
+ QtiGapTextCorrection,
1578
+ QtiHotspotChoiceCorrection,
1579
+ QtiHottextCorrection,
1580
+ QtiSimpleAssociableChoiceCorrection,
1581
+ QtiSimpleChoiceCorrection,
1582
+ QtiAssessmentItemCorrection,
1583
+ QtiItemCorrection,
1584
+ TestNavigationCorrection,
1585
+ QtiTestCorrection,
1586
+ TestCheckItemCorrection,
1587
+ ItemCorrectResponseMode,
1588
+ ItemShowCandidateCorrection,
1589
+ ItemShowCorrectResponse,
1590
+ TestShowCorrectResponse,
1591
+ QtiAssociateInteractionCorrection,
1592
+ QtiChoiceInteractionCorrection,
1593
+ QtiExtendedTextInteractionCorrection,
1594
+ QtiGapMatchInteractionCorrection,
1595
+ QtiGraphicAssociateInteractionCorrection,
1596
+ QtiGraphicGapMatchInteractionCorrection,
1597
+ QtiGraphicOrderInteractionCorrection,
1598
+ QtiHotspotInteractionCorrection,
1599
+ QtiHottextInteractionCorrection,
1600
+ QtiInlineChoiceInteractionCorrection,
1601
+ QtiMatchInteractionCorrection,
1602
+ QtiOrderInteractionCorrection,
1603
+ QtiPortableCustomInteractionCorrection,
1604
+ QtiSelectPointInteractionCorrection,
1605
+ QtiSliderInteractionCorrection,
1606
+ QtiTextEntryInteractionCorrection
1607
+ };
1608
+ //# sourceMappingURL=chunk-XU7HNT4F.js.map