@citolab/qti-components 8.0.1 → 8.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 (72) hide show
  1. package/cdn/index.global.js +1 -1
  2. package/cdn/index.js +648 -816
  3. package/dist/base.d.ts +61 -4
  4. package/dist/base.js +7 -1
  5. package/dist/{chunk-V5G7X53Y.js → chunk-3IACYIU2.js} +2 -2
  6. package/dist/chunk-4DB4SC5K.js +100 -0
  7. package/dist/chunk-4DB4SC5K.js.map +1 -0
  8. package/dist/{chunk-F4BNJWYN.js → chunk-4XR2JHCP.js} +646 -677
  9. package/dist/chunk-4XR2JHCP.js.map +1 -0
  10. package/dist/{chunk-7V4HJFHW.js → chunk-6ITD55QE.js} +2 -2
  11. package/dist/{chunk-3AJOQCDU.js → chunk-6V3A7NQP.js} +2 -2
  12. package/dist/{chunk-WVVMNF74.js → chunk-6XJUP55A.js} +248 -234
  13. package/dist/chunk-6XJUP55A.js.map +1 -0
  14. package/dist/{chunk-XU7HNT4F.js → chunk-CF3DXJWM.js} +145 -119
  15. package/dist/{chunk-XU7HNT4F.js.map → chunk-CF3DXJWM.js.map} +1 -1
  16. package/dist/chunk-EKXZI26R.js +917 -0
  17. package/dist/chunk-EKXZI26R.js.map +1 -0
  18. package/dist/{chunk-IJD53WY6.js → chunk-HDEG6BAZ.js} +15 -15
  19. package/dist/{chunk-IJD53WY6.js.map → chunk-HDEG6BAZ.js.map} +1 -1
  20. package/dist/{chunk-LVBIQDPM.js → chunk-IXPDMYKH.js} +147 -101
  21. package/dist/chunk-IXPDMYKH.js.map +1 -0
  22. package/dist/chunk-IYC7LZV6.js +7 -0
  23. package/dist/chunk-IYC7LZV6.js.map +1 -0
  24. package/dist/{chunk-NA2N2WSG.js → chunk-MK2I3MQB.js} +244 -193
  25. package/dist/chunk-MK2I3MQB.js.map +1 -0
  26. package/dist/{chunk-CAO4JRLY.js → chunk-NZLW6EX7.js} +2 -2
  27. package/dist/{chunk-RXGDQDOV.js → chunk-Q52IJELT.js} +479 -393
  28. package/dist/chunk-Q52IJELT.js.map +1 -0
  29. package/dist/{chunk-QECOPVPO.js → chunk-TXIB3SKC.js} +60 -59
  30. package/dist/chunk-TXIB3SKC.js.map +1 -0
  31. package/dist/{computed.types.d-rKLSPvVG.d.ts → computed.types.d-DpKIYO6e.d.ts} +2 -0
  32. package/dist/corrections.d.ts +12 -29
  33. package/dist/corrections.js +9 -10
  34. package/dist/elements.d.ts +86 -87
  35. package/dist/elements.js +5 -6
  36. package/dist/index.d.ts +8 -39
  37. package/dist/index.js +29 -16
  38. package/dist/{interaction.d-D4MstYRz.d.ts → interaction.d-CJiMv8H7.d.ts} +13 -2
  39. package/dist/interactions.d.ts +11 -1299
  40. package/dist/interactions.js +4 -5
  41. package/dist/item.css +370 -532
  42. package/dist/item.d.ts +3 -3
  43. package/dist/item.js +6 -6
  44. package/dist/loader.js +2 -2
  45. package/dist/processing.d.ts +61 -11
  46. package/dist/processing.js +10 -2
  47. package/dist/qti-components-jsx.d.ts +293 -69
  48. package/dist/qti-test.d-_YAlhDbt.d.ts +702 -0
  49. package/dist/qti-text-entry-interaction.d-BGdfV9qG.d.ts +1567 -0
  50. package/dist/test.d.ts +11 -678
  51. package/dist/test.js +7 -8
  52. package/dist/transformers.d.ts +1 -1
  53. package/dist/transformers.js +1 -1
  54. package/package.json +11 -10
  55. package/dist/chunk-64XSIBGO.js +0 -241
  56. package/dist/chunk-64XSIBGO.js.map +0 -1
  57. package/dist/chunk-7HC5SMCU.js +0 -102
  58. package/dist/chunk-7HC5SMCU.js.map +0 -1
  59. package/dist/chunk-F4BNJWYN.js.map +0 -1
  60. package/dist/chunk-H3J7LFRO.js +0 -2243
  61. package/dist/chunk-H3J7LFRO.js.map +0 -1
  62. package/dist/chunk-LVBIQDPM.js.map +0 -1
  63. package/dist/chunk-NA2N2WSG.js.map +0 -1
  64. package/dist/chunk-QECOPVPO.js.map +0 -1
  65. package/dist/chunk-RXGDQDOV.js.map +0 -1
  66. package/dist/chunk-WVVMNF74.js.map +0 -1
  67. package/dist/chunk-X3E5HXG2.js +0 -20
  68. package/dist/chunk-X3E5HXG2.js.map +0 -1
  69. /package/dist/{chunk-V5G7X53Y.js.map → chunk-3IACYIU2.js.map} +0 -0
  70. /package/dist/{chunk-7V4HJFHW.js.map → chunk-6ITD55QE.js.map} +0 -0
  71. /package/dist/{chunk-3AJOQCDU.js.map → chunk-6V3A7NQP.js.map} +0 -0
  72. /package/dist/{chunk-CAO4JRLY.js.map → chunk-NZLW6EX7.js.map} +0 -0
