@citolab/qti-components 7.16.0 → 7.17.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 (64) hide show
  1. package/dist/base.d.ts +48 -8
  2. package/dist/base.js +49 -2
  3. package/dist/base.js.map +1 -1
  4. package/dist/chunk-2DOYPVF5.js +481 -0
  5. package/dist/chunk-2DOYPVF5.js.map +1 -0
  6. package/dist/chunk-2ZEJ3RR5.js +89 -0
  7. package/dist/chunk-2ZEJ3RR5.js.map +1 -0
  8. package/dist/chunk-352OTVTY.js +3330 -0
  9. package/dist/chunk-352OTVTY.js.map +1 -0
  10. package/dist/chunk-C2HQFI2C.js +5927 -0
  11. package/dist/chunk-C2HQFI2C.js.map +1 -0
  12. package/dist/chunk-DWIRLYDS.js +20 -0
  13. package/dist/chunk-DWIRLYDS.js.map +1 -0
  14. package/dist/chunk-EUXUH3YW.js +15 -0
  15. package/dist/chunk-EUXUH3YW.js.map +1 -0
  16. package/dist/chunk-F44CI35W.js +145 -0
  17. package/dist/chunk-F44CI35W.js.map +1 -0
  18. package/dist/chunk-INKI27D5.js +493 -0
  19. package/dist/chunk-INKI27D5.js.map +1 -0
  20. package/dist/chunk-JEUY3MYB.js +2010 -0
  21. package/dist/chunk-JEUY3MYB.js.map +1 -0
  22. package/dist/chunk-O4XIWHTF.js +1139 -0
  23. package/dist/chunk-O4XIWHTF.js.map +1 -0
  24. package/dist/chunk-RI47B4ZT.js +1753 -0
  25. package/dist/chunk-RI47B4ZT.js.map +1 -0
  26. package/dist/chunk-VEV4DGPH.js +31 -0
  27. package/dist/chunk-VEV4DGPH.js.map +1 -0
  28. package/dist/chunk-W4SQRNWO.js +3844 -0
  29. package/dist/chunk-W4SQRNWO.js.map +1 -0
  30. package/dist/computed-item.context-CiddHLPz.d.ts +22 -0
  31. package/dist/computed.context-CH09_LCR.d.ts +45 -0
  32. package/dist/config.context-DAdkDDf5.d.ts +14 -0
  33. package/dist/elements.d.ts +318 -1
  34. package/dist/elements.js +41 -2
  35. package/dist/elements.js.map +1 -1
  36. package/dist/index.d.ts +21 -8
  37. package/dist/index.js +327 -9
  38. package/dist/index.js.map +1 -1
  39. package/dist/interaction-C5Up6-68.d.ts +56 -0
  40. package/dist/interactions.d.ts +913 -1
  41. package/dist/interactions.js +71 -2
  42. package/dist/interactions.js.map +1 -1
  43. package/dist/item.context-BRKXBC3m.d.ts +10 -0
  44. package/dist/item.d.ts +147 -1
  45. package/dist/item.js +22 -2
  46. package/dist/item.js.map +1 -1
  47. package/dist/loader.d.ts +21 -1
  48. package/dist/loader.js +10 -2
  49. package/dist/loader.js.map +1 -1
  50. package/dist/processing.d.ts +393 -1
  51. package/dist/processing.js +102 -2
  52. package/dist/processing.js.map +1 -1
  53. package/dist/qti-feedback-B4cMzOcq.d.ts +21 -0
  54. package/dist/qti-rule-base-dL4opfvi.d.ts +39 -0
  55. package/dist/qti-transform-test-Bz9A3hmD.d.ts +63 -0
  56. package/dist/test.context-Bpw1HNAZ.d.ts +28 -0
  57. package/dist/test.d.ts +569 -1
  58. package/dist/test.js +60 -2
  59. package/dist/test.js.map +1 -1
  60. package/dist/transformers.d.ts +18 -1
  61. package/dist/transformers.js +11 -2
  62. package/dist/transformers.js.map +1 -1
  63. package/dist/variables-CusMRnyJ.d.ts +69 -0
  64. package/package.json +8 -14
