@citolab/qti-components 7.28.0 → 8.0.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 (83) hide show
  1. package/cdn/index.global.js +1 -1
  2. package/cdn/index.js +6178 -2946
  3. package/dist/base.d.ts +9 -50
  4. package/dist/base.js +31 -6
  5. package/dist/chunk-5QYII4OZ.js +1374 -0
  6. package/dist/chunk-5QYII4OZ.js.map +1 -0
  7. package/dist/{chunk-FK2IVMS3.js → chunk-5SJS36BI.js} +4264 -3647
  8. package/dist/chunk-5SJS36BI.js.map +1 -0
  9. package/dist/chunk-64XSIBGO.js +241 -0
  10. package/dist/chunk-64XSIBGO.js.map +1 -0
  11. package/dist/{chunk-F6WP6WEF.js → chunk-7V4HJFHW.js} +3 -3
  12. package/dist/{chunk-F6WP6WEF.js.map → chunk-7V4HJFHW.js.map} +1 -1
  13. package/dist/{chunk-Z2SUBRH5.js → chunk-H3J7LFRO.js} +834 -1005
  14. package/dist/chunk-H3J7LFRO.js.map +1 -0
  15. package/dist/chunk-IJD53WY6.js +719 -0
  16. package/dist/chunk-IJD53WY6.js.map +1 -0
  17. package/dist/chunk-KYEZRYO6.js +1608 -0
  18. package/dist/chunk-KYEZRYO6.js.map +1 -0
  19. package/dist/{chunk-PINGOK2F.js → chunk-LZJHHZQ2.js} +1979 -1775
  20. package/dist/chunk-LZJHHZQ2.js.map +1 -0
  21. package/dist/chunk-MGF5F3S2.js +11 -0
  22. package/dist/chunk-MGF5F3S2.js.map +1 -0
  23. package/dist/chunk-MJVPNN7W.js +11 -0
  24. package/dist/chunk-MJVPNN7W.js.map +1 -0
  25. package/dist/{chunk-YLIYPNMK.js → chunk-NA2N2WSG.js} +1303 -1193
  26. package/dist/chunk-NA2N2WSG.js.map +1 -0
  27. package/dist/chunk-V5G7X53Y.js +11 -0
  28. package/dist/chunk-V5G7X53Y.js.map +1 -0
  29. package/dist/chunk-W4QZPCNC.js +294 -0
  30. package/dist/chunk-W4QZPCNC.js.map +1 -0
  31. package/dist/chunk-WCQ26LV7.js +102 -0
  32. package/dist/chunk-WCQ26LV7.js.map +1 -0
  33. package/dist/{chunk-OQ77SE2W.js → chunk-WVVMNF74.js} +851 -925
  34. package/dist/chunk-WVVMNF74.js.map +1 -0
  35. package/dist/{chunk-N7S3GNUP.js → chunk-X3E5HXG2.js} +2 -2
  36. package/dist/corrections.d.ts +1 -0
  37. package/dist/corrections.js +84 -0
  38. package/dist/elements.d.ts +1 -399
  39. package/dist/elements.js +6 -5
  40. package/dist/index.d.ts +9 -23
  41. package/dist/index.js +189 -76
  42. package/dist/interactions.d.ts +1 -915
  43. package/dist/interactions.js +19 -15
  44. package/dist/item.css +4118 -1920
  45. package/dist/item.d.ts +1 -127
  46. package/dist/item.js +6 -11
  47. package/dist/loader.d.ts +1 -22
  48. package/dist/loader.js +2 -3
  49. package/dist/processing.d.ts +1 -731
  50. package/dist/processing.js +2 -3
  51. package/dist/qti-components-jsx.d.ts +5394 -2718
  52. package/dist/test.d.ts +1 -537
  53. package/dist/test.js +26 -11
  54. package/dist/transformers.d.ts +1 -18
  55. package/dist/transformers.js +1 -2
  56. package/package.json +20 -16
  57. package/custom-elements.json +0 -43161
  58. package/dist/chunk-BPIDAS3T.js +0 -495
  59. package/dist/chunk-BPIDAS3T.js.map +0 -1
  60. package/dist/chunk-FK2IVMS3.js.map +0 -1
  61. package/dist/chunk-LQZCSHN5.js +0 -3673
  62. package/dist/chunk-LQZCSHN5.js.map +0 -1
  63. package/dist/chunk-OQ77SE2W.js.map +0 -1
  64. package/dist/chunk-PINGOK2F.js.map +0 -1
  65. package/dist/chunk-QXBXORM3.js +0 -27
  66. package/dist/chunk-U2FLSHK7.js +0 -510
  67. package/dist/chunk-U2FLSHK7.js.map +0 -1
  68. package/dist/chunk-YLIYPNMK.js.map +0 -1
  69. package/dist/chunk-Z2SUBRH5.js.map +0 -1
  70. package/dist/computed-item.context-DfsW_96S.d.ts +0 -22
  71. package/dist/computed.context-DHbxad0N.d.ts +0 -45
  72. package/dist/config.context-BjZ2bZSY.d.ts +0 -42
  73. package/dist/interaction-Bgzlw7Iu.d.ts +0 -110
  74. package/dist/item.context-CkUeDcaO.d.ts +0 -15
  75. package/dist/qti-condition-expression-B1BYmCcq.d.ts +0 -26
  76. package/dist/qti-feedback-ChmXhZuf.d.ts +0 -21
  77. package/dist/qti-rule-base-ajOnfGXY.d.ts +0 -16
  78. package/dist/qti-transform-test-DMx816H0.d.ts +0 -68
  79. package/dist/qti.context-B8PSyIWm.d.ts +0 -14
  80. package/dist/test.context-CByZZjkR.d.ts +0 -15
  81. package/dist/variables-CMYcDbyW.d.ts +0 -70
  82. /package/dist/{chunk-N7S3GNUP.js.map → chunk-X3E5HXG2.js.map} +0 -0
  83. /package/dist/{chunk-QXBXORM3.js.map → corrections.js.map} +0 -0