@@ -0,0 +1,917 @@
1
+ // ../qti-base/dist/utils/custom-state-set.js
2
+ var CustomStateSetShim = class extends Set {
3
+ #host;
4
+ constructor(host) {
5
+ super();
6
+ this.#host = host;
7
+ }
8
+ add(state3) {
9
+ super.add(state3);
10
+ this.#reflect();
11
+ return this;
12
+ }
13
+ delete(state3) {
14
+ const deleted = super.delete(state3);
15
+ this.#reflect();
16
+ return deleted;
17
+ }
18
+ clear() {
19
+ super.clear();
20
+ this.#reflect();
21
+ }
22
+ #reflect() {
23
+ if (this.#host.isConnected)
24
+ this.#write();
25
+ else
26
+ queueMicrotask(() => this.#write());
27
+ }
28
+ #write() {
29
+ if (this.size === 0)
30
+ this.#host.removeAttribute("data-state");
31
+ else
32
+ this.#host.setAttribute("data-state", Array.from(this).join(" "));
33
+ }
34
+ };
35
+ var PROBE_TAG = "custom-state-support-probe";
36
+ function classifyCustomStateSupport(attachInternals) {
37
+ if (!("states" in ElementInternals.prototype))
38
+ return "missing";
39
+ if (typeof customElements === "undefined")
40
+ return "modern";
41
+ try {
42
+ if (!customElements.get(PROBE_TAG))
43
+ customElements.define(PROBE_TAG, class extends HTMLElement {
44
+ });
45
+ const states = attachInternals.call(document.createElement(PROBE_TAG)).states;
46
+ if (!states)
47
+ return "missing";
48
+ states.add("probe");
49
+ states.delete("probe");
50
+ return "modern";
51
+ } catch {
52
+ return "legacy";
53
+ }
54
+ }
55
+ var installed = false;
56
+ function installCustomStateSetFallback() {
57
+ if (installed)
58
+ return "already-installed";
59
+ if (typeof HTMLElement === "undefined" || typeof ElementInternals === "undefined")
60
+ return "modern";
61
+ const native = HTMLElement.prototype.attachInternals;
62
+ if (!native)
63
+ return "modern";
64
+ const support = classifyCustomStateSupport(native);
65
+ if (support === "modern")
66
+ return support;
67
+ installed = true;
68
+ HTMLElement.prototype.attachInternals = function attachInternals() {
69
+ const internals = native.call(this);
70
+ Object.defineProperty(internals, "states", {
71
+ configurable: true,
72
+ enumerable: true,
73
+ value: new CustomStateSetShim(this)
74
+ });
75
+ return internals;
76
+ };
77
+ return support;
78
+ }
79
+
80
+ // ../qti-base/dist/context/computed-item.context.js
81
+ import { createContext } from "@lit/context";
82
+ var computedItemContext = createContext(/* @__PURE__ */ Symbol("computedItemContext"));
83
+
84
+ // ../qti-base/dist/context/computed.context.js
85
+ import { createContext as createContext2 } from "@lit/context";
86
+ var computedContext = createContext2(/* @__PURE__ */ Symbol("computedContext"));
87
+
88
+ // ../qti-base/dist/context/config.context.js
89
+ import { createContext as createContext3 } from "@lit/context";
90
+ var configContext = createContext3(/* @__PURE__ */ Symbol("configContext"));
91
+
92
+ // ../qti-base/dist/context/interaction.context.js
93
+ import { createContext as createContext4 } from "@lit/context";
94
+ var interactionContext = createContext4(/* @__PURE__ */ Symbol("interactionContext"));
95
+
96
+ // ../qti-base/dist/context/item.context.js
97
+ import { createContext as createContext5 } from "@lit/context";
98
+ var itemContext = createContext5(/* @__PURE__ */ Symbol("item"));
99
+
100
+ // ../qti-base/dist/context/types/item.types.js
101
+ var itemContextVariables = [
102
+ {
103
+ identifier: "completionStatus",
104
+ cardinality: "single",
105
+ baseType: "string",
106
+ value: "unknown",
107
+ type: "outcome"
108
+ },
109
+ {
110
+ identifier: "numAttempts",
111
+ cardinality: "single",
112
+ baseType: "integer",
113
+ value: "0",
114
+ type: "response"
115
+ }
116
+ ];
117
+
118
+ // ../qti-base/dist/context/qti.context.js
119
+ import { createContext as createContext6 } from "@lit/context";
120
+ var SEED_PATTERN = /^[A-Za-z0-9-]+$/;
121
+ var isSeed = (value) => typeof value === "string" && SEED_PATTERN.test(value);
122
+ var asSeed = (value) => isSeed(value) ? value : void 0;
123
+ var qtiContext = createContext6(/* @__PURE__ */ Symbol("qtiContext"));
124
+
125
+ // ../qti-base/dist/context/session.context.js
126
+ import { createContext as createContext7 } from "@lit/context";
127
+ var INITIAL_SESSION_CONTEXT = { view: "candidate" };
128
+ var sessionContext = createContext7(/* @__PURE__ */ Symbol("testContext"));
129
+
130
+ // ../qti-base/dist/context/test.context.js
131
+ import { createContext as createContext8 } from "@lit/context";
132
+ var INITIAL_TEST_CONTEXT = { items: [], testOutcomeVariables: [] };
133
+ var testContext = createContext8(/* @__PURE__ */ Symbol("testContext"));
134
+
135
+ // ../qti-base/dist/styles/box-sizing.styles.js
136
+ import { css } from "lit";
137
+ var boxSizing = css`
138
+ :host {
139
+ box-sizing: border-box;
140
+ }
141
+
142
+ *,
143
+ *::before,
144
+ *::after {
145
+ box-sizing: inherit;
146
+ }
147
+ `;
148
+
149
+ // ../qti-base/dist/styles/drop-region.styles.js
150
+ import { css as css2 } from "lit";
151
+ var dropRegion = css2`
152
+ [part~='drop'] {
153
+ /*
154
+ * The one deliberate opt-out from the border-box reset.
155
+ *
156
+ * --qti-dropzone-min-* is the border-box of the largest chip, and what has to hold that chip is
157
+ * this region's *content* area. Under border-box a theme's dashed drop border is subtracted
158
+ * from the chip's own size, the content comes up short, and the drop grows the moment a chip
159
+ * lands in it. Order's drop did exactly that: 64px empty, 66px full, for a 1px border.
160
+ */
161
+ box-sizing: content-box;
162
+
163
+ /*
164
+ * No padding, and no token for one.
165
+ *
166
+ * A drop reserves --qti-dropzone-min-*, which is the chip's BORDER-box, as this region's CONTENT
167
+ * area — so the chip already fits exactly, with nothing to spare and nothing squeezed. An inset
168
+ * on top would only make the drop bigger than the thing it holds.
169
+ *
170
+ * There was a 1px one, to make the drop read as a frame a hair outside the chip. It never painted:
171
+ * an EMPTY slot has nothing inside the inset to separate from, and a FILLED one hides its edge
172
+ * (DROP-SIZING.md §4), so both states spent 2px on an invisible ring. The one drop that genuinely
173
+ * wants breathing room — match's category card — declares padding on its own rule, which a
174
+ * theme reaches with ::part(drop). A token in between bought nothing.
175
+ */
176
+ display: flex;
177
+ flex-wrap: wrap;
178
+ align-items: flex-start;
179
+ justify-content: flex-start;
180
+ /* No gap either: 0 is the flex initial value, so the declaration only existed to host a token.
181
+ A drop that wants space between chips sets gap on its own rule. */
182
+ /* Containing block for anything a theme draws over the drop. Was 'position: relative' in the
183
+ theme's 'drop' mixin, which put a document stylesheet in charge of a shadow node's position. */
184
+ position: relative;
185
+ }
186
+
187
+ [part~='drop'] > * {
188
+ flex: 0 1 auto;
189
+ align-self: flex-start;
190
+ }
191
+ `;
192
+
193
+ // ../qti-base/dist/styles/validation-message.styles.js
194
+ import { css as css3 } from "lit";
195
+ var validationMessage = css3`
196
+ [part~='message'] {
197
+ display: none;
198
+ }
199
+ `;
200
+
201
+ // ../qti-base/dist/abstract/interaction.js
202
+ import { consume, provide } from "@lit/context";
203
+ import { LitElement } from "lit";
204
+ import { property } from "lit/decorators.js";
205
+ var __decorate = function(decorators, target, key, desc) {
206
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
207
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
208
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
209
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
210
+ };
211
+ var Interaction = class extends LitElement {
212
+ #didLogDisableAfterIfMaxChoicesReachedDeprecation;
213
+ static {
214
+ this.formAssociated = true;
215
+ }
216
+ get internals() {
217
+ return this._internals;
218
+ }
219
+ /** Extension point used by wrappers that should not register as candidate inputs. */
220
+ get registersWithItem() {
221
+ return true;
222
+ }
223
+ get isInline() {
224
+ return false;
225
+ }
226
+ get responseVariable() {
227
+ if (!this._context?.variables) {
228
+ return void 0;
229
+ }
230
+ const responseVariables = this._context.variables.filter((v) => v.type === "response");
231
+ const responseIdentifier = this.getAttribute("response-identifier");
232
+ return responseVariables.find((v) => v.identifier === responseIdentifier);
233
+ }
234
+ get value() {
235
+ return JSON.stringify(this.response);
236
+ }
237
+ set value(val) {
238
+ this.response = val ? JSON.parse(val) : null;
239
+ }
240
+ reportValidity() {
241
+ const isValid = this._internals.validity.valid;
242
+ const mode = this.validationDisplayMode;
243
+ if (mode === "native" || mode === "both") {
244
+ this._internals.reportValidity();
245
+ }
246
+ if (mode === "inline" || mode === "both") {
247
+ this.updateInlineValidationMessage();
248
+ } else {
249
+ this.clearInlineValidationMessage();
250
+ }
251
+ if (mode === "none") {
252
+ this.clearInlineValidationMessage();
253
+ }
254
+ return isValid;
255
+ }
256
+ get validationDisplayMode() {
257
+ return this.configContext?.validationDisplayMode ?? "inline";
258
+ }
259
+ resolveAllowReorder(options) {
260
+ const defaultWhenUnset = options?.defaultWhenUnset ?? true;
261
+ const config = this.configContext;
262
+ if (!config) {
263
+ return defaultWhenUnset;
264
+ }
265
+ if (config.allowReorder !== void 0) {
266
+ return config.allowReorder;
267
+ }
268
+ return defaultWhenUnset;
269
+ }
270
+ resolveDisableAfterMaxReached(options) {
271
+ const defaultWhenUnset = options?.defaultWhenUnset ?? false;
272
+ const config = this.configContext;
273
+ if (!config) {
274
+ return defaultWhenUnset;
275
+ }
276
+ if (config.disableAfterMaxReached !== void 0) {
277
+ return config.disableAfterMaxReached;
278
+ }
279
+ if (config.disableAfterIfMaxChoicesReached !== void 0) {
280
+ if (!this.#didLogDisableAfterIfMaxChoicesReachedDeprecation) {
281
+ this.#didLogDisableAfterIfMaxChoicesReachedDeprecation = true;
282
+ console.log("[QTI Config] `disableAfterIfMaxChoicesReached` is deprecated. Use `disableAfterMaxReached` instead.");
283
+ }
284
+ return config.disableAfterIfMaxChoicesReached;
285
+ }
286
+ return defaultWhenUnset;
287
+ }
288
+ setInteractionValidity(isValid, validityMessage = "", anchor, options) {
289
+ const validityAnchor = anchor ?? this;
290
+ this._internals.setValidity(isValid ? {} : { customError: true }, validityMessage, validityAnchor);
291
+ if (options?.suppressInline !== true && (this.validationDisplayMode === "inline" || this.validationDisplayMode === "both")) {
292
+ this.updateInlineValidationMessage();
293
+ }
294
+ }
295
+ updateInlineValidationMessage() {
296
+ const validationMessageElement = this.getValidationMessageElement();
297
+ if (!validationMessageElement) {
298
+ return;
299
+ }
300
+ if (!this._internals.validity.valid) {
301
+ validationMessageElement.textContent = this._internals.validationMessage;
302
+ validationMessageElement.style.setProperty("display", "block", "important");
303
+ return;
304
+ }
305
+ validationMessageElement.textContent = "";
306
+ validationMessageElement.style.display = "none";
307
+ }
308
+ clearInlineValidationMessage() {
309
+ const validationMessageElement = this.getValidationMessageElement();
310
+ if (!validationMessageElement) {
311
+ return;
312
+ }
313
+ validationMessageElement.textContent = "";
314
+ validationMessageElement.style.display = "none";
315
+ }
316
+ getValidationMessageElement() {
317
+ return this.shadowRoot?.querySelector("#validation-message");
318
+ }
319
+ reset() {
320
+ this.response = null;
321
+ }
322
+ formResetCallback() {
323
+ this.reset();
324
+ }
325
+ connectedCallback() {
326
+ super.connectedCallback();
327
+ if (!this.registersWithItem) {
328
+ return;
329
+ }
330
+ this.dispatchEvent(new CustomEvent("qti-register-interaction", {
331
+ bubbles: true,
332
+ composed: true,
333
+ cancelable: false,
334
+ detail: {
335
+ interactionElement: this,
336
+ responseIdentifier: this.responseIdentifier
337
+ }
338
+ }));
339
+ }
340
+ saveResponse(value, state3) {
341
+ this.dispatchEvent(new CustomEvent("qti-interaction-response", {
342
+ bubbles: true,
343
+ composed: true,
344
+ cancelable: false,
345
+ detail: {
346
+ responseIdentifier: this.responseIdentifier,
347
+ response: Array.isArray(value) ? [...value] : value,
348
+ ...state3 !== void 0 ? { state: state3 } : {}
349
+ }
350
+ }));
351
+ }
352
+ constructor() {
353
+ super();
354
+ this.#didLogDisableAfterIfMaxChoicesReachedDeprecation = false;
355
+ this._interactionContext = { choiceRole: null, draggablesSelector: null };
356
+ this.disabled = false;
357
+ this.readonly = false;
358
+ this._internals = this.attachInternals();
359
+ }
360
+ /**
361
+ * Optional lifecycle bridge for interaction subclasses that do not need changed-property data.
362
+ *
363
+ * The parameter is `PropertyValues`, matching LitElement, and NOT `PropertyValues<this>`.
364
+ * The polymorphic `this` narrowed the parameter per subclass, which made every interaction
365
+ * class structurally incompatible with `LitElement` — so `Constructor<LitElement>`, the
366
+ * standard constraint for a Lit mixin, rejected all of them and consumers could not wrap an
367
+ * interaction in a mixin without casting. `this` buys nothing in a parameter position and
368
+ * costs contravariant compatibility with the base class.
369
+ *
370
+ * Still optional, so subclasses that ignore the argument can override with `firstUpdated()`.
371
+ */
372
+ firstUpdated(_changedProperties) {
373
+ }
374
+ };
375
+ __decorate([
376
+ consume({ context: itemContext, subscribe: true })
377
+ ], Interaction.prototype, "_context", void 0);
378
+ __decorate([
379
+ consume({ context: configContext, subscribe: true })
380
+ ], Interaction.prototype, "configContext", void 0);
381
+ __decorate([
382
+ provide({ context: interactionContext })
383
+ ], Interaction.prototype, "_interactionContext", void 0);
384
+ __decorate([
385
+ property({ type: String, attribute: "response-identifier" })
386
+ ], Interaction.prototype, "responseIdentifier", void 0);
387
+ __decorate([
388
+ property({ reflect: true, type: Boolean })
389
+ ], Interaction.prototype, "disabled", void 0);
390
+ __decorate([
391
+ property({ reflect: true, type: Boolean })
392
+ ], Interaction.prototype, "readonly", void 0);
393
+ __decorate([
394
+ property({ type: String })
395
+ ], Interaction.prototype, "name", void 0);
396
+
397
+ // ../qti-base/dist/abstract/qti-variable-declaration.js
398
+ import { html, LitElement as LitElement2 } from "lit";
399
+ var QtiVariableDeclaration = class extends LitElement2 {
400
+ render() {
401
+ return html`<slot></slot>`;
402
+ }
403
+ defaultValues(cardinality) {
404
+ const htmlValues = Array.from(this.querySelectorAll("qti-default-value > qti-value"));
405
+ if (htmlValues.length === 0) {
406
+ return null;
407
+ }
408
+ const defaultValues = htmlValues.map((n) => n.innerHTML.trim());
409
+ if (defaultValues.length > 1 || cardinality === "multiple" || cardinality === "ordered") {
410
+ return defaultValues;
411
+ }
412
+ return defaultValues[0];
413
+ }
414
+ };
415
+
416
+ // ../qti-base/dist/abstract/qti-expression.js
417
+ import { consume as consume2 } from "@lit/context";
418
+ import { css as css4, html as html2, LitElement as LitElement3 } from "lit";
419
+ import { state } from "lit/decorators.js";
420
+ var __decorate2 = function(decorators, target, key, desc) {
421
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
422
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
423
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
424
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
425
+ };
426
+ var QtiExpression = class extends LitElement3 {
427
+ constructor() {
428
+ super(...arguments);
429
+ this.getVariables = () => (
430
+ // FIXME: if this itself is multiple, this will never enter the qti-multiple switch
431
+ // See this example here: https://github.com/1EdTech/qti-examples/blob/master/qtiv3-examples/packaging/items/Example05-feedbackBlock-adaptive.xml
432
+ Array.from(this.children).map((e) => {
433
+ console.debug("getVariables", e.tagName.toLowerCase());
434
+ switch (e.tagName.toLowerCase()) {
435
+ case "qti-base-value": {
436
+ return {
437
+ baseType: e.getAttribute("base-type"),
438
+ value: e.textContent.trim(),
439
+ cardinality: "single"
440
+ };
441
+ }
442
+ case "qti-variable": {
443
+ const identifier = e.getAttribute("identifier") || "";
444
+ if (identifier === "QTI_CONTEXT") {
445
+ if (!this.qtiContext?.QTI_CONTEXT) {
446
+ console.warn("QTI_CONTEXT not available");
447
+ return {
448
+ identifier: "QTI_CONTEXT",
449
+ baseType: "record",
450
+ value: this.qtiContext.QTI_CONTEXT,
451
+ cardinality: "record",
452
+ type: "context"
453
+ };
454
+ }
455
+ return {
456
+ identifier: "QTI_CONTEXT",
457
+ baseType: "record",
458
+ value: this.qtiContext.QTI_CONTEXT,
459
+ cardinality: "record",
460
+ type: "context"
461
+ };
462
+ }
463
+ const variable = this.context.variables.find((v) => v.identifier === identifier) || null;
464
+ return variable;
465
+ }
466
+ case "qti-multiple": {
467
+ const multiple = e;
468
+ const values = multiple.getResult();
469
+ console.debug("values", values);
470
+ if (values?.length > 0) {
471
+ return {
472
+ identifier: "",
473
+ baseType: values[0].baseType,
474
+ value: values.map((v) => v.value),
475
+ cardinality: "multiple",
476
+ type: "response"
477
+ };
478
+ }
479
+ return null;
480
+ }
481
+ case "qti-ordered": {
482
+ const multiple = e;
483
+ const values = multiple.getResult();
484
+ if (values?.length > 0) {
485
+ return {
486
+ identifier: "",
487
+ baseType: values[0].baseType,
488
+ value: values.map((v) => v.value),
489
+ cardinality: "ordered",
490
+ type: "response"
491
+ };
492
+ }
493
+ return null;
494
+ }
495
+ case "qti-repeat": {
496
+ const repeat = e;
497
+ const values = repeat.getResult();
498
+ if (values?.length > 0) {
499
+ return values;
500
+ }
501
+ return null;
502
+ }
503
+ case "qti-correct": {
504
+ const identifier = e.getAttribute("identifier") || "";
505
+ const responseVariable = this.context?.variables?.find((v) => v.identifier === identifier) || null;
506
+ return {
507
+ baseType: responseVariable.baseType,
508
+ value: responseVariable.correctResponse,
509
+ cardinality: responseVariable.cardinality
510
+ };
511
+ }
512
+ default: {
513
+ try {
514
+ const expression = e;
515
+ const value = expression.getResult();
516
+ return {
517
+ baseType: "integer",
518
+ value: value?.toString() || null,
519
+ cardinality: "single"
520
+ };
521
+ } catch (error) {
522
+ console.warn("default not sufficient");
523
+ }
524
+ return null;
525
+ }
526
+ }
527
+ }).flatMap((v) => Array.isArray(v) ? v : [v]).filter((v) => v !== null)
528
+ );
529
+ }
530
+ static {
531
+ this.styles = css4`
532
+ slot {
533
+ display: none;
534
+ }
535
+ `;
536
+ }
537
+ render() {
538
+ return html2`<pre>${JSON.stringify(this.result, null, 2)}</pre>
539
+ <slot></slot>`;
540
+ }
541
+ calculate() {
542
+ this.result = this.getResult();
543
+ return this.result;
544
+ }
545
+ getResult() {
546
+ throw new Error("Not implemented");
547
+ }
548
+ };
549
+ __decorate2([
550
+ state()
551
+ ], QtiExpression.prototype, "result", void 0);
552
+ __decorate2([
553
+ consume2({ context: itemContext, subscribe: true }),
554
+ state()
555
+ ], QtiExpression.prototype, "context", void 0);
556
+ __decorate2([
557
+ consume2({ context: qtiContext, subscribe: true }),
558
+ state()
559
+ ], QtiExpression.prototype, "qtiContext", void 0);
560
+
561
+ // ../qti-base/dist/abstract/qti-condition-expression.js
562
+ var QtiConditionExpression = class extends QtiExpression {
563
+ calculate() {
564
+ this.result = this.getResult();
565
+ return this.result;
566
+ }
567
+ getResult() {
568
+ throw new Error("Not implemented");
569
+ }
570
+ };
571
+
572
+ // ../qti-base/dist/utils/utils.js
573
+ var decimalSeparator = () => {
574
+ return new Intl.NumberFormat().format(0.1).replace(/\d/g, "");
575
+ };
576
+ var convertNumberToUniversalFormat = (number) => {
577
+ if (typeof number === "string") {
578
+ return number;
579
+ }
580
+ const dSep = decimalSeparator();
581
+ if (dSep === ".") {
582
+ return number.toLocaleString();
583
+ } else {
584
+ return number.toString().replace(".", "").replace(dSep, ".");
585
+ }
586
+ };
587
+ function IsNullOrUndefined(value) {
588
+ return value === null || value === void 0;
589
+ }
590
+ function removeDoubleSlashes(str) {
591
+ const singleForwardSlashes = str.replace(/([^:]\/)\/+/g, "$1").replace(/\/\//g, "/").replace("http:/", "http://").replace("https:/", "https://");
592
+ return singleForwardSlashes;
593
+ }
594
+
595
+ // ../qti-base/dist/abstract/qti-feedback.js
596
+ import { consume as consume3 } from "@lit/context";
597
+ import { LitElement as LitElement4 } from "lit";
598
+ import { property as property2, state as state2 } from "lit/decorators.js";
599
+ var __decorate3 = function(decorators, target, key, desc) {
600
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
601
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
602
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
603
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
604
+ };
605
+ var QtiFeedback = class extends LitElement4 {
606
+ constructor() {
607
+ super(...arguments);
608
+ this.showHide = "show";
609
+ }
610
+ connectedCallback() {
611
+ super.connectedCallback();
612
+ this.dispatchEvent(new CustomEvent("qti-register-feedback", {
613
+ bubbles: true,
614
+ composed: true,
615
+ detail: this
616
+ }));
617
+ }
618
+ checkShowFeedback(outcomeIdentifier) {
619
+ const outcomeVariable = this._context.variables.find((v) => v.identifier === outcomeIdentifier) || null;
620
+ if (this.outcomeIdentifier !== outcomeIdentifier || !outcomeVariable)
621
+ return;
622
+ let isFound = false;
623
+ if (Array.isArray(outcomeVariable.value)) {
624
+ isFound = outcomeVariable.value.includes(this.identifier);
625
+ } else {
626
+ isFound = !IsNullOrUndefined(this.identifier) && !IsNullOrUndefined(outcomeVariable?.value) && this.identifier === outcomeVariable.value || false;
627
+ }
628
+ this.#showFeedback(isFound);
629
+ }
630
+ #showFeedback(value) {
631
+ this.showStatus = value && this.showHide === "show" || !value && this.showHide === "hide" ? "on" : "off";
632
+ }
633
+ };
634
+ __decorate3([
635
+ property2({ type: String, attribute: "show-hide" })
636
+ ], QtiFeedback.prototype, "showHide", void 0);
637
+ __decorate3([
638
+ property2({ type: String, attribute: "outcome-identifier" })
639
+ ], QtiFeedback.prototype, "outcomeIdentifier", void 0);
640
+ __decorate3([
641
+ property2({ type: String })
642
+ ], QtiFeedback.prototype, "identifier", void 0);
643
+ __decorate3([
644
+ property2({ type: String, attribute: false })
645
+ ], QtiFeedback.prototype, "showStatus", void 0);
646
+ __decorate3([
647
+ consume3({ context: itemContext, subscribe: true }),
648
+ state2()
649
+ ], QtiFeedback.prototype, "_context", void 0);
650
+
651
+ // ../qti-base/dist/lib/response.js
652
+ function parseResponseAttribute(raw) {
653
+ if (raw == null)
654
+ return null;
655
+ if (!raw.includes(",")) {
656
+ const trimmed = raw.trim();
657
+ return trimmed.length > 0 ? trimmed : null;
658
+ }
659
+ const tokens = raw.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
660
+ if (tokens.length === 0)
661
+ return null;
662
+ if (tokens.length === 1)
663
+ return tokens[0];
664
+ return tokens;
665
+ }
666
+ function serializeResponseAttribute(value) {
667
+ if (value == null)
668
+ return null;
669
+ if (Array.isArray(value)) {
670
+ const tokens = value.map((token) => token.trim()).filter((token) => token.length > 0);
671
+ return tokens.length > 0 ? tokens.join(",") : null;
672
+ }
673
+ const trimmed = value.trim();
674
+ return trimmed.length > 0 ? trimmed : null;
675
+ }
676
+ function* iterResponseValues(raw) {
677
+ if (raw == null)
678
+ return;
679
+ if (Array.isArray(raw)) {
680
+ for (const v of raw) {
681
+ const t = v.trim();
682
+ if (t)
683
+ yield t;
684
+ }
685
+ return;
686
+ }
687
+ if (!raw.includes(",")) {
688
+ const t = raw.trim();
689
+ if (t)
690
+ yield t;
691
+ return;
692
+ }
693
+ for (const v of raw.split(",")) {
694
+ const t = v.trim();
695
+ if (t)
696
+ yield t;
697
+ }
698
+ }
699
+ function parsePair(value) {
700
+ const [source, target, ...rest] = value.trim().split(/\s+/);
701
+ if (!source || !target || rest.length > 0) {
702
+ throw new Error(`malformed directed-pair value: "${value}" (expected "source target")`);
703
+ }
704
+ return { source, target };
705
+ }
706
+ function serializePair(source, target) {
707
+ return `${source} ${target}`;
708
+ }
709
+ function parsePoint(value) {
710
+ const [xStr, yStr, ...rest] = value.trim().split(/\s+/);
711
+ const x = Number(xStr);
712
+ const y = Number(yStr);
713
+ if (!Number.isFinite(x) || !Number.isFinite(y) || rest.length > 0) {
714
+ throw new Error(`malformed point value: "${value}" (expected "x y" numeric coords)`);
715
+ }
716
+ return { x, y };
717
+ }
718
+ function serializePoint(x, y) {
719
+ return `${x} ${y}`;
720
+ }
721
+ function responseAttributeConverter(opts) {
722
+ const { emptyAs } = opts;
723
+ const isEmptySentinel = (value) => value === emptyAs || value == null || value === "" || Array.isArray(value) && value.length === 0;
724
+ return {
725
+ fromAttribute: (value) => {
726
+ const parsed = parseResponseAttribute(value);
727
+ return parsed === null ? emptyAs : parsed;
728
+ },
729
+ toAttribute: (value) => {
730
+ if (isEmptySentinel(value))
731
+ return null;
732
+ return serializeResponseAttribute(value);
733
+ }
734
+ };
735
+ }
736
+
737
+ // ../qti-base/dist/lib/qti-rule-base.js
738
+ import { LitElement as LitElement5 } from "lit";
739
+ var QtiRuleElement = class extends LitElement5 {
740
+ };
741
+
742
+ // ../qti-base/dist/utils/scoring-helper.js
743
+ var ScoringHelper = class {
744
+ /**
745
+ * Checks if a given point is within a specified area.
746
+ * @param point The point to test, represented as a string "x y" (e.g., "102 113").
747
+ * @param areaKey The area definition, including shape and coordinates (e.g., "circle,102,113,16").
748
+ * @param baseType The base type of the response, must be "point" for this method to proceed.
749
+ * @returns True if the point is within the area; false otherwise.
750
+ */
751
+ static isPointInArea(point, areaKey, baseType) {
752
+ if (baseType !== "point") {
753
+ console.warn(`Base type ${baseType} is not supported for point area mapping.`);
754
+ return false;
755
+ }
756
+ const [px, py] = point.split(" ").map(Number);
757
+ const [shape, ...coords] = areaKey.split(",");
758
+ const coordinates = coords.map(Number);
759
+ switch (shape.toLowerCase()) {
760
+ case "circle":
761
+ case "default": {
762
+ const [cx, cy, radius] = coordinates;
763
+ if (coordinates.length !== 3) {
764
+ console.warn(`Invalid circle definition: ${areaKey}`);
765
+ return false;
766
+ }
767
+ const distance = Math.sqrt((px - cx) ** 2 + (py - cy) ** 2);
768
+ return distance <= radius;
769
+ }
770
+ case "rect": {
771
+ const [x1, y1, x2, y2] = coordinates;
772
+ if (coordinates.length !== 4) {
773
+ console.warn(`Invalid rectangle definition: ${areaKey}`);
774
+ return false;
775
+ }
776
+ return px >= x1 && px <= x2 && py >= y1 && py <= y2;
777
+ }
778
+ case "ellipse": {
779
+ const [cx, cy, rx, ry] = coordinates;
780
+ if (coordinates.length !== 4) {
781
+ console.warn(`Invalid ellipse definition: ${areaKey}`);
782
+ return false;
783
+ }
784
+ const normalizedX = (px - cx) ** 2 / rx ** 2;
785
+ const normalizedY = (py - cy) ** 2 / ry ** 2;
786
+ return normalizedX + normalizedY <= 1;
787
+ }
788
+ case "poly": {
789
+ if (coordinates.length < 6 || coordinates.length % 2 !== 0) {
790
+ console.warn(`Invalid polygon definition: ${areaKey}`);
791
+ return false;
792
+ }
793
+ const vertices = [];
794
+ for (let i = 0; i < coordinates.length; i += 2) {
795
+ vertices.push({ x: coordinates[i], y: coordinates[i + 1] });
796
+ }
797
+ return this.isPointInPolygon({ x: px, y: py }, vertices);
798
+ }
799
+ default:
800
+ console.warn(`Unsupported shape type: ${shape}`);
801
+ return false;
802
+ }
803
+ }
804
+ /**
805
+ * Checks if a point is inside a polygon using the ray-casting algorithm.
806
+ * @param point The point to test.
807
+ * @param vertices The vertices of the polygon in order (array of {x, y} objects).
808
+ * @returns True if the point is inside the polygon; false otherwise.
809
+ */
810
+ static isPointInPolygon(point, vertices) {
811
+ let inside = false;
812
+ for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) {
813
+ const xi = vertices[i].x, yi = vertices[i].y;
814
+ const xj = vertices[j].x, yj = vertices[j].y;
815
+ const intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
816
+ if (intersect)
817
+ inside = !inside;
818
+ }
819
+ return inside;
820
+ }
821
+ static compareSingleValues(value1, value2, baseType) {
822
+ switch (baseType) {
823
+ case "identifier":
824
+ case "string":
825
+ return value1 === value2;
826
+ case "integer": {
827
+ const int1 = parseInt(value1, 10);
828
+ const int2 = parseInt(value2, 10);
829
+ if (!isNaN(int1) && !isNaN(int2)) {
830
+ return int1 === int2;
831
+ } else {
832
+ console.error(`Cannot convert ${value1} and/or ${value2} to int.`);
833
+ }
834
+ break;
835
+ }
836
+ case "float": {
837
+ const float1 = parseFloat(value1);
838
+ const float2 = parseFloat(value2);
839
+ if (!isNaN(float1) && !isNaN(float2)) {
840
+ return float1 === float2;
841
+ } else {
842
+ console.error(`couldn't convert ${value1} and/or ${value2} to float.`);
843
+ }
844
+ break;
845
+ }
846
+ case "pair":
847
+ case "directedPair": {
848
+ const pair1 = value1.split(" ").sort();
849
+ const pair2 = value2.split(" ").sort();
850
+ if (pair1.length === 2 && pair2.length === 2) {
851
+ if (baseType === "pair") {
852
+ pair1.sort();
853
+ pair2.sort();
854
+ }
855
+ return pair1.join(" ") === pair2.join(" ");
856
+ } else {
857
+ console.error(`compared two pair but one of the values does not have 2 values: 1: ${value1} 2: ${value2}`);
858
+ }
859
+ break;
860
+ }
861
+ }
862
+ return false;
863
+ }
864
+ };
865
+
866
+ // ../qti-base/dist/elements.js
867
+ var qtiBaseElements = [];
868
+
869
+ // ../qti-base/dist/register.js
870
+ installCustomStateSetFallback();
871
+ for (const { tag, ctor } of qtiBaseElements) {
872
+ if (!customElements.get(tag)) {
873
+ customElements.define(tag, ctor);
874
+ }
875
+ }
876
+
877
+ export {
878
+ CustomStateSetShim,
879
+ classifyCustomStateSupport,
880
+ installCustomStateSetFallback,
881
+ computedItemContext,
882
+ computedContext,
883
+ configContext,
884
+ interactionContext,
885
+ itemContext,
886
+ itemContextVariables,
887
+ isSeed,
888
+ asSeed,
889
+ qtiContext,
890
+ INITIAL_SESSION_CONTEXT,
891
+ sessionContext,
892
+ INITIAL_TEST_CONTEXT,
893
+ testContext,
894
+ boxSizing,
895
+ dropRegion,
896
+ validationMessage,
897
+ Interaction,
898
+ QtiVariableDeclaration,
899
+ QtiExpression,
900
+ QtiConditionExpression,
901
+ decimalSeparator,
902
+ convertNumberToUniversalFormat,
903
+ IsNullOrUndefined,
904
+ removeDoubleSlashes,
905
+ QtiFeedback,
906
+ parseResponseAttribute,
907
+ serializeResponseAttribute,
908
+ iterResponseValues,
909
+ parsePair,
910
+ serializePair,
911
+ parsePoint,
912
+ serializePoint,
913
+ responseAttributeConverter,
914
+ QtiRuleElement,
915
+ ScoringHelper
916
+ };
917
+ //# sourceMappingURL=chunk-EKXZI26R.js.map