@citolab/qti-components 8.1.0 → 8.2.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 (56) hide show
  1. package/cdn/index.global.js +1 -1
  2. package/cdn/index.js +274 -168
  3. package/dist/base.d.ts +5 -5
  4. package/dist/base.js +1 -1
  5. package/dist/{chunk-IXPDMYKH.js → chunk-43XBJ47H.js} +3 -3
  6. package/dist/{chunk-EKXZI26R.js → chunk-7D3NKAGZ.js} +54 -3
  7. package/dist/chunk-7D3NKAGZ.js.map +1 -0
  8. package/dist/{chunk-Q52IJELT.js → chunk-CRLXQZFA.js} +594 -259
  9. package/dist/chunk-CRLXQZFA.js.map +1 -0
  10. package/dist/{chunk-TXIB3SKC.js → chunk-LMGJOBJA.js} +3 -3
  11. package/dist/{chunk-6V3A7NQP.js → chunk-M7K5C522.js} +2 -2
  12. package/dist/{chunk-CF3DXJWM.js → chunk-OIUK5BFU.js} +22 -6
  13. package/dist/{chunk-CF3DXJWM.js.map → chunk-OIUK5BFU.js.map} +1 -1
  14. package/dist/{chunk-MK2I3MQB.js → chunk-OVBG223G.js} +4 -5
  15. package/dist/{chunk-MK2I3MQB.js.map → chunk-OVBG223G.js.map} +1 -1
  16. package/dist/{chunk-4XR2JHCP.js → chunk-Q6ZK4LRH.js} +26 -2
  17. package/dist/chunk-Q6ZK4LRH.js.map +1 -0
  18. package/dist/chunk-TLTYXWW7.js +7 -0
  19. package/dist/chunk-TLTYXWW7.js.map +1 -0
  20. package/dist/{chunk-3IACYIU2.js → chunk-U7YCXDYC.js} +2 -2
  21. package/dist/{chunk-NZLW6EX7.js → chunk-UC3T3NI3.js} +2 -2
  22. package/dist/{chunk-6XJUP55A.js → chunk-YLRMOX6C.js} +3 -3
  23. package/dist/chunk-YLRMOX6C.js.map +1 -0
  24. package/dist/{computed.types.d-DpKIYO6e.d.ts → computed.types.d-CJarqC6s.d.ts} +12 -0
  25. package/dist/corrections.d.ts +14 -4
  26. package/dist/corrections.js +7 -7
  27. package/dist/elements.d.ts +1 -1
  28. package/dist/elements.js +3 -3
  29. package/dist/index.d.ts +5 -5
  30. package/dist/index.js +14 -12
  31. package/dist/interactions.js +3 -3
  32. package/dist/item.css +81 -0
  33. package/dist/item.d.ts +1 -1
  34. package/dist/item.js +4 -4
  35. package/dist/loader.d.ts +1 -1
  36. package/dist/processing.d.ts +3 -3
  37. package/dist/processing.js +2 -2
  38. package/dist/qti-components-jsx.d.ts +7704 -7127
  39. package/dist/{qti-condition-expression.d-aMvBvpc6.d.ts → qti-condition-expression.d-CYwXOUb1.d.ts} +1 -1
  40. package/dist/{qti-expression.d-BietT_8b.d.ts → qti-expression.d-B_-m9emA.d.ts} +11 -1
  41. package/dist/{qti-feedback.d-DZbad0RN.d.ts → qti-feedback.d-B1EIF-fU.d.ts} +1 -0
  42. package/dist/{qti-test.d-_YAlhDbt.d.ts → qti-test.d-C6lCEMRB.d.ts} +38 -3
  43. package/dist/test.d.ts +5 -5
  44. package/dist/test.js +7 -5
  45. package/package.json +9 -9
  46. package/dist/chunk-4XR2JHCP.js.map +0 -1
  47. package/dist/chunk-6XJUP55A.js.map +0 -1
  48. package/dist/chunk-EKXZI26R.js.map +0 -1
  49. package/dist/chunk-IYC7LZV6.js +0 -7
  50. package/dist/chunk-IYC7LZV6.js.map +0 -1
  51. package/dist/chunk-Q52IJELT.js.map +0 -1
  52. /package/dist/{chunk-IXPDMYKH.js.map → chunk-43XBJ47H.js.map} +0 -0
  53. /package/dist/{chunk-TXIB3SKC.js.map → chunk-LMGJOBJA.js.map} +0 -0
  54. /package/dist/{chunk-6V3A7NQP.js.map → chunk-M7K5C522.js.map} +0 -0
  55. /package/dist/{chunk-3IACYIU2.js.map → chunk-U7YCXDYC.js.map} +0 -0
  56. /package/dist/{chunk-NZLW6EX7.js.map → chunk-UC3T3NI3.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  QtiModalFeedback
3
- } from "./chunk-6XJUP55A.js";
3
+ } from "./chunk-YLRMOX6C.js";
4
4
  import {
5
5
  item_default
6
- } from "./chunk-IYC7LZV6.js";
6
+ } from "./chunk-TLTYXWW7.js";
7
7
  import {
8
8
  watch
9
9
  } from "./chunk-4DB4SC5K.js";
@@ -20,7 +20,7 @@ import {
20
20
  qtiContext,
21
21
  sessionContext,
22
22
  testContext
23
- } from "./chunk-EKXZI26R.js";
23
+ } from "./chunk-7D3NKAGZ.js";
24
24
 
25
25
  // ../qti-test/dist/components/qti-assessment-item-ref/qti-assessment-item-ref.js
26
26
  import { LitElement } from "lit";
@@ -191,9 +191,72 @@ __decorate3([
191
191
  consume2({ context: testContext, subscribe: true })
192
192
  ], QtiAssessmentTest.prototype, "_testContext", void 0);
193
193
 
194
+ // ../qti-test/dist/components/qti-item-session-control/qti-item-session-control.js
195
+ import { html as html3, LitElement as LitElement4 } from "lit";
196
+ import { property as property4 } from "lit/decorators.js";
197
+ var __decorate4 = function(decorators, target, key, desc) {
198
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
199
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
200
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
201
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
202
+ };
203
+ var stringToBooleanConverter3 = {
204
+ fromAttribute(value) {
205
+ return value === "true";
206
+ },
207
+ toAttribute(value) {
208
+ return value ? "true" : "false";
209
+ }
210
+ };
211
+ var QtiItemSessionControl = class extends LitElement4 {
212
+ constructor() {
213
+ super(...arguments);
214
+ this.maxAttempts = 1;
215
+ this.showFeedback = false;
216
+ this.allowReview = true;
217
+ this.showSolution = false;
218
+ this.allowComment = false;
219
+ this.allowSkipping = true;
220
+ this.validateResponses = false;
221
+ }
222
+ async connectedCallback() {
223
+ super.connectedCallback();
224
+ await this.updateComplete;
225
+ this.dispatchEvent(new CustomEvent("qti-item-session-control-connected", {
226
+ detail: this,
227
+ bubbles: true,
228
+ composed: true
229
+ }));
230
+ }
231
+ render() {
232
+ return html3`<slot></slot>`;
233
+ }
234
+ };
235
+ __decorate4([
236
+ property4({ type: Number, attribute: "max-attempts" })
237
+ ], QtiItemSessionControl.prototype, "maxAttempts", void 0);
238
+ __decorate4([
239
+ property4({ type: Boolean, converter: stringToBooleanConverter3, attribute: "show-feedback" })
240
+ ], QtiItemSessionControl.prototype, "showFeedback", void 0);
241
+ __decorate4([
242
+ property4({ type: Boolean, converter: stringToBooleanConverter3, attribute: "allow-review" })
243
+ ], QtiItemSessionControl.prototype, "allowReview", void 0);
244
+ __decorate4([
245
+ property4({ type: Boolean, converter: stringToBooleanConverter3, attribute: "show-solution" })
246
+ ], QtiItemSessionControl.prototype, "showSolution", void 0);
247
+ __decorate4([
248
+ property4({ type: Boolean, converter: stringToBooleanConverter3, attribute: "allow-comment" })
249
+ ], QtiItemSessionControl.prototype, "allowComment", void 0);
250
+ __decorate4([
251
+ property4({ type: Boolean, converter: stringToBooleanConverter3, attribute: "allow-skipping" })
252
+ ], QtiItemSessionControl.prototype, "allowSkipping", void 0);
253
+ __decorate4([
254
+ property4({ type: Boolean, converter: stringToBooleanConverter3, attribute: "validate-responses" })
255
+ ], QtiItemSessionControl.prototype, "validateResponses", void 0);
256
+
194
257
  // ../qti-test/dist/components/qti-outcome-processing/qti-outcome-processing.js