@@ -0,0 +1,1374 @@
1
+ import {
2
+ ActiveElementMixin,
3
+ QtiGapText,
4
+ QtiHotspotChoice,
5
+ QtiHottext,
6
+ QtiSimpleAssociableChoice,
7
+ QtiSimpleChoice,
8
+ c as c2,
9
+ dragDropContext,
10
+ elements,
11
+ elements10,
12
+ elements11,
13
+ elements12,
14
+ elements13,
15
+ elements14,
16
+ elements15,
17
+ elements16,
18
+ elements2,
19
+ elements3,
20
+ elements4,
21
+ elements5,
22
+ elements6,
23
+ elements7,
24
+ elements8,
25
+ elements9
26
+ } from "./chunk-5SJS36BI.js";
27
+ import {
28
+ Interaction,
29
+ T,
30
+ boxSizing,
31
+ c,
32
+ dropRegion,
33
+ i,
34
+ i2,
35
+ n2 as n,
36
+ r,
37
+ removeDoubleSlashes
38
+ } from "./chunk-H3J7LFRO.js";
39
+
40
+ // ../interactions/core/dist/elements/qti-associable-hotspot/qti-associable-hotspot.styles.js
41
+ var qti_associable_hotspot_styles_default = [
42
+ boxSizing,
43
+ dropRegion,
44
+ i`
45
+ /* A drop target — see qti-gap.styles.ts. The interaction publishes the chip measurements as
46
+ custom properties on its host; the properties themselves live here. */
47
+ :host {
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ user-select: none;
52
+ position: absolute;
53
+ min-height: var(--qti-dropzone-min-height, 0);
54
+ min-width: var(--qti-dropzone-min-width, 0);
55
+ }
56
+
57
+ /* The hotspot's own box comes from its authored coords; the drop region simply fills it. */
58
+ [part~='drop'] {
59
+ width: 100%;
60
+ height: 100%;
61
+ }
62
+ `
63
+ ];
64
+
65
+ // ../interactions/core/dist/elements/qti-associable-hotspot/qti-associable-hotspot.js
66
+ var __decorate = function(decorators, target, key, desc) {
67
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
68
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
69
+ 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;
70
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
71
+ };
72
+ var QtiAssociableHotspot = class extends i2 {
73
+ constructor() {
74
+ super(...arguments);
75
+ this.internals = this.attachInternals();
76
+ this.acceptsDeclarativeDrops = true;
77
+ }
78
+ static {
79
+ this.styles = qti_associable_hotspot_styles_default;
80
+ }
81
+ /** The chips this hotspot holds. Stable node references, cloned once at drop time. */
82
+ get drags() {
83
+ return this._dragDrop?.nodesByTarget?.[this.identifier] ?? [];
84
+ }
85
+ connectedCallback() {
86
+ super.connectedCallback();
87
+ this.dispatchEvent(new CustomEvent("qti-register-hotspot", {
88
+ bubbles: true,
89
+ composed: true,
90
+ cancelable: false
91
+ }));
92
+ }
93
+ render() {
94
+ return T`<div part="drop">
95
+ ${c2(this.drags, (node) => node.getAttribute("identifier"), (node) => node)}
96
+ </div>`;
97
+ }
98
+ };
99
+ __decorate([
100
+ n({ type: String, reflect: true })
101
+ ], QtiAssociableHotspot.prototype, "identifier", void 0);
102
+ __decorate([
103
+ c({ context: dragDropContext, subscribe: true }),
104
+ r()
105
+ ], QtiAssociableHotspot.prototype, "_dragDrop", void 0);
106
+
107
+ // ../interactions/core/dist/elements/qti-gap/qti-gap.styles.js
108
+ var qti_gap_styles_default = [
109
+ boxSizing,
110
+ dropRegion,
111
+ i`
112
+ /*
113
+ * A drop target. The interaction measures its chips and publishes the result as custom
114
+ * properties on its own host; the actual CSS properties live here, not in a style attribute.
115
+ *
116
+ * An authored data-choices-container-width takes the width axis: autoSizeDropzoneWidth goes
117
+ * false and the mixin never publishes --qti-dropzone-min-width at all, so the fallback below
118
+ * applies and the authored inline width is free to win. That works only because the token is
119
+ * never declared globally — min-width beats width, so a declared floor would silently outrank
120
+ * the author. See DROP-SIZING.md §2 and tools/stylelint/no-declared-measured-token.js.
121
+ */
122
+ /*
123
+ * A gap sits in a line of prose, so it is inline-flex and rides the text baseline. All four of
124
+ * these were in the theme, set on this host from the document — which also meant the
125
+ * 'display: flex' this file declared never took effect.
126
+ */
127
+ :host {
128
+ display: inline-flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ vertical-align: middle;
132
+ user-select: none;
133
+ }
134
+
135
+ /*
136
+ * The chip's box is reserved on the slot, not on the host.
137
+ *
138
+ * --qti-dropzone-min-* is the border-box of the widest and tallest chip, so whatever carries
139
+ * that minimum must have no border or padding of its own — otherwise the theme's dashed gap
140
+ * border eats into the reservation, the gap comes up short, and it grows the instant a chip
141
+ * lands in it. That reflows the sentence and drags the neighbouring gaps sideways while the
142
+ * user is still on their way to one.
143
+ *
144
+ * It cannot live on :host. A theme's universal reset matches the host element from the
145
+ * document, and document rules outrank :host — so the host is border-box no matter what this
146
+ * file says. This div is inside the shadow tree, out of the document's reach, and inherits
147
+ * border-box harmlessly since it has no border or padding of its own.
148
+ */
149
+ [part~='drop'] {
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ min-height: var(--qti-dropzone-min-height, 0);
154
+ min-width: var(--qti-dropzone-min-width, 0);
155
+ }
156
+ `
157
+ ];
158
+
159
+ // ../interactions/core/dist/elements/qti-gap/qti-gap.js
160
+ var __decorate2 = function(decorators, target, key, desc) {
161
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
162
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
163
+ 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;
164
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
165
+ };
166
+ var QtiGap = class extends i2 {
167
+ constructor() {
168
+ super(...arguments);
169
+ this.internals = this.attachInternals();
170
+ this.acceptsDeclarativeDrops = true;
171
+ this.tabindex = 0;
172
+ }
173
+ static {
174
+ this.styles = qti_gap_styles_default;
175
+ }
176
+ /** The chips this gap holds. Stable node references, cloned once at drop time by the interaction. */
177
+ get drags() {
178
+ return this._dragDrop?.nodesByTarget?.[this.identifier] ?? [];
179
+ }
180
+ render() {
181
+ return T`<div part="drop">
182
+ ${c2(this.drags, (node) => node.getAttribute("identifier"), (node) => node)}
183
+ </div>`;
184
+ }
185
+ };
186
+ __decorate2([
187
+ n({ type: Number, reflect: true })
188
+ ], QtiGap.prototype, "tabindex", void 0);
189
+ __decorate2([
190
+ n({ type: String, reflect: true })
191
+ ], QtiGap.prototype, "identifier", void 0);
192
+ __decorate2([
193
+ c({ context: dragDropContext, subscribe: true }),
194
+ r()
195
+ ], QtiGap.prototype, "_dragDrop", void 0);
196
+
197
+ // ../interactions/core/dist/elements/qti-gap-img/qti-gap-img.styles.js
198
+ var qti_gap_img_styles_default = [
199
+ boxSizing,
200
+ i`
201
+ /* Centred, because a gap-img is a picture inside a drop the same size as it. Was in the theme's
202
+ graphic-gap-match rules, on the host from the document. */
203
+ :host {
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ user-select: none;
208
+ }
209
+ `
210
+ ];
211
+
212
+ // ../interactions/core/dist/elements/qti-gap-img/qti-gap-img.js
213
+ var __decorate3 = function(decorators, target, key, desc) {
214
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
215
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
216
+ 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;
217
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
218
+ };
219
+ var QtiGapImg = class extends i2 {
220
+ constructor() {
221
+ super(...arguments);
222
+ this.tabindex = 0;
223
+ this.internals = this.attachInternals();
224
+ }
225
+ static {
226
+ this.styles = qti_gap_img_styles_default;
227
+ }
228
+ connectedCallback() {
229
+ this.setAttribute("slot", "drags");
230
+ }
231
+ };
232
+ __decorate3([
233
+ n({ type: Number, reflect: true })
234
+ ], QtiGapImg.prototype, "tabindex", void 0);
235
+
236
+ // ../interactions/core/dist/elements/qti-inline-choice/qti-inline-choice.styles.js
237
+ var styles = i`
238
+ /*
239
+ * An option row: one line, clipped, with its text centred against the row's min-height. All four
240
+ * were in the theme, set on this host from the document — which is also why the 'display: block'
241
+ * this file declared never took effect.
242
+ */
243
+ :host {
244
+ display: inline-flex;
245
+ align-items: center;
246
+ white-space: nowrap;
247
+ overflow: hidden;
248
+ box-sizing: border-box;
249
+ }
250
+
251
+ slot {
252
+ display: inline;
253
+ }
254
+ `;
255
+ var qti_inline_choice_styles_default = styles;
256
+
257
+ // ../interactions/core/dist/elements/qti-inline-choice/qti-inline-choice.js
258
+ var __decorate4 = function(decorators, target, key, desc) {
259
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
260
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
261
+ 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;
262
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
263
+ };
264
+ var QtiInlineChoice = class extends ActiveElementMixin(i2, "qti-inline-choice") {
265
+ static {
266
+ this.styles = qti_inline_choice_styles_default;
267
+ }
268
+ connectedCallback() {
269
+ super.connectedCallback();
270
+ this.addEventListener("click", this.#onSelectInlineChoice);
271
+ this.dispatchEvent(new CustomEvent("qti-inline-choice-register", {
272
+ bubbles: true,
273
+ composed: true,
274
+ cancelable: false
275
+ }));
276
+ }
277
+ disconnectedCallback() {
278
+ this.removeEventListener("click", this.#onSelectInlineChoice);
279
+ }
280
+ render() {
281
+ return T` <slot></slot> `;
282
+ }
283
+ #onSelectInlineChoice() {
284
+ this.dispatchEvent(new CustomEvent("qti-inline-choice-select", {
285
+ bubbles: true,
286
+ cancelable: false,
287
+ composed: true,
288
+ detail: { identifier: this.identifier }
289
+ }));
290
+ }
291
+ };
292
+ __decorate4([
293
+ n({ type: String })
294
+ ], QtiInlineChoice.prototype, "identifier", void 0);
295
+
296
+ // ../interactions/core/dist/elements/qti-prompt/qti-prompt.styles.js
297
+ var qti_prompt_styles_default = [
298
+ boxSizing,
299
+ i`
300
+ :host {
301
+ display: block;
302
+ margin: var(--qti-gap) 0;
303
+ width: 100%;
304
+ }
305
+ `
306
+ ];
307
+
308
+ // ../interactions/core/dist/elements/qti-prompt/qti-prompt.js
309
+ var QtiPrompt = class extends i2 {
310
+ static {
311
+ this.styles = qti_prompt_styles_default;
312
+ }
313
+ render() {
314
+ return T`<slot></slot>`;
315
+ }
316
+ connectedCallback() {
317
+ super.connectedCallback();
318
+ const inInteraction = this.parentElement?.tagName.endsWith("INTERACTION");
319
+ if (inInteraction) {
320
+ this.setAttribute("slot", "prompt");
321
+ }
322
+ }
323
+ };
324
+
325
+ // ../interactions/custom-interaction/dist/qti-custom-interaction.styles.js
326
+ var qti_custom_interaction_styles_default = [
327
+ boxSizing,
328
+ i`
329
+ /* The box this element already had. See the note in qti-match-interaction.styles.ts. */
330
+ :host {
331
+ display: block;
332
+ }
333
+ `
334
+ ];
335
+
336
+ // ../interactions/custom-interaction/dist/qti-custom-interaction.js
337
+ var __decorate5 = function(decorators, target, key, desc) {
338
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
339
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
340
+ 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;
341
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
342
+ };
343
+ var registerCES = `
344
+ const postToParentWindows = (type, data) => {
345
+ window.top.postMessage(data ? { type, data } : { type }, '*');
346
+ let w = window.parent;
347
+ while (w) {
348
+ if (w !== window.top) {
349
+ w.postMessage({ type, data }, '*');
350
+ }
351
+ if (w !== w.parent) {
352
+ w = w.parent;
353
+ } else {
354
+ w = null;
355
+ }
356
+ }
357
+ };
358
+
359
+ window.CES = {
360
+ media: null,
361
+ response: null,
362
+ load: () => {
363
+ let resolveCount = 0;
364
+
365
+ const handleMessage = (event) => {
366
+ if (event.data.type === "mediaData") {
367
+ const media = event.data.data;
368
+ CES.media = media;
369
+ resolveCount++;
370
+ } else if (event.data.type === "responseData") {
371
+ const response = event.data.data;
372
+ if (response && Array.isArray(response) && response.length > 0) {
373
+ // state is stored in the first element of the array
374
+ CES.response = response[0];
375
+ // Wait a short moment to ensure CES.response is set
376
+ setTimeout(() => {
377
+ // Re-create the Controller instance if it exists
378
+ if (typeof Controller === 'function') {
379
+ console.log("Re-creating Controller instance");
380
+ ctrl = new Controller();
381
+ }
382
+ }, 50);
383
+ } else {
384
+ CES.response = response;
385
+ }
386
+ resolveCount++;
387
+ }
388
+ if (resolveCount === 2) {
389
+ //window.removeEventListener("message", handleMessage);
390
+ }
391
+ };
392
+ window.addEventListener("message", handleMessage);
393
+ postToParentWindows("getMedia");
394
+ postToParentWindows("getResponse");
395
+ },
396
+ setResponse: (data) => {
397
+ postToParentWindows("setResponse", data);
398
+ },
399
+ getResponse: () => {
400
+ return CES.response;
401
+ },
402
+ getMedia: () => {
403
+ return CES.media;
404
+ },
405
+ setStageHeight: () => {
406
+ postToParentWindows("setStageHeight");
407
+ },
408
+ };
409
+ CES.load();
410
+ `;
411
+ var ciBootstrap = `
412
+ (function() {
413
+ var blobUrlReceived = null;
414
+
415
+ const createIframe = () => {
416
+ if (!blobUrlReceived || !document.body) return;
417
+ var n = document.createElement('iframe');
418
+ n.frameBorder = '0';
419
+ n.scrolling = 'no';
420
+ n.src = blobUrlReceived;
421
+ n.style.width = '100%';
422
+ n.style.height = '100%';
423
+ document.body.appendChild(n);
424
+ };
425
+
426
+ const handleMessage = (event) => {
427
+ if (event.data.type === 'blobUrl') {
428
+ blobUrlReceived = event.data.data;
429
+ window.removeEventListener('message', handleMessage);
430
+ createIframe();
431
+ }
432
+ };
433
+
434
+ window.addEventListener('message', handleMessage);
435
+
436
+ // Wait for document ready before requesting blobUrl
437
+ const requestBlobUrl = () => {
438
+ let w = window.parent;
439
+ while (w) {
440
+ w.postMessage({ type: 'getBlobUrl' }, '*');
441
+ if (w !== w.parent) {
442
+ w = w.parent;
443
+ } else {
444
+ w = null;
445
+ }
446
+ }
447
+ };
448
+
449
+ if (document.readyState === 'loading') {
450
+ document.addEventListener('DOMContentLoaded', requestBlobUrl);
451
+ } else {
452
+ requestBlobUrl();
453
+ }
454
+ })();
455
+ `;
456
+ var QtiCustomInteraction = class extends Interaction {
457
+ #rawResponse;
458
+ #manifestUrl;
459
+ #resourceBaseUrl;
460
+ static {
461
+ this.styles = qti_custom_interaction_styles_default;
462
+ }
463
+ constructor() {
464
+ super();
465
+ this.#rawResponse = "";
466
+ this.#manifestUrl = null;
467
+ this.#resourceBaseUrl = null;
468
+ this._errorMessage = null;
469
+ this.#contentBlobUrl = null;
470
+ this.handlePostMessage = this.handlePostMessage.bind(this);
471
+ }
472
+ // Pre-created blob URL for the index.html with injected CES proxy
473
+ #contentBlobUrl;
474
+ connectedCallback() {
475
+ super.connectedCallback();
476
+ let manifestPath = this.data;
477
+ if (!manifestPath) {
478
+ const objectEl = this.querySelector("object[data]");
479
+ if (objectEl) {
480
+ manifestPath = objectEl.getAttribute("data");
481
+ const width = objectEl.getAttribute("width");
482
+ const height = objectEl.getAttribute("height");
483
+ if (width)
484
+ this.setAttribute("width", width);
485
+ if (height)
486
+ this.setAttribute("height", height);
487
+ }
488
+ }
489
+ if (!manifestPath) {
490
+ this._errorMessage = "No manifest path found (neither data attribute nor object child)";
491
+ return;
492
+ }
493
+ const uriToManifest = manifestPath.startsWith("http") || manifestPath.startsWith("blob") ? manifestPath : removeDoubleSlashes((this.baseItemUrl || "") + "/" + manifestPath);
494
+ this.#manifestUrl = new URL(uriToManifest, window.location.href).toString();
495
+ fetch(uriToManifest).then((response) => {
496
+ return response.json();
497
+ }).then((data) => {
498
+ this.manifest = data;
499
+ this.setupCES();
500
+ }).catch((err) => {
501
+ this._errorMessage = err;
502
+ });
503
+ }
504
+ /**
505
+ * Sets up the CES custom interaction by creating the iframe structure and
506
+ * handling the CES API communication via postMessage.
507
+ *
508
+ * For interactions that use CES, this method:
509
+ * 1. Fetches the original bootstrap.js and checks if it uses CES
510
+ * 2. If CES is used, fetches index.html and injects the registerCES proxy
511
+ * 3. Creates a blob URL for the modified HTML (stored in _contentBlobUrl)
512
+ * 4. Replaces bootstrap.js with ciBootstrap that loads the blob URL
513
+ * 5. Sets up postMessage listeners for CES API calls
514
+ */
515
+ async setupCES() {
516
+ await this.updateComplete;
517
+ const iframe = this.shadowRoot?.querySelector("#pciContainer");
518
+ if (!iframe) {
519
+ this._errorMessage = "pciContainer iframe not found";
520
+ return;
521
+ }
522
+ if (!iframe.getAttribute("src")) {
523
+ iframe.setAttribute("src", "about:blank");
524
+ }
525
+ let iframeDoc = iframe.contentDocument ?? iframe.contentWindow?.document ?? null;
526
+ if (!iframeDoc) {
527
+ await new Promise((resolve) => iframe.addEventListener("load", () => resolve(), { once: true }));
528
+ iframeDoc = iframe.contentDocument ?? iframe.contentWindow?.document ?? null;
529
+ }
530
+ if (!iframeDoc) {
531
+ this._errorMessage = "pciContainer iframe document not available";
532
+ return;
533
+ }
534
+ if (!this.manifest.script || this.manifest.script.length === 0) {
535
+ this._errorMessage = "No script found in manifest";
536
+ return;
537
+ }
538
+ if (!this.manifest.style || this.manifest.style.length === 0) {
539
+ this._errorMessage = "No style found in manifest";
540
+ return;
541
+ }
542
+ const cssRef = this.manifest.style[0];
543
+ const baseCandidates = this.#getBaseCandidates();
544
+ const cssResolved = await this.#resolveResourceWithFallback(cssRef, baseCandidates);
545
+ const cssUrl = cssResolved.url;
546
+ if (cssResolved.baseUrl && !this.#resourceBaseUrl) {
547
+ this.#resourceBaseUrl = cssResolved.baseUrl;
548
+ }
549
+ const usesCES = true;
550
+ if (usesCES) {
551
+ let indexUrl = "";
552
+ if (this.manifest.media && this.manifest.media.length > 0) {
553
+ const mediaRef = this.manifest.media[0];
554
+ const indexResolved = await this.#resolveResourceWithFallback(mediaRef, baseCandidates, {
555
+ returnText: true
556
+ });
557
+ indexUrl = indexResolved.url;
558
+ if (indexResolved.baseUrl && !this.#resourceBaseUrl) {
559
+ this.#resourceBaseUrl = indexResolved.baseUrl;
560
+ }
561
+ if (indexResolved.text) {
562
+ let html = indexResolved.text;
563
+ const alreadyModified = html.includes("postToParentWindows") || html.includes("window.CES");
564
+ if (!alreadyModified) {
565
+ const cesScript = "<script>" + registerCES + "</script>";
566
+ const headIndex = html.indexOf("<head>");
567
+ if (headIndex !== -1) {
568
+ html = html.slice(0, headIndex + 6) + cesScript + html.slice(headIndex + 6);
569
+ } else {
570
+ html = cesScript + html;
571
+ }
572
+ }
573
+ const blob = new Blob([html], { type: "text/html" });
574
+ this.#contentBlobUrl = URL.createObjectURL(blob);
575
+ }
576
+ } else {
577
+ const manifestPath = this.data || "manifest.json";
578
+ const basePath = manifestPath.includes("/") ? manifestPath.substring(0, manifestPath.lastIndexOf("/")) : "";
579
+ const indexPath = basePath ? `${basePath}/index.html` : "index.html";
580
+ const indexResolved = await this.#resolveResourceWithFallback(indexPath, baseCandidates, {
581
+ returnText: true
582
+ });
583
+ indexUrl = indexResolved.url;
584
+ if (indexResolved.baseUrl && !this.#resourceBaseUrl) {
585
+ this.#resourceBaseUrl = indexResolved.baseUrl;
586
+ }
587
+ if (indexResolved.text) {
588
+ let html = indexResolved.text;
589
+ const alreadyModified = html.includes("postToParentWindows") || html.includes("window.CES");
590
+ if (!alreadyModified) {
591
+ const cesScript = "<script>" + registerCES + "</script>";
592
+ const headIndex = html.indexOf("<head>");
593
+ if (headIndex !== -1) {
594
+ html = html.slice(0, headIndex + 6) + cesScript + html.slice(headIndex + 6);
595
+ } else {
596
+ html = cesScript + html;
597
+ }
598
+ }
599
+ const blob = new Blob([html], { type: "text/html" });
600
+ this.#contentBlobUrl = URL.createObjectURL(blob);
601
+ }
602
+ }
603
+ if (indexUrl && !this.#contentBlobUrl) {
604
+ try {
605
+ const indexResponse = await fetch(indexUrl);
606
+ if (indexResponse.ok) {
607
+ let html = await indexResponse.text();
608
+ const alreadyModified = html.includes("postToParentWindows") || html.includes("window.CES");
609
+ if (!alreadyModified) {
610
+ const cesScript = "<script>" + registerCES + "</script>";
611
+ const headIndex = html.indexOf("<head>");
612
+ if (headIndex !== -1) {
613
+ html = html.slice(0, headIndex + 6) + cesScript + html.slice(headIndex + 6);
614
+ } else {
615
+ html = cesScript + html;
616
+ }
617
+ }
618
+ const blob = new Blob([html], { type: "text/html" });
619
+ this.#contentBlobUrl = URL.createObjectURL(blob);
620
+ } else {
621
+ console.error(`Failed to fetch index.html: ${indexResponse.status}`);
622
+ }
623
+ } catch (e) {
624
+ console.error(`Error fetching index.html: ${e}`);
625
+ }
626
+ }
627
+ }
628
+ const inlineScript = `<script>${ciBootstrap}</script>`;
629
+ window.addEventListener("message", this.handlePostMessage);
630
+ const htmlContent = `
631
+ <!DOCTYPE html>
632
+ <html>
633
+ <head>
634
+ ${cssUrl ? `<link href='${cssUrl}' rel="stylesheet" />` : ""}
635
+ ${inlineScript}
636
+ </head>
637
+ <body></body>
638
+ </html>
639
+ `;
640
+ iframe.srcdoc = htmlContent;
641
+ }
642
+ #getIFrames() {
643
+ const iframesInShadowRoot = this.shadowRoot.querySelectorAll("iframe");
644
+ const iframe = this.querySelectorAll("iframe");
645
+ const outerIFrames = [...iframesInShadowRoot, ...iframe];
646
+ for (const iframe2 of outerIFrames) {
647
+ const iframeSrc = iframe2.src;
648
+ const isSameOrigin = new URL(iframeSrc, window.location.href).origin === window.location.origin;
649
+ if (isSameOrigin) {
650
+ try {
651
+ const outerDoc = iframe2.contentDocument || iframe2.contentWindow.document;
652
+ if (outerDoc) {
653
+ this.#getInnerIFrames(outerDoc, outerIFrames);
654
+ }
655
+ } catch (e) {
656
+ console.error("Error accessing nested iframe:", e);
657
+ }
658
+ }
659
+ }
660
+ outerIFrames.forEach((iframe2, index) => {
661
+ if (outerIFrames.indexOf(iframe2) !== index) {
662
+ outerIFrames.splice(index, 1);
663
+ }
664
+ });
665
+ return outerIFrames;
666
+ }
667
+ #getInnerIFrames(iframeDocument, iframes = []) {
668
+ const currentIframes = iframeDocument.querySelectorAll("iframe");
669
+ currentIframes.forEach((iframe) => {
670
+ iframes.push(iframe);
671
+ const iframeSrc = iframe.src;
672
+ const isSameOrigin = new URL(iframeSrc, window.location.href).origin === window.location.origin;
673
+ if (isSameOrigin) {
674
+ try {
675
+ const nestedDoc = iframe.contentDocument || iframe.contentWindow.document;
676
+ this.#getInnerIFrames(nestedDoc, iframes);
677
+ } catch (e) {
678
+ console.error("Error accessing nested iframe:", e);
679
+ }
680
+ } else {
681
+ console.warn("Skipped cross-origin iframe:", iframeSrc);
682
+ }
683
+ });
684
+ return iframes;
685
+ }
686
+ #postToWindowAndIframes(type, data) {
687
+ window.postMessage({ type, data }, "*");
688
+ const iframes = this.#getIFrames();
689
+ for (const iframe of iframes) {
690
+ if (iframe.contentWindow) {
691
+ iframe.contentWindow.postMessage({ type, data }, "*");
692
+ }
693
+ }
694
+ }
695
+ handlePostMessage(event) {
696
+ const { type, data } = event.data;
697
+ switch (type) {
698
+ case "setResponse":
699
+ if (data === null || !(Array.isArray(data) && data.length === 1 && data[0] === "")) {
700
+ this.#rawResponse = data;
701
+ this.saveResponse(data);
702
+ }
703
+ break;
704
+ case "getResponse": {
705
+ this.#postToWindowAndIframes("responseData", this.#rawResponse);
706
+ break;
707
+ }
708
+ case "getBlobUrl": {
709
+ if (this.#contentBlobUrl) {
710
+ this.#postToWindowAndIframes("blobUrl", this.#contentBlobUrl);
711
+ if (event.source && event.source !== window) {
712
+ event.source.postMessage({ type: "blobUrl", data: this.#contentBlobUrl }, "*");
713
+ }
714
+ }
715
+ break;
716
+ }
717
+ case "getMedia": {
718
+ const baseCandidates = this.#getBaseCandidates();
719
+ const mediaData = this.manifest.media.map((media) => {
720
+ if (media.startsWith("http") || media.startsWith("blob")) {
721
+ return media;
722
+ }
723
+ const baseUrl = this.#resourceBaseUrl || baseCandidates[0];
724
+ if (!baseUrl) {
725
+ return media;
726
+ }
727
+ return new URL(media, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
728
+ });
729
+ this.#postToWindowAndIframes("mediaData", mediaData);
730
+ break;
731
+ }
732
+ case "setStageHeight":
733
+ console.log("setStageHeight not implemented");
734
+ break;
735
+ }
736
+ }
737
+ validate() {
738
+ if (!this.#rawResponse) {
739
+ return false;
740
+ }
741
+ if (Array.isArray(this.#rawResponse)) {
742
+ if (this.#rawResponse.length === 0) {
743
+ return false;
744
+ }
745
+ for (const value of this.#rawResponse) {
746
+ if (value !== "" && value !== null) {
747
+ return true;
748
+ }
749
+ }
750
+ }
751
+ return true;
752
+ }
753
+ get response() {
754
+ return this.#rawResponse || null;
755
+ }
756
+ set response(val) {
757
+ if (typeof val === "string") {
758
+ this.#rawResponse = val;
759
+ this.saveResponse(val);
760
+ } else if (Array.isArray(val)) {
761
+ this.#rawResponse = val;
762
+ } else if (!val) {
763
+ } else {
764
+ throw new Error("Value must be a string or an array of strings");
765
+ }
766
+ }
767
+ disconnectedCallback() {
768
+ window.removeEventListener("message", this.handlePostMessage);
769
+ super.disconnectedCallback();
770
+ }
771
+ render() {
772
+ return T`<iframe
773
+ width=${this.getAttribute("width")}
774
+ height=${this.getAttribute("height")}
775
+ frameborder="0"
776
+ title="pciContainer"
777
+ id="pciContainer"
778
+ >
779
+ </iframe>
780
+ ${this._errorMessage && T`<div style="color:red">
781
+ <h1>Error</h1>
782
+ ${this._errorMessage}
783
+ </div>`}`;
784
+ }
785
+ #getBaseCandidates() {
786
+ const candidates = [this.#resourceBaseUrl, this.baseRefUrl, this.baseItemUrl, this.#getManifestBaseUrl()];
787
+ const resolved = candidates.filter(Boolean).map((base) => new URL(base, window.location.href).toString());
788
+ const manifestParent = this.#getManifestParentBaseUrl();
789
+ if (manifestParent) {
790
+ resolved.push(manifestParent);
791
+ }
792
+ return [...new Set(resolved)];
793
+ }
794
+ #getManifestBaseUrl() {
795
+ if (!this.#manifestUrl) {
796
+ return null;
797
+ }
798
+ return new URL(".", this.#manifestUrl).toString();
799
+ }
800
+ #getManifestParentBaseUrl() {
801
+ if (!this.#manifestUrl) {
802
+ return null;
803
+ }
804
+ return new URL("..", this.#manifestUrl).toString();
805
+ }
806
+ async #resolveResourceWithFallback(ref, baseCandidates, options = {}) {
807
+ if (!ref) {
808
+ return { url: "", baseUrl: null };
809
+ }
810
+ if (ref.startsWith("http") || ref.startsWith("blob")) {
811
+ return { url: ref, baseUrl: null };
812
+ }
813
+ for (const base of baseCandidates) {
814
+ const baseUrl = new URL(base, window.location.href).toString();
815
+ const url = new URL(ref, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
816
+ try {
817
+ const response = await fetch(url);
818
+ if (response.ok) {
819
+ if (options.returnText) {
820
+ const text = await response.text();
821
+ return { url, baseUrl, text };
822
+ }
823
+ return { url, baseUrl };
824
+ }
825
+ } catch (e) {
826
+ }
827
+ }
828
+ return { url: "", baseUrl: null };
829
+ }
830
+ };
831
+ __decorate5([
832
+ n({ type: String, attribute: "data" })
833
+ ], QtiCustomInteraction.prototype, "data", void 0);
834
+ __decorate5([
835
+ n({ type: String, attribute: "data-base-item" })
836
+ ], QtiCustomInteraction.prototype, "baseItemUrl", void 0);
837
+ __decorate5([
838
+ n({ type: String, attribute: "data-base-ref" })
839
+ ], QtiCustomInteraction.prototype, "baseRefUrl", void 0);
840
+ __decorate5([
841
+ n({ type: String, attribute: "id" })
842
+ ], QtiCustomInteraction.prototype, "id", void 0);
843
+ __decorate5([
844
+ r()
845
+ ], QtiCustomInteraction.prototype, "_errorMessage", void 0);
846
+
847
+ // ../interactions/end-attempt-interaction/dist/qti-end-attempt-interaction.styles.js
848
+ var qti_end_attempt_interaction_styles_default = [boxSizing];
849
+
850
+ // ../interactions/end-attempt-interaction/dist/qti-end-attempt-interaction.js
851
+ var __decorate6 = function(decorators, target, key, desc) {
852
+ var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
853
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
854
+ 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;
855
+ return c3 > 3 && r2 && Object.defineProperty(target, key, r2), r2;
856
+ };
857
+ var QtiEndAttemptInteraction = class extends i2 {
858
+ constructor() {
859
+ super(...arguments);
860
+ this.disabled = false;
861
+ this.countAttempt = null;
862
+ this.title = "end attempt";
863
+ }
864
+ static {
865
+ this.styles = qti_end_attempt_interaction_styles_default;
866
+ }
867
+ render() {
868
+ return T`<button ?disabled=${this.disabled} part="button" @click=${this.endAttempt}>${this.title}</button>`;
869
+ }
870
+ endAttempt(_) {
871
+ this.dispatchEvent(new CustomEvent("end-attempt", {
872
+ bubbles: true,
873
+ composed: true,
874
+ detail: { responseIdentifier: this.responseIdentifier, countAttempt: this.countAttempt === "true" }
875
+ }));
876
+ }
877
+ };
878
+ __decorate6([
879
+ n({ type: String, attribute: "response-identifier" })
880
+ ], QtiEndAttemptInteraction.prototype, "responseIdentifier", void 0);
881
+ __decorate6([
882
+ n({ reflect: true, type: Boolean })
883
+ ], QtiEndAttemptInteraction.prototype, "disabled", void 0);
884
+ __decorate6([
885
+ n({ type: String, attribute: "count-attempt" })
886
+ ], QtiEndAttemptInteraction.prototype, "countAttempt", void 0);
887
+ __decorate6([
888
+ n({ type: String })
889
+ ], QtiEndAttemptInteraction.prototype, "title", void 0);
890
+
891
+ // ../interactions/media-interaction/dist/qti-media-interaction.styles.js
892
+ var qti_media_interaction_styles_default = [
893
+ boxSizing,
894
+ i`
895
+ /* The box this element already had. See the note in qti-match-interaction.styles.ts. */
896
+ :host {
897
+ display: block;
898
+ }
899
+ `
900
+ ];
901
+
902
+ // ../interactions/media-interaction/dist/qti-media-interaction.js
903
+ var QtiMediaInteraction = class extends Interaction {
904
+ static {
905
+ this.styles = qti_media_interaction_styles_default;
906
+ }
907
+ #value = null;
908
+ reset() {
909
+ this.#value = null;
910
+ }
911
+ validate() {
912
+ return true;
913
+ }
914
+ get response() {
915
+ return this.#value ? this.#value.toString() : null;
916
+ }
917
+ set response(val) {
918
+ if (val) {
919
+ const isNumber = !isNaN(parseInt(val?.toString()));
920
+ if (isNumber) {
921
+ this.#value = parseInt(val.toString());
922
+ } else {
923
+ throw new Error(`Value must be a number ${val}`);
924
+ }
925
+ }
926
+ }
927
+ static get properties() {
928
+ return {
929
+ ...Interaction.properties,
930
+ ...{
931
+ step: {
932
+ type: Number,
933
+ attribute: "step",
934
+ default: 10
935
+ }
936
+ }
937
+ };
938
+ }
939
+ render() {
940
+ return T` <slot name="prompt"></slot>
941
+ <slot></slot>`;
942
+ }
943
+ constructor() {
944
+ super();
945
+ }
946
+ connectedCallback() {
947
+ super.connectedCallback();
948
+ const mediaObject = this.querySelector("audio") || this.querySelector("video") || this.querySelector("object");
949
+ if (mediaObject) {
950
+ mediaObject.addEventListener("ended", () => {
951
+ this.#value++;
952
+ this.saveResponse(this.value);
953
+ });
954
+ }
955
+ }
956
+ };
957
+
958
+ // ../interactions/position-object-interaction/dist/qti-position-object-interaction.styles.js
959
+ var qti_position_object_interaction_styles_default = [
960
+ boxSizing,
961
+ i`
962
+ :host {
963
+ display: block;
964
+ }
965
+ ::slotted(img) {
966
+ position: absolute;
967
+ cursor: move;
968
+ user-select: none;
969
+ left: 50%;
970
+ transform: translateX(-50%);
971
+ }
972
+ `
973
+ ];
974
+
975
+ // ../interactions/position-object-interaction/dist/qti-position-object-interaction.js
976
+ var QtiPositionObjectInteraction = class extends i2 {
977
+ static {
978
+ this.styles = qti_position_object_interaction_styles_default;
979
+ }
980
+ render() {
981
+ return T`<slot></slot>`;
982
+ }
983
+ };
984
+
985
+ // ../interactions/position-object-interaction/dist/qti-position-object-stage.styles.js
986
+ var qti_position_object_stage_styles_default = [
987
+ boxSizing,
988
+ i`
989
+ :host {
990
+ display: inline-block;
991
+ position: relative;
992
+ }
993
+ `
994
+ ];
995
+
996
+ // ../interactions/position-object-interaction/dist/qti-position-object-stage.js
997
+ var QtiPositionObjectStage = class extends i2 {
998
+ static {
999
+ this.styles = qti_position_object_stage_styles_default;
1000
+ }
1001
+ render() {
1002
+ return T`<slot></slot>`;
1003
+ }
1004
+ constructor() {
1005
+ super();
1006
+ this.removeMoveListener = this.removeMoveListener.bind(this);
1007
+ this.dragElementHandler = this.dragElementHandler.bind(this);
1008
+ }
1009
+ // Define a function to handle the mousemove event on the draggable element
1010
+ dragElementHandler(event) {
1011
+ event.preventDefault();
1012
+ const deltaX = event.clientX - this.startX;
1013
+ const deltaY = event.clientY - this.startY;
1014
+ this.dragElement.style.left = this.dragElement.offsetLeft + deltaX + "px";
1015
+ this.dragElement.style.top = this.dragElement.offsetTop + deltaY + "px";
1016
+ this.startX = event.clientX;
1017
+ this.startY = event.clientY;
1018
+ }
1019
+ firstUpdated(a) {
1020
+ super.firstUpdated(a);
1021
+ this.dragElement = this.querySelector("qti-position-object-interaction>img");
1022
+ this.startX = 0;
1023
+ this.startY = 0;
1024
+ this.dragElement.addEventListener("mousedown", (event) => {
1025
+ this.startX = event.clientX;
1026
+ this.startY = event.clientY;
1027
+ document.addEventListener("mousemove", this.dragElementHandler, true);
1028
+ });
1029
+ document.addEventListener("mouseup", this.removeMoveListener);
1030
+ }
1031
+ removeMoveListener() {
1032
+ document.removeEventListener("mousemove", this.dragElementHandler, true);
1033
+ }
1034
+ disconnectedCallback() {
1035
+ super.disconnectedCallback();
1036
+ document.removeEventListener("mousemove", this.dragElementHandler);
1037
+ document.removeEventListener("mouseup", this.removeMoveListener);
1038
+ }
1039
+ };
1040
+
1041
+ // ../interactions/upload-interaction/dist/qti-upload-interaction.styles.js
1042
+ var qti_upload_interaction_styles_default = [
1043
+ boxSizing,
1044
+ i`
1045
+ :host {
1046
+ display: block;
1047
+ margin: 1em 0;
1048
+ }
1049
+ input[type='file'] {
1050
+ display: block;
1051
+ margin-top: 0.5em;
1052
+ }
1053
+ `
1054
+ ];
1055
+
1056
+ // ../interactions/upload-interaction/dist/qti-upload-interaction.js
1057
+ var QtiUploadInteraction = class extends Interaction {
1058
+ static {
1059
+ this.styles = qti_upload_interaction_styles_default;
1060
+ }
1061
+ #file = null;
1062
+ #base64 = null;
1063
+ reset() {
1064
+ this.#file = null;
1065
+ this.#base64 = null;
1066
+ this.saveResponse(null);
1067
+ }
1068
+ validate() {
1069
+ return this.#base64 !== null;
1070
+ }
1071
+ get response() {
1072
+ return this.#base64;
1073
+ }
1074
+ set response(base64) {
1075
+ if (typeof base64 === "string") {
1076
+ this.#base64 = base64;
1077
+ this.saveResponse(base64);
1078
+ } else if (base64 === null) {
1079
+ this.reset();
1080
+ } else {
1081
+ throw new Error("Value must be a Base64-encoded string or null");
1082
+ }
1083
+ }
1084
+ static get properties() {
1085
+ return {
1086
+ ...Interaction.properties
1087
+ };
1088
+ }
1089
+ render() {
1090
+ return T`
1091
+ <div>
1092
+ <slot name="prompt"></slot>
1093
+ <input type="file" @change="${this.#onFileChange}" ?disabled="${this.disabled}" ?readonly="${this.readonly}" />
1094
+ </div>
1095
+ `;
1096
+ }
1097
+ async #onFileChange(event) {
1098
+ const input = event.target;
1099
+ if (input.files && input.files.length > 0) {
1100
+ this.#file = input.files[0];
1101
+ this.#base64 = await this.#convertToBase64(this.#file);
1102
+ this.saveResponse(this.#base64);
1103
+ this.dispatchEvent(new CustomEvent("qti-interaction-response", {
1104
+ detail: { response: this.#base64 }
1105
+ }));
1106
+ }
1107
+ }
1108
+ #convertToBase64(file) {
1109
+ return new Promise((resolve, reject) => {
1110
+ const reader = new FileReader();
1111
+ reader.onload = () => resolve(reader.result);
1112
+ reader.onerror = () => reject(reader.error);
1113
+ reader.readAsDataURL(file);
1114
+ });
1115
+ }
1116
+ };
1117
+
1118
+ // ../interactions/core/dist/elements/qti-associable-hotspot/elements.js
1119
+ var elements17 = [{ tag: "qti-associable-hotspot", ctor: QtiAssociableHotspot }];
1120
+
1121
+ // ../interactions/core/dist/elements/qti-gap/elements.js
1122
+ var elements18 = [{ tag: "qti-gap", ctor: QtiGap }];
1123
+
1124
+ // ../interactions/core/dist/elements/qti-gap-img/elements.js
1125
+ var elements19 = [{ tag: "qti-gap-img", ctor: QtiGapImg }];
1126
+
1127
+ // ../interactions/core/dist/elements/qti-gap-text/elements.js
1128
+ var elements20 = [{ tag: "qti-gap-text", ctor: QtiGapText }];
1129
+
1130
+ // ../interactions/core/dist/elements/qti-hotspot-choice/elements.js
1131
+ var elements21 = [{ tag: "qti-hotspot-choice", ctor: QtiHotspotChoice }];
1132
+
1133
+ // ../interactions/core/dist/elements/qti-hottext/elements.js
1134
+ var elements22 = [{ tag: "qti-hottext", ctor: QtiHottext }];
1135
+
1136
+ // ../interactions/core/dist/elements/qti-inline-choice/elements.js
1137
+ var elements23 = [{ tag: "qti-inline-choice", ctor: QtiInlineChoice }];
1138
+
1139
+ // ../interactions/core/dist/elements/qti-prompt/elements.js
1140
+ var elements24 = [{ tag: "qti-prompt", ctor: QtiPrompt }];
1141
+
1142
+ // ../interactions/core/dist/elements/qti-simple-associable-choice/elements.js
1143
+ var elements25 = [{ tag: "qti-simple-associable-choice", ctor: QtiSimpleAssociableChoice }];
1144
+
1145
+ // ../interactions/core/dist/elements/qti-simple-choice/elements.js
1146
+ var elements26 = [{ tag: "qti-simple-choice", ctor: QtiSimpleChoice }];
1147
+
1148
+ // ../interactions/core/dist/elements.js
1149
+ var qtiInteractionCoreElements = [
1150
+ ...elements17,
1151
+ ...elements18,
1152
+ ...elements19,
1153
+ ...elements20,
1154
+ ...elements21,
1155
+ ...elements22,
1156
+ ...elements23,
1157
+ ...elements24,
1158
+ ...elements25,
1159
+ ...elements26
1160
+ ];
1161
+
1162
+ // ../interactions/custom-interaction/dist/elements.js
1163
+ var elements27 = [{ tag: "qti-custom-interaction", ctor: QtiCustomInteraction }];
1164
+
1165
+ // ../interactions/end-attempt-interaction/dist/elements.js
1166
+ var elements28 = [{ tag: "qti-end-attempt-interaction", ctor: QtiEndAttemptInteraction }];
1167
+
1168
+ // ../interactions/media-interaction/dist/elements.js
1169
+ var elements29 = [{ tag: "qti-media-interaction", ctor: QtiMediaInteraction }];
1170
+
1171
+ // ../interactions/position-object-interaction/dist/elements.js
1172
+ var elements30 = [
1173
+ { tag: "qti-position-object-interaction", ctor: QtiPositionObjectInteraction },
1174
+ { tag: "qti-position-object-stage", ctor: QtiPositionObjectStage }
1175
+ ];
1176
+
1177
+ // ../interactions/upload-interaction/dist/elements.js
1178
+ var elements31 = [{ tag: "qti-upload-interaction", ctor: QtiUploadInteraction }];
1179
+
1180
+ // ../qti-interactions/dist/elements.js
1181
+ var qtiInteractionElements = [
1182
+ ...qtiInteractionCoreElements,
1183
+ ...elements,
1184
+ ...elements2,
1185
+ ...elements27,
1186
+ ...elements28,
1187
+ ...elements3,
1188
+ ...elements4,
1189
+ ...elements5,
1190
+ ...elements6,
1191
+ ...elements7,
1192
+ ...elements8,
1193
+ ...elements9,
1194
+ ...elements10,
1195
+ ...elements11,
1196
+ ...elements29,
1197
+ ...elements12,
1198
+ ...elements13,
1199
+ ...elements30,
1200
+ ...elements14,
1201
+ ...elements15,
1202
+ ...elements16,
1203
+ ...elements31
1204
+ ];
1205
+
1206
+ // ../qti-interactions/dist/register-all.js
1207
+ for (const { tag, ctor } of qtiInteractionElements) {
1208
+ if (!customElements.get(tag)) {
1209
+ customElements.define(tag, ctor);
1210
+ }
1211
+ }
1212
+
1213
+ // ../interactions/associate-interaction/dist/register.js
1214
+ for (const { tag, ctor } of elements) {
1215
+ if (!customElements.get(tag)) {
1216
+ customElements.define(tag, ctor);
1217
+ }
1218
+ }
1219
+
1220
+ // ../interactions/choice-interaction/dist/register.js
1221
+ for (const { tag, ctor } of elements2) {
1222
+ if (!customElements.get(tag)) {
1223
+ customElements.define(tag, ctor);
1224
+ }
1225
+ }
1226
+
1227
+ // ../interactions/custom-interaction/dist/register.js
1228
+ for (const { tag, ctor } of elements27) {
1229
+ if (!customElements.get(tag)) {
1230
+ customElements.define(tag, ctor);
1231
+ }
1232
+ }
1233
+
1234
+ // ../interactions/end-attempt-interaction/dist/register.js
1235
+ for (const { tag, ctor } of elements28) {
1236
+ if (!customElements.get(tag)) {
1237
+ customElements.define(tag, ctor);
1238
+ }
1239
+ }
1240
+
1241
+ // ../interactions/extended-text-interaction/dist/register.js
1242
+ for (const { tag, ctor } of elements3) {
1243
+ if (!customElements.get(tag)) {
1244
+ customElements.define(tag, ctor);
1245
+ }
1246
+ }
1247
+
1248
+ // ../interactions/gap-match-interaction/dist/register.js
1249
+ for (const { tag, ctor } of elements4) {
1250
+ if (!customElements.get(tag)) {
1251
+ customElements.define(tag, ctor);
1252
+ }
1253
+ }
1254
+
1255
+ // ../interactions/graphic-associate-interaction/dist/register.js
1256
+ for (const { tag, ctor } of elements5) {
1257
+ if (!customElements.get(tag)) {
1258
+ customElements.define(tag, ctor);
1259
+ }
1260
+ }
1261
+
1262
+ // ../interactions/graphic-gap-match-interaction/dist/register.js
1263
+ for (const { tag, ctor } of elements6) {
1264
+ if (!customElements.get(tag)) {
1265
+ customElements.define(tag, ctor);
1266
+ }
1267
+ }
1268
+
1269
+ // ../interactions/graphic-order-interaction/dist/register.js
1270
+ for (const { tag, ctor } of elements7) {
1271
+ if (!customElements.get(tag)) {
1272
+ customElements.define(tag, ctor);
1273
+ }
1274
+ }
1275
+
1276
+ // ../interactions/hotspot-interaction/dist/register.js
1277
+ for (const { tag, ctor } of elements8) {
1278
+ if (!customElements.get(tag)) {
1279
+ customElements.define(tag, ctor);
1280
+ }
1281
+ }
1282
+
1283
+ // ../interactions/hottext-interaction/dist/register.js
1284
+ for (const { tag, ctor } of elements9) {
1285
+ if (!customElements.get(tag)) {
1286
+ customElements.define(tag, ctor);
1287
+ }
1288
+ }
1289
+
1290
+ // ../interactions/inline-choice-interaction/dist/register.js
1291
+ for (const { tag, ctor } of elements10) {
1292
+ if (!customElements.get(tag)) {
1293
+ customElements.define(tag, ctor);
1294
+ }
1295
+ }
1296
+
1297
+ // ../interactions/match-interaction/dist/register.js
1298
+ for (const { tag, ctor } of elements11) {
1299
+ if (!customElements.get(tag)) {
1300
+ customElements.define(tag, ctor);
1301
+ }
1302
+ }
1303
+
1304
+ // ../interactions/media-interaction/dist/register.js
1305
+ for (const { tag, ctor } of elements29) {
1306
+ if (!customElements.get(tag)) {
1307
+ customElements.define(tag, ctor);
1308
+ }
1309
+ }
1310
+
1311
+ // ../interactions/order-interaction/dist/register.js
1312
+ for (const { tag, ctor } of elements12) {
1313
+ if (!customElements.get(tag)) {
1314
+ customElements.define(tag, ctor);
1315
+ }
1316
+ }
1317
+
1318
+ // ../interactions/portable-custom-interaction/dist/register.js
1319
+ for (const { tag, ctor } of elements13) {
1320
+ if (!customElements.get(tag)) {
1321
+ customElements.define(tag, ctor);
1322
+ }
1323
+ }
1324
+
1325
+ // ../interactions/position-object-interaction/dist/register.js
1326
+ for (const { tag, ctor } of elements30) {
1327
+ if (!customElements.get(tag)) {
1328
+ customElements.define(tag, ctor);
1329
+ }
1330
+ }
1331
+
1332
+ // ../interactions/select-point-interaction/dist/register.js
1333
+ for (const { tag, ctor } of elements14) {
1334
+ if (!customElements.get(tag)) {
1335
+ customElements.define(tag, ctor);
1336
+ }
1337
+ }
1338
+
1339
+ // ../interactions/slider-interaction/dist/register.js
1340
+ for (const { tag, ctor } of elements15) {
1341
+ if (!customElements.get(tag)) {
1342
+ customElements.define(tag, ctor);
1343
+ }
1344
+ }
1345
+
1346
+ // ../interactions/text-entry-interaction/dist/register.js
1347
+ for (const { tag, ctor } of elements16) {
1348
+ if (!customElements.get(tag)) {
1349
+ customElements.define(tag, ctor);
1350
+ }
1351
+ }
1352
+
1353
+ // ../interactions/upload-interaction/dist/register.js
1354
+ for (const { tag, ctor } of elements31) {
1355
+ if (!customElements.get(tag)) {
1356
+ customElements.define(tag, ctor);
1357
+ }
1358
+ }
1359
+
1360
+ export {
1361
+ QtiAssociableHotspot,
1362
+ QtiGap,
1363
+ QtiGapImg,
1364
+ QtiInlineChoice,
1365
+ QtiPrompt,
1366
+ QtiCustomInteraction,
1367
+ QtiEndAttemptInteraction,
1368
+ QtiMediaInteraction,
1369
+ QtiPositionObjectInteraction,
1370
+ QtiPositionObjectStage,
1371
+ QtiUploadInteraction,
1372
+ qtiInteractionElements
1373
+ };
1374
+ //# sourceMappingURL=chunk-5QYII4OZ.js.map