@@ -0,0 +1,3330 @@
1
+ import {
2
+ QtiModalFeedback
3
+ } from "./chunk-O4XIWHTF.js";
4
+ import {
5
+ item_default,
6
+ m
7
+ } from "./chunk-W4SQRNWO.js";
8
+ import {
9
+ watch
10
+ } from "./chunk-2ZEJ3RR5.js";
11
+ import {
12
+ qtiTransformItem,
13
+ qtiTransformTest
14
+ } from "./chunk-INKI27D5.js";
15
+ import {
16
+ E,
17
+ INITIAL_SESSION_CONTEXT,
18
+ INITIAL_TEST_CONTEXT,
19
+ W,
20
+ c,
21
+ computedContext,
22
+ configContext,
23
+ e,
24
+ i,
25
+ i2,
26
+ n,
27
+ qtiContext,
28
+ r,
29
+ sessionContext,
30
+ t,
31
+ testContext,
32
+ w,
33
+ x
34
+ } from "./chunk-JEUY3MYB.js";
35
+ import {
36
+ __decorateClass
37
+ } from "./chunk-EUXUH3YW.js";
38
+
39
+ // ../qti-test/src/mixins/test-navigation.mixin.ts
40
+ var TestNavigationMixin = (superClass) => {
41
+ class TestNavigationClass extends superClass {
42
+ constructor(...args) {
43
+ super(...args);
44
+ this.navigate = null;
45
+ this.cacheTransform = false;
46
+ this.requestTimeout = 3e4;
47
+ this.postLoadTransformCallback = null;
48
+ this.postLoadTestTransformCallback = null;
49
+ // Navigation state tracking
50
+ this._activeController = null;
51
+ this._loadResults = [];
52
+ // Simple loading progress tracking
53
+ this._loadingState = {
54
+ expectedItems: 0,
55
+ connectedItems: 0,
56
+ expectedStimulus: 0,
57
+ loadedStimulus: 0,
58
+ isComplete: false
59
+ };
60
+ // Track loaded/loading stimulus hrefs to prevent duplicates
61
+ this._loadedStimulusHrefs = /* @__PURE__ */ new Set();
62
+ this._loadingStimulusHrefs = /* @__PURE__ */ new Set();
63
+ this._bindEventHandlers();
64
+ }
65
+ // ===========================================
66
+ // PUBLIC API
67
+ // ===========================================
68
+ /**
69
+ * Navigate to a specific item or section
70
+ * @param type - Navigation type ('item' or 'section')
71
+ * @param id - Target identifier (optional, falls back to first available)
72
+ */
73
+ navigateTo(type, id) {
74
+ const targetId = id || this._getDefaultNavigationId(type);
75
+ if (targetId) {
76
+ this.dispatchEvent(
77
+ new CustomEvent("qti-request-navigation", {
78
+ detail: { type, id: targetId },
79
+ bubbles: true,
80
+ composed: true
81
+ })
82
+ );
83
+ }
84
+ }
85
+ // ===========================================
86
+ // EVENT HANDLER SETUP
87
+ // ===========================================
88
+ _bindEventHandlers() {
89
+ this.addEventListener("qti-request-navigation", this._handleNavigationRequest.bind(this));
90
+ this.addEventListener("qti-assessment-test-connected", this._handleTestConnected.bind(this));
91
+ this.addEventListener("qti-assessment-item-connected", this._handleItemConnected.bind(this));
92
+ this.addEventListener("qti-assessment-stimulus-ref-connected", this._handleStimulusRefConnected.bind(this));
93
+ }
94
+ _handleTestConnected(e2) {
95
+ this._testElement = e2.detail;
96
+ this._initializeNavigation();
97
+ }
98
+ /**
99
+ * Handle item connection events - track connected items and discover stimulus references
100
+ */
101
+ _handleItemConnected(e2) {
102
+ const itemRef = e2.detail;
103
+ this._loadingState.connectedItems++;
104
+ const stimulusRefs = itemRef.querySelectorAll("qti-assessment-stimulus-ref");
105
+ this._loadingState.expectedStimulus += stimulusRefs.length;
106
+ this._checkLoadingComplete();
107
+ }
108
+ /**
109
+ * Handle stimulus reference connection events with duplicate prevention
110
+ */
111
+ async _handleStimulusRefConnected(e2) {
112
+ e2.preventDefault();
113
+ const { element, item } = e2;
114
+ console.info("Stimulus ref connected:", {
115
+ identifier: element.identifier,
116
+ href: element.href,
117
+ item: item.identifier
118
+ });
119
+ if (this._loadedStimulusHrefs.has(element.href)) {
120
+ console.info("Stimulus already loaded, skipping:", element.href);
121
+ this._loadingState.loadedStimulus++;
122
+ this._checkLoadingComplete();
123
+ return;
124
+ }
125
+ if (this._loadingStimulusHrefs.has(element.href)) {
126
+ console.info("Stimulus already loading, skipping duplicate:", element.href);
127
+ this._loadingState.loadedStimulus++;
128
+ this._checkLoadingComplete();
129
+ return;
130
+ }
131
+ this._loadingStimulusHrefs.add(element.href);
132
+ console.info("Starting stimulus load:", element.href);
133
+ try {
134
+ await this._loadStimulusRef(element, item);
135
+ this._loadedStimulusHrefs.add(element.href);
136
+ this._loadingState.loadedStimulus++;
137
+ console.info("Stimulus loaded successfully:", element.href);
138
+ this._checkLoadingComplete();
139
+ } catch (error) {
140
+ if (error.name !== "AbortError") {
141
+ console.warn(`Failed to load stimulus ${element.identifier}:`, error);
142
+ }
143
+ this._loadingState.loadedStimulus++;
144
+ this._checkLoadingComplete();
145
+ } finally {
146
+ this._loadingStimulusHrefs.delete(element.href);
147
+ }
148
+ }
149
+ // ===========================================
150
+ // NAVIGATION FLOW
151
+ // ===========================================
152
+ _getDefaultNavigationId(type) {
153
+ if (type === "section") {
154
+ return this._testElement?.querySelector("qti-assessment-section")?.identifier;
155
+ }
156
+ return this.sessionContext?.navItemRefId ?? this._testElement?.querySelector("qti-assessment-item-ref")?.identifier;
157
+ }
158
+ _initializeNavigation() {
159
+ if (!this.navigate) return;
160
+ const id = this._getDefaultNavigationId(this.navigate);
161
+ if (id) {
162
+ this.navigateTo(this.navigate, id);
163
+ }
164
+ }
165
+ /**
166
+ * Main navigation request handler with proper lifecycle management
167
+ */
168
+ async _handleNavigationRequest({ detail }) {
169
+ if (!detail?.id) return;
170
+ this._cancelPreviousNavigation();
171
+ try {
172
+ this._dispatchLoadingStarted(detail.type, detail.id);
173
+ this._activeController = new AbortController();
174
+ await this._executeNavigation(detail.type, detail.id);
175
+ } catch (error) {
176
+ this._handleNavigationError(error, detail.type, detail.id);
177
+ } finally {
178
+ this._activeController = null;
179
+ this._dispatchLoadingEnded(detail.type, detail.id);
180
+ }
181
+ }
182
+ _handleNavigationError(error, type, id) {
183
+ if (error.name !== "AbortError") {
184
+ this._dispatchError(this._createNavigationError(error, type, id));
185
+ }
186
+ }
187
+ async _executeNavigation(type, id) {
188
+ if (type === "item") {
189
+ await this._navigateToItem(id);
190
+ } else {
191
+ await this._navigateToSection(id);
192
+ }
193
+ }
194
+ /**
195
+ * Navigate to specific item with simple state tracking
196
+ */
197
+ async _navigateToItem(itemId) {
198
+ const itemRef = this._findItemRef(itemId);
199
+ this._updateSessionContext(itemRef, itemId);
200
+ this._resetLoadingState();
201
+ this._loadingState.expectedItems = 1;
202
+ await this._loadItems([itemId]);
203
+ await this._waitForLoadingComplete();
204
+ this._dispatchTestLoaded(this._loadResults);
205
+ }
206
+ /**
207
+ * Navigate to section with simple state tracking
208
+ */
209
+ async _navigateToSection(sectionId) {
210
+ const sectionEl = this._findSection(sectionId);
211
+ const navPartId = sectionEl?.closest("qti-test-part")?.identifier;
212
+ this.sessionContext = {
213
+ ...this.sessionContext,
214
+ navPartId,
215
+ navSectionId: sectionId,
216
+ navItemRefId: null
217
+ };
218
+ const itemIds = this._getSectionItemIds(sectionId);
219
+ this._resetLoadingState();
220
+ this._loadingState.expectedItems = itemIds.length;
221
+ await this._loadItems(itemIds);
222
+ await this._waitForLoadingComplete();
223
+ this._dispatchTestLoaded(this._loadResults);
224
+ }
225
+ // ===========================================
226
+ // LOADING STATE MANAGEMENT
227
+ // ===========================================
228
+ /**
229
+ * Reset loading state for new navigation
230
+ */
231
+ _resetLoadingState() {
232
+ this._loadingState = {
233
+ expectedItems: 0,
234
+ connectedItems: 0,
235
+ expectedStimulus: 0,
236
+ loadedStimulus: 0,
237
+ isComplete: false
238
+ };
239
+ this._loadedStimulusHrefs.clear();
240
+ this._loadingStimulusHrefs.clear();
241
+ }
242
+ /**
243
+ * Check if loading is complete and dispatch events accordingly
244
+ */
245
+ _checkLoadingComplete() {
246
+ const allItemsConnected = this._loadingState.connectedItems >= this._loadingState.expectedItems;
247
+ const allStimulusLoaded = this._loadingState.loadedStimulus >= this._loadingState.expectedStimulus;
248
+ if (allItemsConnected && allStimulusLoaded && !this._loadingState.isComplete) {
249
+ this._loadingState.isComplete = true;
250
+ }
251
+ }
252
+ /**
253
+ * Wait for loading to complete with simple polling
254
+ */
255
+ async _waitForLoadingComplete() {
256
+ while (!this._loadingState.isComplete) {
257
+ await new Promise((resolve) => setTimeout(resolve, 10));
258
+ }
259
+ }
260
+ /**
261
+ * Get current loading progress for external consumption
262
+ */
263
+ getLoadingProgress() {
264
+ return { ...this._loadingState };
265
+ }
266
+ /**
267
+ * Load stimulus reference with simple tracking
268
+ */
269
+ async _loadStimulusRef(element, item) {
270
+ console.info("Loading stimulus:", element.href);
271
+ const stimulus = await this._loadStimulus(element.href);
272
+ console.info("Stimulus loaded, applying content:", stimulus ? "has content" : "no content");
273
+ this._applyStimulusContent(stimulus, element, item);
274
+ }
275
+ _applyStimulusContent(stimulus, element, item) {
276
+ if (!stimulus) {
277
+ console.warn("No stimulus content to apply");
278
+ return;
279
+ }
280
+ const elements = stimulus.querySelectorAll("qti-stimulus-body, qti-stylesheet");
281
+ console.info(`Found ${elements.length} stimulus elements to apply for ${element.identifier}`);
282
+ if (elements.length === 0) {
283
+ console.warn("No qti-stimulus-body or qti-stylesheet elements found in stimulus");
284
+ return;
285
+ }
286
+ let targets = [];
287
+ const specificTarget = document.querySelector(
288
+ `qti-assessment-item[identifier="${item.identifier}"] [data-stimulus-idref="${element.identifier}"]`
289
+ );
290
+ if (specificTarget) {
291
+ targets.push(specificTarget);
292
+ console.info("Found specific target:", specificTarget);
293
+ } else {
294
+ const allTargetsWithId = Array.from(this.querySelectorAll(`[data-stimulus-idref="${element.identifier}"]`));
295
+ if (allTargetsWithId.length > 0) {
296
+ targets = allTargetsWithId;
297
+ console.info("Found targets by identifier:", allTargetsWithId.length);
298
+ } else {
299
+ const allStimulusTargets = Array.from(this.querySelectorAll("[data-stimulus-idref]"));
300
+ targets = allStimulusTargets;
301
+ console.info("Using fallback targets:", allStimulusTargets.length);
302
+ }
303
+ }
304
+ if (targets.length === 0) {
305
+ console.warn("No targets found for stimulus content");
306
+ return;
307
+ }
308
+ targets.forEach((target, index) => {
309
+ target.innerHTML = "";
310
+ const clonedElements = Array.from(elements).map((el) => el.cloneNode(true));
311
+ target.append(...clonedElements);
312
+ console.info(`Applied stimulus content to target ${index + 1}/${targets.length}`);
313
+ });
314
+ }
315
+ // ===========================================
316
+ // LOADING INFRASTRUCTURE
317
+ // ===========================================
318
+ /**
319
+ * Cancel previous navigation and clean up all state
320
+ */
321
+ _cancelPreviousNavigation() {
322
+ if (this._activeController) {
323
+ this._activeController.abort();
324
+ console.info("Previous navigation request cancelled");
325
+ }
326
+ this._clearNavigationState();
327
+ }
328
+ _clearNavigationState() {
329
+ this._clearLoadedItems();
330
+ this._resetLoadingState();
331
+ this._loadResults = [];
332
+ }
333
+ /**
334
+ * Load items with simple tracking
335
+ */
336
+ async _loadItems(itemIds) {
337
+ if (!this._testElement || itemIds.length === 0) return;
338
+ const itemRefs = itemIds.map((id) => this._findItemRef(id));
339
+ this._clearLoadedItems();
340
+ this._clearStimulusRef();
341
+ const results = await Promise.all(itemRefs.map((ref) => this._loadSingleItem(ref)));
342
+ const validResults = results.filter(Boolean);
343
+ validResults.forEach(({ itemRef, doc }) => {
344
+ if (itemRef && doc) itemRef.xmlDoc = doc;
345
+ });
346
+ this._loadResults = validResults;
347
+ }
348
+ async _loadSingleItem(itemRef) {
349
+ try {
350
+ let transformer = await qtiTransformItem(this.cacheTransform).load(
351
+ itemRef.href,
352
+ this._activeController?.signal
353
+ );
354
+ if (this.postLoadTransformCallback) {
355
+ transformer = await this.postLoadTransformCallback(transformer, itemRef);
356
+ }
357
+ return { itemRef, doc: transformer.htmlDoc() };
358
+ } catch (error) {
359
+ if (error.name === "AbortError") {
360
+ console.info(`Item load for ${itemRef.identifier} was aborted`);
361
+ throw error;
362
+ }
363
+ console.warn(`Failed to load item ${itemRef.identifier}:`, error);
364
+ return null;
365
+ }
366
+ }
367
+ async _loadStimulus(href) {
368
+ const transformer = await qtiTransformItem().load(href, this._activeController?.signal);
369
+ return transformer.htmlDoc();
370
+ }
371
+ // ===========================================
372
+ // UTILITIES
373
+ // ===========================================
374
+ _findItemRef(itemId) {
375
+ const itemRef = this._testElement?.querySelector(
376
+ `qti-assessment-item-ref[identifier="${itemId}"]`
377
+ );
378
+ if (!itemRef) {
379
+ throw new Error(`Item with identifier "${itemId}" not found`);
380
+ }
381
+ return itemRef;
382
+ }
383
+ _findSection(sectionId) {
384
+ return this._testElement?.querySelector(`qti-assessment-section[identifier="${sectionId}"]`) || null;
385
+ }
386
+ _updateSessionContext(itemRef, itemId) {
387
+ const navPartId = itemRef.closest("qti-test-part")?.identifier;
388
+ const navSectionId = itemRef.closest("qti-assessment-section")?.identifier;
389
+ this.sessionContext = {
390
+ ...this.sessionContext,
391
+ navPartId,
392
+ navSectionId,
393
+ navItemRefId: itemId
394
+ };
395
+ }
396
+ _getSectionItemIds(sectionId) {
397
+ return Array.from(
398
+ this._testElement.querySelectorAll(
399
+ `qti-assessment-section[identifier="${sectionId}"] > qti-assessment-item-ref`
400
+ )
401
+ ).map((ref) => ref.identifier).filter(Boolean);
402
+ }
403
+ _clearLoadedItems() {
404
+ const itemRefs = this._testElement?.querySelectorAll("qti-assessment-item-ref");
405
+ Array.from(itemRefs || []).forEach((element) => {
406
+ element.xmlDoc = null;
407
+ });
408
+ }
409
+ _clearStimulusRef() {
410
+ this.querySelectorAll("[data-stimulus-idref]").forEach((el) => el.innerHTML = "");
411
+ }
412
+ _createNavigationError(error, type, id) {
413
+ return {
414
+ message: error.message || `Failed to load ${type}: ${id}`,
415
+ details: error,
416
+ itemId: type === "item" ? id : void 0,
417
+ sectionId: type === "section" ? id : void 0
418
+ };
419
+ }
420
+ // ===========================================
421
+ // EVENT DISPATCHING
422
+ // ===========================================
423
+ _dispatchLoadingStarted(type, id) {
424
+ this.dispatchEvent(
425
+ new CustomEvent("qti-navigation-loading-started", {
426
+ detail: { type, id },
427
+ bubbles: true,
428
+ composed: true
429
+ })
430
+ );
431
+ }
432
+ _dispatchLoadingEnded(type, id) {
433
+ this.dispatchEvent(
434
+ new CustomEvent("qti-navigation-loading-ended", {
435
+ detail: { type, id },
436
+ bubbles: true,
437
+ composed: true
438
+ })
439
+ );
440
+ }
441
+ _dispatchError(error) {
442
+ this.dispatchEvent(
443
+ new CustomEvent("qti-navigation-error", {
444
+ detail: error,
445
+ bubbles: true,
446
+ composed: true
447
+ })
448
+ );
449
+ }
450
+ _dispatchTestLoaded(results) {
451
+ requestAnimationFrame(() => {
452
+ this.dispatchEvent(
453
+ new CustomEvent("qti-test-loaded", {
454
+ detail: results.map(({ itemRef }) => ({
455
+ identifier: itemRef?.identifier,
456
+ element: itemRef
457
+ })),
458
+ bubbles: true,
459
+ composed: true
460
+ })
461
+ );
462
+ });
463
+ }
464
+ }
465
+ __decorateClass([
466
+ n({ type: String })
467
+ ], TestNavigationClass.prototype, "navigate", 2);
468
+ __decorateClass([
469
+ n({ type: Boolean, attribute: "cache-transform" })
470
+ ], TestNavigationClass.prototype, "cacheTransform", 2);
471
+ __decorateClass([
472
+ n({ type: Number })
473
+ ], TestNavigationClass.prototype, "requestTimeout", 2);
474
+ __decorateClass([
475
+ n({ attribute: false })
476
+ ], TestNavigationClass.prototype, "postLoadTransformCallback", 2);
477
+ __decorateClass([
478
+ n({ attribute: false })
479
+ ], TestNavigationClass.prototype, "postLoadTestTransformCallback", 2);
480
+ return TestNavigationClass;
481
+ };
482
+
483
+ // ../qti-test/src/mixins/test-view.mixin.ts
484
+ var TestViewMixin = (superClass) => {
485
+ class TestViewClass extends superClass {
486
+ constructor(...args) {
487
+ super(...args);
488
+ this.sessionContext = { ...this.sessionContext, view: "candidate" };
489
+ this.addEventListener("on-test-switch-view", (e2) => {
490
+ this.sessionContext = { ...this.sessionContext, view: e2.detail };
491
+ this._updateElementView();
492
+ });
493
+ this.addEventListener("qti-assessment-test-connected", () => {
494
+ this._updateElementView();
495
+ });
496
+ this.addEventListener("qti-assessment-item-connected", (e2) => {
497
+ this._updateElementView();
498
+ this._setCorrectResponseVisibility(e2.detail);
499
+ });
500
+ }
501
+ willUpdate(changedProperties) {
502
+ super.willUpdate(changedProperties);
503
+ if (changedProperties.has("sessionContext")) {
504
+ this._updateElementView();
505
+ }
506
+ }
507
+ // Method to handle view updates for elements based on the current context view
508
+ _updateElementView() {
509
+ if (this._testElement) {
510
+ const viewElements = Array.from(this._testElement.querySelectorAll("[view]"));
511
+ viewElements.forEach((element) => {
512
+ element.classList.toggle("show", element.getAttribute("view") === this.sessionContext.view);
513
+ });
514
+ const assessmentItemRef = this._testElement.querySelector(
515
+ `qti-assessment-item-ref[identifier="${this.sessionContext.navItemRefId}"]`
516
+ );
517
+ const assessmentItem = assessmentItemRef?.assessmentItem;
518
+ if (assessmentItem) {
519
+ assessmentItem.showCorrectResponse(this.sessionContext.view === "scorer");
520
+ }
521
+ }
522
+ }
523
+ // Event handler for connected QTI assessment items
524
+ _setCorrectResponseVisibility(assessmentItem) {
525
+ assessmentItem.showCorrectResponse(this.sessionContext.view === "scorer");
526
+ }
527
+ }
528
+ return TestViewClass;
529
+ };
530
+
531
+ // ../qti-test/src/mixins/test-base.ts
532
+ var TestBaseMixin = (superClass) => {
533
+ class TestBaseClass extends superClass {
534
+ constructor(...args) {
535
+ super(...args);
536
+ this.testContext = INITIAL_TEST_CONTEXT;
537
+ this.sessionContext = INITIAL_SESSION_CONTEXT;
538
+ /**
539
+ * Updates the variables of an assessment item in the test context.
540
+ * - Matches the assessment item with the corresponding test context item.
541
+ * - If the item is not found, logs a warning.
542
+ * - Updates variables in the test context if exactly one variable exists.
543
+ * - Otherwise, syncs the assessment item's variables with the test context.
544
+ *
545
+ * @param assessmentItem - The assessment item to update.
546
+ */
547
+ this._updateItemInTestContext = (assessmentItem) => {
548
+ const context = assessmentItem._context;
549
+ const identifier = context.identifier;
550
+ const fullVariables = context.variables;
551
+ const itemContext = this.testContext.items.find((i4) => i4?.identifier === identifier);
552
+ if (!itemContext) {
553
+ console.warn(`Item IDs between assessment.xml and item.xml should match: ${identifier} is not found!`);
554
+ return;
555
+ }
556
+ if (itemContext.variables?.length === 1) {
557
+ this._updateItemVariablesInTestContext(identifier, fullVariables);
558
+ } else {
559
+ const newVariables = [...assessmentItem.variables];
560
+ for (const variable of itemContext.variables) {
561
+ const currentVariable = newVariables.find((v) => v.identifier === variable.identifier);
562
+ if (currentVariable) {
563
+ currentVariable.value = variable.value;
564
+ } else {
565
+ newVariables.push(variable);
566
+ }
567
+ }
568
+ assessmentItem.variables = newVariables;
569
+ }
570
+ };
571
+ this._initializeEventListeners();
572
+ }
573
+ updateItemVariables(itemRefID, variables) {
574
+ const itemContext = this.testContext.items.find((item) => item.identifier === itemRefID);
575
+ if (itemContext) {
576
+ itemContext.variables = itemContext.variables.map((variable) => {
577
+ const updatedVariable = variables.find((v) => v.identifier === variable.identifier);
578
+ return updatedVariable ? { ...variable, ...updatedVariable } : variable;
579
+ });
580
+ }
581
+ const itemRef = this._testElement.querySelector(
582
+ `qti-assessment-item-ref[identifier="${itemRefID}"]`
583
+ );
584
+ if (itemRef && itemRef.assessmentItem) {
585
+ itemRef.assessmentItem.variables = variables;
586
+ }
587
+ }
588
+ _initializeEventListeners() {
589
+ this.addEventListener("qti-assessment-test-connected", (e2) => {
590
+ this.testContext = INITIAL_TEST_CONTEXT;
591
+ this.sessionContext = INITIAL_SESSION_CONTEXT;
592
+ if (this.testContext && this.testContext.items.length > 0) return;
593
+ this._testElement = e2.detail;
594
+ const items = Array.from(this._testElement.querySelectorAll("qti-assessment-item-ref")).map((itemRef) => {
595
+ return {
596
+ href: itemRef.href,
597
+ identifier: itemRef.identifier,
598
+ category: itemRef.category,
599
+ variables: [
600
+ {
601
+ identifier: "completionStatus",
602
+ value: "not_attempted",
603
+ type: "outcome"
604
+ }
605
+ ]
606
+ };
607
+ });
608
+ this.testContext = { ...this.testContext, items };
609
+ });
610
+ this.addEventListener("qti-assessment-item-connected", (e2) => {
611
+ const assessmentItem = e2.detail;
612
+ const assessmentRefId = assessmentItem.closest("qti-assessment-item-ref")?.identifier;
613
+ if (assessmentRefId) {
614
+ assessmentItem.assessmentItemRefId = assessmentRefId;
615
+ }
616
+ this._updateItemInTestContext(e2.detail);
617
+ });
618
+ this.addEventListener("qti-item-context-updated", (e2) => {
619
+ this._updateItemVariablesInTestContext(e2.detail.itemContext.identifier, e2.detail.itemContext.variables);
620
+ });
621
+ }
622
+ _updateItemVariablesInTestContext(identifier, variables) {
623
+ this.testContext = {
624
+ ...this.testContext,
625
+ // Spread existing test context properties
626
+ items: this.testContext.items.map((itemContext) => {
627
+ if (itemContext.identifier !== identifier) {
628
+ return itemContext;
629
+ }
630
+ return {
631
+ ...itemContext,
632
+ // Keep other properties of the item context
633
+ variables: variables.map((variable) => {
634
+ const matchingVariable = itemContext.variables.find((v) => v.identifier === variable.identifier);
635
+ return matchingVariable ? { ...matchingVariable, ...variable } : variable;
636
+ })
637
+ };
638
+ })
639
+ };
640
+ this.dispatchEvent(
641
+ new CustomEvent("qti-test-context-updated", { detail: this.testContext, bubbles: false, composed: false })
642
+ );
643
+ }
644
+ }
645
+ __decorateClass([
646
+ n({ attribute: false, type: Object }),
647
+ e({ context: testContext })
648
+ ], TestBaseClass.prototype, "testContext", 2);
649
+ __decorateClass([
650
+ n({ attribute: false, type: Object }),
651
+ e({ context: sessionContext })
652
+ ], TestBaseClass.prototype, "sessionContext", 2);
653
+ return TestBaseClass;
654
+ };
655
+ var TestBase = class extends TestBaseMixin(i2) {
656
+ };
657
+
658
+ // ../qti-test/src/mixins/test-processing.mixin.ts
659
+ var TestProcessingMixin = (superClass) => {
660
+ class TestProcessingElement extends superClass {
661
+ constructor(...args) {
662
+ super(...args);
663
+ this.addEventListener("qti-register-variable", (e2) => {
664
+ this.testContext = {
665
+ ...this.testContext,
666
+ testOutcomeVariables: [...this.testContext.testOutcomeVariables || [], e2.detail.variable]
667
+ };
668
+ e2.stopPropagation();
669
+ });
670
+ this.addEventListener(
671
+ "qti-set-outcome-value",
672
+ (e2) => {
673
+ const { outcomeIdentifier, value } = e2.detail;
674
+ this.updateOutcomeVariable(outcomeIdentifier, value);
675
+ e2.stopPropagation();
676
+ }
677
+ );
678
+ }
679
+ outcomeProcessing() {
680
+ const outcomeProcessor = this.querySelector("qti-outcome-processing");
681
+ if (!outcomeProcessor) return false;
682
+ outcomeProcessor?.process();
683
+ return true;
684
+ }
685
+ /* --------------------------- ENABLED WHEN UNIT TESTING OUTCOME PROCESSING ------------------------------------ */
686
+ updateOutcomeVariable(identifier, value) {
687
+ const outcomeVariable = this.getOutcome(identifier);
688
+ if (!outcomeVariable) {
689
+ console.warn(`Can not set qti-outcome-identifier: ${identifier}, it is not available`);
690
+ return;
691
+ }
692
+ this.testContext = {
693
+ ...this.testContext,
694
+ testOutcomeVariables: this.testContext.testOutcomeVariables?.map((v) => {
695
+ if (v.identifier !== identifier) {
696
+ return v;
697
+ }
698
+ return {
699
+ ...v,
700
+ value: outcomeVariable.cardinality === "single" ? value : [...v.value, value]
701
+ };
702
+ })
703
+ };
704
+ }
705
+ getOutcome(identifier) {
706
+ return this.getVariable(identifier);
707
+ }
708
+ getVariable(identifier) {
709
+ return this.testContext.testOutcomeVariables?.find((v) => v.identifier === identifier) || null;
710
+ }
711
+ /* --------------------------- ENABLED WHEN UNIT TESTING OUTCOME PROCESSING ------------------------------------ */
712
+ }
713
+ return TestProcessingElement;
714
+ };
715
+
716
+ // ../qti-test/src/components/qti-test/qti-test.ts
717
+ var QtiTest = class extends TestNavigationMixin(TestViewMixin(TestProcessingMixin(TestBaseMixin(i2)))) {
718
+ // TODO: Properly implement IQtiTest interface
719
+ // export class QtiTest extends TestLoaderMixin(TestNavigationMixin(TestViewMixin(TestBase))) {
720
+ /**
721
+ * Renders the component's template.
722
+ * Provides a default `<slot>` for content projection.
723
+ */
724
+ async connectedCallback() {
725
+ super.connectedCallback();
726
+ await this.updateComplete;
727
+ this.dispatchEvent(new CustomEvent("qti-test-connected", { detail: this }));
728
+ }
729
+ render() {
730
+ return x`<slot></slot>`;
731
+ }
732
+ };
733
+ QtiTest = __decorateClass([
734
+ t("qti-test")
735
+ ], QtiTest);
736
+
737
+ // ../qti-test/src/components/qti-assessment-item-ref/qti-assessment-item-ref.ts
738
+ var stringToBooleanConverter = {
739
+ fromAttribute(value) {
740
+ return value === "true";
741
+ },
742
+ toAttribute(value) {
743
+ return value ? "true" : "false";
744
+ }
745
+ };
746
+ var QtiAssessmentItemRef = class extends i2 {
747
+ constructor() {
748
+ super(...arguments);
749
+ // @consume({ context: computedContext, subscribe: true })
750
+ // private computedContext: ComputedContext;
751
+ this.weigths = /* @__PURE__ */ new Map();
752
+ }
753
+ // the XMLDocument
754
+ createRenderRoot() {
755
+ return this;
756
+ }
757
+ get assessmentItem() {
758
+ return this.renderRoot?.querySelector("qti-assessment-item");
759
+ }
760
+ async connectedCallback() {
761
+ super.connectedCallback();
762
+ await this.updateComplete;
763
+ this.dispatchEvent(
764
+ new CustomEvent("qti-assessment-item-ref-connected", {
765
+ bubbles: true,
766
+ composed: true,
767
+ detail: { identifier: this.identifier, href: this.href, category: this.category }
768
+ })
769
+ );
770
+ }
771
+ render() {
772
+ return this.myTemplate ? this.myTemplate({ xmlDoc: this.xmlDoc }) : this.xmlDoc;
773
+ }
774
+ };
775
+ __decorateClass([
776
+ n({ type: String })
777
+ ], QtiAssessmentItemRef.prototype, "category", 2);
778
+ __decorateClass([
779
+ n({ type: String })
780
+ ], QtiAssessmentItemRef.prototype, "identifier", 2);
781
+ __decorateClass([
782
+ n({ type: Boolean, converter: stringToBooleanConverter })
783
+ ], QtiAssessmentItemRef.prototype, "required", 2);
784
+ __decorateClass([
785
+ n({ type: Boolean, converter: stringToBooleanConverter })
786
+ ], QtiAssessmentItemRef.prototype, "fixed", 2);
787
+ __decorateClass([
788
+ n({ type: String })
789
+ ], QtiAssessmentItemRef.prototype, "href", 2);
790
+ __decorateClass([
791
+ n({ type: Object, attribute: false })
792
+ ], QtiAssessmentItemRef.prototype, "xmlDoc", 2);
793
+ if (!customElements.get("qti-assessment-item-ref")) {
794
+ customElements.define("qti-assessment-item-ref", QtiAssessmentItemRef);
795
+ }
796
+
797
+ // ../qti-test/src/components/qti-assessment-section/qti-assessment-section.ts
798
+ var stringToBooleanConverter2 = {
799
+ fromAttribute(value) {
800
+ return value === "true";
801
+ },
802
+ toAttribute(value) {
803
+ return value ? "true" : "false";
804
+ }
805
+ };
806
+ var QtiAssessmentSection = class extends i2 {
807
+ constructor() {
808
+ super(...arguments);
809
+ this._title = "";
810
+ }
811
+ get title() {
812
+ return this._title;
813
+ }
814
+ set title(value) {
815
+ this._title = value;
816
+ this.removeAttribute("title");
817
+ this.setAttribute("data-title", value);
818
+ }
819
+ async connectedCallback() {
820
+ this._title = this.getAttribute("title") || "";
821
+ super.connectedCallback();
822
+ await this.updateComplete;
823
+ this.dispatchEvent(
824
+ new Event("qti-assessment-section-connected", {
825
+ bubbles: true,
826
+ composed: true
827
+ })
828
+ );
829
+ }
830
+ render() {
831
+ return x`<slot name="qti-rubric-block"></slot><slot></slot>`;
832
+ }
833
+ };
834
+ __decorateClass([
835
+ n({ type: String })
836
+ ], QtiAssessmentSection.prototype, "identifier", 2);
837
+ __decorateClass([
838
+ n({ type: String })
839
+ ], QtiAssessmentSection.prototype, "required", 2);
840
+ __decorateClass([
841
+ n({ type: Boolean, converter: stringToBooleanConverter2 })
842
+ ], QtiAssessmentSection.prototype, "fixed", 2);
843
+ __decorateClass([
844
+ n({ type: Boolean, converter: stringToBooleanConverter2 })
845
+ ], QtiAssessmentSection.prototype, "visible", 2);
846
+ __decorateClass([
847
+ n({ type: Boolean, converter: stringToBooleanConverter2, attribute: "keep-together" })
848
+ ], QtiAssessmentSection.prototype, "keepTogether", 2);
849
+ __decorateClass([
850
+ c({ context: testContext, subscribe: true })
851
+ ], QtiAssessmentSection.prototype, "_testContext", 2);
852
+ if (!customElements.get("qti-assessment-section")) {
853
+ customElements.define("qti-assessment-section", QtiAssessmentSection);
854
+ }
855
+
856
+ // ../qti-test/src/components/qti-assessment-test/qti-assessment-test.ts
857
+ var QtiAssessmentTest = class extends i2 {
858
+ constructor() {
859
+ super(...arguments);
860
+ this._title = "";
861
+ }
862
+ get title() {
863
+ return this._title;
864
+ }
865
+ set title(value) {
866
+ this._title = value;
867
+ this.removeAttribute("title");
868
+ this.setAttribute("data-title", value);
869
+ }
870
+ async connectedCallback() {
871
+ super.connectedCallback();
872
+ await this.updateComplete;
873
+ this.dispatchEvent(
874
+ new CustomEvent("qti-assessment-test-connected", {
875
+ detail: this,
876
+ bubbles: true,
877
+ composed: true
878
+ })
879
+ );
880
+ }
881
+ render() {
882
+ return x` <slot></slot>`;
883
+ }
884
+ };
885
+ __decorateClass([
886
+ n({ type: String })
887
+ ], QtiAssessmentTest.prototype, "identifier", 2);
888
+ __decorateClass([
889
+ n({ type: String })
890
+ ], QtiAssessmentTest.prototype, "title", 1);
891
+ __decorateClass([
892
+ c({ context: testContext, subscribe: true })
893
+ ], QtiAssessmentTest.prototype, "_testContext", 2);
894
+ QtiAssessmentTest = __decorateClass([
895
+ t("qti-assessment-test")
896
+ ], QtiAssessmentTest);
897
+
898
+ // ../qti-test/src/components/qti-test-part/qti-test-part.ts
899
+ var QtiTestPart = class extends i2 {
900
+ constructor() {
901
+ super(...arguments);
902
+ this.identifier = "";
903
+ this.class = "";
904
+ this.navigationMode = "nonlinear";
905
+ this.submissionMode = "individual";
906
+ this._title = "";
907
+ }
908
+ get title() {
909
+ return this._title;
910
+ }
911
+ set title(value) {
912
+ this._title = value;
913
+ this.removeAttribute("title");
914
+ this.setAttribute("data-title", value);
915
+ }
916
+ async connectedCallback() {
917
+ super.connectedCallback();
918
+ await this.updateComplete;
919
+ this.dispatchEvent(
920
+ new Event("qti-test-part-connected", {
921
+ bubbles: true,
922
+ composed: true
923
+ })
924
+ );
925
+ }
926
+ render() {
927
+ return x` <slot></slot>`;
928
+ }
929
+ };
930
+ __decorateClass([
931
+ n({ type: String })
932
+ ], QtiTestPart.prototype, "identifier", 2);
933
+ __decorateClass([
934
+ n({ type: String })
935
+ ], QtiTestPart.prototype, "class", 2);
936
+ __decorateClass([
937
+ n({ type: String, attribute: "navigation-mode" })
938
+ ], QtiTestPart.prototype, "navigationMode", 2);
939
+ __decorateClass([
940
+ n({ type: String, attribute: "submission-mode" })
941
+ ], QtiTestPart.prototype, "submissionMode", 2);
942
+ QtiTestPart = __decorateClass([
943
+ t("qti-test-part")
944
+ ], QtiTestPart);
945
+ if (!customElements.get("qti-test-part")) {
946
+ customElements.define("qti-test-part", QtiTestPart);
947
+ }
948
+
949
+ // ../qti-test/src/components/qti-test-feedback/qti-test-feedback.ts
950
+ var QtiTestFeedback = class extends QtiModalFeedback {
951
+ render() {
952
+ return x``;
953
+ }
954
+ };
955
+ QtiTestFeedback.styles = i`
956
+ :host {
957
+ color: gray;
958
+ }
959
+ `;
960
+ QtiTestFeedback = __decorateClass([
961
+ t("qti-test-feedback")
962
+ ], QtiTestFeedback);
963
+
964
+ // ../qti-test/src/components/test-navigation/test-navigation.ts
965
+ var TestNavigation = class extends i2 {
966
+ constructor() {
967
+ super();
968
+ this.identifier = void 0;
969
+ this.initContext = [];
970
+ this.qtiContext = {
971
+ QTI_CONTEXT: {
972
+ testIdentifier: "",
973
+ candidateIdentifier: "",
974
+ environmentIdentifier: "default"
975
+ }
976
+ };
977
+ this.configContext = {};
978
+ this.autoScoreItems = false;
979
+ this.addEventListener("qti-assessment-test-connected", this._handleTestConnected.bind(this));
980
+ this.addEventListener("qti-assessment-item-connected", this._handleItemConnected.bind(this));
981
+ this.addEventListener("qti-interaction-changed", this._handleInteractionChanged.bind(this));
982
+ this.addEventListener("test-end-attempt", this._handleTestEndAttempt.bind(this));
983
+ this.addEventListener("test-show-correct-response", this._handleTestShowCorrectResponse.bind(this));
984
+ this.addEventListener("test-show-candidate-correction", this._handleTestShowCandidateCorrection.bind(this));
985
+ this.addEventListener("test-update-outcome-variable", this._handleTestUpdateOutcomeVariable.bind(this));
986
+ }
987
+ /**
988
+ * Handles the 'test-end-attempt' event.
989
+ * @private
990
+ * @listens TestNavigation#test-end-attempt
991
+ * @param {CustomEvent} event - The custom event object.
992
+ */
993
+ _handleTestEndAttempt(_event) {
994
+ const qtiItemEl = this._testElement.querySelector(
995
+ `qti-assessment-item-ref[identifier="${this._sessionContext.navItemRefId}"]`
996
+ );
997
+ const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
998
+ const reportValidityAfterScoring = this.configContext?.reportValidityAfterScoring === true ? true : false;
999
+ qtiAssessmentItemEl.processResponse(true, reportValidityAfterScoring);
1000
+ }
1001
+ // protected createRenderRoot(): HTMLElement | DocumentFragment {
1002
+ // return this;
1003
+ // }
1004
+ // myTemplate: TemplateFunction;
1005
+ // connectedCallback(): void {
1006
+ // super.connectedCallback();
1007
+ // const templateElement = this.querySelector<HTMLTemplateElement>('template');
1008
+ // if (!templateElement) {
1009
+ // this.myTemplate = null;
1010
+ // return;
1011
+ // }
1012
+ // this.myTemplate = prepareTemplate(templateElement);
1013
+ // }
1014
+ /**
1015
+ * Handles the 'test-show-correct-response' event.
1016
+ * @private
1017
+ * @listens TestNavigation#test-show-correct-response
1018
+ * @param {CustomEvent} event - The custom event object.
1019
+ */
1020
+ _handleTestShowCorrectResponse(event) {
1021
+ const qtiItemEl = this._testElement.querySelector(
1022
+ `qti-assessment-item-ref[identifier="${this._sessionContext.navItemRefId}"]`
1023
+ );
1024
+ const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
1025
+ if (!qtiAssessmentItemEl) return;
1026
+ qtiAssessmentItemEl.showCorrectResponse(event.detail);
1027
+ }
1028
+ /**
1029
+ * Handles the 'test-show-candidate-correction' event.
1030
+ * @private
1031
+ * @listens TestNavigation#test-show-candidate-correction
1032
+ * @param {CustomEvent} event - The custom event object.
1033
+ */
1034
+ _handleTestShowCandidateCorrection(event) {
1035
+ const qtiItemEl = this._testElement.querySelector(
1036
+ `qti-assessment-item-ref[identifier="${this._sessionContext.navItemRefId}"]`
1037
+ );
1038
+ const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
1039
+ qtiAssessmentItemEl.showCandidateCorrection(event.detail);
1040
+ }
1041
+ _handleTestUpdateOutcomeVariable(event) {
1042
+ const qtiItemEl = this._testElement.querySelector(
1043
+ `qti-assessment-item-ref[identifier="${event.detail.assessmentItemRefId}"]`
1044
+ );
1045
+ const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
1046
+ qtiAssessmentItemEl.setOutcomeVariable(event.detail.outcomeVariableId, event.detail.value);
1047
+ }
1048
+ _handleInteractionChanged(_event) {
1049
+ if (this.autoScoreItems) {
1050
+ const assessmentItem = _event.composedPath()[0].closest(
1051
+ "qti-assessment-item"
1052
+ );
1053
+ const scoreOutcomeIdentifier = assessmentItem.variables.find((v) => v.identifier === "SCORE");
1054
+ if (scoreOutcomeIdentifier && scoreOutcomeIdentifier.externalScored === null && assessmentItem.adaptive === "false") {
1055
+ const reportValidityAfterScoring = this.configContext?.reportValidityAfterScoring === true ? true : false;
1056
+ assessmentItem.processResponse(true, reportValidityAfterScoring);
1057
+ }
1058
+ }
1059
+ }
1060
+ render() {
1061
+ return x`<slot></slot>`;
1062
+ }
1063
+ /* PK: on test connected we can build the computed context */
1064
+ _handleTestConnected(event) {
1065
+ this._testElement = event.detail;
1066
+ if (!this.qtiContext.QTI_CONTEXT?.testIdentifier) {
1067
+ const currentContext = this.qtiContext.QTI_CONTEXT || {
1068
+ testIdentifier: "",
1069
+ candidateIdentifier: "not set",
1070
+ environmentIdentifier: "default"
1071
+ };
1072
+ this.qtiContext = {
1073
+ QTI_CONTEXT: {
1074
+ ...currentContext,
1075
+ testIdentifier: this._testElement.identifier,
1076
+ environmentIdentifier: currentContext.environmentIdentifier || "default"
1077
+ }
1078
+ };
1079
+ }
1080
+ const contextDeclarations = this._testElement.querySelectorAll('qti-context-declaration[identifier="QTI_CONTEXT"]');
1081
+ contextDeclarations.forEach((declaration) => {
1082
+ const defaultValues = this._extractDefaultValues(declaration);
1083
+ if (Object.keys(defaultValues).length > 0) {
1084
+ this.qtiContext = {
1085
+ QTI_CONTEXT: {
1086
+ ...defaultValues,
1087
+ // Default values first
1088
+ ...this.qtiContext.QTI_CONTEXT
1089
+ // Runtime values override defaults
1090
+ }
1091
+ };
1092
+ }
1093
+ });
1094
+ const testPartElements = Array.from(this._testElement?.querySelectorAll(`qti-test-part`) || []);
1095
+ this.computedContext = {
1096
+ identifier: this._testElement.identifier,
1097
+ title: this._testElement.title,
1098
+ view: this._sessionContext?.view,
1099
+ testParts: testPartElements.map((testPart) => {
1100
+ const sectionElements = [...testPart.querySelectorAll(`qti-assessment-section`)];
1101
+ return {
1102
+ active: false,
1103
+ identifier: testPart.identifier,
1104
+ navigationMode: testPart.navigationMode,
1105
+ submissionMode: testPart.submissionMode,
1106
+ sections: sectionElements.map((section) => {
1107
+ const itemElements = [...section.querySelectorAll(`qti-assessment-item-ref`)];
1108
+ return {
1109
+ active: false,
1110
+ identifier: section.identifier,
1111
+ title: section.title,
1112
+ items: itemElements.map((item) => ({
1113
+ ...this.initContext?.find((i4) => i4.identifier === item.identifier),
1114
+ active: false,
1115
+ identifier: item.identifier,
1116
+ categories: item.category ? item.category?.split(" ") : [],
1117
+ href: item.href,
1118
+ variables: []
1119
+ }))
1120
+ };
1121
+ })
1122
+ };
1123
+ })
1124
+ };
1125
+ }
1126
+ /**
1127
+ * Extract default values from a qti-context-declaration element
1128
+ */
1129
+ _extractDefaultValues(declaration) {
1130
+ const defaultValues = {};
1131
+ const defaultValueElement = declaration.querySelector("qti-default-value");
1132
+ if (!defaultValueElement) {
1133
+ return defaultValues;
1134
+ }
1135
+ const valueElements = defaultValueElement.querySelectorAll("qti-value[field-identifier]");
1136
+ valueElements.forEach((valueElement) => {
1137
+ const fieldIdentifier = valueElement.getAttribute("field-identifier");
1138
+ const baseType = valueElement.getAttribute("base-type") || "string";
1139
+ const textContent = valueElement.textContent?.trim() || "";
1140
+ if (fieldIdentifier) {
1141
+ let value = textContent;
1142
+ switch (baseType) {
1143
+ case "integer":
1144
+ value = parseInt(textContent, 10);
1145
+ break;
1146
+ case "float":
1147
+ case "duration":
1148
+ value = parseFloat(textContent);
1149
+ break;
1150
+ case "boolean":
1151
+ value = textContent.toLowerCase() === "true";
1152
+ break;
1153
+ case "string":
1154
+ default:
1155
+ value = textContent;
1156
+ break;
1157
+ }
1158
+ defaultValues[fieldIdentifier] = value;
1159
+ }
1160
+ });
1161
+ return defaultValues;
1162
+ }
1163
+ /* PK: on item connected we can add item only properties in the xml */
1164
+ _handleItemConnected(event) {
1165
+ const itemElement = event.detail;
1166
+ this.computedContext = {
1167
+ ...this.computedContext,
1168
+ testParts: this.computedContext.testParts.map((testPart) => {
1169
+ return {
1170
+ ...testPart,
1171
+ sections: testPart.sections.map((section) => {
1172
+ return {
1173
+ ...section,
1174
+ items: section.items.map((item) => {
1175
+ if (item.identifier !== itemElement.parentElement.getAttribute("identifier")) {
1176
+ return item;
1177
+ }
1178
+ const scoreOutcome = itemElement.querySelector(
1179
+ "qti-outcome-declaration[identifier='SCORE']"
1180
+ );
1181
+ const externalScored = scoreOutcome?.getAttribute("externalScored");
1182
+ const responseDeclarations = itemElement.querySelectorAll("qti-response-declaration");
1183
+ const containsCorrectResponse = Array.from(responseDeclarations).some(
1184
+ (r3) => r3.querySelector("qti-correct-response")
1185
+ );
1186
+ const containsMapping = Array.from(responseDeclarations).some((r3) => {
1187
+ const mapping = r3.querySelector("qti-mapping");
1188
+ const areaMapping = r3.querySelector("qti-area-mapping");
1189
+ return mapping?.querySelector("qti-map-entry") || areaMapping?.querySelector("qti-area-map-entry");
1190
+ });
1191
+ const hasCorrectResponse = containsCorrectResponse || containsMapping;
1192
+ const hasResponseProcessing = itemElement.querySelector("qti-response-processing") ? true : false;
1193
+ return {
1194
+ ...item,
1195
+ assessmentItemIdentifier: itemElement.getAttribute("identifier"),
1196
+ label: itemElement.getAttribute("label"),
1197
+ title: itemElement.title,
1198
+ externalScored,
1199
+ adaptive: itemElement.adaptive == "true" || false,
1200
+ timeDependent: itemElement.timeDependent == "true" || false,
1201
+ variables: itemElement.variables,
1202
+ hasCorrectResponse,
1203
+ hasResponseProcessing
1204
+ };
1205
+ })
1206
+ };
1207
+ })
1208
+ };
1209
+ })
1210
+ };
1211
+ }
1212
+ /* PK: on every change of the candidate we will recomputed the computedContext */
1213
+ willUpdate(_changedProperties) {
1214
+ if (!this.computedContext) return;
1215
+ let itemIndex = 1;
1216
+ this.computedContext = {
1217
+ ...this.computedContext,
1218
+ view: this._sessionContext?.view,
1219
+ testParts: this.computedContext.testParts.map((testPart) => {
1220
+ return {
1221
+ ...testPart,
1222
+ active: this._sessionContext?.navPartId === testPart.identifier || false,
1223
+ sections: testPart.sections.map((section) => {
1224
+ return {
1225
+ ...section,
1226
+ active: this._sessionContext?.navSectionId === section.identifier || false,
1227
+ completed: section.items.every(
1228
+ (item) => this._testContext.items.find((i4) => i4.identifier === item.identifier)?.variables.find((v) => v.identifier === "completionStatus").value === "completed"
1229
+ ),
1230
+ items: section.items.map((item) => {
1231
+ const itemContext = this._testContext?.items.find((i4) => i4.identifier === item.identifier);
1232
+ const computedItem = {
1233
+ ...item,
1234
+ ...itemContext,
1235
+ ...this.initContext?.find((i4) => i4.identifier === item.identifier)
1236
+ };
1237
+ const rawscore = computedItem.variables?.find((vr) => vr.identifier == "SCORE")?.value;
1238
+ const score = rawscore === void 0 || rawscore === null ? null : parseFloat(rawscore?.toString());
1239
+ const completionStatus = computedItem.variables?.find((v) => v.identifier === "completionStatus")?.value;
1240
+ const response = computedItem.variables?.find((v) => v.identifier === "RESPONSE")?.value || "";
1241
+ const numAttempts = computedItem.variables?.find((v) => v.identifier === "numAttempts")?.value || 0;
1242
+ const active = this._sessionContext?.navItemRefId === computedItem.identifier || false;
1243
+ const index = item.categories.includes(this.configContext?.infoItemCategory) ? null : itemIndex++;
1244
+ const rawMaxScore = item.variables?.find((vr) => vr.identifier == "MAXSCORE")?.value;
1245
+ const maxScore = rawMaxScore === void 0 || rawMaxScore === null ? null : parseFloat(rawMaxScore?.toString());
1246
+ return {
1247
+ ...computedItem,
1248
+ completionStatus,
1249
+ numAttempts,
1250
+ score,
1251
+ response,
1252
+ index,
1253
+ // type,
1254
+ active,
1255
+ // correct,
1256
+ maxScore
1257
+ // incorrect,
1258
+ // completed
1259
+ };
1260
+ })
1261
+ };
1262
+ })
1263
+ };
1264
+ })
1265
+ };
1266
+ this.dispatchEvent(
1267
+ new CustomEvent("qti-computed-context-updated", {
1268
+ detail: this.computedContext,
1269
+ bubbles: true
1270
+ })
1271
+ );
1272
+ }
1273
+ };
1274
+ __decorateClass([
1275
+ n({ type: String })
1276
+ ], TestNavigation.prototype, "identifier", 2);
1277
+ __decorateClass([
1278
+ r()
1279
+ ], TestNavigation.prototype, "initContext", 2);
1280
+ __decorateClass([
1281
+ r(),
1282
+ e({ context: qtiContext })
1283
+ ], TestNavigation.prototype, "qtiContext", 2);
1284
+ __decorateClass([
1285
+ r(),
1286
+ e({ context: configContext })
1287
+ ], TestNavigation.prototype, "configContext", 2);
1288
+ __decorateClass([
1289
+ r(),
1290
+ c({ context: testContext, subscribe: true })
1291
+ ], TestNavigation.prototype, "_testContext", 2);
1292
+ __decorateClass([
1293
+ r(),
1294
+ c({ context: sessionContext, subscribe: true })
1295
+ ], TestNavigation.prototype, "_sessionContext", 2);
1296
+ __decorateClass([
1297
+ r(),
1298
+ e({ context: computedContext })
1299
+ ], TestNavigation.prototype, "computedContext", 2);
1300
+ __decorateClass([
1301
+ n({ type: Boolean, attribute: "auto-score-items" })
1302
+ ], TestNavigation.prototype, "autoScoreItems", 2);
1303
+ TestNavigation = __decorateClass([
1304
+ t("test-navigation")
1305
+ ], TestNavigation);
1306
+
1307
+ // ../qti-test/src/components/styles.ts
1308
+ var form = i`
1309
+ display: inline-flex;
1310
+ align-items: center;
1311
+ cursor: pointer;
1312
+ padding: 0.5rem 1rem;
1313
+ border-radius: 0.25rem;
1314
+ user-select: none;
1315
+ `;
1316
+ var btn = i`
1317
+ background-color: lightgray;
1318
+ ${form};
1319
+ `;
1320
+ var dis = i`
1321
+ cursor: not-allowed;
1322
+ opacity: 0.8;
1323
+ `;
1324
+ var ind = i`
1325
+ ${form};
1326
+ border: 1px solid gray;
1327
+ `;
1328
+
1329
+ // ../qti-test/src/components/test-next/test-next.ts
1330
+ var TestNext = class extends i2 {
1331
+ constructor() {
1332
+ super();
1333
+ this._internalDisabled = true;
1334
+ this._internals = this.attachInternals();
1335
+ this._internals.role = "button";
1336
+ this._internals.ariaLabel = "Next item";
1337
+ this.addEventListener("click", (e2) => {
1338
+ e2.preventDefault();
1339
+ if (!this._internalDisabled) this._requestItem(this.sectionItems[this.itemIndex + 1].identifier);
1340
+ });
1341
+ }
1342
+ _handleTestElementChange(_oldValue, newValue) {
1343
+ if (newValue) {
1344
+ this._internalDisabled = false;
1345
+ }
1346
+ }
1347
+ connectedCallback() {
1348
+ super.connectedCallback();
1349
+ this.checkDisabled();
1350
+ }
1351
+ willUpdate(_changedProperties) {
1352
+ if (!this.computedContext) return;
1353
+ const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
1354
+ if (!testPart) return;
1355
+ this.sectionItems = testPart.sections.flatMap((section) => section.items);
1356
+ this.itemIndex = this.sectionItems.findIndex((item) => item.active);
1357
+ this.checkDisabled();
1358
+ }
1359
+ checkDisabled() {
1360
+ this._internalDisabled = !this.computedContext || this.itemIndex < 0 || this.itemIndex >= this.sectionItems?.length - 1;
1361
+ }
1362
+ _requestItem(identifier) {
1363
+ this.dispatchEvent(
1364
+ new CustomEvent("qti-request-navigation", {
1365
+ composed: true,
1366
+ bubbles: true,
1367
+ detail: {
1368
+ type: "item",
1369
+ id: identifier
1370
+ }
1371
+ })
1372
+ );
1373
+ }
1374
+ render() {
1375
+ return x`<slot></slot>`;
1376
+ }
1377
+ };
1378
+ TestNext.styles = i`
1379
+ :host {
1380
+ ${btn};
1381
+ }
1382
+ :host([disabled]) {
1383
+ ${dis};
1384
+ }
1385
+ `;
1386
+ __decorateClass([
1387
+ n({ type: Boolean, reflect: true, attribute: "disabled" })
1388
+ ], TestNext.prototype, "_internalDisabled", 2);
1389
+ __decorateClass([
1390
+ c({ context: computedContext, subscribe: true })
1391
+ ], TestNext.prototype, "computedContext", 2);
1392
+ __decorateClass([
1393
+ watch("computedContext")
1394
+ ], TestNext.prototype, "_handleTestElementChange", 1);
1395
+ TestNext = __decorateClass([
1396
+ t("test-next")
1397
+ ], TestNext);
1398
+
1399
+ // ../qti-test/src/components/test-prev/test-prev.ts
1400
+ var TestPrev = class extends i2 {
1401
+ constructor() {
1402
+ super();
1403
+ this._internalDisabled = true;
1404
+ this.addEventListener("click", (e2) => {
1405
+ e2.preventDefault();
1406
+ if (!this._internalDisabled) this._requestItem(this.sectionItems[this.itemIndex - 1].identifier);
1407
+ });
1408
+ }
1409
+ _handleTestElementChange(_oldValue, newValue) {
1410
+ if (newValue) {
1411
+ this._internalDisabled = false;
1412
+ }
1413
+ }
1414
+ willUpdate(_changedProperties) {
1415
+ if (!this.computedContext) return;
1416
+ const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
1417
+ if (!testPart) return;
1418
+ this.sectionItems = testPart.sections.flatMap((section) => section.items);
1419
+ this.itemIndex = this.sectionItems.findIndex((item) => item.active);
1420
+ this.checkDisabled();
1421
+ }
1422
+ checkDisabled() {
1423
+ this._internalDisabled = !this.computedContext || this.itemIndex === 0 || this.itemIndex === -1;
1424
+ }
1425
+ _requestItem(identifier) {
1426
+ this.dispatchEvent(
1427
+ new CustomEvent("qti-request-navigation", {
1428
+ composed: true,
1429
+ bubbles: true,
1430
+ detail: {
1431
+ type: "item",
1432
+ id: identifier
1433
+ }
1434
+ })
1435
+ );
1436
+ }
1437
+ render() {
1438
+ return x`<slot></slot>`;
1439
+ }
1440
+ };
1441
+ TestPrev.styles = i`
1442
+ :host {
1443
+ ${btn};
1444
+ }
1445
+ :host([disabled]) {
1446
+ ${dis};
1447
+ }
1448
+ `;
1449
+ __decorateClass([
1450
+ n({ type: Boolean, reflect: true, attribute: "disabled" })
1451
+ ], TestPrev.prototype, "_internalDisabled", 2);
1452
+ __decorateClass([
1453
+ c({ context: computedContext, subscribe: true })
1454
+ ], TestPrev.prototype, "computedContext", 2);
1455
+ __decorateClass([
1456
+ watch("computedContext")
1457
+ ], TestPrev.prototype, "_handleTestElementChange", 1);
1458
+ TestPrev = __decorateClass([
1459
+ t("test-prev")
1460
+ ], TestPrev);
1461
+
1462
+ // ../qti-test/src/components/test-view-toggle/test-view.ts
1463
+ var TestView = class extends i2 {
1464
+ constructor() {
1465
+ super(...arguments);
1466
+ this.label = "view";
1467
+ this._handleViewOptionsChange = () => {
1468
+ this.updateViewOptions();
1469
+ };
1470
+ this._viewOptions = TestView.DEFAULT_VIEW_OPTIONS;
1471
+ }
1472
+ connectedCallback() {
1473
+ super.connectedCallback();
1474
+ this.updateViewOptions();
1475
+ }
1476
+ updateViewOptions() {
1477
+ if (this.viewOptions) {
1478
+ const options = this.viewOptions.split(",").map((opt) => opt.trim());
1479
+ this._viewOptions = options.filter((opt) => TestView.DEFAULT_VIEW_OPTIONS.includes(opt));
1480
+ } else {
1481
+ this._viewOptions = TestView.DEFAULT_VIEW_OPTIONS;
1482
+ }
1483
+ }
1484
+ _switchView(view) {
1485
+ this.dispatchEvent(
1486
+ new CustomEvent("on-test-switch-view", {
1487
+ composed: true,
1488
+ bubbles: true,
1489
+ detail: view
1490
+ })
1491
+ );
1492
+ }
1493
+ render() {
1494
+ return x`
1495
+ <label part="label" for="viewSelect">${this.label}</label>
1496
+ <select
1497
+ part="select"
1498
+ id="viewSelect"
1499
+ @change=${(e2) => {
1500
+ const el = e2.target;
1501
+ this._switchView(el.value);
1502
+ }}
1503
+ >
1504
+ ${this._viewOptions.map(
1505
+ (v) => x`<option value="${v}" ?selected=${v === this.sessionContext.view}>${v}</option>`
1506
+ )}
1507
+ </select>
1508
+ `;
1509
+ }
1510
+ };
1511
+ TestView.DEFAULT_VIEW_OPTIONS = ["author", "candidate", "proctor", "scorer", "testConstructor", "tutor"];
1512
+ __decorateClass([
1513
+ c({ context: sessionContext, subscribe: true })
1514
+ ], TestView.prototype, "sessionContext", 2);
1515
+ __decorateClass([
1516
+ n({ type: String })
1517
+ ], TestView.prototype, "label", 2);
1518
+ __decorateClass([
1519
+ n({ type: String, attribute: "view-options" })
1520
+ ], TestView.prototype, "viewOptions", 2);
1521
+ __decorateClass([
1522
+ watch("viewOptions", { waitUntilFirstUpdate: true })
1523
+ ], TestView.prototype, "_handleViewOptionsChange", 2);
1524
+ __decorateClass([
1525
+ r()
1526
+ ], TestView.prototype, "_viewOptions", 2);
1527
+ TestView = __decorateClass([
1528
+ t("test-view")
1529
+ ], TestView);
1530
+
1531
+ // ../qti-test/src/components/test-item-link/test-item-link.ts
1532
+ var TestItemLink = class extends i2 {
1533
+ constructor() {
1534
+ super();
1535
+ this.itemId = null;
1536
+ this.addEventListener("click", () => this._requestItem(this.itemId));
1537
+ }
1538
+ _requestItem(identifier) {
1539
+ this.dispatchEvent(
1540
+ new CustomEvent("qti-request-navigation", {
1541
+ composed: true,
1542
+ bubbles: true,
1543
+ detail: {
1544
+ type: "item",
1545
+ id: identifier
1546
+ }
1547
+ })
1548
+ );
1549
+ }
1550
+ render() {
1551
+ return x` <slot></slot> `;
1552
+ }
1553
+ };
1554
+ TestItemLink.styles = i`
1555
+ :host {
1556
+ ${btn};
1557
+ }
1558
+ :host([disabled]) {
1559
+ ${dis};
1560
+ }
1561
+ `;
1562
+ __decorateClass([
1563
+ n({ type: String, attribute: "item-id" })
1564
+ ], TestItemLink.prototype, "itemId", 2);
1565
+ TestItemLink = __decorateClass([
1566
+ t("test-item-link")
1567
+ ], TestItemLink);
1568
+
1569
+ // ../qti-test/src/components/test-end-attempt/test-end-attempt.ts
1570
+ var TestEndAttempt = class extends i2 {
1571
+ constructor() {
1572
+ super();
1573
+ this.addEventListener("click", () => this.dispatchEvent(new CustomEvent("test-end-attempt", { bubbles: true })));
1574
+ }
1575
+ render() {
1576
+ return x` <slot></slot> `;
1577
+ }
1578
+ };
1579
+ TestEndAttempt.styles = i`
1580
+ :host {
1581
+ ${btn};
1582
+ }
1583
+ :host([disabled]) {
1584
+ ${dis};
1585
+ }
1586
+ `;
1587
+ TestEndAttempt = __decorateClass([
1588
+ t("test-end-attempt")
1589
+ ], TestEndAttempt);
1590
+
1591
+ // ../qti-test/src/components/test-show-correct-response/test-show-correct-response.ts
1592
+ var TestShowCorrectResponse = class extends i2 {
1593
+ constructor() {
1594
+ super(...arguments);
1595
+ this.shown = false;
1596
+ this.disabled = false;
1597
+ this.showCorrectText = "Show correct response";
1598
+ this.hideCorrectText = "Hide correct response";
1599
+ this.noCorrectResponseText = "No correct response specified";
1600
+ }
1601
+ // Store previous active item reference
1602
+ willUpdate(_changedProperties) {
1603
+ const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
1604
+ if (this._previousActiveItem !== activeItem) {
1605
+ this.shown = false;
1606
+ this._previousActiveItem = activeItem;
1607
+ }
1608
+ if (activeItem) {
1609
+ const containsCorrectResponse = !!activeItem?.variables?.some((v) => v["correctResponse"]);
1610
+ const containsMapping = !!activeItem?.variables?.some((v) => {
1611
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
1612
+ });
1613
+ this.disabled = !containsCorrectResponse && !containsMapping;
1614
+ } else {
1615
+ this.disabled = true;
1616
+ }
1617
+ }
1618
+ _toggleState() {
1619
+ if (this.disabled) return;
1620
+ this.shown = !this.shown;
1621
+ this.dispatchEvent(
1622
+ new CustomEvent("test-show-correct-response", {
1623
+ detail: this.shown,
1624
+ bubbles: true
1625
+ })
1626
+ );
1627
+ }
1628
+ _getDisplayedText() {
1629
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
1630
+ }
1631
+ render() {
1632
+ return x` <div @click="${this._toggleState}">${this._getDisplayedText()}</div> `;
1633
+ }
1634
+ };
1635
+ TestShowCorrectResponse.styles = i`
1636
+ :host {
1637
+ ${btn};
1638
+ }
1639
+ :host([disabled]) {
1640
+ ${dis};
1641
+ }
1642
+ `;
1643
+ __decorateClass([
1644
+ c({ context: computedContext, subscribe: true })
1645
+ ], TestShowCorrectResponse.prototype, "computedContext", 2);
1646
+ __decorateClass([
1647
+ n({ type: Boolean, reflect: true })
1648
+ ], TestShowCorrectResponse.prototype, "shown", 2);
1649
+ __decorateClass([
1650
+ n({ type: Boolean, reflect: true })
1651
+ ], TestShowCorrectResponse.prototype, "disabled", 2);
1652
+ __decorateClass([
1653
+ n({ type: String })
1654
+ ], TestShowCorrectResponse.prototype, "showCorrectText", 2);
1655
+ __decorateClass([
1656
+ n({ type: String })
1657
+ ], TestShowCorrectResponse.prototype, "hideCorrectText", 2);
1658
+ __decorateClass([
1659
+ n({ type: String })
1660
+ ], TestShowCorrectResponse.prototype, "noCorrectResponseText", 2);
1661
+ TestShowCorrectResponse = __decorateClass([
1662
+ t("test-show-correct-response")
1663
+ ], TestShowCorrectResponse);
1664
+
1665
+ // ../../node_modules/.pnpm/@heximal+expressions@0.1.5/node_modules/@heximal/expressions/lib/constants.js
1666
+ var KEYWORDS = ["this"];
1667
+ var UNARY_OPERATORS = ["+", "-", "!"];
1668
+ var BINARY_OPERATORS = [
1669
+ "=",
1670
+ "+",
1671
+ "-",
1672
+ "*",
1673
+ "/",
1674
+ "%",
1675
+ "^",
1676
+ "==",
1677
+ "!=",
1678
+ ">",
1679
+ "<",
1680
+ ">=",
1681
+ "<=",
1682
+ "||",
1683
+ "&&",
1684
+ "??",
1685
+ "&",
1686
+ "===",
1687
+ "!==",
1688
+ "|",
1689
+ "|>"
1690
+ ];
1691
+ var PRECEDENCE = {
1692
+ "!": 0,
1693
+ ":": 0,
1694
+ ",": 0,
1695
+ ")": 0,
1696
+ "]": 0,
1697
+ "}": 0,
1698
+ "|>": 1,
1699
+ "?": 2,
1700
+ "??": 3,
1701
+ "||": 4,
1702
+ "&&": 5,
1703
+ "|": 6,
1704
+ "^": 7,
1705
+ "&": 8,
1706
+ // equality
1707
+ "!=": 9,
1708
+ "==": 9,
1709
+ "!==": 9,
1710
+ "===": 9,
1711
+ // relational
1712
+ ">=": 10,
1713
+ ">": 10,
1714
+ "<=": 10,
1715
+ "<": 10,
1716
+ // additive
1717
+ "+": 11,
1718
+ "-": 11,
1719
+ // multiplicative
1720
+ "%": 12,
1721
+ "/": 12,
1722
+ "*": 12,
1723
+ // postfix
1724
+ "(": 13,
1725
+ "[": 13,
1726
+ ".": 13,
1727
+ "{": 13
1728
+ // not sure this is correct
1729
+ };
1730
+ var POSTFIX_PRECEDENCE = 13;
1731
+
1732
+ // ../../node_modules/.pnpm/@heximal+expressions@0.1.5/node_modules/@heximal/expressions/lib/tokenizer.js
1733
+ var TWO_CHAR_OPS = ["==", "!=", "<=", ">=", "||", "&&", "??", "|>"];
1734
+ var THREE_CHAR_OPS = ["===", "!=="];
1735
+ var Kind;
1736
+ (function(Kind2) {
1737
+ Kind2[Kind2["STRING"] = 1] = "STRING";
1738
+ Kind2[Kind2["IDENTIFIER"] = 2] = "IDENTIFIER";
1739
+ Kind2[Kind2["DOT"] = 3] = "DOT";
1740
+ Kind2[Kind2["COMMA"] = 4] = "COMMA";
1741
+ Kind2[Kind2["COLON"] = 5] = "COLON";
1742
+ Kind2[Kind2["INTEGER"] = 6] = "INTEGER";
1743
+ Kind2[Kind2["DECIMAL"] = 7] = "DECIMAL";
1744
+ Kind2[Kind2["OPERATOR"] = 8] = "OPERATOR";
1745
+ Kind2[Kind2["GROUPER"] = 9] = "GROUPER";
1746
+ Kind2[Kind2["KEYWORD"] = 10] = "KEYWORD";
1747
+ Kind2[Kind2["ARROW"] = 11] = "ARROW";
1748
+ })(Kind || (Kind = {}));
1749
+ var token = (kind, value, precedence = 0) => ({
1750
+ kind,
1751
+ value,
1752
+ precedence
1753
+ });
1754
+ var isWhitespace = (ch) => ch === 9 || ch === 10 || ch === 13 || ch === 32;
1755
+ var isIdentOrKeywordStart = (ch) => ch === 95 || ch === 36 || // ch &= ~32 puts ch into the range [65,90] [A-Z] only if ch was already in
1756
+ // the that range or in the range [97,122] [a-z]. We must mutate ch only after
1757
+ // checking other characters, thus the comma operator.
1758
+ (ch &= ~32, 65 <= ch && ch <= 90);
1759
+ var isIdentifier = (ch) => isIdentOrKeywordStart(ch) || isNumber(ch);
1760
+ var isKeyword = (str) => KEYWORDS.indexOf(str) !== -1;
1761
+ var isQuote = (ch) => ch === 34 || ch === 39;
1762
+ var isNumber = (ch) => 48 <= ch && ch <= 57;
1763
+ var isOperator = (ch) => ch === 43 || ch === 45 || ch === 42 || ch === 47 || ch === 33 || ch === 38 || ch === 37 || ch === 60 || ch === 61 || ch === 62 || ch === 63 || ch === 94 || ch === 124;
1764
+ var _isGrouper = (ch) => ch === 40 || ch === 41 || ch === 91 || ch === 93 || ch === 123 || ch === 125;
1765
+ var escapeString = (str) => str.replace(/\\(.)/g, (_match, group) => {
1766
+ switch (group) {
1767
+ case "n":
1768
+ return "\n";
1769
+ case "r":
1770
+ return "\r";
1771
+ case "t":
1772
+ return " ";
1773
+ case "b":
1774
+ return "\b";
1775
+ case "f":
1776
+ return "\f";
1777
+ default:
1778
+ return group;
1779
+ }
1780
+ });
1781
+ var Tokenizer = class {
1782
+ #input;
1783
+ #index = -1;
1784
+ #tokenStart = 0;
1785
+ #next;
1786
+ constructor(input) {
1787
+ this.#input = input;
1788
+ this.#advance();
1789
+ }
1790
+ nextToken() {
1791
+ while (isWhitespace(this.#next)) {
1792
+ this.#advance(true);
1793
+ }
1794
+ if (isQuote(this.#next))
1795
+ return this.#tokenizeString();
1796
+ if (isIdentOrKeywordStart(this.#next)) {
1797
+ return this.#tokenizeIdentOrKeyword();
1798
+ }
1799
+ if (isNumber(this.#next))
1800
+ return this.#tokenizeNumber();
1801
+ if (this.#next === 46)
1802
+ return this.#tokenizeDot();
1803
+ if (this.#next === 44)
1804
+ return this.#tokenizeComma();
1805
+ if (this.#next === 58)
1806
+ return this.#tokenizeColon();
1807
+ if (isOperator(this.#next))
1808
+ return this.#tokenizeOperator();
1809
+ if (_isGrouper(this.#next))
1810
+ return this.#tokenizeGrouper();
1811
+ this.#advance();
1812
+ if (this.#next !== void 0) {
1813
+ throw new Error(`Expected end of input, got ${this.#next}`);
1814
+ }
1815
+ return void 0;
1816
+ }
1817
+ #advance(resetTokenStart) {
1818
+ this.#index++;
1819
+ if (this.#index < this.#input.length) {
1820
+ this.#next = this.#input.charCodeAt(this.#index);
1821
+ if (resetTokenStart === true) {
1822
+ this.#tokenStart = this.#index;
1823
+ }
1824
+ } else {
1825
+ this.#next = void 0;
1826
+ }
1827
+ }
1828
+ #getValue(lookahead = 0) {
1829
+ const v = this.#input.substring(this.#tokenStart, this.#index + lookahead);
1830
+ if (lookahead === 0) {
1831
+ this.#clearValue();
1832
+ }
1833
+ return v;
1834
+ }
1835
+ #clearValue() {
1836
+ this.#tokenStart = this.#index;
1837
+ }
1838
+ #tokenizeString() {
1839
+ const _us = "unterminated string";
1840
+ const quoteChar = this.#next;
1841
+ this.#advance(true);
1842
+ while (this.#next !== quoteChar) {
1843
+ if (this.#next === void 0)
1844
+ throw new Error(_us);
1845
+ if (this.#next === 92) {
1846
+ this.#advance();
1847
+ if (this.#next === void 0)
1848
+ throw new Error(_us);
1849
+ }
1850
+ this.#advance();
1851
+ }
1852
+ const t2 = token(Kind.STRING, escapeString(this.#getValue()));
1853
+ this.#advance();
1854
+ return t2;
1855
+ }
1856
+ #tokenizeIdentOrKeyword() {
1857
+ do {
1858
+ this.#advance();
1859
+ } while (isIdentifier(this.#next));
1860
+ const value = this.#getValue();
1861
+ const kind = isKeyword(value) ? Kind.KEYWORD : Kind.IDENTIFIER;
1862
+ return token(kind, value);
1863
+ }
1864
+ #tokenizeNumber() {
1865
+ do {
1866
+ this.#advance();
1867
+ } while (isNumber(this.#next));
1868
+ if (this.#next === 46)
1869
+ return this.#tokenizeDot();
1870
+ return token(Kind.INTEGER, this.#getValue());
1871
+ }
1872
+ #tokenizeDot() {
1873
+ this.#advance();
1874
+ if (isNumber(this.#next))
1875
+ return this.#tokenizeFraction();
1876
+ this.#clearValue();
1877
+ return token(Kind.DOT, ".", POSTFIX_PRECEDENCE);
1878
+ }
1879
+ #tokenizeComma() {
1880
+ this.#advance(true);
1881
+ return token(Kind.COMMA, ",");
1882
+ }
1883
+ #tokenizeColon() {
1884
+ this.#advance(true);
1885
+ return token(Kind.COLON, ":");
1886
+ }
1887
+ #tokenizeFraction() {
1888
+ do {
1889
+ this.#advance();
1890
+ } while (isNumber(this.#next));
1891
+ return token(Kind.DECIMAL, this.#getValue());
1892
+ }
1893
+ #tokenizeOperator() {
1894
+ this.#advance();
1895
+ let op = this.#getValue(2);
1896
+ if (THREE_CHAR_OPS.indexOf(op) !== -1) {
1897
+ this.#advance();
1898
+ this.#advance();
1899
+ } else {
1900
+ op = this.#getValue(1);
1901
+ if (op === "=>") {
1902
+ this.#advance();
1903
+ return token(Kind.ARROW, op);
1904
+ }
1905
+ if (TWO_CHAR_OPS.indexOf(op) !== -1) {
1906
+ this.#advance();
1907
+ }
1908
+ }
1909
+ op = this.#getValue();
1910
+ return token(Kind.OPERATOR, op, PRECEDENCE[op]);
1911
+ }
1912
+ #tokenizeGrouper() {
1913
+ const value = String.fromCharCode(this.#next);
1914
+ const t2 = token(Kind.GROUPER, value, PRECEDENCE[value]);
1915
+ this.#advance(true);
1916
+ return t2;
1917
+ }
1918
+ };
1919
+
1920
+ // ../../node_modules/.pnpm/@heximal+expressions@0.1.5/node_modules/@heximal/expressions/lib/parser.js
1921
+ var parse = (expr, astFactory2) => new Parser(expr, astFactory2).parse();
1922
+ var Parser = class {
1923
+ #kind;
1924
+ #tokenizer;
1925
+ #ast;
1926
+ #token;
1927
+ #value;
1928
+ constructor(input, astFactory2) {
1929
+ this.#tokenizer = new Tokenizer(input);
1930
+ this.#ast = astFactory2;
1931
+ }
1932
+ parse() {
1933
+ this.#advance();
1934
+ return this.#parseExpression();
1935
+ }
1936
+ #advance(kind, value) {
1937
+ if (!this._matches(kind, value)) {
1938
+ throw new Error(`Expected kind ${kind} (${value}), was ${this.#token?.kind} (${this.#token?.value})`);
1939
+ }
1940
+ const t2 = this.#tokenizer.nextToken();
1941
+ this.#token = t2;
1942
+ this.#kind = t2?.kind;
1943
+ this.#value = t2?.value;
1944
+ }
1945
+ _matches(kind, value) {
1946
+ return !(kind && this.#kind !== kind || value && this.#value !== value);
1947
+ }
1948
+ #parseExpression() {
1949
+ if (!this.#token)
1950
+ return this.#ast.empty();
1951
+ const expr = this.#parseUnary();
1952
+ return expr === void 0 ? void 0 : this.#parsePrecedence(expr, 0);
1953
+ }
1954
+ // #parsePrecedence and #parseBinary implement the precedence climbing
1955
+ // algorithm as described in:
1956
+ // http://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method
1957
+ #parsePrecedence(left, precedence) {
1958
+ if (left === void 0) {
1959
+ throw new Error("Expected left to be defined.");
1960
+ }
1961
+ while (this.#token) {
1962
+ if (this._matches(Kind.GROUPER, "(")) {
1963
+ const args = this.#parseArguments();
1964
+ left = this.#ast.invoke(left, void 0, args);
1965
+ } else if (this._matches(Kind.GROUPER, "[")) {
1966
+ const indexExpr = this.#parseIndex();
1967
+ left = this.#ast.index(left, indexExpr);
1968
+ } else if (this._matches(Kind.DOT)) {
1969
+ this.#advance();
1970
+ const right = this.#parseUnary();
1971
+ left = this.#makeInvokeOrGetter(left, right);
1972
+ } else if (this._matches(Kind.KEYWORD)) {
1973
+ break;
1974
+ } else if (this._matches(Kind.OPERATOR) && this.#token.precedence >= precedence) {
1975
+ left = this.#value === "?" ? this.#parseTernary(left) : this.#parseBinary(left, this.#token);
1976
+ } else {
1977
+ break;
1978
+ }
1979
+ }
1980
+ return left;
1981
+ }
1982
+ #makeInvokeOrGetter(left, right) {
1983
+ if (right === void 0) {
1984
+ throw new Error("expected identifier");
1985
+ }
1986
+ if (right.type === "ID") {
1987
+ return this.#ast.getter(left, right.value);
1988
+ } else if (right.type === "Invoke" && right.receiver.type === "ID") {
1989
+ const method = right.receiver;
1990
+ return this.#ast.invoke(left, method.value, right.arguments);
1991
+ } else {
1992
+ throw new Error(`expected identifier: ${right}`);
1993
+ }
1994
+ }
1995
+ #parseBinary(left, op) {
1996
+ if (BINARY_OPERATORS.indexOf(op.value) === -1) {
1997
+ throw new Error(`unknown operator: ${op.value}`);
1998
+ }
1999
+ this.#advance();
2000
+ let right = this.#parseUnary();
2001
+ while ((this.#kind === Kind.OPERATOR || this.#kind === Kind.DOT || this.#kind === Kind.GROUPER) && this.#token.precedence > op.precedence) {
2002
+ right = this.#parsePrecedence(right, this.#token.precedence);
2003
+ }
2004
+ return this.#ast.binary(left, op.value, right);
2005
+ }
2006
+ #parseUnary() {
2007
+ if (this._matches(Kind.OPERATOR)) {
2008
+ const value = this.#value;
2009
+ this.#advance();
2010
+ if (value === "+" || value === "-") {
2011
+ if (this._matches(Kind.INTEGER)) {
2012
+ return this.#parseInteger(value);
2013
+ } else if (this._matches(Kind.DECIMAL)) {
2014
+ return this.#parseDecimal(value);
2015
+ }
2016
+ }
2017
+ if (UNARY_OPERATORS.indexOf(value) === -1)
2018
+ throw new Error(`unexpected token: ${value}`);
2019
+ const expr = this.#parsePrecedence(this.#parsePrimary(), POSTFIX_PRECEDENCE);
2020
+ return this.#ast.unary(value, expr);
2021
+ }
2022
+ return this.#parsePrimary();
2023
+ }
2024
+ #parseTernary(condition) {
2025
+ this.#advance(Kind.OPERATOR, "?");
2026
+ const trueExpr = this.#parseExpression();
2027
+ this.#advance(Kind.COLON);
2028
+ const falseExpr = this.#parseExpression();
2029
+ return this.#ast.ternary(condition, trueExpr, falseExpr);
2030
+ }
2031
+ #parsePrimary() {
2032
+ switch (this.#kind) {
2033
+ case Kind.KEYWORD:
2034
+ const keyword = this.#value;
2035
+ if (keyword === "this") {
2036
+ this.#advance();
2037
+ return this.#ast.id(keyword);
2038
+ } else if (KEYWORDS.indexOf(keyword) !== -1) {
2039
+ throw new Error(`unexpected keyword: ${keyword}`);
2040
+ }
2041
+ throw new Error(`unrecognized keyword: ${keyword}`);
2042
+ case Kind.IDENTIFIER:
2043
+ return this.#parseInvokeOrIdentifier();
2044
+ case Kind.STRING:
2045
+ return this.#parseString();
2046
+ case Kind.INTEGER:
2047
+ return this.#parseInteger();
2048
+ case Kind.DECIMAL:
2049
+ return this.#parseDecimal();
2050
+ case Kind.GROUPER:
2051
+ if (this.#value === "(") {
2052
+ return this.#parseParenOrFunction();
2053
+ } else if (this.#value === "{") {
2054
+ return this.#parseMap();
2055
+ } else if (this.#value === "[") {
2056
+ return this.#parseList();
2057
+ }
2058
+ return void 0;
2059
+ case Kind.COLON:
2060
+ throw new Error('unexpected token ":"');
2061
+ default:
2062
+ return void 0;
2063
+ }
2064
+ }
2065
+ #parseList() {
2066
+ const items = [];
2067
+ do {
2068
+ this.#advance();
2069
+ if (this._matches(Kind.GROUPER, "]"))
2070
+ break;
2071
+ items.push(this.#parseExpression());
2072
+ } while (this._matches(Kind.COMMA));
2073
+ this.#advance(Kind.GROUPER, "]");
2074
+ return this.#ast.list(items);
2075
+ }
2076
+ #parseMap() {
2077
+ const entries = {};
2078
+ do {
2079
+ this.#advance();
2080
+ if (this._matches(Kind.GROUPER, "}"))
2081
+ break;
2082
+ const key = this.#value;
2083
+ if (this._matches(Kind.STRING) || this._matches(Kind.IDENTIFIER)) {
2084
+ this.#advance();
2085
+ }
2086
+ this.#advance(Kind.COLON);
2087
+ entries[key] = this.#parseExpression();
2088
+ } while (this._matches(Kind.COMMA));
2089
+ this.#advance(Kind.GROUPER, "}");
2090
+ return this.#ast.map(entries);
2091
+ }
2092
+ #parseInvokeOrIdentifier() {
2093
+ const value = this.#value;
2094
+ if (value === "true") {
2095
+ this.#advance();
2096
+ return this.#ast.literal(true);
2097
+ }
2098
+ if (value === "false") {
2099
+ this.#advance();
2100
+ return this.#ast.literal(false);
2101
+ }
2102
+ if (value === "null") {
2103
+ this.#advance();
2104
+ return this.#ast.literal(null);
2105
+ }
2106
+ if (value === "undefined") {
2107
+ this.#advance();
2108
+ return this.#ast.literal(void 0);
2109
+ }
2110
+ const identifier = this.#parseIdentifier();
2111
+ const args = this.#parseArguments();
2112
+ return !args ? identifier : this.#ast.invoke(identifier, void 0, args);
2113
+ }
2114
+ #parseIdentifier() {
2115
+ if (!this._matches(Kind.IDENTIFIER)) {
2116
+ throw new Error(`expected identifier: ${this.#value}`);
2117
+ }
2118
+ const value = this.#value;
2119
+ this.#advance();
2120
+ return this.#ast.id(value);
2121
+ }
2122
+ #parseArguments() {
2123
+ if (!this._matches(Kind.GROUPER, "(")) {
2124
+ return void 0;
2125
+ }
2126
+ const args = [];
2127
+ do {
2128
+ this.#advance();
2129
+ if (this._matches(Kind.GROUPER, ")")) {
2130
+ break;
2131
+ }
2132
+ const expr = this.#parseExpression();
2133
+ args.push(expr);
2134
+ } while (this._matches(Kind.COMMA));
2135
+ this.#advance(Kind.GROUPER, ")");
2136
+ return args;
2137
+ }
2138
+ #parseIndex() {
2139
+ this.#advance();
2140
+ const expr = this.#parseExpression();
2141
+ this.#advance(Kind.GROUPER, "]");
2142
+ return expr;
2143
+ }
2144
+ #parseParenOrFunction() {
2145
+ const expressions = this.#parseArguments();
2146
+ if (this._matches(Kind.ARROW)) {
2147
+ this.#advance();
2148
+ const body = this.#parseExpression();
2149
+ const params = expressions?.map((e2) => e2.value) ?? [];
2150
+ return this.#ast.arrowFunction(params, body);
2151
+ } else {
2152
+ return this.#ast.paren(expressions[0]);
2153
+ }
2154
+ }
2155
+ #parseString() {
2156
+ const value = this.#ast.literal(this.#value);
2157
+ this.#advance();
2158
+ return value;
2159
+ }
2160
+ #parseInteger(prefix = "") {
2161
+ const value = this.#ast.literal(parseInt(`${prefix}${this.#value}`, 10));
2162
+ this.#advance();
2163
+ return value;
2164
+ }
2165
+ #parseDecimal(prefix = "") {
2166
+ const value = this.#ast.literal(parseFloat(`${prefix}${this.#value}`));
2167
+ this.#advance();
2168
+ return value;
2169
+ }
2170
+ };
2171
+
2172
+ // ../../node_modules/.pnpm/@heximal+expressions@0.1.5/node_modules/@heximal/expressions/lib/eval.js
2173
+ var { hasOwn, fromEntries } = Object;
2174
+ var _BINARY_OPERATORS = {
2175
+ "+": (a, b) => a + b,
2176
+ "-": (a, b) => a - b,
2177
+ "*": (a, b) => a * b,
2178
+ "/": (a, b) => a / b,
2179
+ "%": (a, b) => a % b,
2180
+ "==": (a, b) => a == b,
2181
+ "!=": (a, b) => a != b,
2182
+ "===": (a, b) => a === b,
2183
+ "!==": (a, b) => a !== b,
2184
+ ">": (a, b) => a > b,
2185
+ ">=": (a, b) => a >= b,
2186
+ "<": (a, b) => a < b,
2187
+ "<=": (a, b) => a <= b,
2188
+ "||": (a, b) => a || b,
2189
+ "&&": (a, b) => a && b,
2190
+ "??": (a, b) => a ?? b,
2191
+ "|": (a, f) => f(a),
2192
+ "|>": (a, f) => f(a)
2193
+ };
2194
+ var _UNARY_OPERATORS = {
2195
+ "+": (a) => a,
2196
+ "-": (a) => -a,
2197
+ "!": (a) => !a
2198
+ };
2199
+ var EvalAstFactory = class {
2200
+ empty() {
2201
+ return {
2202
+ type: "Empty",
2203
+ evaluate(scope) {
2204
+ return scope;
2205
+ },
2206
+ getIds(idents) {
2207
+ return idents;
2208
+ }
2209
+ };
2210
+ }
2211
+ // TODO(justinfagnani): just use a JS literal?
2212
+ literal(v) {
2213
+ return {
2214
+ type: "Literal",
2215
+ value: v,
2216
+ evaluate(_scope) {
2217
+ return this.value;
2218
+ },
2219
+ getIds(idents) {
2220
+ return idents;
2221
+ }
2222
+ };
2223
+ }
2224
+ id(v) {
2225
+ return {
2226
+ type: "ID",
2227
+ value: v,
2228
+ evaluate(scope) {
2229
+ if (this.value === "this")
2230
+ return scope;
2231
+ return scope?.[this.value];
2232
+ },
2233
+ getIds(idents) {
2234
+ idents.push(this.value);
2235
+ return idents;
2236
+ }
2237
+ };
2238
+ }
2239
+ unary(op, expr) {
2240
+ const f = _UNARY_OPERATORS[op];
2241
+ return {
2242
+ type: "Unary",
2243
+ operator: op,
2244
+ child: expr,
2245
+ evaluate(scope) {
2246
+ return f(this.child.evaluate(scope));
2247
+ },
2248
+ getIds(idents) {
2249
+ return this.child.getIds(idents);
2250
+ }
2251
+ };
2252
+ }
2253
+ binary(l, op, r3) {
2254
+ const f = _BINARY_OPERATORS[op];
2255
+ return {
2256
+ type: "Binary",
2257
+ operator: op,
2258
+ left: l,
2259
+ right: r3,
2260
+ evaluate(scope) {
2261
+ if (this.operator === "=") {
2262
+ if (this.left.type !== "ID" && this.left.type !== "Getter" && this.left.type !== "Index") {
2263
+ throw new Error(`Invalid assignment target: ${this.left}`);
2264
+ }
2265
+ const value = this.right.evaluate(scope);
2266
+ let receiver = void 0;
2267
+ let property;
2268
+ if (this.left.type === "Getter") {
2269
+ receiver = this.left.receiver.evaluate(scope);
2270
+ property = this.left.name;
2271
+ } else if (this.left.type === "Index") {
2272
+ receiver = this.left.receiver.evaluate(scope);
2273
+ property = this.left.argument.evaluate(scope);
2274
+ } else if (this.left.type === "ID") {
2275
+ receiver = scope;
2276
+ property = this.left.value;
2277
+ }
2278
+ return receiver === void 0 ? void 0 : receiver[property] = value;
2279
+ }
2280
+ return f(this.left.evaluate(scope), this.right.evaluate(scope));
2281
+ },
2282
+ getIds(idents) {
2283
+ this.left.getIds(idents);
2284
+ this.right.getIds(idents);
2285
+ return idents;
2286
+ }
2287
+ };
2288
+ }
2289
+ getter(g, n2) {
2290
+ return {
2291
+ type: "Getter",
2292
+ receiver: g,
2293
+ name: n2,
2294
+ evaluate(scope) {
2295
+ return this.receiver.evaluate(scope)?.[this.name];
2296
+ },
2297
+ getIds(idents) {
2298
+ this.receiver.getIds(idents);
2299
+ return idents;
2300
+ }
2301
+ };
2302
+ }
2303
+ invoke(receiver, method, args) {
2304
+ if (method != null && typeof method !== "string") {
2305
+ throw new Error("method not a string");
2306
+ }
2307
+ return {
2308
+ type: "Invoke",
2309
+ receiver,
2310
+ method,
2311
+ arguments: args,
2312
+ evaluate(scope) {
2313
+ const receiver2 = this.receiver.evaluate(scope);
2314
+ const _this = this.method ? receiver2 : scope?.["this"] ?? scope;
2315
+ const f = this.method ? receiver2?.[method] : receiver2;
2316
+ const args2 = this.arguments ?? [];
2317
+ const argValues = args2.map((a) => a?.evaluate(scope));
2318
+ return f?.apply?.(_this, argValues);
2319
+ },
2320
+ getIds(idents) {
2321
+ this.receiver.getIds(idents);
2322
+ this.arguments?.forEach((a) => a?.getIds(idents));
2323
+ return idents;
2324
+ }
2325
+ };
2326
+ }
2327
+ paren(e2) {
2328
+ return e2;
2329
+ }
2330
+ index(e2, a) {
2331
+ return {
2332
+ type: "Index",
2333
+ receiver: e2,
2334
+ argument: a,
2335
+ evaluate(scope) {
2336
+ return this.receiver.evaluate(scope)?.[this.argument.evaluate(scope)];
2337
+ },
2338
+ getIds(idents) {
2339
+ this.receiver.getIds(idents);
2340
+ return idents;
2341
+ }
2342
+ };
2343
+ }
2344
+ ternary(c2, t2, f) {
2345
+ return {
2346
+ type: "Ternary",
2347
+ condition: c2,
2348
+ trueExpr: t2,
2349
+ falseExpr: f,
2350
+ evaluate(scope) {
2351
+ const c3 = this.condition.evaluate(scope);
2352
+ if (c3) {
2353
+ return this.trueExpr.evaluate(scope);
2354
+ } else {
2355
+ return this.falseExpr.evaluate(scope);
2356
+ }
2357
+ },
2358
+ getIds(idents) {
2359
+ this.condition.getIds(idents);
2360
+ this.trueExpr.getIds(idents);
2361
+ this.falseExpr.getIds(idents);
2362
+ return idents;
2363
+ }
2364
+ };
2365
+ }
2366
+ map(entries) {
2367
+ return {
2368
+ type: "Map",
2369
+ entries,
2370
+ evaluate(scope) {
2371
+ const map = {};
2372
+ if (entries && this.entries) {
2373
+ for (const key in entries) {
2374
+ const val = this.entries[key];
2375
+ if (val) {
2376
+ map[key] = val.evaluate(scope);
2377
+ }
2378
+ }
2379
+ }
2380
+ return map;
2381
+ },
2382
+ getIds(idents) {
2383
+ if (entries && this.entries) {
2384
+ for (const key in entries) {
2385
+ const val = this.entries[key];
2386
+ if (val) {
2387
+ val.getIds(idents);
2388
+ }
2389
+ }
2390
+ }
2391
+ return idents;
2392
+ }
2393
+ };
2394
+ }
2395
+ // TODO(justinfagnani): if the list is deeply literal
2396
+ list(l) {
2397
+ return {
2398
+ type: "List",
2399
+ items: l,
2400
+ evaluate(scope) {
2401
+ return this.items?.map((a) => a?.evaluate(scope));
2402
+ },
2403
+ getIds(idents) {
2404
+ this.items?.forEach((i4) => i4?.getIds(idents));
2405
+ return idents;
2406
+ }
2407
+ };
2408
+ }
2409
+ arrowFunction(params, body) {
2410
+ return {
2411
+ type: "ArrowFunction",
2412
+ params,
2413
+ body,
2414
+ evaluate(scope) {
2415
+ const params2 = this.params;
2416
+ const body2 = this.body;
2417
+ return function(...args) {
2418
+ const paramsObj = fromEntries(params2.map((p, i4) => [p, args[i4]]));
2419
+ const newScope = new Proxy(scope ?? {}, {
2420
+ set(target, prop, value) {
2421
+ if (hasOwn(paramsObj, prop)) {
2422
+ paramsObj[prop] = value;
2423
+ }
2424
+ return target[prop] = value;
2425
+ },
2426
+ get(target, prop) {
2427
+ if (hasOwn(paramsObj, prop)) {
2428
+ return paramsObj[prop];
2429
+ }
2430
+ return target[prop];
2431
+ }
2432
+ });
2433
+ return body2.evaluate(newScope);
2434
+ };
2435
+ },
2436
+ getIds(idents) {
2437
+ return this.body.getIds(idents).filter((id) => !this.params.includes(id));
2438
+ }
2439
+ };
2440
+ }
2441
+ };
2442
+
2443
+ // ../../node_modules/.pnpm/lit-html@3.3.1/node_modules/lit-html/node/private-ssr-support.js
2444
+ var r2 = null;
2445
+ var i3 = { boundAttributeSuffix: W.M, marker: W.P, markerMatch: W.A, HTML_RESULT: W.C, getTemplateHtml: W.L, overrideDirectiveResolve: (e2, t2) => class extends e2 {
2446
+ _$AS(e3, r3) {
2447
+ return t2(this, r3);
2448
+ }
2449
+ }, patchDirectiveResolve: (e2, t2) => {
2450
+ if (e2.prototype._$AS.name !== t2.name) {
2451
+ r2 ??= e2.prototype._$AS.name;
2452
+ for (let i4 = e2.prototype; i4 !== Object.prototype; i4 = Object.getPrototypeOf(i4)) if (i4.hasOwnProperty(r2)) return void (i4[r2] = t2);
2453
+ throw Error("Internal error: It is possible that both dev mode and production mode Lit was mixed together during SSR. Please comment on the issue: https://github.com/lit/lit/issues/4527");
2454
+ }
2455
+ }, setDirectiveClass(e2, t2) {
2456
+ e2._$litDirective$ = t2;
2457
+ }, getAttributePartCommittedValue: (e2, r3, i4) => {
2458
+ let o = w;
2459
+ return e2.j = (e3) => o = e3, e2._$AI(r3, e2, i4), o;
2460
+ }, connectedDisconnectable: (e2) => ({ ...e2, _$AU: true }), resolveDirective: W.V, AttributePart: W.H, PropertyPart: W.B, BooleanAttributePart: W.N, EventPart: W.U, ElementPart: W.F, TemplateInstance: W.R, isIterable: W.D, ChildPart: W.I };
2461
+
2462
+ // ../../node_modules/.pnpm/@heximal+templates@0.1.5/node_modules/@heximal/templates/index.js
2463
+ var { AttributePart, PropertyPart, BooleanAttributePart, EventPart } = i3;
2464
+ var astFactory = new EvalAstFactory();
2465
+ var expressionCache = /* @__PURE__ */ new Map();
2466
+ var toCamelCase = (s) => s.replace(/-(-|\w)/g, (_, p1) => p1.toUpperCase());
2467
+ var getSingleValue = (s, model) => {
2468
+ let ast = expressionCache.get(s);
2469
+ if (ast === void 0) {
2470
+ if (expressionCache.has(s)) {
2471
+ return void 0;
2472
+ }
2473
+ s = s.trim();
2474
+ if (s.startsWith("{{") && s.endsWith("}}")) {
2475
+ const expression = s.substring(2, s.length - 2).trim();
2476
+ ast = new Parser(expression, astFactory).parse();
2477
+ expressionCache.set(s, ast);
2478
+ }
2479
+ }
2480
+ return ast?.evaluate(model);
2481
+ };
2482
+ var ifHandler = (template, model, handlers, renderers) => {
2483
+ const ifAttribute = template.getAttribute("if");
2484
+ if (ifAttribute !== null && getSingleValue(ifAttribute, model)) {
2485
+ return evaluateTemplate(template, model, handlers, renderers);
2486
+ }
2487
+ return void 0;
2488
+ };
2489
+ var bindingRegex = /(?<!\\){{(.*?)(?:(?<!\\)}})/g;
2490
+ var hasEscapedBindingMarkers = (s) => /(?:\\{{)|(?:\\}})/g.test(s);
2491
+ var unescapeBindingMarkers = (s) => s.replaceAll(/\\{{/g, "{{").replace(/\\}}/g, "}}");
2492
+ var repeatHandler = (template, model, handlers, renderers) => {
2493
+ const repeatAttribute = template.getAttribute("repeat");
2494
+ if (repeatAttribute !== null) {
2495
+ const items = getSingleValue(repeatAttribute, model);
2496
+ if (!items[Symbol.iterator]) {
2497
+ return E;
2498
+ }
2499
+ const litTemplate = getLitTemplate(template);
2500
+ let index = -1;
2501
+ const result = [];
2502
+ for (const item of items) {
2503
+ index++;
2504
+ const itemModel = Object.create(model);
2505
+ itemModel.item = item;
2506
+ itemModel.index = index;
2507
+ itemModel["this"] = model["this"] ?? model;
2508
+ const values = [];
2509
+ for (const part of litTemplate.parts) {
2510
+ const value = part.update(itemModel, handlers, renderers);
2511
+ if (part.type === 1) {
2512
+ values.push(...value);
2513
+ } else {
2514
+ values.push(value);
2515
+ }
2516
+ }
2517
+ const templateResult = {
2518
+ _$litType$: litTemplate,
2519
+ values
2520
+ };
2521
+ result.push(templateResult);
2522
+ }
2523
+ return result;
2524
+ }
2525
+ return void 0;
2526
+ };
2527
+ var defaultHandlers = {
2528
+ if: ifHandler,
2529
+ repeat: repeatHandler
2530
+ };
2531
+ var prepareTemplate = (template, handlers = defaultHandlers, renderers = {}, superTemplate) => {
2532
+ const litTemplate = getLitTemplate(template);
2533
+ const templateRenderers = litTemplate.renderers;
2534
+ if (superTemplate) {
2535
+ const superLitTemplate = getLitTemplate(superTemplate);
2536
+ const superRenderers = superLitTemplate.renderers;
2537
+ const superCallRenderer = templateRenderers["super"];
2538
+ if (superCallRenderer !== void 0) {
2539
+ renderers = {
2540
+ // sub template's own renderes
2541
+ ...templateRenderers,
2542
+ // passed-in renderers
2543
+ ...renderers,
2544
+ // a super call renderer
2545
+ super: (model, handlers2, renderers2) => {
2546
+ renderers2 = {
2547
+ // super template's own blocks
2548
+ ...superRenderers,
2549
+ // passed-in renderers
2550
+ ...renderers2,
2551
+ // sub template's overrides will be added by the inner super call
2552
+ super: (model2, handlers3, renderers3) => {
2553
+ return evaluateTemplate(superTemplate, model2, handlers3, renderers3);
2554
+ }
2555
+ };
2556
+ return superCallRenderer(model, handlers2, renderers2);
2557
+ }
2558
+ };
2559
+ } else {
2560
+ renderers = {
2561
+ // super template's own blocks
2562
+ ...superRenderers,
2563
+ // sub template's overrides
2564
+ ...templateRenderers,
2565
+ // passed-in renderers
2566
+ ...renderers
2567
+ };
2568
+ template = superTemplate;
2569
+ }
2570
+ } else {
2571
+ renderers = {
2572
+ // template's named blocks
2573
+ ...templateRenderers,
2574
+ // passed-in renderers
2575
+ ...renderers
2576
+ };
2577
+ }
2578
+ return (model) => evaluateTemplate(template, model, handlers, renderers);
2579
+ };
2580
+ var evaluateTemplate = (template, model, handlers = defaultHandlers, renderers = {}) => {
2581
+ const litTemplate = getLitTemplate(template);
2582
+ const values = [];
2583
+ for (const part of litTemplate.parts) {
2584
+ const value = part.update(model, handlers, renderers);
2585
+ if (part.type === 1) {
2586
+ values.push(...value);
2587
+ } else {
2588
+ values.push(value);
2589
+ }
2590
+ }
2591
+ const templateResult = {
2592
+ _$litType$: litTemplate,
2593
+ values
2594
+ };
2595
+ return templateResult;
2596
+ };
2597
+ var litTemplateCache = /* @__PURE__ */ new Map();
2598
+ var getLitTemplate = (template) => {
2599
+ let litTemplate = litTemplateCache.get(template);
2600
+ if (litTemplate === void 0) {
2601
+ litTemplateCache.set(template, litTemplate = makeLitTemplate(template));
2602
+ }
2603
+ return litTemplate;
2604
+ };
2605
+ var makeLitTemplate = (template) => {
2606
+ const litTemplate = {
2607
+ h: void 0,
2608
+ el: template.cloneNode(true),
2609
+ parts: [],
2610
+ renderers: {}
2611
+ };
2612
+ const walker = document.createTreeWalker(litTemplate.el.content, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT);
2613
+ let node = walker.currentNode;
2614
+ let nodeIndex = -1;
2615
+ const elementsToRemove = [];
2616
+ while ((node = walker.nextNode()) !== null) {
2617
+ if (node.nodeType === Node.COMMENT_NODE) {
2618
+ nodeIndex++;
2619
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
2620
+ nodeIndex++;
2621
+ const element = node;
2622
+ if (element.tagName === "TEMPLATE") {
2623
+ const type = element.getAttribute("type");
2624
+ const name = element.getAttribute("name");
2625
+ const call = element.getAttribute("call");
2626
+ if (call !== null || type !== null || name !== null) {
2627
+ element.parentNode.insertBefore(document.createComment(""), element);
2628
+ elementsToRemove.push(element);
2629
+ let update;
2630
+ if (call !== null) {
2631
+ const templateName = call.trim();
2632
+ const templateNameIsExpression = templateName.startsWith("{{") && templateName.endsWith("}}");
2633
+ update = (model, handlers, renderers) => {
2634
+ const dataAttr = element.getAttribute("data");
2635
+ const data = dataAttr === null ? void 0 : getSingleValue(dataAttr, model);
2636
+ const renderer = templateNameIsExpression ? getSingleValue(templateName, model) : renderers[call];
2637
+ return renderer?.(data, handlers, renderers);
2638
+ };
2639
+ } else if (type !== null) {
2640
+ update = (model, handlers, renderers) => {
2641
+ const handler = handlers[type];
2642
+ return handler?.(element, model, handlers, renderers);
2643
+ };
2644
+ } else {
2645
+ if (name === "super") {
2646
+ litTemplate.renderers["super"] = (model, handlers, renderers) => {
2647
+ const superRenderer = renderers["super"];
2648
+ const superCallTemplate = getLitTemplate(element);
2649
+ renderers = {
2650
+ ...renderers,
2651
+ ...superCallTemplate.renderers
2652
+ };
2653
+ return superRenderer(model, handlers, renderers);
2654
+ };
2655
+ } else {
2656
+ litTemplate.renderers[name] = (model, handlers, renderers) => {
2657
+ return evaluateTemplate(element, model, handlers, renderers);
2658
+ };
2659
+ }
2660
+ update = (model, handlers, renderers) => {
2661
+ const renderer = renderers[name];
2662
+ return renderer?.(model, handlers, renderers);
2663
+ };
2664
+ }
2665
+ litTemplate.parts.push({
2666
+ type: 2,
2667
+ // text binding
2668
+ index: nodeIndex,
2669
+ update
2670
+ });
2671
+ continue;
2672
+ }
2673
+ }
2674
+ const attributeNames = element.getAttributeNames();
2675
+ for (const attributeName of attributeNames) {
2676
+ const attributeValue = element.getAttribute(attributeName);
2677
+ const splitValue = attributeValue.split(bindingRegex);
2678
+ if (splitValue.length === 1) {
2679
+ if (hasEscapedBindingMarkers(attributeValue)) {
2680
+ element.setAttribute(attributeName, unescapeBindingMarkers(attributeValue));
2681
+ }
2682
+ continue;
2683
+ }
2684
+ element.removeAttribute(attributeName);
2685
+ let name = attributeName;
2686
+ let ctor = AttributePart;
2687
+ const prefix = attributeName[0];
2688
+ if (prefix === ".") {
2689
+ name = toCamelCase(attributeName.substring(1));
2690
+ ctor = PropertyPart;
2691
+ } else if (prefix === "?") {
2692
+ name = attributeName.substring(1);
2693
+ ctor = BooleanAttributePart;
2694
+ } else if (prefix === "@") {
2695
+ name = toCamelCase(attributeName.substring(1));
2696
+ ctor = EventPart;
2697
+ }
2698
+ const strings = [unescapeBindingMarkers(splitValue[0])];
2699
+ const exprs = [];
2700
+ for (let i4 = 1; i4 < splitValue.length; i4 += 2) {
2701
+ const exprText = splitValue[i4];
2702
+ exprs.push(parse(exprText, astFactory));
2703
+ strings.push(unescapeBindingMarkers(splitValue[i4 + 1]));
2704
+ }
2705
+ litTemplate.parts.push({
2706
+ type: 1,
2707
+ // attribute binding
2708
+ index: nodeIndex,
2709
+ name,
2710
+ strings,
2711
+ ctor,
2712
+ update: (model, _handlers, _renderers) => {
2713
+ return exprs.map((expr) => expr.evaluate(model));
2714
+ }
2715
+ });
2716
+ }
2717
+ } else if (node.nodeType === Node.TEXT_NODE) {
2718
+ let textNode = node;
2719
+ const text = textNode.textContent;
2720
+ const strings = text.split(bindingRegex);
2721
+ if (strings.length > 1) {
2722
+ textNode.textContent = unescapeBindingMarkers(strings[0]);
2723
+ } else if (hasEscapedBindingMarkers(text)) {
2724
+ textNode.textContent = unescapeBindingMarkers(text);
2725
+ }
2726
+ for (let i4 = 1; i4 < strings.length; i4 += 2) {
2727
+ const exprText = strings[i4];
2728
+ const expr = parse(exprText, astFactory);
2729
+ litTemplate.parts.push({
2730
+ type: 2,
2731
+ index: ++nodeIndex,
2732
+ update: (model, _handlers) => expr.evaluate(model)
2733
+ });
2734
+ const newTextNode = new Text(strings[i4 + 1].replace("\\{{", "{{"));
2735
+ textNode.parentNode.insertBefore(newTextNode, textNode.nextSibling);
2736
+ textNode.parentNode.insertBefore(document.createComment(""), textNode.nextSibling);
2737
+ textNode = newTextNode;
2738
+ walker.currentNode = newTextNode;
2739
+ }
2740
+ }
2741
+ }
2742
+ for (const e2 of elementsToRemove) {
2743
+ e2.remove();
2744
+ }
2745
+ return litTemplate;
2746
+ };
2747
+
2748
+ // ../qti-test/src/components/test-paging-buttons-stamp/test-paging-buttons-stamp.ts
2749
+ var TestPagingButtonsStamp = class extends i2 {
2750
+ createRenderRoot() {
2751
+ return this;
2752
+ }
2753
+ constructor() {
2754
+ super();
2755
+ this._internals = this.attachInternals();
2756
+ this._internals.ariaLabel = "pagination";
2757
+ }
2758
+ connectedCallback() {
2759
+ super.connectedCallback();
2760
+ const templateElement = this.querySelector("template");
2761
+ this.myTemplate = prepareTemplate(templateElement);
2762
+ }
2763
+ render() {
2764
+ if (!this.computedContext) return x``;
2765
+ const items = this.computedContext.testParts.flatMap(
2766
+ (testPart) => testPart.sections.flatMap((section) => section.items)
2767
+ );
2768
+ return x` ${items.map((item) => this.myTemplate({ item, view: this.computedContext.view }))} `;
2769
+ }
2770
+ };
2771
+ __decorateClass([
2772
+ c({ context: computedContext, subscribe: true })
2773
+ ], TestPagingButtonsStamp.prototype, "computedContext", 2);
2774
+ TestPagingButtonsStamp = __decorateClass([
2775
+ t("test-paging-buttons-stamp")
2776
+ ], TestPagingButtonsStamp);
2777
+
2778
+ // ../qti-test/src/components/test-container/test-container.ts
2779
+ var TestContainer = class extends i2 {
2780
+ constructor() {
2781
+ super(...arguments);
2782
+ this.testURL = null;
2783
+ this.testDoc = null;
2784
+ this.testXML = null;
2785
+ /** Template content if provided */
2786
+ this.templateContent = null;
2787
+ }
2788
+ async handleTestURLChange() {
2789
+ if (!this.testURL) return;
2790
+ try {
2791
+ let api = await qtiTransformTest().load(this.testURL);
2792
+ const qtiTest = this.closest("qti-test");
2793
+ if (qtiTest?.postLoadTestTransformCallback) {
2794
+ const tempDoc = api.htmlDoc();
2795
+ const testElement = tempDoc.querySelector("qti-assessment-test");
2796
+ if (testElement) {
2797
+ api = await qtiTest.postLoadTestTransformCallback(api, testElement);
2798
+ }
2799
+ }
2800
+ this.testDoc = api.htmlDoc();
2801
+ } catch (error) {
2802
+ console.error("Error loading or parsing XML:", error);
2803
+ }
2804
+ }
2805
+ handleTestXMLChange() {
2806
+ if (!this.testXML) return;
2807
+ try {
2808
+ this.testDoc = qtiTransformTest().parse(this.testXML).htmlDoc();
2809
+ } catch (error) {
2810
+ console.error("Error parsing XML:", error);
2811
+ }
2812
+ }
2813
+ async connectedCallback() {
2814
+ super.connectedCallback();
2815
+ this.initializeTemplateContent();
2816
+ this.applyStyles();
2817
+ if (this.testURL) {
2818
+ this.handleTestURLChange();
2819
+ }
2820
+ if (this.testXML) {
2821
+ this.handleTestXMLChange();
2822
+ }
2823
+ }
2824
+ initializeTemplateContent() {
2825
+ const template = this.querySelector("template");
2826
+ this.templateContent = template ? template.content : x``;
2827
+ }
2828
+ applyStyles() {
2829
+ const sheet = new CSSStyleSheet();
2830
+ sheet.replaceSync(item_default);
2831
+ this.shadowRoot.adoptedStyleSheets = [sheet];
2832
+ }
2833
+ render() {
2834
+ return x`
2835
+ ${this.templateContent}
2836
+ <slot></slot>
2837
+ ${m(this.testDoc, x`<span>Loading...</span>`)}
2838
+ `;
2839
+ }
2840
+ };
2841
+ __decorateClass([
2842
+ n({ type: String, attribute: "test-url" })
2843
+ ], TestContainer.prototype, "testURL", 2);
2844
+ __decorateClass([
2845
+ r()
2846
+ ], TestContainer.prototype, "testDoc", 2);
2847
+ __decorateClass([
2848
+ r()
2849
+ ], TestContainer.prototype, "testXML", 2);
2850
+ __decorateClass([
2851
+ watch("testURL", { waitUntilFirstUpdate: true })
2852
+ ], TestContainer.prototype, "handleTestURLChange", 1);
2853
+ __decorateClass([
2854
+ watch("testXML", { waitUntilFirstUpdate: true })
2855
+ ], TestContainer.prototype, "handleTestXMLChange", 1);
2856
+ TestContainer = __decorateClass([
2857
+ t("test-container")
2858
+ ], TestContainer);
2859
+
2860
+ // ../qti-test/src/components/test-print-item-variables/test-print-item-variables.ts
2861
+ var TestPrintVariables = class extends i2 {
2862
+ render() {
2863
+ const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
2864
+ if (!activeItem || !activeItem.variables) return x``;
2865
+ const responseVariables = activeItem.variables.filter((v) => v.type === "response");
2866
+ const outcomeVariables = activeItem.variables.filter((v) => v.type === "outcome");
2867
+ const renderTable = (variables, title) => x`
2868
+ <h3>${title}</h3>
2869
+ <table>
2870
+ <thead>
2871
+ <tr>
2872
+ <th>Identifier</th>
2873
+ <th>Value</th>
2874
+ <th>Cardinality</th>
2875
+ <th>Base Type</th>
2876
+ <th>Correct Response / Mappings</th>
2877
+ </tr>
2878
+ </thead>
2879
+ <tbody>
2880
+ ${variables.map((v) => {
2881
+ const correctResponse = v.correctResponse ? Array.isArray(v.correctResponse) ? v.correctResponse.join(", ") : v.correctResponse : "";
2882
+ const mapEntries = v.mapping?.mapEntries?.map((m2) => `${m2.mapKey}=${m2.mappedValue}pt`).join(", ") || "";
2883
+ const areaMapEntries = v.areaMapping?.areaMapEntries?.map((m2) => `${m2.shape}(${m2.coords})=${m2.mappedValue}pt`).join(", ") || "";
2884
+ return x`
2885
+ <tr>
2886
+ <td>${v.identifier}</td>
2887
+ <td>${Array.isArray(v.value) ? v.value.join(", ") : v.value}</td>
2888
+ <td>${v.cardinality}</td>
2889
+ <td>${v.baseType}</td>
2890
+ <td>${correctResponse || mapEntries || areaMapEntries}</td>
2891
+ </tr>
2892
+ `;
2893
+ })}
2894
+ </tbody>
2895
+ </table>
2896
+ `;
2897
+ return x`
2898
+ ${renderTable(responseVariables, "Response Variables")} ${renderTable(outcomeVariables, "Outcome Variables")}
2899
+ `;
2900
+ }
2901
+ };
2902
+ TestPrintVariables.styles = i`
2903
+ table {
2904
+ width: 100%;
2905
+ border-collapse: collapse;
2906
+ margin: 20px 0;
2907
+ font-size: 14px;
2908
+ text-align: left;
2909
+ }
2910
+ th,
2911
+ td {
2912
+ border: 1px solid #ddd;
2913
+ padding: 8px;
2914
+ }
2915
+ th {
2916
+ background-color: #f4f4f4;
2917
+ font-weight: bold;
2918
+ }
2919
+ h3 {
2920
+ margin-top: 20px;
2921
+ font-size: 16px;
2922
+ }
2923
+ `;
2924
+ __decorateClass([
2925
+ c({ context: computedContext, subscribe: true })
2926
+ ], TestPrintVariables.prototype, "computedContext", 2);
2927
+ TestPrintVariables = __decorateClass([
2928
+ t("test-print-item-variables")
2929
+ ], TestPrintVariables);
2930
+
2931
+ // ../qti-test/src/components/test-section-buttons-stamp/test-section-buttons-stamp.ts
2932
+ var TestSectionButtonsStamp = class extends i2 {
2933
+ createRenderRoot() {
2934
+ return this;
2935
+ }
2936
+ constructor() {
2937
+ super();
2938
+ this._internals = this.attachInternals();
2939
+ this._internals.ariaLabel = "pagination";
2940
+ }
2941
+ connectedCallback() {
2942
+ super.connectedCallback();
2943
+ const templateElement = this.querySelector("template");
2944
+ this.myTemplate = prepareTemplate(templateElement);
2945
+ }
2946
+ render() {
2947
+ if (!this.computedContext) return x``;
2948
+ const sections = this.computedContext.testParts.flatMap((testPart) => testPart.sections);
2949
+ return x` ${sections.map((item) => this.myTemplate({ item }))} `;
2950
+ }
2951
+ };
2952
+ __decorateClass([
2953
+ c({ context: computedContext, subscribe: true })
2954
+ ], TestSectionButtonsStamp.prototype, "computedContext", 2);
2955
+ TestSectionButtonsStamp = __decorateClass([
2956
+ t("test-section-buttons-stamp")
2957
+ ], TestSectionButtonsStamp);
2958
+
2959
+ // ../qti-test/src/components/test-section-link/test-section-link.ts
2960
+ var TestSectionLink = class extends i2 {
2961
+ constructor() {
2962
+ super();
2963
+ this.sectionId = null;
2964
+ this.addEventListener("click", () => this._requestItem(this.sectionId));
2965
+ }
2966
+ _requestItem(identifier) {
2967
+ this.dispatchEvent(
2968
+ new CustomEvent("qti-request-navigation", {
2969
+ composed: true,
2970
+ bubbles: true,
2971
+ detail: {
2972
+ type: "section",
2973
+ id: identifier
2974
+ }
2975
+ })
2976
+ );
2977
+ }
2978
+ render() {
2979
+ return x` <slot></slot> `;
2980
+ }
2981
+ };
2982
+ TestSectionLink.styles = i`
2983
+ :host {
2984
+ ${btn};
2985
+ }
2986
+ :host([disabled]) {
2987
+ ${dis};
2988
+ }
2989
+ `;
2990
+ __decorateClass([
2991
+ n({ type: String, attribute: "section-id" })
2992
+ ], TestSectionLink.prototype, "sectionId", 2);
2993
+ TestSectionLink = __decorateClass([
2994
+ t("test-section-link")
2995
+ ], TestSectionLink);
2996
+
2997
+ // ../qti-test/src/components/test-print-context/test-print-context.ts
2998
+ var TestPrintContext = class extends i2 {
2999
+ render() {
3000
+ return x` <small><pre>${JSON.stringify(this.computedContext, null, 2)}</pre></small> `;
3001
+ }
3002
+ };
3003
+ __decorateClass([
3004
+ r(),
3005
+ c({ context: computedContext, subscribe: true })
3006
+ ], TestPrintContext.prototype, "computedContext", 2);
3007
+ TestPrintContext = __decorateClass([
3008
+ t("test-print-context")
3009
+ ], TestPrintContext);
3010
+
3011
+ // ../qti-test/src/components/test-stamp/test-stamp.ts
3012
+ var TestStamp = class extends i2 {
3013
+ constructor() {
3014
+ super(...arguments);
3015
+ this.debug = false;
3016
+ this.stampContext = {
3017
+ view: "candidate",
3018
+ activeItem: {},
3019
+ activeSection: {
3020
+ items: []
3021
+ },
3022
+ activeTestpart: {
3023
+ items: [],
3024
+ sections: []
3025
+ },
3026
+ test: {}
3027
+ };
3028
+ }
3029
+ createRenderRoot() {
3030
+ return this;
3031
+ }
3032
+ connectedCallback() {
3033
+ super.connectedCallback();
3034
+ const templateElement = this.querySelector("template");
3035
+ if (!templateElement) {
3036
+ this.myTemplate = null;
3037
+ return;
3038
+ }
3039
+ this.myTemplate = prepareTemplate(templateElement);
3040
+ }
3041
+ willUpdate(_changedProperties) {
3042
+ if (!this.computedContext) {
3043
+ return;
3044
+ }
3045
+ const activeTestPart = this.computedContext.testParts.find((testPart) => testPart.active);
3046
+ const activeSection = activeTestPart?.sections.find((section) => section.active);
3047
+ const activeItem = activeSection?.items.find((item) => item.active);
3048
+ const { variables, ...augmentedItem } = activeItem || {};
3049
+ if (!activeTestPart || !activeSection || !activeItem) {
3050
+ return;
3051
+ }
3052
+ const augmentedTestPart = {
3053
+ ...activeTestPart,
3054
+ items: activeTestPart.sections.flatMap((section) => section.items.map(({ variables: variables2, ...rest }) => rest)),
3055
+ sections: activeTestPart.sections.map((section) => ({
3056
+ ...section,
3057
+ items: section.items.map(({ variables: variables2, ...rest }) => rest)
3058
+ }))
3059
+ };
3060
+ const augmentedSection = { ...activeSection, items: activeSection.items.map(({ variables: variables2, ...rest }) => rest) };
3061
+ const { testParts, ...activeTest } = this.computedContext;
3062
+ this.stampContext = {
3063
+ view: this.computedContext.view,
3064
+ activeItem: augmentedItem,
3065
+ activeSection: augmentedSection,
3066
+ activeTestpart: augmentedTestPart,
3067
+ test: activeTest
3068
+ };
3069
+ this.dispatchEvent(
3070
+ new CustomEvent("qti-stamp-context-updated", {
3071
+ detail: this.stampContext,
3072
+ bubbles: true
3073
+ })
3074
+ );
3075
+ }
3076
+ render() {
3077
+ return x` ${this.debug ? x`<small><pre>${JSON.stringify(this.stampContext, null, 2)}</pre></small>` : E}
3078
+ ${this.myTemplate ? this.myTemplate(this.stampContext) : E}`;
3079
+ }
3080
+ };
3081
+ __decorateClass([
3082
+ n({ type: Boolean, reflect: true })
3083
+ ], TestStamp.prototype, "debug", 2);
3084
+ __decorateClass([
3085
+ r(),
3086
+ c({ context: computedContext, subscribe: true })
3087
+ ], TestStamp.prototype, "computedContext", 2);
3088
+ __decorateClass([
3089
+ r()
3090
+ ], TestStamp.prototype, "stampContext", 2);
3091
+ TestStamp = __decorateClass([
3092
+ t("test-stamp")
3093
+ ], TestStamp);
3094
+
3095
+ // ../qti-test/src/components/test-scoring-buttons/test-scoring-buttons.ts
3096
+ var TestScoringButtons = class extends i2 {
3097
+ constructor() {
3098
+ super();
3099
+ this.view = "";
3100
+ this.disabled = false;
3101
+ this.myTemplate = null;
3102
+ this.addEventListener("click", (e2) => {
3103
+ const target = e2.target;
3104
+ const value = parseFloat(target.value);
3105
+ if (target.tagName === "INPUT") {
3106
+ this._changeOutcomeScore(value);
3107
+ }
3108
+ });
3109
+ }
3110
+ createRenderRoot() {
3111
+ return this;
3112
+ }
3113
+ connectedCallback() {
3114
+ super.connectedCallback();
3115
+ const templateElement = this.querySelector("template");
3116
+ if (!templateElement) {
3117
+ this.myTemplate = null;
3118
+ return;
3119
+ }
3120
+ this.myTemplate = prepareTemplate(templateElement);
3121
+ }
3122
+ _changeOutcomeScore(value) {
3123
+ const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
3124
+ const sectionItems = testPart.sections.flatMap((section) => section.items);
3125
+ const currentItemIdentifier = sectionItems.find((item) => item.active)?.identifier;
3126
+ this.dispatchEvent(
3127
+ new CustomEvent("test-update-outcome-variable", {
3128
+ detail: {
3129
+ assessmentItemRefId: currentItemIdentifier,
3130
+ outcomeVariableId: "SCORE",
3131
+ value
3132
+ },
3133
+ bubbles: true
3134
+ })
3135
+ );
3136
+ }
3137
+ render() {
3138
+ const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
3139
+ if (!activeItem || !activeItem.variables) return x``;
3140
+ const maxScore = activeItem.variables.find((vr) => vr.identifier == "MAXSCORE")?.value;
3141
+ const scoreOutcome = activeItem.variables.find((vr) => vr.identifier == "SCORE");
3142
+ const score = scoreOutcome?.value;
3143
+ const disabled = !(scoreOutcome?.externalScored === "human");
3144
+ if (!maxScore || !scoreOutcome) return E;
3145
+ const scores = [...Array(Number(maxScore) + 1).keys()];
3146
+ return x`${this.myTemplate ? this.myTemplate({ scores, score, disabled }) : E}`;
3147
+ }
3148
+ };
3149
+ __decorateClass([
3150
+ n({ type: String, attribute: "view" })
3151
+ ], TestScoringButtons.prototype, "view", 2);
3152
+ __decorateClass([
3153
+ n({ type: Boolean })
3154
+ ], TestScoringButtons.prototype, "disabled", 2);
3155
+ __decorateClass([
3156
+ c({ context: computedContext, subscribe: true })
3157
+ ], TestScoringButtons.prototype, "computedContext", 2);
3158
+ TestScoringButtons = __decorateClass([
3159
+ t("test-scoring-buttons")
3160
+ ], TestScoringButtons);
3161
+
3162
+ // ../qti-test/src/components/test-view-toggle/test-view-toggle.ts
3163
+ var TestViewToggle = class extends i2 {
3164
+ createRenderRoot() {
3165
+ return this;
3166
+ }
3167
+ connectedCallback() {
3168
+ super.connectedCallback();
3169
+ const templateElement = this.querySelector("template");
3170
+ if (!templateElement) {
3171
+ this.myTemplate = null;
3172
+ return;
3173
+ }
3174
+ this.myTemplate = prepareTemplate(templateElement);
3175
+ }
3176
+ _switchView(view) {
3177
+ this.dispatchEvent(
3178
+ new CustomEvent("on-test-switch-view", {
3179
+ composed: true,
3180
+ bubbles: true,
3181
+ detail: view
3182
+ })
3183
+ );
3184
+ }
3185
+ firstUpdated(_changedProperties) {
3186
+ this.addEventListener("click", () => {
3187
+ if (this.sessionContext?.view === "scorer") {
3188
+ this._switchView("candidate");
3189
+ } else {
3190
+ this._switchView("scorer");
3191
+ }
3192
+ });
3193
+ }
3194
+ render() {
3195
+ return x`${this.myTemplate ? this.myTemplate({
3196
+ view: this.sessionContext?.view
3197
+ }) : E}`;
3198
+ }
3199
+ };
3200
+ __decorateClass([
3201
+ c({ context: sessionContext, subscribe: true })
3202
+ ], TestViewToggle.prototype, "sessionContext", 2);
3203
+ TestViewToggle = __decorateClass([
3204
+ t("test-view-toggle")
3205
+ ], TestViewToggle);
3206
+
3207
+ // ../qti-test/src/components/test-scoring-feedback/test-scoring-feedback.ts
3208
+ var TestScoringFeedback = class extends i2 {
3209
+ constructor() {
3210
+ super(...arguments);
3211
+ this.view = null;
3212
+ }
3213
+ render() {
3214
+ const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
3215
+ if (!activeItem || !activeItem.variables) return x``;
3216
+ if (activeItem["category"] === "dep-informational") return x`<div>${this.dataset.informational}</div>`;
3217
+ const completionStatus = activeItem?.variables.find((v) => v.identifier === "completionStatus")?.value;
3218
+ const scoreOutcome = activeItem?.variables.find((vr) => vr.identifier == "SCORE");
3219
+ const score = parseFloat(scoreOutcome?.value);
3220
+ const externalScored = activeItem["externalScored"];
3221
+ const feedbackText = () => {
3222
+ if (completionStatus !== "completed") {
3223
+ return this.dataset.textNoResponse;
3224
+ }
3225
+ if (!externalScored && score !== void 0 && !Number.isNaN(score)) {
3226
+ return score > 0 ? this.dataset.textCorrect : this.dataset.textIncorrect;
3227
+ }
3228
+ if (externalScored === "externalMachine") {
3229
+ return Number.isNaN(score) || score === void 0 ? this.dataset.scoreUnknown : `We hebben je antwoord ${score === 0 ? "geen punten" : score == 1 ? "\xE9\xE9n punt" : `${score} punten`} gegeven. Je kunt je score zelf aanpassen als je denkt dat dat niet klopt.`;
3230
+ }
3231
+ if (externalScored === "human") {
3232
+ return Number.isNaN(score) ? "" : "Deze score heb je zelf toegekend.";
3233
+ }
3234
+ return this.dataset.inProgress;
3235
+ };
3236
+ return x`<div>${feedbackText()}</div>`;
3237
+ }
3238
+ };
3239
+ __decorateClass([
3240
+ c({ context: computedContext, subscribe: true })
3241
+ ], TestScoringFeedback.prototype, "computedContext", 2);
3242
+ __decorateClass([
3243
+ n({ type: String, attribute: "view" })
3244
+ ], TestScoringFeedback.prototype, "view", 2);
3245
+ TestScoringFeedback = __decorateClass([
3246
+ t("test-scoring-feedback")
3247
+ ], TestScoringFeedback);
3248
+
3249
+ // ../qti-test/src/components/test-check-item/test-check-item.ts
3250
+ var TestCheckItem = class extends i2 {
3251
+ constructor() {
3252
+ super();
3253
+ this.addEventListener("click", () => {
3254
+ this.dispatchEvent(new CustomEvent("test-end-attempt", { bubbles: true }));
3255
+ this.dispatchEvent(
3256
+ new CustomEvent("test-show-correct-response", {
3257
+ detail: true,
3258
+ bubbles: true
3259
+ })
3260
+ );
3261
+ const qtiTest = this.closest("qti-test");
3262
+ const testContainer = qtiTest.querySelector("test-container");
3263
+ const viewElements = Array.from(testContainer.shadowRoot.querySelectorAll("[view]"));
3264
+ viewElements.forEach((element) => {
3265
+ element.classList.toggle("show", true);
3266
+ });
3267
+ });
3268
+ }
3269
+ render() {
3270
+ return x` <slot></slot> `;
3271
+ }
3272
+ };
3273
+ TestCheckItem.styles = i`
3274
+ :host {
3275
+ ${btn};
3276
+ }
3277
+ :host([disabled]) {
3278
+ ${dis};
3279
+ }
3280
+ `;
3281
+ TestCheckItem = __decorateClass([
3282
+ t("test-check-item")
3283
+ ], TestCheckItem);
3284
+
3285
+ export {
3286
+ QtiTest,
3287
+ QtiAssessmentItemRef,
3288
+ QtiAssessmentSection,
3289
+ QtiAssessmentTest,
3290
+ QtiTestPart,
3291
+ QtiTestFeedback,
3292
+ TestNavigation,
3293
+ TestNext,
3294
+ TestPrev,
3295
+ TestView,
3296
+ TestItemLink,
3297
+ TestEndAttempt,
3298
+ TestShowCorrectResponse,
3299
+ TestPagingButtonsStamp,
3300
+ TestContainer,
3301
+ TestPrintVariables,
3302
+ TestSectionButtonsStamp,
3303
+ TestSectionLink,
3304
+ TestPrintContext,
3305
+ TestStamp,
3306
+ TestScoringButtons,
3307
+ TestViewToggle,
3308
+ TestScoringFeedback,
3309
+ TestCheckItem
3310
+ };
3311
+ /*! Bundled license information:
3312
+
3313
+ @heximal/expressions/lib/constants.js:
3314
+ @heximal/expressions/lib/tokenizer.js:
3315
+ @heximal/expressions/lib/parser.js:
3316
+ @heximal/expressions/lib/ast_factory.js:
3317
+ @heximal/expressions/lib/eval.js:
3318
+ (*
3319
+ * @license
3320
+ * Portions Copyright (c) 2013, the Dart project authors.
3321
+ *)
3322
+
3323
+ lit-html/node/private-ssr-support.js:
3324
+ (**
3325
+ * @license
3326
+ * Copyright 2019 Google LLC
3327
+ * SPDX-License-Identifier: BSD-3-Clause
3328
+ *)
3329
+ */
3330
+ //# sourceMappingURL=chunk-352OTVTY.js.map