195
- import { css, html as html3, LitElement as LitElement4 } from "lit";
196
- var QtiOutcomeProcessing = class extends LitElement4 {
258
+ import { css, html as html4, LitElement as LitElement5 } from "lit";
259
+ var QtiOutcomeProcessing = class extends LitElement5 {
197
260
  static {
198
261
  this.styles = [
199
262
  css`
@@ -204,7 +267,7 @@ var QtiOutcomeProcessing = class extends LitElement4 {
204
267
  ];
205
268
  }
206
269
  render() {
207
- return html3`<slot></slot>`;
270
+ return html4`<slot></slot>`;
208
271
  }
209
272
  process() {
210
273
  const logic = new QtiOutcomeProcessingProcessor();
@@ -221,7 +284,7 @@ var QtiOutcomeProcessingProcessor = class {
221
284
  };
222
285
 
223
286
  // ../qti-test/dist/components/qti-test-feedback/qti-test-feedback.js
224
- import { css as css2, html as html4 } from "lit";
287
+ import { css as css2, html as html5 } from "lit";
225
288
  var QtiTestFeedback = class extends QtiModalFeedback {
226
289
  static {
227
290
  this.styles = css2`
@@ -231,20 +294,20 @@ var QtiTestFeedback = class extends QtiModalFeedback {
231
294
  `;
232
295
  }
233
296
  render() {
234
- return html4``;
297
+ return html5``;
235
298
  }
236
299
  };
237
300
 
238
301
  // ../qti-test/dist/components/qti-test-part/qti-test-part.js
239
- import { html as html5, LitElement as LitElement5 } from "lit";
240
- import { property as property4 } from "lit/decorators.js";
241
- var __decorate4 = function(decorators, target, key, desc) {
302
+ import { html as html6, LitElement as LitElement6 } from "lit";
303
+ import { property as property5 } from "lit/decorators.js";
304
+ var __decorate5 = function(decorators, target, key, desc) {
242
305
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
243
306
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
244
307
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
245
308
  return c > 3 && r && Object.defineProperty(target, key, r), r;
246
309
  };
247
- var QtiTestPart = class extends LitElement5 {
310
+ var QtiTestPart = class extends LitElement6 {
248
311
  constructor() {
249
312
  super(...arguments);
250
313
  this.identifier = "";
@@ -271,25 +334,25 @@ var QtiTestPart = class extends LitElement5 {
271
334
  }));
272
335
  }
273
336
  render() {
274
- return html5` <slot></slot>`;
337
+ return html6` <slot></slot>`;
275
338
  }
276
339
  };
277
- __decorate4([
278
- property4({ type: String })
340
+ __decorate5([
341
+ property5({ type: String })
279
342
  ], QtiTestPart.prototype, "identifier", void 0);
280
- __decorate4([
281
- property4({ type: String })
343
+ __decorate5([
344
+ property5({ type: String })
282
345
  ], QtiTestPart.prototype, "class", void 0);
283
- __decorate4([
284
- property4({ type: String, attribute: "navigation-mode" })
346
+ __decorate5([
347
+ property5({ type: String, attribute: "navigation-mode" })
285
348
  ], QtiTestPart.prototype, "navigationMode", void 0);
286
- __decorate4([
287
- property4({ type: String, attribute: "submission-mode" })
349
+ __decorate5([
350
+ property5({ type: String, attribute: "submission-mode" })
288
351
  ], QtiTestPart.prototype, "submissionMode", void 0);
289
352
 
290
353
  // ../qti-test/dist/components/qti-test-variables/qti-test-variables.js
291
354
  import { consume as consume3 } from "@lit/context";
292
- var __decorate5 = function(decorators, target, key, desc) {
355
+ var __decorate6 = function(decorators, target, key, desc) {
293
356
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
294
357
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
295
358
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -334,7 +397,7 @@ var QtiTestVariables = class extends QtiExpression {
334
397
  return this.getResult();
335
398
  }
336
399
  };
337
- __decorate5([
400
+ __decorate6([
338
401
  consume3({ context: testContext, subscribe: true })
339
402
  ], QtiTestVariables.prototype, "_testContext", void 0);
340
403
  var QtiTestVariablesExpression = class {
@@ -362,12 +425,12 @@ var QtiTestVariablesExpression = class {
362
425
 
363
426
  // ../qti-test/dist/components/qti-test/qti-test.js
364
427
  import { provide as provide2 } from "@lit/context";
365
- import { html as html6, LitElement as LitElement7 } from "lit";
428
+ import { html as html7, LitElement as LitElement8 } from "lit";
366
429
  import { state } from "lit/decorators.js";
367
430
 
368
431
  // ../qti-test/dist/mixins/test-navigation.mixin.js
369
- import { property as property5 } from "lit/decorators.js";
370
- var __decorate6 = function(decorators, target, key, desc) {
432
+ import { property as property6 } from "lit/decorators.js";
433
+ var __decorate7 = function(decorators, target, key, desc) {
371
434
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
372
435
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
373
436
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -845,17 +908,17 @@ var TestNavigationMixin = (superClass) => {
845
908
  });
846
909
  }
847
910
  }
848
- __decorate6([
849
- property5({ type: String })
911
+ __decorate7([
912
+ property6({ type: String })
850
913
  ], TestNavigationClass.prototype, "navigate", void 0);
851
- __decorate6([
852
- property5({ type: Number })
914
+ __decorate7([
915
+ property6({ type: Number })
853
916
  ], TestNavigationClass.prototype, "requestTimeout", void 0);
854
- __decorate6([
855
- property5({ attribute: false })
917
+ __decorate7([
918
+ property6({ attribute: false })
856
919
  ], TestNavigationClass.prototype, "postLoadTransformCallback", void 0);
857
- __decorate6([
858
- property5({ attribute: false })
920
+ __decorate7([
921
+ property6({ attribute: false })
859
922
  ], TestNavigationClass.prototype, "postLoadTestTransformCallback", void 0);
860
923
  return TestNavigationClass;
861
924
  };
@@ -910,9 +973,9 @@ var TestViewMixin = (superClass) => {
910
973
 
911
974
  // ../qti-test/dist/mixins/test-base.js
912
975
  import { provide } from "@lit/context";
913
- import { LitElement as LitElement6 } from "lit";
914
- import { property as property6 } from "lit/decorators.js";
915
- var __decorate7 = function(decorators, target, key, desc) {
976
+ import { LitElement as LitElement7 } from "lit";
977
+ import { property as property7 } from "lit/decorators.js";
978
+ var __decorate8 = function(decorators, target, key, desc) {
916
979
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
917
980
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
918
981
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -964,11 +1027,11 @@ var TestBaseMixin = (superClass) => {
964
1027
  }
965
1028
  }
966
1029
  _initializeEventListeners() {
967
- this.addEventListener("qti-assessment-test-connected", (e) => {
1030
+ this.addEventListener("qti-testdoc-loaded", () => {
968
1031
  this.testContext = INITIAL_TEST_CONTEXT;
969
1032
  this.sessionContext = INITIAL_SESSION_CONTEXT;
970
- if (this.testContext && this.testContext.items.length > 0)
971
- return;
1033
+ });
1034
+ this.addEventListener("qti-assessment-test-connected", (e) => {
972
1035
  this._testElement = e.detail;
973
1036
  const items = Array.from(this._testElement.querySelectorAll("qti-assessment-item-ref")).map((itemRef) => {
974
1037
  return {
@@ -1020,17 +1083,17 @@ var TestBaseMixin = (superClass) => {
1020
1083
  this.dispatchEvent(new CustomEvent("qti-test-context-updated", { detail: this.testContext, bubbles: false, composed: false }));
1021
1084
  }
1022
1085
  }
1023
- __decorate7([
1024
- property6({ attribute: false, type: Object }),
1086
+ __decorate8([
1087
+ property7({ attribute: false, type: Object }),
1025
1088
  provide({ context: testContext })
1026
1089
  ], TestBaseClass.prototype, "testContext", void 0);
1027
- __decorate7([
1028
- property6({ attribute: false, type: Object }),
1090
+ __decorate8([
1091
+ property7({ attribute: false, type: Object }),
1029
1092
  provide({ context: sessionContext })
1030
1093
  ], TestBaseClass.prototype, "sessionContext", void 0);
1031
1094
  return TestBaseClass;
1032
1095
  };
1033
- var TestBase = class extends TestBaseMixin(LitElement6) {
1096
+ var TestBase = class extends TestBaseMixin(LitElement7) {
1034
1097
  };
1035
1098
 
1036
1099
  // ../qti-test/dist/mixins/test-processing.mixin.js
@@ -1089,13 +1152,13 @@ var TestProcessingMixin = (superClass) => {
1089
1152
  };
1090
1153
 
1091
1154
  // ../qti-test/dist/components/qti-test/qti-test.js
1092
- var __decorate8 = function(decorators, target, key, desc) {
1155
+ var __decorate9 = function(decorators, target, key, desc) {
1093
1156
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1094
1157
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1095
1158
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1096
1159
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1097
1160
  };
1098
- var QtiTest = class extends TestNavigationMixin(TestViewMixin(TestProcessingMixin(TestBaseMixin(LitElement7)))) {
1161
+ var QtiTest = class extends TestNavigationMixin(TestViewMixin(TestProcessingMixin(TestBaseMixin(LitElement8)))) {
1099
1162
  constructor() {
1100
1163
  super(...arguments);
1101
1164
  this.configContext = {};
@@ -1110,16 +1173,16 @@ var QtiTest = class extends TestNavigationMixin(TestViewMixin(TestProcessingMixi
1110
1173
  this.dispatchEvent(new CustomEvent("qti-test-connected", { detail: this }));
1111
1174
  }
1112
1175
  render() {
1113
- return html6`<slot></slot>`;
1176
+ return html7`<slot></slot>`;
1114
1177
  }
1115
1178
  };
1116
- __decorate8([
1179
+ __decorate9([
1117
1180
  state(),
1118
1181
  provide2({ context: configContext })
1119
1182
  ], QtiTest.prototype, "configContext", void 0);
1120
1183
 
1121
1184
  // ../qti-test/dist/components/test-check-item/test-check-item.js
1122
- import { css as css4, html as html7, LitElement as LitElement8 } from "lit";
1185
+ import { css as css4, html as html8, LitElement as LitElement9 } from "lit";
1123
1186
 
1124
1187
  // ../qti-test/dist/components/styles.js
1125
1188
  import { css as css3 } from "lit";
@@ -1145,7 +1208,7 @@ var ind = css3`
1145
1208
  `;
1146
1209
 
1147
1210
  // ../qti-test/dist/components/test-check-item/test-check-item.js
1148
- var TestCheckItem = class extends LitElement8 {
1211
+ var TestCheckItem = class extends LitElement9 {
1149
1212
  static {
1150
1213
  this.styles = css4`
1151
1214
  :host {
@@ -1169,22 +1232,22 @@ var TestCheckItem = class extends LitElement8 {
1169
1232
  });
1170
1233
  }
1171
1234
  render() {
1172
- return html7` <slot></slot> `;
1235
+ return html8` <slot></slot> `;
1173
1236
  }
1174
1237
  };
1175
1238
 
1176
1239
  // ../qti-test/dist/components/test-container/test-container.js
1177
1240
  import { consume as consume4 } from "@lit/context";
1178
- import { LitElement as LitElement9, html as html8 } from "lit";
1179
- import { property as property7, state as state2 } from "lit/decorators.js";
1241
+ import { LitElement as LitElement10, html as html9 } from "lit";
1242
+ import { property as property8, state as state2 } from "lit/decorators.js";
1180
1243
  import { until } from "lit/directives/until.js";
1181
- var __decorate9 = function(decorators, target, key, desc) {
1244
+ var __decorate10 = function(decorators, target, key, desc) {
1182
1245
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1183
1246
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1184
1247
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1185
1248
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1186
1249
  };
1187
- var TestContainer = class extends LitElement9 {
1250
+ var TestContainer = class extends LitElement10 {
1188
1251
  constructor() {
1189
1252
  super(...arguments);
1190
1253
  this.customElementRegistry = null;
@@ -1224,17 +1287,34 @@ var TestContainer = class extends LitElement9 {
1224
1287
  api = await qtiTest.postLoadTestTransformCallback(api, testElement);
1225
1288
  }
1226
1289
  }
1227
- this.testDoc = api.htmlDoc(this.#resolvedCustomElementRegistry ?? void 0);
1290
+ this.#setTestDoc(api.htmlDoc(this.#resolvedCustomElementRegistry ?? void 0));
1228
1291
  } catch (error) {
1229
1292
  console.error("Error loading or parsing XML:", error);
1230
1293
  }
1231
1294
  }
1295
+ /**
1296
+ * Assign a new test document and announce it.
1297
+ *
1298
+ * The announcement has to happen here rather than on
1299
+ * `qti-assessment-test-connected`, because by then the new document's
1300
+ * children have already connected and registered themselves — a host that
1301
+ * resets its context on that event throws those registrations away. Lit's
1302
+ * re-render is async, so a handler running now still gets in before any child
1303
+ * of the new `qti-assessment-test` connects.
1304
+ */
1305
+ #setTestDoc(testDoc) {
1306
+ this.testDoc = testDoc;
1307
+ this.dispatchEvent(new CustomEvent("qti-testdoc-loaded", {
1308
+ bubbles: true,
1309
+ composed: true
1310
+ }));
1311
+ }
1232
1312
  handleTestXMLChange() {
1233
1313
  if (!this.testXML)
1234
1314
  return;
1235
1315
  try {
1236
1316
  const explicitSeed = this.qtiContext?.QTI_CONTEXT?.seed;
1237
- this.testDoc = qtiTransformTest().parse(this.testXML).shuffleOrdering(explicitSeed).htmlDoc(this.#resolvedCustomElementRegistry ?? void 0);
1317
+ this.#setTestDoc(qtiTransformTest().parse(this.testXML).shuffleOrdering(explicitSeed).htmlDoc(this.#resolvedCustomElementRegistry ?? void 0));
1238
1318
  } catch (error) {
1239
1319
  console.error("Error parsing XML:", error);
1240
1320
  }
@@ -1252,7 +1332,7 @@ var TestContainer = class extends LitElement9 {
1252
1332
  }
1253
1333
  #initializeTemplateContent() {
1254
1334
  const template = this.querySelector("template");
1255
- this.#templateContent = template ? template.content : html8``;
1335
+ this.#templateContent = template ? template.content : html9``;
1256
1336
  }
1257
1337
  #applyStyles() {
1258
1338
  const sheet = new CSSStyleSheet();
@@ -1260,39 +1340,52 @@ var TestContainer = class extends LitElement9 {
1260
1340
  this.shadowRoot.adoptedStyleSheets = [sheet];
1261
1341
  }
1262
1342
  render() {
1263
- return html8`
1343
+ return html9`
1264
1344
  ${this.#templateContent}
1265
1345
  <slot></slot>
1266
- ${until(this.testDoc, html8`<span>Loading...</span>`)}
1346
+ ${until(this.testDoc, html9`<span>Loading...</span>`)}
1267
1347
  `;
1268
1348
  }
1269
1349
  };
1270
- __decorate9([
1271
- property7({ attribute: false })
1350
+ __decorate10([
1351
+ property8({ attribute: false })
1272
1352
  ], TestContainer.prototype, "customElementRegistry", void 0);
1273
- __decorate9([
1274
- property7({ type: String, attribute: "test-url" })
1353
+ __decorate10([
1354
+ property8({ type: String, attribute: "test-url" })
1275
1355
  ], TestContainer.prototype, "testURL", void 0);
1276
- __decorate9([
1356
+ __decorate10([
1277
1357
  state2()
1278
1358
  ], TestContainer.prototype, "testDoc", void 0);
1279
- __decorate9([
1359
+ __decorate10([
1280
1360
  state2()
1281
1361
  ], TestContainer.prototype, "testXML", void 0);
1282
- __decorate9([
1362
+ __decorate10([
1283
1363
  state2(),
1284
1364
  consume4({ context: qtiContext, subscribe: true })
1285
1365
  ], TestContainer.prototype, "qtiContext", void 0);
1286
- __decorate9([
1366
+ __decorate10([
1287
1367
  watch("testURL", { waitUntilFirstUpdate: true })
1288
1368
  ], TestContainer.prototype, "handleTestURLChange", null);
1289
- __decorate9([
1369
+ __decorate10([
1290
1370
  watch("testXML", { waitUntilFirstUpdate: true })
1291
1371
  ], TestContainer.prototype, "handleTestXMLChange", null);
1292
1372
 
1293
1373
  // ../qti-test/dist/components/test-end-attempt/test-end-attempt.js
1294
- import { css as css5, html as html9, LitElement as LitElement10 } from "lit";
1295
- var TestEndAttempt = class extends LitElement10 {
1374
+ import { css as css5, html as html10, LitElement as LitElement11 } from "lit";
1375
+ import { property as property9 } from "lit/decorators.js";
1376
+ import { consume as consume5 } from "@lit/context";
1377
+ var __decorate11 = function(decorators, target, key, desc) {
1378
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1379
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1380
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1381
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1382
+ };
1383
+ var TestEndAttempt = class extends LitElement11 {
1384
+ _handleTestElementChange(_oldValue, newValue) {
1385
+ if (newValue) {
1386
+ this.requestUpdate();
1387
+ }
1388
+ }
1296
1389
  static {
1297
1390
  this.styles = css5`
1298
1391
  :host {
@@ -1305,23 +1398,69 @@ var TestEndAttempt = class extends LitElement10 {
1305
1398
  }
1306
1399
  constructor() {
1307
1400
  super();
1308
- this.addEventListener("click", () => this.dispatchEvent(new CustomEvent("test-end-attempt", { bubbles: true })));
1401
+ this._internalDisabled = false;
1402
+ this.addEventListener("click", (e) => {
1403
+ e.preventDefault();
1404
+ if (!this._internalDisabled) {
1405
+ this.dispatchEvent(new CustomEvent("test-end-attempt", { bubbles: true }));
1406
+ }
1407
+ });
1408
+ }
1409
+ willUpdate(_changedProperties) {
1410
+ this.checkDisabled();
1411
+ }
1412
+ checkDisabled() {
1413
+ const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
1414
+ if (!testPart)
1415
+ return;
1416
+ const sectionItems = testPart.sections.flatMap((section) => section.items);
1417
+ const itemIndex = sectionItems.findIndex((item) => item.active);
1418
+ const activeItem = sectionItems[itemIndex];
1419
+ if (!activeItem) {
1420
+ this._internalDisabled = false;
1421
+ return;
1422
+ }
1423
+ const maxAttempts = activeItem.maxAttempts ?? 1;
1424
+ const numAttempts = activeItem.numAttempts || 0;
1425
+ if (!activeItem.adaptive && activeItem.optimal) {
1426
+ this._internalDisabled = true;
1427
+ return;
1428
+ }
1429
+ if (!activeItem.adaptive && maxAttempts > 0 && numAttempts >= maxAttempts) {
1430
+ this._internalDisabled = true;
1431
+ return;
1432
+ }
1433
+ if (activeItem.allowSkipping === false) {
1434
+ this._internalDisabled = activeItem.valid === false || activeItem.isDefaultResponse;
1435
+ } else {
1436
+ this._internalDisabled = false;
1437
+ }
1309
1438
  }
1310
1439
  render() {
1311
- return html9` <slot></slot> `;
1440
+ return html10` <slot></slot> `;
1312
1441
  }
1313
1442
  };
1443
+ __decorate11([
1444
+ consume5({ context: computedContext, subscribe: true }),
1445
+ property9({ attribute: false })
1446
+ ], TestEndAttempt.prototype, "computedContext", void 0);
1447
+ __decorate11([
1448
+ property9({ type: Boolean, reflect: true, attribute: "disabled" })
1449
+ ], TestEndAttempt.prototype, "_internalDisabled", void 0);
1450
+ __decorate11([
1451
+ watch("computedContext")
1452
+ ], TestEndAttempt.prototype, "_handleTestElementChange", null);
1314
1453
 
1315
1454
  // ../qti-test/dist/components/test-item-link/test-item-link.js
1316
- import { css as css6, html as html10, LitElement as LitElement11 } from "lit";
1317
- import { property as property8 } from "lit/decorators.js";
1318
- var __decorate10 = function(decorators, target, key, desc) {
1455
+ import { css as css6, html as html11, LitElement as LitElement12 } from "lit";
1456
+ import { property as property10 } from "lit/decorators.js";
1457
+ var __decorate12 = function(decorators, target, key, desc) {
1319
1458
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1320
1459
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1321
1460
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1322
1461
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1323
1462
  };
1324
- var TestItemLink = class extends LitElement11 {
1463
+ var TestItemLink = class extends LitElement12 {
1325
1464
  static {
1326
1465
  this.styles = css6`
1327
1466
  :host {
@@ -1348,18 +1487,18 @@ var TestItemLink = class extends LitElement11 {
1348
1487
  }));
1349
1488
  }
1350
1489
  render() {
1351
- return html10` <slot></slot> `;
1490
+ return html11` <slot></slot> `;
1352
1491
  }
1353
1492
  };
1354
- __decorate10([
1355
- property8({ type: String, attribute: "item-id" })
1493
+ __decorate12([
1494
+ property10({ type: String, attribute: "item-id" })
1356
1495
  ], TestItemLink.prototype, "itemId", void 0);
1357
1496
 
1358
1497
  // ../qti-test/dist/components/test-item-to-speech/test-item-to-speech.js
1359
- import { html as html11, LitElement as LitElement12, css as css7 } from "lit";
1360
- import { consume as consume5, provide as provide3, createContext } from "@lit/context";
1361
- import { property as property9, state as state3 } from "lit/decorators.js";
1362
- var __decorate11 = function(decorators, target, key, desc) {
1498
+ import { html as html12, LitElement as LitElement13, css as css7 } from "lit";
1499
+ import { consume as consume6, provide as provide3, createContext } from "@lit/context";
1500
+ import { property as property11, state as state3 } from "lit/decorators.js";
1501
+ var __decorate13 = function(decorators, target, key, desc) {
1363
1502
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1364
1503
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1365
1504
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1379,7 +1518,7 @@ function getHighlightSheet() {
1379
1518
  return highlightSheet;
1380
1519
  }
1381
1520
  var ttsContext = createContext(/* @__PURE__ */ Symbol("tts-context"));
1382
- var TtsButtonBase = class extends LitElement12 {
1521
+ var TtsButtonBase = class extends LitElement13 {
1383
1522
  static {
1384
1523
  this.styles = css7`
1385
1524
  :host {
@@ -1403,10 +1542,10 @@ var TtsButtonBase = class extends LitElement12 {
1403
1542
  }
1404
1543
  }
1405
1544
  };
1406
- __decorate11([
1407
- consume5({ context: ttsContext, subscribe: true })
1545
+ __decorate13([
1546
+ consume6({ context: ttsContext, subscribe: true })
1408
1547
  ], TtsButtonBase.prototype, "_tts", void 0);
1409
- var TestItemToSpeech = class extends LitElement12 {
1548
+ var TestItemToSpeech = class extends LitElement13 {
1410
1549
  #internals;
1411
1550
  #itemElements;
1412
1551
  #boundHandleItemConnected;
@@ -1480,7 +1619,7 @@ var TestItemToSpeech = class extends LitElement12 {
1480
1619
  this.#itemElements.clear();
1481
1620
  }
1482
1621
  render() {
1483
- return html11`<slot></slot>`;
1622
+ return html12`<slot></slot>`;
1484
1623
  }
1485
1624
  #handleItemConnected(event) {
1486
1625
  const item = event.detail;
@@ -1702,13 +1841,13 @@ var TestItemToSpeech = class extends LitElement12 {
1702
1841
  }
1703
1842
  }
1704
1843
  };
1705
- __decorate11([
1706
- property9({ type: String })
1844
+ __decorate13([
1845
+ property11({ type: String })
1707
1846
  ], TestItemToSpeech.prototype, "language", void 0);
1708
- __decorate11([
1709
- consume5({ context: sessionContext, subscribe: true })
1847
+ __decorate13([
1848
+ consume6({ context: sessionContext, subscribe: true })
1710
1849
  ], TestItemToSpeech.prototype, "_sessionContext", void 0);
1711
- __decorate11([
1850
+ __decorate13([
1712
1851
  state3(),
1713
1852
  provide3({ context: ttsContext })
1714
1853
  ], TestItemToSpeech.prototype, "_ttsContext", void 0);
@@ -1725,16 +1864,16 @@ var TestTtsPlay = class extends TtsButtonBase {
1725
1864
  }
1726
1865
  render() {
1727
1866
  const playing = this._tts?.state === "playing";
1728
- return html11`
1867
+ return html12`
1729
1868
  <button part="button" @click=${this.#toggle}>
1730
- ${playing ? html11`<slot name="pause">⏸</slot>` : html11`<slot name="play">▶</slot>`}
1869
+ ${playing ? html12`<slot name="pause">⏸</slot>` : html12`<slot name="play">▶</slot>`}
1731
1870
  </button>
1732
1871
  `;
1733
1872
  }
1734
1873
  };
1735
1874
  var TestTtsPause = class extends TtsButtonBase {
1736
1875
  render() {
1737
- return html11`
1876
+ return html12`
1738
1877
  <button part="button" ?disabled=${this._tts?.state !== "playing"} @click=${() => this._tts?.pause()}>
1739
1878
  <slot>⏸</slot>
1740
1879
  </button>
@@ -1743,7 +1882,7 @@ var TestTtsPause = class extends TtsButtonBase {
1743
1882
  };
1744
1883
  var TestTtsResume = class extends TtsButtonBase {
1745
1884
  render() {
1746
- return html11`
1885
+ return html12`
1747
1886
  <button part="button" ?disabled=${this._tts?.state !== "paused"} @click=${() => this._tts?.resume()}>
1748
1887
  <slot>▶</slot>
1749
1888
  </button>
@@ -1752,7 +1891,7 @@ var TestTtsResume = class extends TtsButtonBase {
1752
1891
  };
1753
1892
  var TestTtsStop = class extends TtsButtonBase {
1754
1893
  render() {
1755
- return html11`
1894
+ return html12`
1756
1895
  <button part="button" ?disabled=${this._tts?.state === "idle"} @click=${() => this._tts?.stop()}>
1757
1896
  <slot>■</slot>
1758
1897
  </button>
@@ -1763,7 +1902,7 @@ var TestTtsPrev = class extends TtsButtonBase {
1763
1902
  render() {
1764
1903
  const atStart = (this._tts?.currentElementIndex ?? 0) === 0;
1765
1904
  const noElements = (this._tts?.elementCount ?? 0) === 0;
1766
- return html11`
1905
+ return html12`
1767
1906
  <button part="button" ?disabled=${noElements || atStart} @click=${() => this._tts?.prevElement()}>
1768
1907
  <slot>◀◀</slot>
1769
1908
  </button>
@@ -1774,7 +1913,7 @@ var TestTtsNext = class extends TtsButtonBase {
1774
1913
  render() {
1775
1914
  const atEnd = (this._tts?.elementCount ?? 0) > 0 && (this._tts?.currentElementIndex ?? 0) >= (this._tts?.elementCount ?? 0) - 1;
1776
1915
  const noElements = (this._tts?.elementCount ?? 0) === 0;
1777
- return html11`
1916
+ return html12`
1778
1917
  <button part="button" ?disabled=${noElements || atEnd} @click=${() => this._tts?.nextElement()}>
1779
1918
  <slot>▶▶</slot>
1780
1919
  </button>
@@ -1783,17 +1922,25 @@ var TestTtsNext = class extends TtsButtonBase {
1783
1922
  };
1784
1923
 
1785
1924
  // ../qti-test/dist/components/test-navigation/test-navigation.js
1786
- import { consume as consume6, provide as provide4 } from "@lit/context";
1787
- import { html as html12, LitElement as LitElement13 } from "lit";
1788
- import { property as property10, state as state4 } from "lit/decorators.js";
1789
- var __decorate12 = function(decorators, target, key, desc) {
1925
+ import { consume as consume7, provide as provide4 } from "@lit/context";
1926
+ import { html as html13, LitElement as LitElement14 } from "lit";
1927
+ import { property as property12, state as state4 } from "lit/decorators.js";
1928
+ var __decorate14 = function(decorators, target, key, desc) {
1790
1929
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1791
1930
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1792
1931
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1793
1932
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1794
1933
  };
1795
- var TestNavigation = class extends LitElement13 {
1934
+ var TestNavigation = class extends LitElement14 {
1796
1935
  #testElement;
1936
+ /**
1937
+ * Whether each item's last *ended attempt* reached the optimal outcome, keyed
1938
+ * by item-ref id. Written from #handleItemContextUpdated when processResponse
1939
+ * ends an attempt — never on a plain selection. Mirrors how inline feedback
1940
+ * only re-evaluates when an attempt is processed, so a freshly-picked answer
1941
+ * doesn't flip `done` until the candidate ends the attempt.
1942
+ */
1943
+ #optimality;
1797
1944
  constructor() {
1798
1945
  super();
1799
1946
  this.identifier = void 0;
@@ -1807,12 +1954,52 @@ var TestNavigation = class extends LitElement13 {
1807
1954
  };
1808
1955
  this.configContext = {};
1809
1956
  this.autoScoreItems = false;
1957
+ this.#optimality = /* @__PURE__ */ new Map();
1810
1958
  this.addEventListener("qti-assessment-test-connected", this.#handleTestConnected.bind(this));
1811
1959
  this.addEventListener("qti-assessment-item-connected", this.#handleItemConnected.bind(this));
1812
1960
  this.addEventListener("qti-interaction-changed", this.#handleInteractionChanged.bind(this));
1961
+ this.addEventListener("qti-item-context-updated", this.#handleItemContextUpdated.bind(this));
1813
1962
  this.addEventListener("test-end-attempt", this.#handleTestEndAttempt.bind(this));
1814
1963
  this.addEventListener("test-update-outcome-variable", this.#handleTestUpdateOutcomeVariable.bind(this));
1815
1964
  }
1965
+ /**
1966
+ * Latch whether an item reached its optimal outcome whenever it has just ended
1967
+ * an attempt. processResponse — fired by test-end-attempt, autoscore, or an
1968
+ * in-item end-attempt interaction — flags its context update with
1969
+ * `responseProcessed`; a plain selection updates the context without it, so
1970
+ * `done` can't flip until the candidate actually ends the attempt.
1971
+ */
1972
+ #handleItemContextUpdated(event) {
1973
+ if (!event.detail?.responseProcessed)
1974
+ return;
1975
+ const itemContext = event.detail.itemContext;
1976
+ if (!itemContext?.identifier)
1977
+ return;
1978
+ const optimality = this.#assessOptimality(itemContext);
1979
+ this.#optimality.set(itemContext.identifier, optimality);
1980
+ const computedItem = this.computedItemFor(itemContext.identifier);
1981
+ const numAttempts = Number(itemContext.variables?.find((v) => v.identifier === "numAttempts")?.value) || 0;
1982
+ this.afterAttemptEnded(event.composedPath()[0]?.closest("qti-assessment-item"), computedItem, this.#isItemDone(numAttempts, optimality, computedItem?.maxAttempts));
1983
+ }
1984
+ /**
1985
+ * Extension point for presentation packages reacting to an ended attempt.
1986
+ * `done` is settled here — from the freshly latched optimality and the
1987
+ * session-control cascade — because the computed context only catches up on
1988
+ * the next update, after this event has finished bubbling.
1989
+ */
1990
+ afterAttemptEnded(_assessmentItem, _computedItem, _done) {
1991
+ }
1992
+ /** The computed-context entry for an item, from the session-control cascade. */
1993
+ computedItemFor(identifier) {
1994
+ return this.computedContext?.testParts?.flatMap((part) => part.sections).flatMap((section) => section.items).find((i) => i.identifier === identifier);
1995
+ }
1996
+ /** The assessment item for an item-ref id, if it has been rendered. */
1997
+ #assessmentItemFor(identifier) {
1998
+ if (!identifier)
1999
+ return void 0;
2000
+ const itemRef = this.#testElement?.querySelector(`qti-assessment-item-ref[identifier="${identifier}"]`);
2001
+ return itemRef?.assessmentItem ?? void 0;
2002
+ }
1816
2003
  /** The currently active assessment item, exposed for optional presentation extensions. */
1817
2004
  get activeAssessmentItem() {
1818
2005
  if (!this.#testElement || !this._sessionContext?.navItemRefId)
@@ -1847,11 +2034,12 @@ var TestNavigation = class extends LitElement13 {
1847
2034
  }
1848
2035
  }
1849
2036
  render() {
1850
- return html12`<slot></slot>`;
2037
+ return html13`<slot></slot>`;
1851
2038
  }
1852
2039
  /* PK: on test connected we can build the computed context */
1853
2040
  #handleTestConnected(event) {
1854
2041
  this.#testElement = event.detail;
2042
+ this.#optimality.clear();
1855
2043
  if (!this.qtiContext.QTI_CONTEXT?.testIdentifier) {
1856
2044
  const currentContext = this.qtiContext.QTI_CONTEXT || {
1857
2045
  testIdentifier: "",
@@ -1887,27 +2075,50 @@ var TestNavigation = class extends LitElement13 {
1887
2075
  view: this._sessionContext?.view,
1888
2076
  testParts: testPartElements.map((testPart) => {
1889
2077
  const sectionElements = [...testPart.querySelectorAll(`qti-assessment-section`)];
2078
+ const testPartSessionControl = testPart.querySelector(":scope > qti-item-session-control");
2079
+ const partAllowSkipping = testPartSessionControl ? testPartSessionControl.allowSkipping : true;
2080
+ const partMaxAttempts = testPartSessionControl ? testPartSessionControl.maxAttempts : 1;
2081
+ const partShowFeedback = testPartSessionControl ? testPartSessionControl.showFeedback : false;
2082
+ const partShowSolution = testPartSessionControl ? testPartSessionControl.showSolution : false;
1890
2083
  return {
1891
2084
  active: false,
1892
2085
  identifier: testPart.identifier,
1893
2086
  navigationMode: testPart.navigationMode,
1894
2087
  submissionMode: testPart.submissionMode,
2088
+ allowSkipping: partAllowSkipping,
1895
2089
  sections: sectionElements.map((section) => {
1896
2090
  const itemElements = [...section.querySelectorAll(`qti-assessment-item-ref`)];
2091
+ const sectionSessionControl = section.querySelector(":scope > qti-item-session-control");
2092
+ const sectionAllowSkipping = sectionSessionControl ? sectionSessionControl.allowSkipping : partAllowSkipping;
2093
+ const sectionMaxAttempts = sectionSessionControl ? sectionSessionControl.maxAttempts : partMaxAttempts;
2094
+ const sectionShowFeedback = sectionSessionControl ? sectionSessionControl.showFeedback : partShowFeedback;
2095
+ const sectionShowSolution = sectionSessionControl ? sectionSessionControl.showSolution : partShowSolution;
1897
2096
  return {
1898
2097
  active: false,
1899
2098
  identifier: section.identifier,
1900
2099
  title: section.title,
1901
2100
  navigationMode: section.navigationMode,
1902
2101
  submissionMode: section.submissionMode,
1903
- items: itemElements.map((item) => ({
1904
- ...this.initContext?.find((i) => i.identifier === item.identifier),
1905
- active: false,
1906
- identifier: item.identifier,
1907
- categories: item.category ? item.category?.split(" ") : [],
1908
- href: item.href,
1909
- variables: []
1910
- }))
2102
+ allowSkipping: sectionAllowSkipping,
2103
+ items: itemElements.map((item) => {
2104
+ const itemSessionControl = item.querySelector(":scope > qti-item-session-control");
2105
+ const itemAllowSkipping = itemSessionControl ? itemSessionControl.allowSkipping : sectionAllowSkipping;
2106
+ const itemMaxAttempts = itemSessionControl ? itemSessionControl.maxAttempts : sectionMaxAttempts;
2107
+ const itemShowFeedback = itemSessionControl ? itemSessionControl.showFeedback : sectionShowFeedback;
2108
+ const itemShowSolution = itemSessionControl ? itemSessionControl.showSolution : sectionShowSolution;
2109
+ return {
2110
+ ...this.initContext?.find((i) => i.identifier === item.identifier),
2111
+ active: false,
2112
+ identifier: item.identifier,
2113
+ categories: item.category ? item.category?.split(" ") : [],
2114
+ href: item.href,
2115
+ variables: [],
2116
+ allowSkipping: itemAllowSkipping,
2117
+ maxAttempts: itemMaxAttempts,
2118
+ showFeedback: itemShowFeedback,
2119
+ showSolution: itemShowSolution
2120
+ };
2121
+ })
1911
2122
  };
1912
2123
  })
1913
2124
  };
@@ -2024,11 +2235,50 @@ var TestNavigation = class extends LitElement13 {
2024
2235
  const score = rawscore === void 0 || rawscore === null ? null : parseFloat(rawscore?.toString());
2025
2236
  const completionStatus = computedItem.variables?.find((v) => v.identifier === "completionStatus")?.value;
2026
2237
  const response = computedItem.variables?.find((v) => v.identifier === "RESPONSE")?.value || "";
2027
- const numAttempts = computedItem.variables?.find((v) => v.identifier === "numAttempts")?.value || 0;
2238
+ const numAttempts = Number(computedItem.variables?.find((v) => v.identifier === "numAttempts")?.value) || 0;
2028
2239
  const active = this._sessionContext?.navItemRefId === computedItem.identifier || false;
2240
+ const valid = this.#assessmentItemFor(computedItem.identifier)?.validate(false) ?? true;
2241
+ const responseVars = itemContext?.variables?.filter((v) => v.type === "response") || [];
2242
+ const isDefaultResponse = responseVars.every((v) => {
2243
+ if (v.value === void 0 || v.value === null) {
2244
+ return true;
2245
+ }
2246
+ let fallbackValue;
2247
+ switch (v.baseType) {
2248
+ case "integer":
2249
+ case "float":
2250
+ case "duration":
2251
+ fallbackValue = "0";
2252
+ break;
2253
+ case "boolean":
2254
+ fallbackValue = "false";
2255
+ break;
2256
+ case "string":
2257
+ case "directedPair":
2258
+ case "identifier":
2259
+ case "pair":
2260
+ case "record":
2261
+ default:
2262
+ fallbackValue = "";
2263
+ break;
2264
+ }
2265
+ const defaultValue = v.defaultValue ?? fallbackValue;
2266
+ if (Array.isArray(v.value)) {
2267
+ const dv = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
2268
+ return v.value.length === dv.length && v.value.every((val, i) => val === dv[i]);
2269
+ }
2270
+ return v.value === defaultValue;
2271
+ });
2029
2272
  const index = item.categories.includes(this.configContext?.infoItemCategory) ? null : itemIndex++;
2030
2273
  const rawMaxScore = item.variables?.find((vr) => vr.identifier == "MAXSCORE")?.value;
2031
2274
  const maxScore = rawMaxScore === void 0 || rawMaxScore === null ? null : parseFloat(rawMaxScore?.toString());
2275
+ let optimality = this.#optimality.get(computedItem.identifier);
2276
+ if (optimality === void 0) {
2277
+ optimality = itemContext ? this.#assessOptimality(itemContext) : "unscored";
2278
+ if (numAttempts > 0)
2279
+ this.#optimality.set(computedItem.identifier, optimality);
2280
+ }
2281
+ const done = this.#isItemDone(numAttempts, optimality, computedItem.maxAttempts);
2032
2282
  return {
2033
2283
  ...computedItem,
2034
2284
  completionStatus,
@@ -2037,7 +2287,16 @@ var TestNavigation = class extends LitElement13 {
2037
2287
  response,
2038
2288
  index,
2039
2289
  active,
2040
- maxScore
2290
+ valid,
2291
+ isDefaultResponse,
2292
+ maxScore,
2293
+ done,
2294
+ // Reflects the last *ended* attempt only. Read the latch directly
2295
+ // (not the `optimality` local) — the latch is written solely on
2296
+ // responseProcessed / restore-seed (line above, guarded by
2297
+ // numAttempts > 0), so a freshly-picked mid-attempt selection
2298
+ // computed live is never stored and never counts as optimal.
2299
+ optimal: this.#optimality.get(computedItem.identifier) === "optimal"
2041
2300
  // type,
2042
2301
  // correct,
2043
2302
  // incorrect,
@@ -2054,48 +2313,123 @@ var TestNavigation = class extends LitElement13 {
2054
2313
  bubbles: true
2055
2314
  }));
2056
2315
  }
2316
+ /**
2317
+ * Decide whether an item should be treated as "the candidate is done with it"
2318
+ * for the purpose of gating forward navigation and further attempts.
2319
+ *
2320
+ * - The candidate must have actually ended an attempt (numAttempts > 0).
2321
+ * - Once attempted, the item is done if they reached the optimal outcome
2322
+ * ('optimal'), or there's nothing to improve on ('unscored'). It is only *not*
2323
+ * done while a better attempt is still possible ('suboptimal') and attempts
2324
+ * remain — see #assessOptimality for how "optimal" is determined.
2325
+ *
2326
+ * `optimality` reflects the last *ended attempt* (see #optimality), not the
2327
+ * live selection — so a freshly-picked optimal answer doesn't count as done
2328
+ * until test-end-attempt evaluates it.
2329
+ */
2330
+ #isItemDone(numAttempts, optimality, maxAttempts) {
2331
+ if (numAttempts === 0)
2332
+ return false;
2333
+ if (optimality !== "suboptimal")
2334
+ return true;
2335
+ const max = maxAttempts ?? 1;
2336
+ return max > 0 && numAttempts >= max;
2337
+ }
2338
+ /**
2339
+ * Decide whether an item's submission is as good as it can get — i.e. the
2340
+ * candidate reached the *optimal* outcome, so there's no reason to make them
2341
+ * try again. 'optimal' means best achievable, 'suboptimal' means a better
2342
+ * attempt is still possible, 'unscored' means there's nothing to judge against.
2343
+ * ("optimal" is the spec's own word — a qti-correct-response is defined as
2344
+ * "the (or an) optimal value".)
2345
+ *
2346
+ * Two signals, in order of authority:
2347
+ * 1. The scored outcome: optimal ⟺ SCORE has reached its maximum (MAXSCORE).
2348
+ * This correctly handles partial-credit / qti-mapping items, where an exact
2349
+ * response match would under- or over-judge.
2350
+ * 2. The declared qti-correct-response, for items that aren't scored (no
2351
+ * SCORE/MAXSCORE) — an exact match is the best available proxy. Bookkeeping
2352
+ * variables like numAttempts can be typed 'response' but never declare a
2353
+ * correctResponse, so we filter on a declared correctResponse.
2354
+ *
2355
+ * Items with neither a comparable score nor a correctResponse (essays, info
2356
+ * items, etc.) are 'unscored' — there's no optimal value to require, so one
2357
+ * attempt is enough.
2358
+ */
2359
+ #assessOptimality(item) {
2360
+ const variables = item.variables ?? [];
2361
+ const score = this.#numericVariable(variables, "SCORE");
2362
+ const maxScore = this.#numericVariable(variables, "MAXSCORE");
2363
+ if (score !== null && maxScore !== null) {
2364
+ return score >= maxScore ? "optimal" : "suboptimal";
2365
+ }
2366
+ const responseVars = variables.filter((v) => v.type === "response" && v.correctResponse !== void 0 && v.correctResponse !== null);
2367
+ if (responseVars.length === 0)
2368
+ return "unscored";
2369
+ const allMatch = responseVars.every((v) => {
2370
+ const expected = v.correctResponse;
2371
+ const actual = v.value;
2372
+ if (actual === void 0 || actual === null)
2373
+ return false;
2374
+ if (Array.isArray(expected) && Array.isArray(actual)) {
2375
+ return expected.length === actual.length && expected.every((e) => actual.includes(e));
2376
+ }
2377
+ if (Array.isArray(expected) || Array.isArray(actual))
2378
+ return false;
2379
+ return expected === actual;
2380
+ });
2381
+ return allMatch ? "optimal" : "suboptimal";
2382
+ }
2383
+ /** Read a single numeric outcome value, or null when absent / non-numeric. */
2384
+ #numericVariable(variables, identifier) {
2385
+ const raw = variables?.find((v) => v.identifier === identifier)?.value;
2386
+ if (raw === void 0 || raw === null || Array.isArray(raw))
2387
+ return null;
2388
+ const parsed = parseFloat(raw.toString());
2389
+ return Number.isNaN(parsed) ? null : parsed;
2390
+ }
2057
2391
  };
2058
- __decorate12([
2059
- property10({ type: String })
2392
+ __decorate14([
2393
+ property12({ type: String })
2060
2394
  ], TestNavigation.prototype, "identifier", void 0);
2061
- __decorate12([
2395
+ __decorate14([
2062
2396
  state4()
2063
2397
  ], TestNavigation.prototype, "initContext", void 0);
2064
- __decorate12([
2398
+ __decorate14([
2065
2399
  state4(),
2066
2400
  provide4({ context: qtiContext })
2067
2401
  ], TestNavigation.prototype, "qtiContext", void 0);
2068
- __decorate12([
2402
+ __decorate14([
2069
2403
  state4(),
2070
- consume6({ context: configContext, subscribe: true })
2404
+ consume7({ context: configContext, subscribe: true })
2071
2405
  ], TestNavigation.prototype, "configContext", void 0);
2072
- __decorate12([
2406
+ __decorate14([
2073
2407
  state4(),
2074
- consume6({ context: testContext, subscribe: true })
2408
+ consume7({ context: testContext, subscribe: true })
2075
2409
  ], TestNavigation.prototype, "_testContext", void 0);
2076
- __decorate12([
2410
+ __decorate14([
2077
2411
  state4(),
2078
- consume6({ context: sessionContext, subscribe: true })
2412
+ consume7({ context: sessionContext, subscribe: true })
2079
2413
  ], TestNavigation.prototype, "_sessionContext", void 0);
2080
- __decorate12([
2414
+ __decorate14([
2081
2415
  state4(),
2082
2416
  provide4({ context: computedContext })
2083
2417
  ], TestNavigation.prototype, "computedContext", void 0);
2084
- __decorate12([
2085
- property10({ type: Boolean, attribute: "auto-score-items" })
2418
+ __decorate14([
2419
+ property12({ type: Boolean, attribute: "auto-score-items" })
2086
2420
  ], TestNavigation.prototype, "autoScoreItems", void 0);
2087
2421
 
2088
2422
  // ../qti-test/dist/components/test-next/test-next.js
2089
- import { css as css8, html as html13, LitElement as LitElement14 } from "lit";
2090
- import { property as property11 } from "lit/decorators.js";
2091
- import { consume as consume7 } from "@lit/context";
2092
- var __decorate13 = function(decorators, target, key, desc) {
2423
+ import { css as css8, html as html14, LitElement as LitElement15 } from "lit";
2424
+ import { property as property13 } from "lit/decorators.js";
2425
+ import { consume as consume8 } from "@lit/context";
2426
+ var __decorate15 = function(decorators, target, key, desc) {
2093
2427
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2094
2428
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2095
2429
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2096
2430
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2097
2431
  };
2098
- var TestNext = class extends LitElement14 {
2432
+ var TestNext = class extends LitElement15 {
2099
2433
  _handleTestElementChange(_oldValue, newValue) {
2100
2434
  if (newValue) {
2101
2435
  this.requestUpdate();
@@ -2147,8 +2481,7 @@ var TestNext = class extends LitElement14 {
2147
2481
  const submissionMode = activeSection?.submissionMode || testPart.submissionMode;
2148
2482
  const activeItem = this.sectionItems[this.itemIndex];
2149
2483
  const isLinearIndividual = navigationMode === "linear" && submissionMode === "individual";
2150
- const numAttempts = Number(activeItem?.variables.find((v) => v.identifier === "numAttempts")?.value) || 0;
2151
- this._internalDisabled = !this.computedContext || this.itemIndex < 0 || this.itemIndex >= (this.sectionItems?.length ?? 0) - 1 || isLinearIndividual && numAttempts === 0;
2484
+ this._internalDisabled = !this.computedContext || this.itemIndex < 0 || this.itemIndex >= (this.sectionItems?.length ?? 0) - 1 || isLinearIndividual && !activeItem?.done;
2152
2485
  }
2153
2486
  _requestItem(identifier) {
2154
2487
  this.dispatchEvent(new CustomEvent("qti-request-navigation", {
@@ -2161,21 +2494,21 @@ var TestNext = class extends LitElement14 {
2161
2494
  }));
2162
2495
  }
2163
2496
  render() {
2164
- return html13`<slot></slot>`;
2497
+ return html14`<slot></slot>`;
2165
2498
  }
2166
2499
  };
2167
- __decorate13([
2168
- property11({ type: Boolean, reflect: true, attribute: "disabled" })
2500
+ __decorate15([
2501
+ property13({ type: Boolean, reflect: true, attribute: "disabled" })
2169
2502
  ], TestNext.prototype, "_internalDisabled", void 0);
2170
- __decorate13([
2171
- consume7({ context: computedContext, subscribe: true })
2503
+ __decorate15([
2504
+ consume8({ context: computedContext, subscribe: true })
2172
2505
  ], TestNext.prototype, "computedContext", void 0);
2173
- __decorate13([
2506
+ __decorate15([
2174
2507
  watch("computedContext")
2175
2508
  ], TestNext.prototype, "_handleTestElementChange", null);
2176
2509
 
2177
2510
  // ../qti-test/dist/components/test-paging-buttons-stamp/test-paging-buttons-stamp.js
2178
- import { html as html14, LitElement as LitElement15 } from "lit";
2511
+ import { html as html15, LitElement as LitElement16 } from "lit";
2179
2512
 
2180
2513
  // ../../node_modules/.pnpm/@heximal+templates@0.1.5/node_modules/@heximal/templates/index.js
2181
2514
  import { render as renderLit, nothing } from "lit-html";
@@ -2782,18 +3115,18 @@ var EvalAstFactory = class {
2782
3115
  }
2783
3116
  const value = this.right.evaluate(scope);
2784
3117
  let receiver = void 0;
2785
- let property18;
3118
+ let property20;
2786
3119
  if (this.left.type === "Getter") {
2787
3120
  receiver = this.left.receiver.evaluate(scope);
2788
- property18 = this.left.name;
3121
+ property20 = this.left.name;
2789
3122
  } else if (this.left.type === "Index") {
2790
3123
  receiver = this.left.receiver.evaluate(scope);
2791
- property18 = this.left.argument.evaluate(scope);
3124
+ property20 = this.left.argument.evaluate(scope);
2792
3125
  } else if (this.left.type === "ID") {
2793
3126
  receiver = scope;
2794
- property18 = this.left.value;
3127
+ property20 = this.left.value;
2795
3128
  }
2796
- return receiver === void 0 ? void 0 : receiver[property18] = value;
3129
+ return receiver === void 0 ? void 0 : receiver[property20] = value;
2797
3130
  }
2798
3131
  return f(this.left.evaluate(scope), this.right.evaluate(scope));
2799
3132
  },
@@ -3246,14 +3579,14 @@ var makeLitTemplate = (template) => {
3246
3579
  };
3247
3580
 
3248
3581
  // ../qti-test/dist/components/test-paging-buttons-stamp/test-paging-buttons-stamp.js
3249
- import { consume as consume8 } from "@lit/context";
3250
- var __decorate14 = function(decorators, target, key, desc) {
3582
+ import { consume as consume9 } from "@lit/context";
3583
+ var __decorate16 = function(decorators, target, key, desc) {
3251
3584
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3252
3585
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3253
3586
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3254
3587
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3255
3588
  };
3256
- var TestPagingButtonsStamp = class extends LitElement15 {
3589
+ var TestPagingButtonsStamp = class extends LitElement16 {
3257
3590
  #internals;
3258
3591
  createRenderRoot() {
3259
3592
  return this;
@@ -3270,26 +3603,26 @@ var TestPagingButtonsStamp = class extends LitElement15 {
3270
3603
  }
3271
3604
  render() {
3272
3605
  if (!this.computedContext)
3273
- return html14``;
3606
+ return html15``;
3274
3607
  const items = this.computedContext.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items));
3275
- return html14` ${items.map((item) => this.myTemplate({ item, view: this.computedContext.view }))} `;
3608
+ return html15` ${items.map((item) => this.myTemplate({ item, view: this.computedContext.view }))} `;
3276
3609
  }
3277
3610
  };
3278
- __decorate14([
3279
- consume8({ context: computedContext, subscribe: true })
3611
+ __decorate16([
3612
+ consume9({ context: computedContext, subscribe: true })
3280
3613
  ], TestPagingButtonsStamp.prototype, "computedContext", void 0);
3281
3614
 
3282
3615
  // ../qti-test/dist/components/test-prev/test-prev.js
3283
- import { css as css9, html as html15, LitElement as LitElement16 } from "lit";
3284
- import { property as property12 } from "lit/decorators.js";
3285
- import { consume as consume9 } from "@lit/context";
3286
- var __decorate15 = function(decorators, target, key, desc) {
3616
+ import { css as css9, html as html16, LitElement as LitElement17 } from "lit";
3617
+ import { property as property14 } from "lit/decorators.js";
3618
+ import { consume as consume10 } from "@lit/context";
3619
+ var __decorate17 = function(decorators, target, key, desc) {
3287
3620
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3288
3621
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3289
3622
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3290
3623
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3291
3624
  };
3292
- var TestPrev = class extends LitElement16 {
3625
+ var TestPrev = class extends LitElement17 {
3293
3626
  _handleTestElementChange(_oldValue, newValue) {
3294
3627
  if (newValue) {
3295
3628
  this.requestUpdate();
@@ -3341,49 +3674,49 @@ var TestPrev = class extends LitElement16 {
3341
3674
  }));
3342
3675
  }
3343
3676
  render() {
3344
- return html15`<slot></slot>`;
3677
+ return html16`<slot></slot>`;
3345
3678
  }
3346
3679
  };
3347
- __decorate15([
3348
- property12({ type: Boolean, reflect: true, attribute: "disabled" })
3680
+ __decorate17([
3681
+ property14({ type: Boolean, reflect: true, attribute: "disabled" })
3349
3682
  ], TestPrev.prototype, "_internalDisabled", void 0);
3350
- __decorate15([
3351
- consume9({ context: computedContext, subscribe: true })
3683
+ __decorate17([
3684
+ consume10({ context: computedContext, subscribe: true })
3352
3685
  ], TestPrev.prototype, "computedContext", void 0);
3353
- __decorate15([
3686
+ __decorate17([
3354
3687
  watch("computedContext")
3355
3688
  ], TestPrev.prototype, "_handleTestElementChange", null);
3356
3689
 
3357
3690
  // ../qti-test/dist/components/test-print-context/test-print-context.js
3358
- import { html as html16, LitElement as LitElement17 } from "lit";
3359
- import { consume as consume10 } from "@lit/context";
3691
+ import { html as html17, LitElement as LitElement18 } from "lit";
3692
+ import { consume as consume11 } from "@lit/context";
3360
3693
  import { state as state5 } from "lit/decorators.js";
3361
- var __decorate16 = function(decorators, target, key, desc) {
3694
+ var __decorate18 = function(decorators, target, key, desc) {
3362
3695
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3363
3696
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3364
3697
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3365
3698
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3366
3699
  };
3367
- var TestPrintContext = class extends LitElement17 {
3700
+ var TestPrintContext = class extends LitElement18 {
3368
3701
  render() {
3369
- return html16` <small><pre>${JSON.stringify(this.computedContext, null, 2)}</pre></small> `;
3702
+ return html17` <small><pre>${JSON.stringify(this.computedContext, null, 2)}</pre></small> `;
3370
3703
  }
3371
3704
  };
3372
- __decorate16([
3705
+ __decorate18([
3373
3706
  state5(),
3374
- consume10({ context: computedContext, subscribe: true })
3707
+ consume11({ context: computedContext, subscribe: true })
3375
3708
  ], TestPrintContext.prototype, "computedContext", void 0);
3376
3709
 
3377
3710
  // ../qti-test/dist/components/test-print-item-variables/test-print-item-variables.js
3378
- import { html as html17, css as css10, LitElement as LitElement18 } from "lit";
3379
- import { consume as consume11 } from "@lit/context";
3380
- var __decorate17 = function(decorators, target, key, desc) {
3711
+ import { html as html18, css as css10, LitElement as LitElement19 } from "lit";
3712
+ import { consume as consume12 } from "@lit/context";
3713
+ var __decorate19 = function(decorators, target, key, desc) {
3381
3714
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3382
3715
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3383
3716
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3384
3717
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3385
3718
  };
3386
- var TestPrintVariables = class extends LitElement18 {
3719
+ var TestPrintVariables = class extends LitElement19 {
3387
3720
  static {
3388
3721
  this.styles = css10`
3389
3722
  table {
@@ -3411,10 +3744,10 @@ var TestPrintVariables = class extends LitElement18 {
3411
3744
  render() {
3412
3745
  const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
3413
3746
  if (!activeItem || !activeItem.variables)
3414
- return html17``;
3747
+ return html18``;
3415
3748
  const responseVariables = activeItem.variables.filter((v) => v.type === "response");
3416
3749
  const outcomeVariables = activeItem.variables.filter((v) => v.type === "outcome");
3417
- const renderTable = (variables, title) => html17`
3750
+ const renderTable = (variables, title) => html18`
3418
3751
  <h3>${title}</h3>
3419
3752
  <table>
3420
3753
  <thead>
@@ -3431,7 +3764,7 @@ var TestPrintVariables = class extends LitElement18 {
3431
3764
  const correctResponse = v.correctResponse ? Array.isArray(v.correctResponse) ? v.correctResponse.join(", ") : v.correctResponse : "";
3432
3765
  const mapEntries = v.mapping?.mapEntries?.map((m) => `${m.mapKey}=${m.mappedValue}pt`).join(", ") || "";
3433
3766
  const areaMapEntries = v.areaMapping?.areaMapEntries?.map((m) => `${m.shape}(${m.coords})=${m.mappedValue}pt`).join(", ") || "";
3434
- return html17`
3767
+ return html18`
3435
3768
  <tr>
3436
3769
  <td>${v.identifier}</td>
3437
3770
  <td>${Array.isArray(v.value) ? v.value.join(", ") : v.value}</td>
@@ -3444,26 +3777,26 @@ var TestPrintVariables = class extends LitElement18 {
3444
3777
  </tbody>
3445
3778
  </table>
3446
3779
  `;
3447
- return html17`
3780
+ return html18`
3448
3781
  ${renderTable(responseVariables, "Response Variables")} ${renderTable(outcomeVariables, "Outcome Variables")}
3449
3782
  `;
3450
3783
  }
3451
3784
  };
3452
- __decorate17([
3453
- consume11({ context: computedContext, subscribe: true })
3785
+ __decorate19([
3786
+ consume12({ context: computedContext, subscribe: true })
3454
3787
  ], TestPrintVariables.prototype, "computedContext", void 0);
3455
3788
 
3456
3789
  // ../qti-test/dist/components/test-scoring-buttons/test-scoring-buttons.js
3457
- import { html as html18, LitElement as LitElement19, nothing as nothing2 } from "lit";
3458
- import { consume as consume12 } from "@lit/context";
3459
- import { property as property13 } from "lit/decorators.js";
3460
- var __decorate18 = function(decorators, target, key, desc) {
3790
+ import { html as html19, LitElement as LitElement20, nothing as nothing2 } from "lit";
3791
+ import { consume as consume13 } from "@lit/context";
3792
+ import { property as property15 } from "lit/decorators.js";
3793
+ var __decorate20 = function(decorators, target, key, desc) {
3461
3794
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3462
3795
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3463
3796
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3464
3797
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3465
3798
  };
3466
- var TestScoringButtons = class extends LitElement19 {
3799
+ var TestScoringButtons = class extends LitElement20 {
3467
3800
  createRenderRoot() {
3468
3801
  return this;
3469
3802
  }
@@ -3492,7 +3825,7 @@ var TestScoringButtons = class extends LitElement19 {
3492
3825
  render() {
3493
3826
  const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
3494
3827
  if (!activeItem || !activeItem.variables)
3495
- return html18``;
3828
+ return html19``;
3496
3829
  const maxScore = activeItem.variables.find((vr) => vr.identifier == "MAXSCORE")?.value;
3497
3830
  const scoreOutcome = activeItem.variables.find((vr) => vr.identifier == "SCORE");
3498
3831
  const score = scoreOutcome?.value;
@@ -3500,7 +3833,7 @@ var TestScoringButtons = class extends LitElement19 {
3500
3833
  if (!maxScore || !scoreOutcome)
3501
3834
  return nothing2;
3502
3835
  const scores = [...Array(Number(maxScore) + 1).keys()];
3503
- return html18`${this.myTemplate ? this.myTemplate({ scores, score, disabled }) : nothing2}`;
3836
+ return html19`${this.myTemplate ? this.myTemplate({ scores, score, disabled }) : nothing2}`;
3504
3837
  }
3505
3838
  constructor() {
3506
3839
  super();
@@ -3516,27 +3849,27 @@ var TestScoringButtons = class extends LitElement19 {
3516
3849
  });
3517
3850
  }
3518
3851
  };
3519
- __decorate18([
3520
- property13({ type: String, attribute: "view" })
3852
+ __decorate20([
3853
+ property15({ type: String, attribute: "view" })
3521
3854
  ], TestScoringButtons.prototype, "view", void 0);
3522
- __decorate18([
3523
- property13({ type: Boolean })
3855
+ __decorate20([
3856
+ property15({ type: Boolean })
3524
3857
  ], TestScoringButtons.prototype, "disabled", void 0);
3525
- __decorate18([
3526
- consume12({ context: computedContext, subscribe: true })
3858
+ __decorate20([
3859
+ consume13({ context: computedContext, subscribe: true })
3527
3860
  ], TestScoringButtons.prototype, "computedContext", void 0);
3528
3861
 
3529
3862
  // ../qti-test/dist/components/test-scoring-feedback/test-scoring-feedback.js
3530
- import { consume as consume13 } from "@lit/context";
3531
- import { html as html19, LitElement as LitElement20 } from "lit";
3532
- import { property as property14 } from "lit/decorators.js";
3533
- var __decorate19 = function(decorators, target, key, desc) {
3863
+ import { consume as consume14 } from "@lit/context";
3864
+ import { html as html20, LitElement as LitElement21 } from "lit";
3865
+ import { property as property16 } from "lit/decorators.js";
3866
+ var __decorate21 = function(decorators, target, key, desc) {
3534
3867
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3535
3868
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3536
3869
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3537
3870
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3538
3871
  };
3539
- var TestScoringFeedback = class extends LitElement20 {
3872
+ var TestScoringFeedback = class extends LitElement21 {
3540
3873
  constructor() {
3541
3874
  super(...arguments);
3542
3875
  this.view = null;
@@ -3544,9 +3877,9 @@ var TestScoringFeedback = class extends LitElement20 {
3544
3877
  render() {
3545
3878
  const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
3546
3879
  if (!activeItem || !activeItem.variables)
3547
- return html19``;
3880
+ return html20``;
3548
3881
  if (activeItem["category"] === "dep-informational")
3549
- return html19`<div>${this.dataset.informational}</div>`;
3882
+ return html20`<div>${this.dataset.informational}</div>`;
3550
3883
  const completionStatus = activeItem?.variables.find((v) => v.identifier === "completionStatus")?.value;
3551
3884
  const scoreOutcome = activeItem?.variables.find((vr) => vr.identifier == "SCORE");
3552
3885
  const score = parseFloat(scoreOutcome?.value);
@@ -3566,26 +3899,26 @@ var TestScoringFeedback = class extends LitElement20 {
3566
3899
  }
3567
3900
  return this.dataset.inProgress;
3568
3901
  };
3569
- return html19`<div>${feedbackText()}</div>`;
3902
+ return html20`<div>${feedbackText()}</div>`;
3570
3903
  }
3571
3904
  };
3572
- __decorate19([
3573
- consume13({ context: computedContext, subscribe: true })
3905
+ __decorate21([
3906
+ consume14({ context: computedContext, subscribe: true })
3574
3907
  ], TestScoringFeedback.prototype, "computedContext", void 0);
3575
- __decorate19([
3576
- property14({ type: String, attribute: "view" })
3908
+ __decorate21([
3909
+ property16({ type: String, attribute: "view" })
3577
3910
  ], TestScoringFeedback.prototype, "view", void 0);
3578
3911
 
3579
3912
  // ../qti-test/dist/components/test-section-buttons-stamp/test-section-buttons-stamp.js
3580
- import { html as html20, LitElement as LitElement21 } from "lit";
3581
- import { consume as consume14 } from "@lit/context";
3582
- var __decorate20 = function(decorators, target, key, desc) {
3913
+ import { html as html21, LitElement as LitElement22 } from "lit";
3914
+ import { consume as consume15 } from "@lit/context";
3915
+ var __decorate22 = function(decorators, target, key, desc) {
3583
3916
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3584
3917
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3585
3918
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3586
3919
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3587
3920
  };
3588
- var TestSectionButtonsStamp = class extends LitElement21 {
3921
+ var TestSectionButtonsStamp = class extends LitElement22 {
3589
3922
  #internals;
3590
3923
  createRenderRoot() {
3591
3924
  return this;
@@ -3602,25 +3935,25 @@ var TestSectionButtonsStamp = class extends LitElement21 {
3602
3935
  }
3603
3936
  render() {
3604
3937
  if (!this.computedContext)
3605
- return html20``;
3938
+ return html21``;
3606
3939
  const sections = this.computedContext.testParts.flatMap((testPart) => testPart.sections);
3607
- return html20` ${sections.map((item) => this.myTemplate({ item }))} `;
3940
+ return html21` ${sections.map((item) => this.myTemplate({ item }))} `;
3608
3941
  }
3609
3942
  };
3610
- __decorate20([
3611
- consume14({ context: computedContext, subscribe: true })
3943
+ __decorate22([
3944
+ consume15({ context: computedContext, subscribe: true })
3612
3945
  ], TestSectionButtonsStamp.prototype, "computedContext", void 0);
3613
3946
 
3614
3947
  // ../qti-test/dist/components/test-section-link/test-section-link.js
3615
- import { css as css11, html as html21, LitElement as LitElement22 } from "lit";
3616
- import { property as property15 } from "lit/decorators.js";
3617
- var __decorate21 = function(decorators, target, key, desc) {
3948
+ import { css as css11, html as html22, LitElement as LitElement23 } from "lit";
3949
+ import { property as property17 } from "lit/decorators.js";
3950
+ var __decorate23 = function(decorators, target, key, desc) {
3618
3951
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3619
3952
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3620
3953
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3621
3954
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3622
3955
  };
3623
- var TestSectionLink = class extends LitElement22 {
3956
+ var TestSectionLink = class extends LitElement23 {
3624
3957
  static {
3625
3958
  this.styles = css11`
3626
3959
  :host {
@@ -3647,24 +3980,24 @@ var TestSectionLink = class extends LitElement22 {
3647
3980
  this.addEventListener("click", () => this.#requestItem(this.sectionId));
3648
3981
  }
3649
3982
  render() {
3650
- return html21` <slot></slot> `;
3983
+ return html22` <slot></slot> `;
3651
3984
  }
3652
3985
  };
3653
- __decorate21([
3654
- property15({ type: String, attribute: "section-id" })
3986
+ __decorate23([
3987
+ property17({ type: String, attribute: "section-id" })
3655
3988
  ], TestSectionLink.prototype, "sectionId", void 0);
3656
3989
 
3657
3990
  // ../qti-test/dist/components/test-stamp/test-stamp.js
3658
- import { html as html22, LitElement as LitElement23, nothing as nothing3 } from "lit";
3659
- import { property as property16, state as state6 } from "lit/decorators.js";
3660
- import { consume as consume15 } from "@lit/context";
3661
- var __decorate22 = function(decorators, target, key, desc) {
3991
+ import { html as html23, LitElement as LitElement24, nothing as nothing3 } from "lit";
3992
+ import { property as property18, state as state6 } from "lit/decorators.js";
3993
+ import { consume as consume16 } from "@lit/context";
3994
+ var __decorate24 = function(decorators, target, key, desc) {
3662
3995
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3663
3996
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3664
3997
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3665
3998
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3666
3999
  };
3667
- var TestStamp = class extends LitElement23 {
4000
+ var TestStamp = class extends LitElement24 {
3668
4001
  constructor() {
3669
4002
  super(...arguments);
3670
4003
  this.debug = false;
@@ -3727,33 +4060,33 @@ var TestStamp = class extends LitElement23 {
3727
4060
  }));
3728
4061
  }
3729
4062
  render() {
3730
- return html22` ${this.debug ? html22`<small><pre>${JSON.stringify(this.stampContext, null, 2)}</pre></small>` : nothing3}
4063
+ return html23` ${this.debug ? html23`<small><pre>${JSON.stringify(this.stampContext, null, 2)}</pre></small>` : nothing3}
3731
4064
  ${this.myTemplate ? this.myTemplate(this.stampContext) : nothing3}`;
3732
4065
  }
3733
4066
  };
3734
- __decorate22([
3735
- property16({ type: Boolean, reflect: true })
4067
+ __decorate24([
4068
+ property18({ type: Boolean, reflect: true })
3736
4069
  ], TestStamp.prototype, "debug", void 0);
3737
- __decorate22([
4070
+ __decorate24([
3738
4071
  state6(),
3739
- consume15({ context: computedContext, subscribe: true })
4072
+ consume16({ context: computedContext, subscribe: true })
3740
4073
  ], TestStamp.prototype, "computedContext", void 0);
3741
- __decorate22([
4074
+ __decorate24([
3742
4075
  state6()
3743
4076
  ], TestStamp.prototype, "stampContext", void 0);
3744
4077
 
3745
4078
  // ../qti-test/dist/components/test-view-toggle/test-view.js
3746
- import { html as html23, LitElement as LitElement24 } from "lit";
3747
- import { property as property17, state as state7 } from "lit/decorators.js";
3748
- import { consume as consume16 } from "@lit/context";
3749
- var __decorate23 = function(decorators, target, key, desc) {
4079
+ import { html as html24, LitElement as LitElement25 } from "lit";
4080
+ import { property as property19, state as state7 } from "lit/decorators.js";
4081
+ import { consume as consume17 } from "@lit/context";
4082
+ var __decorate25 = function(decorators, target, key, desc) {
3750
4083
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3751
4084
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3752
4085
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3753
4086
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3754
4087
  };
3755
4088
  var _a;
3756
- var TestView = class extends LitElement24 {
4089
+ var TestView = class extends LitElement25 {
3757
4090
  constructor() {
3758
4091
  super(...arguments);
3759
4092
  this.label = "view";
@@ -3785,7 +4118,7 @@ var TestView = class extends LitElement24 {
3785
4118
  }));
3786
4119
  }
3787
4120
  render() {
3788
- return html23`
4121
+ return html24`
3789
4122
  <label part="label" for="viewSelect">${this.label}</label>
3790
4123
  <select
3791
4124
  part="select"
@@ -3795,38 +4128,38 @@ var TestView = class extends LitElement24 {
3795
4128
  this._switchView(el.value);
3796
4129
  }}
3797
4130
  >
3798
- ${this._viewOptions.map((v) => html23`<option value="${v}" ?selected=${v === this.sessionContext.view}>${v}</option>`)}
4131
+ ${this._viewOptions.map((v) => html24`<option value="${v}" ?selected=${v === this.sessionContext.view}>${v}</option>`)}
3799
4132
  </select>
3800
4133
  `;
3801
4134
  }
3802
4135
  };
3803
4136
  _a = TestView;
3804
- __decorate23([
3805
- consume16({ context: sessionContext, subscribe: true })
4137
+ __decorate25([
4138
+ consume17({ context: sessionContext, subscribe: true })
3806
4139
  ], TestView.prototype, "sessionContext", void 0);
3807
- __decorate23([
3808
- property17({ type: String })
4140
+ __decorate25([
4141
+ property19({ type: String })
3809
4142
  ], TestView.prototype, "label", void 0);
3810
- __decorate23([
3811
- property17({ type: String, attribute: "view-options" })
4143
+ __decorate25([
4144
+ property19({ type: String, attribute: "view-options" })
3812
4145
  ], TestView.prototype, "viewOptions", void 0);
3813
- __decorate23([
4146
+ __decorate25([
3814
4147
  watch("viewOptions", { waitUntilFirstUpdate: true })
3815
4148
  ], TestView.prototype, "_handleViewOptionsChange", void 0);
3816
- __decorate23([
4149
+ __decorate25([
3817
4150
  state7()
3818
4151
  ], TestView.prototype, "_viewOptions", void 0);
3819
4152
 
3820
4153
  // ../qti-test/dist/components/test-view-toggle/test-view-toggle.js
3821
- import { consume as consume17 } from "@lit/context";
3822
- import { html as html24, LitElement as LitElement25, nothing as nothing4 } from "lit";
3823
- var __decorate24 = function(decorators, target, key, desc) {
4154
+ import { consume as consume18 } from "@lit/context";
4155
+ import { html as html25, LitElement as LitElement26, nothing as nothing4 } from "lit";
4156
+ var __decorate26 = function(decorators, target, key, desc) {
3824
4157
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3825
4158
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3826
4159
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3827
4160
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3828
4161
  };
3829
- var TestViewToggle = class extends LitElement25 {
4162
+ var TestViewToggle = class extends LitElement26 {
3830
4163
  createRenderRoot() {
3831
4164
  return this;
3832
4165
  }
@@ -3856,13 +4189,13 @@ var TestViewToggle = class extends LitElement25 {
3856
4189
  });
3857
4190
  }
3858
4191
  render() {
3859
- return html24`${this.myTemplate ? this.myTemplate({
4192
+ return html25`${this.myTemplate ? this.myTemplate({
3860
4193
  view: this.sessionContext?.view
3861
4194
  }) : nothing4}`;
3862
4195
  }
3863
4196
  };
3864
- __decorate24([
3865
- consume17({ context: sessionContext, subscribe: true })
4197
+ __decorate26([
4198
+ consume18({ context: sessionContext, subscribe: true })
3866
4199
  ], TestViewToggle.prototype, "sessionContext", void 0);
3867
4200
 
3868
4201
  // ../qti-test/dist/elements.js
@@ -3870,6 +4203,7 @@ var qtiTestElements = [
3870
4203
  { tag: "qti-assessment-item-ref", ctor: QtiAssessmentItemRef },
3871
4204
  { tag: "qti-assessment-section", ctor: QtiAssessmentSection },
3872
4205
  { tag: "qti-assessment-test", ctor: QtiAssessmentTest },
4206
+ { tag: "qti-item-session-control", ctor: QtiItemSessionControl },
3873
4207
  { tag: "qti-outcome-processing", ctor: QtiOutcomeProcessing },
3874
4208
  { tag: "qti-test", ctor: QtiTest },
3875
4209
  { tag: "qti-test-feedback", ctor: QtiTestFeedback },
@@ -3905,6 +4239,7 @@ export {
3905
4239
  QtiAssessmentItemRef,
3906
4240
  QtiAssessmentSection,
3907
4241
  QtiAssessmentTest,
4242
+ QtiItemSessionControl,
3908
4243
  QtiOutcomeProcessing,
3909
4244
  QtiOutcomeProcessingProcessor,
3910
4245
  QtiTestFeedback,
@@ -3951,4 +4286,4 @@ export {
3951
4286
  * Portions Copyright (c) 2013, the Dart project authors.
3952
4287
  *)
3953
4288
  */
3954
- //# sourceMappingURL=chunk-Q52IJELT.js.map
4289
+ //# sourceMappingURL=chunk-CRLXQZFA.js.map