@qti-components/elements 1.10.0 → 2.0.0-next.5

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.
@@ -5,16 +5,6 @@ import type { RegisteredInteraction } from '@qti-components/base';
5
5
  import type { VariableDeclaration, VariableValue } from '@qti-components/base';
6
6
  import type { OutcomeVariable, ResponseVariable } from '@qti-components/base';
7
7
  import type { ItemContext } from '@qti-components/base';
8
- /**
9
- * @summary The qti-assessment-item element contains all the other QTI 3 item structures.
10
- * @documentation https://www.imsglobal.org/spec/qti/v3p0/impl#h.dltnnj87l0yj
11
- *
12
- * @dependency qti-feedback
13
- * @dependency qti-responseprocessing
14
- *
15
- * @event qti-item-context-updated - Emitted when through a user action the itemContext is updated,
16
- * typically on interaction with the interactions, or calling response processing.
17
- */
18
8
  export declare class QtiAssessmentItem extends LitElement {
19
9
  #private;
20
10
  identifier: string;
@@ -57,6 +47,16 @@ export declare class QtiAssessmentItem extends LitElement {
57
47
  render(): import("lit").TemplateResult<1>;
58
48
  connectedCallback(): void;
59
49
  disconnectedCallback(): void;
50
+ /**
51
+ * Scores the attempt. The item does not score itself: its response processing does, like a
52
+ * backend, and sends the outcomes back in `cito-item-set-outcomes`, which the item applies in one
53
+ * update. The item keeps the attempt count (QTI info model §2.2.2: the delivery engine maintains
54
+ * numAttempts) and hands the new count to the run, whose rules may read it. Without response
55
+ * processing the item only counts the attempt. A run that throws still counts the attempt and applies
56
+ * no outcomes.
57
+ *
58
+ * @returns whether the item has response processing.
59
+ */
60
60
  processResponse(countNumAttempts?: boolean, reportValidityAfterScoring?: boolean): boolean;
61
61
  resetResponses(): void;
62
62
  protected getResponse(identifier: string): Readonly<ResponseVariable>;
@@ -9,16 +9,6 @@ import { LitElement, html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { itemContext, itemContextVariables } from '@qti-components/base';
11
11
  import { dispatchPublic } from '../../public-events';
12
- /**
13
- * @summary The qti-assessment-item element contains all the other QTI 3 item structures.
14
- * @documentation https://www.imsglobal.org/spec/qti/v3p0/impl#h.dltnnj87l0yj
15
- *
16
- * @dependency qti-feedback
17
- * @dependency qti-responseprocessing
18
- *
19
- * @event qti-item-context-updated - Emitted when through a user action the itemContext is updated,
20
- * typically on interaction with the interactions, or calling response processing.
21
- */
22
12
  export class QtiAssessmentItem extends LitElement {
23
13
  constructor() {
24
14
  super(...arguments);
@@ -64,13 +54,38 @@ export class QtiAssessmentItem extends LitElement {
64
54
  const { responseIdentifier, countAttempt } = e.detail;
65
55
  this.validate();
66
56
  this.updateResponseVariable(responseIdentifier, 'true');
67
- this.processResponse(countAttempt);
57
+ this.#processResponse(countAttempt, true, responseIdentifier);
68
58
  };
69
- this.#handleSetOutcomeValue = (e) => {
59
+ /**
60
+ * Applies the outcomes of a processing run, ours or a backend's, in one update, with the
61
+ * interactions' validity.
62
+ *
63
+ * completionStatus is response processing's to set (QTI info model §2.2.2). When an attempt of a
64
+ * non-adaptive item ends and processing left it `unknown`, the item marks it `completed`: the
65
+ * attempt is done. TAO does the same, and the 1EdTech results-reporting reference results expect
66
+ * it. An adaptive item is completed only by its own processing.
67
+ */
68
+ this.#handleSetOutcomes = (e) => {
70
69
  e.stopImmediatePropagation();
71
- const { outcomeIdentifier, value } = e.detail;
72
- this.updateOutcomeVariable(outcomeIdentifier, value);
73
- e.stopPropagation();
70
+ const { outcomes = [], responseProcessed = false } = e.detail ?? {};
71
+ const completion = [];
72
+ if (responseProcessed && this.adaptive === 'false') {
73
+ const sent = outcomes.find(o => o.identifier === 'completionStatus')?.value;
74
+ if ((sent ?? this.getVariable('completionStatus')?.value) === 'unknown') {
75
+ completion.push({ identifier: 'completionStatus', value: 'completed' });
76
+ }
77
+ }
78
+ this.#applyOutcomes([...outcomes, ...completion], this.#checkValidity(false));
79
+ if (responseProcessed) {
80
+ // `responseProcessed` marks this as the end of an attempt (vs. a plain
81
+ // selection, which fires the same event without the flag). Lets listeners
82
+ // react only when an attempt actually ends.
83
+ this.dispatchEvent(new CustomEvent('qti-item-context-updated', {
84
+ bubbles: true,
85
+ composed: true,
86
+ detail: { itemContext: this._context, responseProcessed: true }
87
+ }));
88
+ }
74
89
  };
75
90
  this.#handleSetTemplateValue = (e) => {
76
91
  e.stopImmediatePropagation();
@@ -196,6 +211,8 @@ export class QtiAssessmentItem extends LitElement {
196
211
  // The interactions take the answers over from the context, some of them a moment from now, and
197
212
  // a different answer can mean a different verdict. Look once they have.
198
213
  void this.updateComplete.then(() => this.#revalidate());
214
+ if (this.isConnected)
215
+ this.#beginFirstAttempt();
199
216
  }
200
217
  get state() {
201
218
  return this._context.state;
@@ -238,6 +255,7 @@ export class QtiAssessmentItem extends LitElement {
238
255
  this.updateComplete.then(() => {
239
256
  // The verdict goes in first, so whoever hears the item connect reads it with the rest.
240
257
  this.validate(false);
258
+ this.#beginFirstAttempt();
241
259
  dispatchPublic(this, 'cito-item-rendered', this);
242
260
  // Internal plumbing: the test and the item container listen for this name.
243
261
  this.dispatchEvent(new CustomEvent('qti-assessment-item-connected', {
@@ -256,7 +274,7 @@ export class QtiAssessmentItem extends LitElement {
256
274
  this.addEventListener('qti-register-variable', this.#handleRegisterVariable);
257
275
  this.addEventListener('qti-register-interaction', this.#handleRegisterInteraction);
258
276
  this.addEventListener('end-attempt', this.#handleEndAttempt);
259
- this.addEventListener('qti-set-outcome-value', this.#handleSetOutcomeValue);
277
+ this.addEventListener('cito-item-set-outcomes', this.#handleSetOutcomes);
260
278
  this.addEventListener('qti-set-template-value', this.#handleSetTemplateValue);
261
279
  this.addEventListener('qti-set-correct-response', this.#handleSetCorrectResponse);
262
280
  this.addEventListener('qti-interaction-response', this.#handleUpdateResponseVariable);
@@ -265,7 +283,7 @@ export class QtiAssessmentItem extends LitElement {
265
283
  this.removeEventListener('qti-register-variable', this.#handleRegisterVariable);
266
284
  this.removeEventListener('qti-register-interaction', this.#handleRegisterInteraction);
267
285
  this.removeEventListener('end-attempt', this.#handleEndAttempt);
268
- this.removeEventListener('qti-set-outcome-value', this.#handleSetOutcomeValue);
286
+ this.removeEventListener('cito-item-set-outcomes', this.#handleSetOutcomes);
269
287
  this.removeEventListener('qti-set-template-value', this.#handleSetTemplateValue);
270
288
  this.removeEventListener('qti-set-correct-response', this.#handleSetCorrectResponse);
271
289
  this.removeEventListener('qti-interaction-response', this.#handleUpdateResponseVariable);
@@ -273,7 +291,16 @@ export class QtiAssessmentItem extends LitElement {
273
291
  #handleRegisterVariable;
274
292
  #handleRegisterInteraction;
275
293
  #handleEndAttempt;
276
- #handleSetOutcomeValue;
294
+ /**
295
+ * Applies the outcomes of a processing run, ours or a backend's, in one update, with the
296
+ * interactions' validity.
297
+ *
298
+ * completionStatus is response processing's to set (QTI info model §2.2.2). When an attempt of a
299
+ * non-adaptive item ends and processing left it `unknown`, the item marks it `completed`: the
300
+ * attempt is done. TAO does the same, and the 1EdTech results-reporting reference results expect
301
+ * it. An adaptive item is completed only by its own processing.
302
+ */
303
+ #handleSetOutcomes;
277
304
  #handleSetTemplateValue;
278
305
  #handleSetCorrectResponse;
279
306
  #handleUpdateResponseVariable;
@@ -294,35 +321,72 @@ export class QtiAssessmentItem extends LitElement {
294
321
  this.#templateProcessing.process();
295
322
  this.#initialContext = { ...this._context, variables: this._context.variables };
296
323
  }
324
+ /**
325
+ * Scores the attempt. The item does not score itself: its response processing does, like a
326
+ * backend, and sends the outcomes back in `cito-item-set-outcomes`, which the item applies in one
327
+ * update. The item keeps the attempt count (QTI info model §2.2.2: the delivery engine maintains
328
+ * numAttempts) and hands the new count to the run, whose rules may read it. Without response
329
+ * processing the item only counts the attempt. A run that throws still counts the attempt and applies
330
+ * no outcomes.
331
+ *
332
+ * @returns whether the item has response processing.
333
+ */
297
334
  processResponse(countNumAttempts = true, reportValidityAfterScoring = true) {
298
- this.validate(reportValidityAfterScoring);
299
- if (countNumAttempts) {
300
- this.updateOutcomeVariable('numAttempts', (+this._context.variables.find(v => v.identifier === 'numAttempts')?.value + 1).toString());
301
- }
302
- if (this.adaptive === 'false') {
303
- this.#resetOutcomeVariablesBeforeResponseProcessing();
304
- }
305
- const responseProcessor = this.querySelector('qti-response-processing');
306
- const canProcess = !!(responseProcessor && typeof responseProcessor.process === 'function');
307
- if (canProcess) {
308
- responseProcessor.process();
309
- }
310
- if (this.adaptive === 'false') {
311
- // if adaptive, completionStatus is set by the processing template
312
- this.updateOutcomeVariable('completionStatus', this.#getCompletionStatus());
335
+ return this.#processResponse(countNumAttempts, reportValidityAfterScoring);
336
+ }
337
+ #processResponse(countNumAttempts, reportValidity, endedBy) {
338
+ this.#checkValidity(reportValidity);
339
+ this.#resetOtherEndAttempts(endedBy);
340
+ const numAttempts = +this.getVariable('numAttempts')?.value + (countNumAttempts ? 1 : 0);
341
+ const processor = this.querySelector('qti-response-processing');
342
+ if (typeof processor?.processResponse === 'function') {
343
+ try {
344
+ processor.processResponse({ numAttempts });
345
+ }
346
+ catch (error) {
347
+ // The attempt started, so it counts (QTI info model §2.2.2), as in TAO and Amp-up; the
348
+ // outcomes of a run that failed are not applied.
349
+ this.#applyOutcomes([{ identifier: 'numAttempts', value: String(numAttempts) }]);
350
+ throw error;
351
+ }
352
+ return true;
313
353
  }
314
- // `responseProcessed` marks this as the end of an attempt (vs. a plain
315
- // selection, which fires the same event without the flag). Lets listeners
316
- // react only when an attempt actually ends.
317
- this.dispatchEvent(new CustomEvent('qti-item-context-updated', {
318
- bubbles: true,
319
- composed: true,
320
- detail: { itemContext: this._context, responseProcessed: true }
321
- }));
322
- return canProcess;
354
+ dispatchPublic(this, 'cito-item-set-outcomes', {
355
+ outcomes: [{ identifier: 'numAttempts', value: String(numAttempts) }],
356
+ responseProcessed: true
357
+ });
358
+ return false;
359
+ }
360
+ /**
361
+ * QTI info model §2.4.5: when an attempt ends any other way than through an end-attempt
362
+ * interaction, that interaction's response is `false`.
363
+ */
364
+ #resetOtherEndAttempts(endedBy) {
365
+ const others = Array.from(this.querySelectorAll('qti-end-attempt-interaction'), el => el.getAttribute('response-identifier')).filter(id => id && id !== endedBy);
366
+ if (others.length === 0)
367
+ return;
368
+ this._context = {
369
+ ...this._context,
370
+ variables: this._context.variables.map(v => (others.includes(v.identifier) ? { ...v, value: 'false' } : v))
371
+ };
323
372
  }
324
373
  resetResponses() {
325
374
  this._context = this.#initialContext;
375
+ if (this.isConnected)
376
+ this.#beginFirstAttempt();
377
+ }
378
+ /**
379
+ * QTI info model §2.2.2: completionStatus is `not_attempted` until the first attempt starts, then
380
+ * `unknown`. The attempt starts when the candidate gets the item, so an item on screen is never
381
+ * `not_attempted` — also not after a reset or a restore that says so.
382
+ */
383
+ #beginFirstAttempt() {
384
+ if (this.getVariable('completionStatus')?.value !== 'not_attempted')
385
+ return;
386
+ this._context = {
387
+ ...this._context,
388
+ variables: this._context.variables.map(v => v.identifier === 'completionStatus' ? { ...v, value: 'unknown' } : v)
389
+ };
326
390
  }
327
391
  getResponse(identifier) {
328
392
  return this.getVariable(identifier);
@@ -358,10 +422,8 @@ export class QtiAssessmentItem extends LitElement {
358
422
  responseIdentifier: identifier,
359
423
  response: Array.isArray(value) ? [...value] : value
360
424
  });
361
- if (this.adaptive === 'false') {
362
- // if adaptive, completionStatus is set by the processing template
363
- this.updateOutcomeVariable('completionStatus', this.#getCompletionStatus());
364
- }
425
+ // A different answer can mean a different verdict.
426
+ this.validate(false);
365
427
  }
366
428
  /**
367
429
  * Updates the template variable with the specified identifier to the given value.
@@ -449,32 +511,53 @@ export class QtiAssessmentItem extends LitElement {
449
511
  * @param value - The new value for the response variable.
450
512
  */
451
513
  updateOutcomeVariable(identifier, value) {
452
- const outcomeVariable = this.getOutcome(identifier);
453
- if (!outcomeVariable) {
454
- console.warn(`Can not set qti-outcome-identifier: ${identifier}, it is not available`);
455
- return;
514
+ this.#applyOutcomes([{ identifier, value }]);
515
+ }
516
+ /**
517
+ * Publishes the outcomes (and the validity) in one update, then a `cito-item-outcome-changed` per
518
+ * outcome with its final value and one `cito-item-outcomes-changed` with all of them.
519
+ * An identifier the item does not declare is skipped with a warning; a later entry for the same
520
+ * identifier wins.
521
+ */
522
+ #applyOutcomes(outcomes, valid) {
523
+ const values = new Map();
524
+ for (const { identifier, value } of outcomes) {
525
+ const variable = this.getVariable(identifier);
526
+ if (!variable) {
527
+ console.warn(`Can not set qti-outcome-identifier: ${identifier}, it is not available`);
528
+ continue;
529
+ }
530
+ values.set(identifier, variable.cardinality === 'single' ? value : Array.isArray(value) ? value : [value]);
456
531
  }
532
+ const validChanged = valid !== undefined && valid !== this._context.valid;
533
+ if (values.size === 0 && !validChanged)
534
+ return;
457
535
  this._context = {
458
536
  ...this._context,
459
- variables: this._context.variables.map(v => {
460
- if (v.identifier !== identifier) {
461
- return v;
462
- }
463
- return {
464
- ...v,
465
- value: outcomeVariable.cardinality === 'single' ? value : Array.isArray(value) ? value : [value]
466
- };
467
- })
537
+ ...(validChanged ? { valid } : {}),
538
+ variables: this._context.variables.map(v => values.has(v.identifier) ? { ...v, value: values.get(v.identifier) } : v)
468
539
  };
469
- dispatchPublic(this, 'cito-item-outcome-changed', {
470
- item: this.identifier,
471
- outcomeIdentifier: identifier,
472
- value: this._context.variables.find(v => v.identifier === identifier)?.value
473
- });
540
+ if (values.size > 0) {
541
+ for (const [outcomeIdentifier, value] of values) {
542
+ dispatchPublic(this, 'cito-item-outcome-changed', {
543
+ item: this.identifier,
544
+ outcomeIdentifier,
545
+ value: value
546
+ });
547
+ }
548
+ dispatchPublic(this, 'cito-item-outcomes-changed', {
549
+ item: this.identifier,
550
+ outcomes: [...values].map(([identifier, value]) => ({ identifier, value }))
551
+ });
552
+ }
474
553
  }
475
554
  validate(reportValidity = true) {
476
- const isValid = this.interactionElements.every(interactionElement => interactionElement.validate());
555
+ const isValid = this.#checkValidity(reportValidity);
477
556
  this.#publishValidity(isValid);
557
+ return isValid;
558
+ }
559
+ #checkValidity(reportValidity) {
560
+ const isValid = this.interactionElements.every(interactionElement => interactionElement.validate());
478
561
  if (reportValidity) {
479
562
  this.reportValidity();
480
563
  }
@@ -506,35 +589,6 @@ export class QtiAssessmentItem extends LitElement {
506
589
  interactionElement.reportValidity();
507
590
  }
508
591
  }
509
- #getCompletionStatus() {
510
- const valid = this.validate(false);
511
- if (valid === true)
512
- return 'completed';
513
- if (valid === false)
514
- return 'incomplete';
515
- return 'not_attempted';
516
- }
517
- #resetOutcomeVariablesBeforeResponseProcessing() {
518
- this._context = {
519
- ...this._context,
520
- variables: this._context.variables.map(variable => {
521
- if (variable.type !== 'outcome') {
522
- return variable;
523
- }
524
- const hasDefaultValue = 'defaultValue' in variable;
525
- const declaredDefault = hasDefaultValue ? variable.defaultValue : null;
526
- let value = declaredDefault ?? null;
527
- if ((value === null || value === undefined) &&
528
- (variable.baseType === 'integer' || variable.baseType === 'float')) {
529
- value = '0';
530
- }
531
- return {
532
- ...variable,
533
- value
534
- };
535
- })
536
- };
537
- }
538
592
  }
539
593
  __decorate([
540
594
  property({ type: String })
@@ -1 +1 @@
1
- {"version":3,"file":"qti-assessment-item.js","sourceRoot":"","sources":["../../../src/components/qti-assessment-item/qti-assessment-item.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAarD;;;;;;;;;GASG;AAEH,MAAM,OAAO,iBAAkB,SAAQ,UAAU;IAAjD;;QAEE,wBAAmB,GAAiC,IAAI,CAAC;QAE7B,eAAU,GAAW,EAAE,CAAC;QACxB,aAAQ,GAAqB,OAAO,CAAC;QACjE;;;;;;WAMG;QACsD,kBAAa,GAA4B,IAAI,CAAC;QAsC/F,aAAQ,GAAgB;YAC9B,SAAS,EAAE,oBAAoB;SAChC,CAAC;QAgGF,oBAAe,GAA0B,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QAClG;;;;WAIG;QACH,4BAAuB,GAAG,IAAI,GAAG,EAAuC,CAAC;QACzE,wBAAmB,GAAG,KAAK,CAAC;QAC5B,2GAA2G;QACjG,wBAAmB,GAA4B,EAAE,CAAC;QAqE5D,4BAAuB,GAAG,CAAC,CAAsB,EAAE,EAAE;YACnD,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAC7B,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;YAC9B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAClH,MAAM,UAAU,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;YACxF,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;YAC1F,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC;YACrC,CAAC,CAAC,eAAe,EAAE,CAAC;QACtB,CAAC,CAAC;QAEF,+BAA0B,GAAG,CAAC,CAAkF,EAAE,EAAE;YAClH,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC7D,CAAC,CAAC;QAEF,sBAAiB,GAAG,CAAC,CAAqE,EAAE,EAAE;YAC5F,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAC7B,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;YACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,sBAAsB,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YACxD,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QACrC,CAAC,CAAC;QAEF,2BAAsB,GAAG,CAAC,CAAuE,EAAE,EAAE;YACnG,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAC7B,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;YAC9C,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YACrD,CAAC,CAAC,eAAe,EAAE,CAAC;QACtB,CAAC,CAAC;QAEF,4BAAuB,GAAG,CAAC,CAA+E,EAAE,EAAE;YAC5G,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAC7B,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC1D,IAAI,CAAC,sBAAsB,CAAC,kBAAkB,EAAE,KAAK,IAAI,SAAS,CAAC,CAAC;YACtE,CAAC;YACD,CAAC,CAAC,eAAe,EAAE,CAAC;QACtB,CAAC,CAAC;QAEF,8BAAyB,GAAG,CAAC,CAA+E,EAAE,EAAE;YAC9G,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAC7B,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;YAC/C,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,EAAE,KAAK,IAAI,SAAS,CAAC,CAAC;YACnE,CAAC,CAAC,eAAe,EAAE,CAAC;QACtB,CAAC,CAAC;QAEF,kCAA6B,GAAG,CAAC,CAAmC,EAAE,EAAE;YACtE,CAAC,CAAC,wBAAwB,EAAE,CAAC;YAE7B,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;YACzD,IAAI,CAAC,sBAAsB,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;YAC1D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC,QAAQ,GAAG;oBACd,GAAG,IAAI,CAAC,QAAQ;oBAChB,KAAK,EAAE;wBACL,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;wBAC9B,CAAC,kBAAkB,CAAC,EAAE,KAAK;qBAC5B;iBACF,CAAC;YACJ,CAAC;YAED,4FAA4F;YAC5F,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAErB,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAA+B,0BAA0B,EAAE;gBACxE,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE;aACvC,CAAC,CACH,CAAC;QACJ,CAAC,CAAC;IAoUJ,CAAC;IA7mBC,UAAU,CAAqB;IAC/B,mBAAmB,CAAsC;IAczD,IAAa,KAAK;QAChB,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,IAAa,KAAK,CAAC,KAAa;QAC9B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAckB,UAAU,CAAC,OAAuB;QACnD,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;QAClH,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;QAClH,IAAI,eAAe,IAAI,eAAe,EAAE,CAAC;YACvC,IAAI,CAAC,QAAQ,GAAG;gBACd,GAAG,IAAI,CAAC,QAAQ;gBAChB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzD,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAOD;;;;OAIG;IACH,IAAW,mBAAmB,CAAC,UAAkB;QAC/C,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC;IACnD,CAAC;IAED,IAAW,mBAAmB;QAC5B,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,IAAW,WAAW;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAW,SAAS;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvC,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,8GAA8G;YAC9G,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,KAAK,OAAO;gBAClD,CAAC,CAAC,EAAE,cAAc,EAAG,CAAqB,CAAC,cAAc,EAAE;gBAC3D,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC,CAAC;IACN,CAAC;IAED,IAAW,SAAS,CAAC,KAAgD;QACnE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QACzC,gGAAgG;QAChG,+EAA+E;QAC/E,MAAM,iBAAiB,GAAG,KAAK,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,CACF,CAAC,CAAC,KAAK,KAAK,IAAI;YAChB,CAAC,CAAC,KAAK,KAAK,SAAS;YACrB,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU;gBACpB,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CACrG,CAAC;QACF,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAC5B,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,KAAoC,CAAC,CACvF,CAAC;QACF,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,IAAI,CAC7C,CAAC,CAAC,EAAE,CACF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;YACtF,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAC1B,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;gBAChD,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,CAAC;gBAC5E,IAAI,aAAa,EAAE,CAAC;oBAClB,OAAO;wBACL,GAAG,QAAQ;wBACX,GAAG,aAAa;wBAChB,kBAAkB,EAAG,QAA4B,CAAC,kBAAkB;wBACpE,UAAU,EAAG,QAA4B,CAAC,UAAU;qBACrD,CAAC;gBACJ,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC,CAAC;SACH,CAAC;QAEF,8FAA8F;QAC9F,IAAI,IAAI,CAAC,mBAAmB,IAAI,gBAAgB,EAAE,CAAC;YACjD,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAChC,CAAC;QAED,+FAA+F;QAC/F,wEAAwE;QACxE,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAW,KAAK;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,IAAW,KAAK,CAAC,KAA2B;QAC1C,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;SACxC,CAAC;IACJ,CAAC;IAED,eAAe,CAAmF;IAClG;;;;OAIG;IACH,uBAAuB,CAAkD;IACzE,mBAAmB,CAAS;IAI5B,iDAAiD;IACjD,IAAI,SAAS,CAAC,WAAkC;QAC9C,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,MAAM,QAAQ,IAAI,WAAW,EAAE,CAAC;gBACnC,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;gBACvE,IAAI,gBAAgB,EAAE,CAAC;oBACrB,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,kBAAkB,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAC9E,CAAC;gBAED,MAAM,WAAW,GAAsC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAClF,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,qBAAqB,CAAC,KAAK,QAAQ,CAAC,kBAAkB,CAC3E,CAAC;gBACF,IAAI,WAAW,EAAE,CAAC;oBAChB,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;gBAC3C,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA,eAAe,CAAC;IAC7B,CAAC;IAEQ,iBAAiB;QACxB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE;YAC5B,uFAAuF;YACvF,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACrB,cAAc,CAAC,IAAI,EAAE,oBAAoB,EAAE,IAAI,CAAC,CAAC;YACjD,2EAA2E;YAC3E,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAoB,+BAA+B,EAAE;gBAClE,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;aACb,CAAC,CACH,CAAC;YACF,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAEQ,oBAAoB;QAC3B,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,KAAK,CAAC,oBAAoB,EAAE,CAAC;IAC/B,CAAC;IAED,qBAAqB;QACnB,IAAI,CAAC,gBAAgB,CAAC,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC7E,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACnF,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC7D,IAAI,CAAC,gBAAgB,CAAC,uBAAuB,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC5E,IAAI,CAAC,gBAAgB,CAAC,wBAAwB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC9E,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAClF,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,EAAE,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACxF,CAAC;IAED,qBAAqB;QACnB,IAAI,CAAC,mBAAmB,CAAC,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAChF,IAAI,CAAC,mBAAmB,CAAC,0BAA0B,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACtF,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChE,IAAI,CAAC,mBAAmB,CAAC,uBAAuB,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC/E,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACjF,IAAI,CAAC,mBAAmB,CAAC,0BAA0B,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACrF,IAAI,CAAC,mBAAmB,CAAC,0BAA0B,EAAE,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC3F,CAAC;IAED,uBAAuB,CAQrB;IAEF,0BAA0B,CAGxB;IAEF,iBAAiB,CAMf;IAEF,sBAAsB,CAKpB;IAEF,uBAAuB,CAOrB;IAEF,yBAAyB,CAKvB;IAEF,6BAA6B,CAyB3B;IAEF,KAAK,CAAC,iBAAiB;QACrB,MAAM,oBAAoB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAa,0BAA0B,CAAC,CAAC,CAAC;QACvG,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;YACvF,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,aAAa,CAAwB,yBAAyB,CAAC,CAAC;QAChG,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAChC,oDAAoD;QACpD,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAChC,CAAC;IAED,sBAAsB;QACpB,IAAI,CAAC,IAAI,CAAC,mBAAmB;YAAE,OAAO;QACtC,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;IAClF,CAAC;IAEM,eAAe,CAAC,gBAAgB,GAAG,IAAI,EAAE,0BAA0B,GAAG,IAAI;QAC/E,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC;QAE1C,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC,qBAAqB,CACxB,aAAa,EACb,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,aAAa,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAC3F,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,8CAA8C,EAAE,CAAC;QACxD,CAAC;QAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAwB,yBAAyB,CAAC,CAAC;QAC/F,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,iBAAiB,IAAI,OAAO,iBAAiB,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC;QAC5F,IAAI,UAAU,EAAE,CAAC;YACf,iBAAiB,CAAC,OAAO,EAAE,CAAC;QAC9B,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC9B,kEAAkE;YAClE,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,EAAE,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAC9E,CAAC;QAED,uEAAuE;QACvE,0EAA0E;QAC1E,4CAA4C;QAC5C,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAA2D,0BAA0B,EAAE;YACpG,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE;SAChE,CAAC,CACH,CAAC;QAEF,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,cAAc;QACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC;IACvC,CAAC;IAES,WAAW,CAAC,UAAkB;QACtC,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAqB,CAAC;IAC1D,CAAC;IAEM,UAAU,CAAC,UAAkB;QAClC,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAoB,CAAC;IACzD,CAAC;IAES,WAAW,CAAC,UAAkB;QACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,IAAI,IAAI,CAAC;IAChF,CAAC;IAED,yFAAyF;IAC/E,4BAA4B,KAAU,CAAC;IAEjD,uGAAuG;IAEvG;;;;;;;;;;OAUG;IACI,sBAAsB,CAAC,UAAkB,EAAE,KAAoC;QACpF,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;SACxG,CAAC;QAEF,IAAI,CAAC,4BAA4B,EAAE,CAAC;QAEpC,cAAc,CAAC,IAAI,EAAE,4BAA4B,EAAE;YACjD,IAAI,EAAE,IAAI,CAAC,UAAU;YACrB,kBAAkB,EAAE,UAAU;YAC9B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK;SACpD,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC9B,kEAAkE;YAClE,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,EAAE,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED;;OAEG;IACI,sBAAsB,CAAC,UAAkB,EAAE,KAAoC;QACpF,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAElG,CAAC;QAEd,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,wCAAwC,UAAU,uBAAuB,CAAC,CAAC;YACxF,OAAO;QACT,CAAC;QAED,MAAM,eAAe,GACnB,gBAAgB,CAAC,WAAW,KAAK,QAAQ;YACvC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;gBACV,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;YACnB,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;gBACrC,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;oBACpB,CAAC,CAAC,KAAK;oBACP,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAElB,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CACzC,CAAC,CAAC,UAAU,KAAK,UAAU;gBACzB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC;oBACE,GAAG,CAAC;oBACJ,KAAK,EAAE,eAAe;iBACvB,CACN;SACF,CAAC;QAEF,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAA+B,0BAA0B,EAAE;YACxE,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE;SACvC,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,qBAAqB,CAAC,UAAkB,EAAE,KAAoC;QACnF,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAEtD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,gDAAgD,UAAU,uBAAuB,CAAC,CAAC;YAChG,OAAO;QACT,CAAC;QAED,MAAM,eAAe,GACnB,gBAAgB,CAAC,WAAW,KAAK,QAAQ;YACvC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;gBACV,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;YACnB,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;gBACrC,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;oBACpB,CAAC,CAAC,KAAK;oBACP,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAElB,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CACzC,CAAC,CAAC,UAAU,KAAK,UAAU;gBACzB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC;oBACE,GAAG,CAAC;oBACJ,eAAe,EAAE,eAAe;iBACjC,CACN;SACF,CAAC;QAEF,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAA+B,0BAA0B,EAAE;YACxE,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE;SACvC,CAAC,CACH,CAAC;IACJ,CAAC;IAEM,kBAAkB,CAAC,UAAkB,EAAE,KAAoC;QAChF,IAAI,CAAC,qBAAqB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAA+B,0BAA0B,EAAE;YACxE,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE;SACvC,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACI,qBAAqB,CAAC,UAAkB,EAAE,KAAoC;QACnF,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAEpD,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,uCAAuC,UAAU,uBAAuB,CAAC,CAAC;YACvF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBACzC,IAAI,CAAC,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;oBAChC,OAAO,CAAC,CAAC;gBACX,CAAC;gBACD,OAAO;oBACL,GAAG,CAAC;oBACJ,KAAK,EAAE,eAAe,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;iBACjG,CAAC;YACJ,CAAC,CAAC;SACH,CAAC;QAEF,cAAc,CAAC,IAAI,EAAE,2BAA2B,EAAE;YAChD,IAAI,EAAE,IAAI,CAAC,UAAU;YACrB,iBAAiB,EAAE,UAAU;YAC7B,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,EAAE,KAAK;SAC7E,CAAC,CAAC;IACL,CAAC;IAEM,QAAQ,CAAC,cAAc,GAAG,IAAI;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,CAAC,kBAAkB,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAE/B,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,0FAA0F;IAC1F,gBAAgB,CAAC,KAAc;QAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAA+B,0BAA0B,EAAE;gBACxE,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE;aACvC,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAEM,cAAc;QACnB,KAAK,MAAM,kBAAkB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC1D,kBAAkB,CAAC,cAAc,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IAED,oBAAoB;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,WAAW,CAAC;QACvC,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,YAAY,CAAC;QACzC,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,8CAA8C;QAC5C,IAAI,CAAC,QAAQ,GAAG;YACd,GAAG,IAAI,CAAC,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;gBAChD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAChC,OAAO,QAAQ,CAAC;gBAClB,CAAC;gBAED,MAAM,eAAe,GAAG,cAAc,IAAI,QAAQ,CAAC;gBACnD,MAAM,eAAe,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;gBAEvE,IAAI,KAAK,GAAG,eAAe,IAAI,IAAI,CAAC;gBACpC,IACE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;oBACvC,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,KAAK,OAAO,CAAC,EAClE,CAAC;oBACD,KAAK,GAAG,GAAG,CAAC;gBACd,CAAC;gBAED,OAAO;oBACL,GAAG,QAAQ;oBACX,KAAK;iBACN,CAAC;YACJ,CAAC,CAAC;SACH,CAAC;IACJ,CAAC;CACF;AA1mB6B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;qDAAyB;AACxB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;mDAAsC;AAQR;IAAxD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;wDAA+C;AAGvG;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAG1B;AAW4B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;mDAAmB;AAMlB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;mDAAmB;AAgBvC;IADP,OAAO,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;mDAGhC","sourcesContent":["import { provide } from '@lit/context';\nimport { LitElement, html } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { itemContext, itemContextVariables } from '@qti-components/base';\n\nimport { dispatchPublic } from '../../public-events';\n\nimport type { PropertyValues } from 'lit';\nimport type { QtiTemplateProcessing } from '../qti-template-processing/qti-template-processing.js';\nimport type { InteractionChangedDetails, OutcomeChangedDetails } from '../../internal/event-types.ts';\nimport type { ResponseInteraction } from '@qti-components/base';\nimport type { RegisteredInteraction } from '@qti-components/base';\nimport type { VariableDeclaration, VariableValue } from '@qti-components/base';\nimport type { OutcomeVariable, ResponseVariable, TemplateVariable } from '@qti-components/base';\nimport type { QtiResponseProcessing } from '../qti-response-processing/qti-response-processing.ts';\nimport type QtiRegisterVariable from '../../internal/events/qti-register-variable.ts';\nimport type { ItemContext } from '@qti-components/base';\n\n/**\n * @summary The qti-assessment-item element contains all the other QTI 3 item structures.\n * @documentation https://www.imsglobal.org/spec/qti/v3p0/impl#h.dltnnj87l0yj\n *\n * @dependency qti-feedback\n * @dependency qti-responseprocessing\n *\n * @event qti-item-context-updated - Emitted when through a user action the itemContext is updated,\n * typically on interaction with the interactions, or calling response processing.\n */\n\nexport class QtiAssessmentItem extends LitElement {\n #itemTitle: string | undefined;\n #templateProcessing: QtiTemplateProcessing | null = null;\n\n @property({ type: String }) identifier: string = '';\n @property({ type: String }) adaptive: 'true' | 'false' = 'false';\n /*\n * `attribute` spelled out: Lit derives `timedependent` from the property\n * name, while QTI writes `time-dependent`. Without it the attribute never\n * reached the property, so an item declaring `time-dependent=\"true\"` still\n * reported false to `test-navigation`, which reads this to build the computed\n * item context.\n */\n @property({ type: String, attribute: 'time-dependent' }) timeDependent: 'true' | 'false' | null = null;\n\n @property({ type: String })\n override get title(): string {\n return this.#itemTitle;\n }\n override set title(value: string) {\n this.#itemTitle = value;\n this.removeAttribute('title');\n this.setAttribute('data-title', value);\n }\n\n /**\n * Locks the item, for example once it is submitted. Published in the item context, which the\n * interactions inside follow, so the item keeps no list of them to set it on.\n */\n @property({ type: Boolean }) disabled: boolean;\n\n /**\n * Shows the item for reading only. Published in the item context, which the interactions inside\n * follow, so the item keeps no list of them to set it on.\n */\n @property({ type: Boolean }) readonly: boolean;\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n const readonlyChanged = changed.has('readonly') && (this.readonly ?? false) !== (this._context.readonly ?? false);\n const disabledChanged = changed.has('disabled') && (this.disabled ?? false) !== (this._context.disabled ?? false);\n if (readonlyChanged || disabledChanged) {\n this._context = {\n ...this._context,\n ...(readonlyChanged ? { readonly: !!this.readonly } : {}),\n ...(disabledChanged ? { disabled: !!this.disabled } : {})\n };\n }\n }\n\n @provide({ context: itemContext })\n private _context: ItemContext = {\n variables: itemContextVariables\n };\n\n /**\n * Sets the identifier for the assessment item reference if this item is in a test.\n *\n * @param identifier - The identifier for the assessment item reference.\n */\n public set assessmentItemRefId(identifier: string) {\n this._context = { ...this._context, identifier };\n }\n\n public get assessmentItemRefId(): string {\n return this._context.identifier;\n }\n\n /**\n * The item's whole context, declarations included, for reading. `variables` is the trimmed view\n * for hosts; the test needs the declarations too, and used to reach for the private field.\n */\n public get itemContext(): Readonly<ItemContext> {\n return this._context;\n }\n\n public get variables(): VariableValue<string | string[] | null>[] {\n return this._context.variables.map(v => ({\n identifier: v.identifier,\n value: v.value,\n type: v.type,\n // add externalscored, a fixed prop to the test, so the testcontext can read and decide how to score this item\n ...(v.type === 'outcome' && v.identifier === 'SCORE'\n ? { externalScored: (v as OutcomeVariable).externalScored }\n : {})\n }));\n }\n\n public set variables(value: VariableValue<string | string[] | null>[]) {\n if (!Array.isArray(value) || value.some(v => !('identifier' in v))) {\n console.warn('variables property should be an array of VariableDeclaration');\n return;\n }\n const previous = this._context.variables;\n // Recognised by their declaration, or by their own type: a template declaration registers after\n // its first render, so a test restoring on connect can get here before it has.\n const restoredTemplates = value.filter(\n v =>\n v.value !== null &&\n v.value !== undefined &&\n (v.type === 'template' ||\n previous.some(declared => declared.identifier === v.identifier && declared.type === 'template'))\n );\n restoredTemplates.forEach(v =>\n this.#restoredTemplateValues.set(v.identifier, v.value as Readonly<string | string[]>)\n );\n const templatesChanged = restoredTemplates.some(\n v =>\n JSON.stringify(previous.find(declared => declared.identifier === v.identifier)?.value) !==\n JSON.stringify(v.value)\n );\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(variable => {\n const matchingValue = value.find(v => v.identifier === variable.identifier);\n if (matchingValue) {\n return {\n ...variable,\n ...matchingValue,\n interpolationTable: (variable as OutcomeVariable).interpolationTable,\n matchTable: (variable as OutcomeVariable).matchTable\n };\n }\n return variable;\n })\n };\n\n // Restored after the first processing run: correct responses still follow the values it drew.\n if (this.#templatesProcessed && templatesChanged) {\n this.#runTemplateProcessing();\n }\n\n // The interactions take the answers over from the context, some of them a moment from now, and\n // a different answer can mean a different verdict. Look once they have.\n void this.updateComplete.then(() => this.#revalidate());\n }\n\n public get state(): ItemContext['state'] {\n return this._context.state;\n }\n\n public set state(value: ItemContext['state']) {\n this._context = {\n ...this._context,\n state: value ? { ...value } : undefined\n };\n }\n\n #initialContext: Readonly<ItemContext> = { ...this._context, variables: this._context.variables };\n /**\n * Template values handed in through `variables` — drawn in an earlier visit or session. Template\n * processing keeps them instead of drawing again, so the candidate sees the question they\n * answered, and still runs, so correct responses are derived from them.\n */\n #restoredTemplateValues = new Map<string, Readonly<string | string[]>>();\n #templatesProcessed = false;\n /** Registered candidate-input interactions. Correction packages may extend their presentation behavior. */\n protected interactionElements: RegisteredInteraction[] = [];\n\n /** @deprecated use variables property instead */\n set responses(myResponses: ResponseInteraction[]) {\n if (myResponses) {\n for (const response of myResponses) {\n const responseVariable = this.getResponse(response.responseIdentifier);\n if (responseVariable) {\n this.updateResponseVariable(response.responseIdentifier, response.response);\n }\n\n const interaction: RegisteredInteraction | undefined = this.interactionElements.find(\n i => i.getAttribute('response-identifier') === response.responseIdentifier\n );\n if (interaction) {\n interaction.response = response.response;\n }\n }\n }\n }\n\n override render() {\n return html`<slot></slot>`;\n }\n\n override connectedCallback(): void {\n this.#attachEventListeners();\n super.connectedCallback();\n this.updateComplete.then(() => {\n // The verdict goes in first, so whoever hears the item connect reads it with the rest.\n this.validate(false);\n dispatchPublic(this, 'cito-item-rendered', this);\n // Internal plumbing: the test and the item container listen for this name.\n this.dispatchEvent(\n new CustomEvent<QtiAssessmentItem>('qti-assessment-item-connected', {\n bubbles: true,\n composed: true,\n detail: this\n })\n );\n this.#processTemplates();\n });\n }\n\n override disconnectedCallback(): void {\n this.#removeEventListeners();\n super.disconnectedCallback();\n }\n\n #attachEventListeners() {\n this.addEventListener('qti-register-variable', this.#handleRegisterVariable);\n this.addEventListener('qti-register-interaction', this.#handleRegisterInteraction);\n this.addEventListener('end-attempt', this.#handleEndAttempt);\n this.addEventListener('qti-set-outcome-value', this.#handleSetOutcomeValue);\n this.addEventListener('qti-set-template-value', this.#handleSetTemplateValue);\n this.addEventListener('qti-set-correct-response', this.#handleSetCorrectResponse);\n this.addEventListener('qti-interaction-response', this.#handleUpdateResponseVariable);\n }\n\n #removeEventListeners() {\n this.removeEventListener('qti-register-variable', this.#handleRegisterVariable);\n this.removeEventListener('qti-register-interaction', this.#handleRegisterInteraction);\n this.removeEventListener('end-attempt', this.#handleEndAttempt);\n this.removeEventListener('qti-set-outcome-value', this.#handleSetOutcomeValue);\n this.removeEventListener('qti-set-template-value', this.#handleSetTemplateValue);\n this.removeEventListener('qti-set-correct-response', this.#handleSetCorrectResponse);\n this.removeEventListener('qti-interaction-response', this.#handleUpdateResponseVariable);\n }\n\n #handleRegisterVariable = (e: QtiRegisterVariable) => {\n e.stopImmediatePropagation();\n const { variable } = e.detail;\n const restored = variable.type === 'template' ? this.#restoredTemplateValues.get(variable.identifier) : undefined;\n const registered = restored !== undefined ? { ...variable, value: restored } : variable;\n this._context = { ...this._context, variables: [...this._context.variables, registered] };\n this.#initialContext = this._context;\n e.stopPropagation();\n };\n\n #handleRegisterInteraction = (e: CustomEvent<{ interaction: string; interactionElement: RegisteredInteraction }>) => {\n e.stopImmediatePropagation();\n this.interactionElements.push(e.detail.interactionElement);\n };\n\n #handleEndAttempt = (e: CustomEvent<{ responseIdentifier: string; countAttempt: boolean }>) => {\n e.stopImmediatePropagation();\n const { responseIdentifier, countAttempt } = e.detail;\n this.validate();\n this.updateResponseVariable(responseIdentifier, 'true');\n this.processResponse(countAttempt);\n };\n\n #handleSetOutcomeValue = (e: CustomEvent<{ outcomeIdentifier: string; value: string | string[] }>) => {\n e.stopImmediatePropagation();\n const { outcomeIdentifier, value } = e.detail;\n this.updateOutcomeVariable(outcomeIdentifier, value);\n e.stopPropagation();\n };\n\n #handleSetTemplateValue = (e: CustomEvent<{ templateIdentifier: string; value: string | string[] | null }>) => {\n e.stopImmediatePropagation();\n const { templateIdentifier, value } = e.detail;\n if (!this.#restoredTemplateValues.has(templateIdentifier)) {\n this.updateTemplateVariable(templateIdentifier, value ?? undefined);\n }\n e.stopPropagation();\n };\n\n #handleSetCorrectResponse = (e: CustomEvent<{ responseIdentifier: string; value: string | string[] | null }>) => {\n e.stopImmediatePropagation();\n const { responseIdentifier, value } = e.detail;\n this.updateCorrectResponse(responseIdentifier, value ?? undefined);\n e.stopPropagation();\n };\n\n #handleUpdateResponseVariable = (e: CustomEvent<ResponseInteraction>) => {\n e.stopImmediatePropagation();\n\n const { responseIdentifier, response, state } = e.detail;\n this.updateResponseVariable(responseIdentifier, response);\n if (state !== undefined) {\n this._context = {\n ...this._context,\n state: {\n ...(this._context.state || {}),\n [responseIdentifier]: state\n }\n };\n }\n\n // The interaction has updated its own validity by now; publish the verdict with the answer.\n this.validate(false);\n\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n };\n\n async #processTemplates(): Promise<void> {\n const templateDeclarations = Array.from(this.querySelectorAll<LitElement>('qti-template-declaration'));\n if (templateDeclarations.length > 0) {\n await Promise.all(templateDeclarations.map(declaration => declaration.updateComplete));\n await Promise.resolve();\n }\n\n this.#templateProcessing = this.querySelector<QtiTemplateProcessing>('qti-template-processing');\n this.#templatesProcessed = true;\n // Run template processing before first presentation\n this.#runTemplateProcessing();\n }\n\n #runTemplateProcessing(): void {\n if (!this.#templateProcessing) return;\n this.#templateProcessing.process();\n this.#initialContext = { ...this._context, variables: this._context.variables };\n }\n\n public processResponse(countNumAttempts = true, reportValidityAfterScoring = true): boolean {\n this.validate(reportValidityAfterScoring);\n\n if (countNumAttempts) {\n this.updateOutcomeVariable(\n 'numAttempts',\n (+this._context.variables.find(v => v.identifier === 'numAttempts')?.value + 1).toString()\n );\n }\n\n if (this.adaptive === 'false') {\n this.#resetOutcomeVariablesBeforeResponseProcessing();\n }\n\n const responseProcessor = this.querySelector<QtiResponseProcessing>('qti-response-processing');\n const canProcess = !!(responseProcessor && typeof responseProcessor.process === 'function');\n if (canProcess) {\n responseProcessor.process();\n }\n if (this.adaptive === 'false') {\n // if adaptive, completionStatus is set by the processing template\n this.updateOutcomeVariable('completionStatus', this.#getCompletionStatus());\n }\n\n // `responseProcessed` marks this as the end of an attempt (vs. a plain\n // selection, which fires the same event without the flag). Lets listeners\n // react only when an attempt actually ends.\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext; responseProcessed: boolean }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context, responseProcessed: true }\n })\n );\n\n return canProcess;\n }\n\n public resetResponses() {\n this._context = this.#initialContext;\n }\n\n protected getResponse(identifier: string): Readonly<ResponseVariable> {\n return this.getVariable(identifier) as ResponseVariable;\n }\n\n public getOutcome(identifier: string): Readonly<OutcomeVariable> {\n return this.getVariable(identifier) as OutcomeVariable;\n }\n\n protected getVariable(identifier: string): Readonly<VariableDeclaration<string | string[] | null>> {\n return this._context.variables.find(v => v.identifier === identifier) || null;\n }\n\n /** Extension point for presentation packages reacting to a candidate response change. */\n protected afterResponseVariableUpdated(): void {}\n\n // saving privates here: ------------------------------------------------------------------------------\n\n /**\n * Updates the response variable with the specified identifier to the given value.\n *\n * @protected\n *\n * This method is intended for internal use within the class and subclasses.\n * It should not be called externally by third parties.\n *\n * @param identifier - The identifier of the response variable to update.\n * @param value - The new value for the response variable.\n */\n public updateResponseVariable(identifier: string, value: string | string[] | undefined) {\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v => (v.identifier !== identifier ? v : { ...v, value: value }))\n };\n\n this.afterResponseVariableUpdated();\n\n dispatchPublic(this, 'cito-item-response-changed', {\n item: this.identifier,\n responseIdentifier: identifier,\n response: Array.isArray(value) ? [...value] : value\n });\n\n if (this.adaptive === 'false') {\n // if adaptive, completionStatus is set by the processing template\n this.updateOutcomeVariable('completionStatus', this.#getCompletionStatus());\n }\n }\n\n /**\n * Updates the template variable with the specified identifier to the given value.\n */\n public updateTemplateVariable(identifier: string, value: string | string[] | undefined) {\n const templateVariable = this._context.variables.find(v => v.identifier === identifier && v.type === 'template') as\n | TemplateVariable\n | undefined;\n\n if (!templateVariable) {\n console.warn(`Can not set qti-template-identifier: ${identifier}, it is not available`);\n return;\n }\n\n const normalizedValue =\n templateVariable.cardinality === 'single'\n ? Array.isArray(value)\n ? value[0]\n : (value ?? null)\n : value === undefined || value === null\n ? null\n : Array.isArray(value)\n ? value\n : [value];\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v =>\n v.identifier !== identifier\n ? v\n : {\n ...v,\n value: normalizedValue\n }\n )\n };\n\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n\n /**\n * Updates the correct response for the specified response variable.\n */\n public updateCorrectResponse(identifier: string, value: string | string[] | undefined) {\n const responseVariable = this.getResponse(identifier);\n\n if (!responseVariable) {\n console.warn(`Can not set correct response for identifier: ${identifier}, it is not available`);\n return;\n }\n\n const normalizedValue =\n responseVariable.cardinality === 'single'\n ? Array.isArray(value)\n ? value[0]\n : (value ?? null)\n : value === undefined || value === null\n ? null\n : Array.isArray(value)\n ? value\n : [value];\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v =>\n v.identifier !== identifier\n ? v\n : {\n ...v,\n correctResponse: normalizedValue\n }\n )\n };\n\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n\n public setOutcomeVariable(identifier: string, value: string | string[] | undefined) {\n this.updateOutcomeVariable(identifier, value);\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n\n /**\n * Updates the outcome variable with the specified identifier to the given value.\n *\n * @protected\n *\n * This method is intended for internal use within the class and subclasses.\n * It should not be called externally by third parties.\n *\n * @param identifier - The identifier of the response variable to update.\n * @param value - The new value for the response variable.\n */\n public updateOutcomeVariable(identifier: string, value: string | string[] | undefined) {\n const outcomeVariable = this.getOutcome(identifier);\n\n if (!outcomeVariable) {\n console.warn(`Can not set qti-outcome-identifier: ${identifier}, it is not available`);\n return;\n }\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v => {\n if (v.identifier !== identifier) {\n return v;\n }\n return {\n ...v,\n value: outcomeVariable.cardinality === 'single' ? value : Array.isArray(value) ? value : [value]\n };\n })\n };\n\n dispatchPublic(this, 'cito-item-outcome-changed', {\n item: this.identifier,\n outcomeIdentifier: identifier,\n value: this._context.variables.find(v => v.identifier === identifier)?.value\n });\n }\n\n public validate(reportValidity = true): boolean {\n const isValid = this.interactionElements.every(interactionElement => interactionElement.validate());\n this.#publishValidity(isValid);\n\n if (reportValidity) {\n this.reportValidity();\n }\n\n return isValid;\n }\n\n /** Puts the verdict in the item context, where the test and anything else can read it. */\n #publishValidity(valid: boolean): void {\n if (this._context.valid !== valid) {\n this._context = { ...this._context, valid };\n }\n }\n\n /**\n * Checks again after something the interactions' validity depends on changed without them saying\n * so (an answer handed in through `variables`), and tells the test if the verdict moved.\n */\n #revalidate(): void {\n const before = this._context.valid;\n this.validate(false);\n if (this._context.valid !== before) {\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n }\n\n public reportValidity() {\n for (const interactionElement of this.interactionElements) {\n interactionElement.reportValidity();\n }\n }\n\n #getCompletionStatus(): 'completed' | 'incomplete' | 'not_attempted' | 'unknown' {\n const valid = this.validate(false);\n if (valid === true) return 'completed';\n if (valid === false) return 'incomplete';\n return 'not_attempted';\n }\n\n #resetOutcomeVariablesBeforeResponseProcessing(): void {\n this._context = {\n ...this._context,\n variables: this._context.variables.map(variable => {\n if (variable.type !== 'outcome') {\n return variable;\n }\n\n const hasDefaultValue = 'defaultValue' in variable;\n const declaredDefault = hasDefaultValue ? variable.defaultValue : null;\n\n let value = declaredDefault ?? null;\n if (\n (value === null || value === undefined) &&\n (variable.baseType === 'integer' || variable.baseType === 'float')\n ) {\n value = '0';\n }\n\n return {\n ...variable,\n value\n };\n })\n };\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-assessment-item': QtiAssessmentItem;\n }\n}\n\ndeclare module '../../public-events' {\n interface PublicItemEventMap {\n /** The item rendered: its interactions are in the DOM and its validity is known. */\n 'cito-item-rendered': CustomEvent<QtiAssessmentItem>;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-assessment-item.js","sourceRoot":"","sources":["../../../src/components/qti-assessment-item/qti-assessment-item.ts"],"names":[],"mappings":"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","sourcesContent":["import { provide } from '@lit/context';\nimport { LitElement, html } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { itemContext, itemContextVariables } from '@qti-components/base';\n\nimport { dispatchPublic } from '../../public-events';\n\nimport type { PropertyValues } from 'lit';\nimport type { QtiTemplateProcessing } from '../qti-template-processing/qti-template-processing.js';\nimport type { InteractionChangedDetails, OutcomeEntry, SetOutcomesDetails } from '../../internal/event-types.ts';\nimport type { ResponseInteraction } from '@qti-components/base';\nimport type { RegisteredInteraction } from '@qti-components/base';\nimport type { VariableDeclaration, VariableValue } from '@qti-components/base';\nimport type { OutcomeVariable, ResponseVariable, TemplateVariable } from '@qti-components/base';\nimport type QtiRegisterVariable from '../../internal/events/qti-register-variable.ts';\nimport type { ItemContext } from '@qti-components/base';\n\n/**\n * @summary The qti-assessment-item element contains all the other QTI 3 item structures.\n * @documentation https://www.imsglobal.org/spec/qti/v3p0/impl#h.dltnnj87l0yj\n *\n * @dependency qti-feedback\n * @dependency qti-responseprocessing\n *\n * @event qti-item-context-updated - Emitted when through a user action the itemContext is updated,\n * typically on interaction with the interactions, or calling response processing.\n */\n\n/**\n * What the item needs of its response processing: start a run for an attempt. The result comes\n * back as an event.\n */\ninterface ResponseProcessor {\n processResponse(options: { numAttempts: number }): void;\n}\n\nexport class QtiAssessmentItem extends LitElement {\n #itemTitle: string | undefined;\n #templateProcessing: QtiTemplateProcessing | null = null;\n\n @property({ type: String }) identifier: string = '';\n @property({ type: String }) adaptive: 'true' | 'false' = 'false';\n /*\n * `attribute` spelled out: Lit derives `timedependent` from the property\n * name, while QTI writes `time-dependent`. Without it the attribute never\n * reached the property, so an item declaring `time-dependent=\"true\"` still\n * reported false to `test-navigation`, which reads this to build the computed\n * item context.\n */\n @property({ type: String, attribute: 'time-dependent' }) timeDependent: 'true' | 'false' | null = null;\n\n @property({ type: String })\n override get title(): string {\n return this.#itemTitle;\n }\n override set title(value: string) {\n this.#itemTitle = value;\n this.removeAttribute('title');\n this.setAttribute('data-title', value);\n }\n\n /**\n * Locks the item, for example once it is submitted. Published in the item context, which the\n * interactions inside follow, so the item keeps no list of them to set it on.\n */\n @property({ type: Boolean }) disabled: boolean;\n\n /**\n * Shows the item for reading only. Published in the item context, which the interactions inside\n * follow, so the item keeps no list of them to set it on.\n */\n @property({ type: Boolean }) readonly: boolean;\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n const readonlyChanged = changed.has('readonly') && (this.readonly ?? false) !== (this._context.readonly ?? false);\n const disabledChanged = changed.has('disabled') && (this.disabled ?? false) !== (this._context.disabled ?? false);\n if (readonlyChanged || disabledChanged) {\n this._context = {\n ...this._context,\n ...(readonlyChanged ? { readonly: !!this.readonly } : {}),\n ...(disabledChanged ? { disabled: !!this.disabled } : {})\n };\n }\n }\n\n @provide({ context: itemContext })\n private _context: ItemContext = {\n variables: itemContextVariables\n };\n\n /**\n * Sets the identifier for the assessment item reference if this item is in a test.\n *\n * @param identifier - The identifier for the assessment item reference.\n */\n public set assessmentItemRefId(identifier: string) {\n this._context = { ...this._context, identifier };\n }\n\n public get assessmentItemRefId(): string {\n return this._context.identifier;\n }\n\n /**\n * The item's whole context, declarations included, for reading. `variables` is the trimmed view\n * for hosts; the test needs the declarations too, and used to reach for the private field.\n */\n public get itemContext(): Readonly<ItemContext> {\n return this._context;\n }\n\n public get variables(): VariableValue<string | string[] | null>[] {\n return this._context.variables.map(v => ({\n identifier: v.identifier,\n value: v.value,\n type: v.type,\n // add externalscored, a fixed prop to the test, so the testcontext can read and decide how to score this item\n ...(v.type === 'outcome' && v.identifier === 'SCORE'\n ? { externalScored: (v as OutcomeVariable).externalScored }\n : {})\n }));\n }\n\n public set variables(value: VariableValue<string | string[] | null>[]) {\n if (!Array.isArray(value) || value.some(v => !('identifier' in v))) {\n console.warn('variables property should be an array of VariableDeclaration');\n return;\n }\n const previous = this._context.variables;\n // Recognised by their declaration, or by their own type: a template declaration registers after\n // its first render, so a test restoring on connect can get here before it has.\n const restoredTemplates = value.filter(\n v =>\n v.value !== null &&\n v.value !== undefined &&\n (v.type === 'template' ||\n previous.some(declared => declared.identifier === v.identifier && declared.type === 'template'))\n );\n restoredTemplates.forEach(v =>\n this.#restoredTemplateValues.set(v.identifier, v.value as Readonly<string | string[]>)\n );\n const templatesChanged = restoredTemplates.some(\n v =>\n JSON.stringify(previous.find(declared => declared.identifier === v.identifier)?.value) !==\n JSON.stringify(v.value)\n );\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(variable => {\n const matchingValue = value.find(v => v.identifier === variable.identifier);\n if (matchingValue) {\n return {\n ...variable,\n ...matchingValue,\n interpolationTable: (variable as OutcomeVariable).interpolationTable,\n matchTable: (variable as OutcomeVariable).matchTable\n };\n }\n return variable;\n })\n };\n\n // Restored after the first processing run: correct responses still follow the values it drew.\n if (this.#templatesProcessed && templatesChanged) {\n this.#runTemplateProcessing();\n }\n\n // The interactions take the answers over from the context, some of them a moment from now, and\n // a different answer can mean a different verdict. Look once they have.\n void this.updateComplete.then(() => this.#revalidate());\n if (this.isConnected) this.#beginFirstAttempt();\n }\n\n public get state(): ItemContext['state'] {\n return this._context.state;\n }\n\n public set state(value: ItemContext['state']) {\n this._context = {\n ...this._context,\n state: value ? { ...value } : undefined\n };\n }\n\n #initialContext: Readonly<ItemContext> = { ...this._context, variables: this._context.variables };\n /**\n * Template values handed in through `variables` — drawn in an earlier visit or session. Template\n * processing keeps them instead of drawing again, so the candidate sees the question they\n * answered, and still runs, so correct responses are derived from them.\n */\n #restoredTemplateValues = new Map<string, Readonly<string | string[]>>();\n #templatesProcessed = false;\n /** Registered candidate-input interactions. Correction packages may extend their presentation behavior. */\n protected interactionElements: RegisteredInteraction[] = [];\n\n /** @deprecated use variables property instead */\n set responses(myResponses: ResponseInteraction[]) {\n if (myResponses) {\n for (const response of myResponses) {\n const responseVariable = this.getResponse(response.responseIdentifier);\n if (responseVariable) {\n this.updateResponseVariable(response.responseIdentifier, response.response);\n }\n\n const interaction: RegisteredInteraction | undefined = this.interactionElements.find(\n i => i.getAttribute('response-identifier') === response.responseIdentifier\n );\n if (interaction) {\n interaction.response = response.response;\n }\n }\n }\n }\n\n override render() {\n return html`<slot></slot>`;\n }\n\n override connectedCallback(): void {\n this.#attachEventListeners();\n super.connectedCallback();\n this.updateComplete.then(() => {\n // The verdict goes in first, so whoever hears the item connect reads it with the rest.\n this.validate(false);\n this.#beginFirstAttempt();\n dispatchPublic(this, 'cito-item-rendered', this);\n // Internal plumbing: the test and the item container listen for this name.\n this.dispatchEvent(\n new CustomEvent<QtiAssessmentItem>('qti-assessment-item-connected', {\n bubbles: true,\n composed: true,\n detail: this\n })\n );\n this.#processTemplates();\n });\n }\n\n override disconnectedCallback(): void {\n this.#removeEventListeners();\n super.disconnectedCallback();\n }\n\n #attachEventListeners() {\n this.addEventListener('qti-register-variable', this.#handleRegisterVariable);\n this.addEventListener('qti-register-interaction', this.#handleRegisterInteraction);\n this.addEventListener('end-attempt', this.#handleEndAttempt);\n this.addEventListener('cito-item-set-outcomes', this.#handleSetOutcomes);\n this.addEventListener('qti-set-template-value', this.#handleSetTemplateValue);\n this.addEventListener('qti-set-correct-response', this.#handleSetCorrectResponse);\n this.addEventListener('qti-interaction-response', this.#handleUpdateResponseVariable);\n }\n\n #removeEventListeners() {\n this.removeEventListener('qti-register-variable', this.#handleRegisterVariable);\n this.removeEventListener('qti-register-interaction', this.#handleRegisterInteraction);\n this.removeEventListener('end-attempt', this.#handleEndAttempt);\n this.removeEventListener('cito-item-set-outcomes', this.#handleSetOutcomes);\n this.removeEventListener('qti-set-template-value', this.#handleSetTemplateValue);\n this.removeEventListener('qti-set-correct-response', this.#handleSetCorrectResponse);\n this.removeEventListener('qti-interaction-response', this.#handleUpdateResponseVariable);\n }\n\n #handleRegisterVariable = (e: QtiRegisterVariable) => {\n e.stopImmediatePropagation();\n const { variable } = e.detail;\n const restored = variable.type === 'template' ? this.#restoredTemplateValues.get(variable.identifier) : undefined;\n const registered = restored !== undefined ? { ...variable, value: restored } : variable;\n this._context = { ...this._context, variables: [...this._context.variables, registered] };\n this.#initialContext = this._context;\n e.stopPropagation();\n };\n\n #handleRegisterInteraction = (e: CustomEvent<{ interaction: string; interactionElement: RegisteredInteraction }>) => {\n e.stopImmediatePropagation();\n this.interactionElements.push(e.detail.interactionElement);\n };\n\n #handleEndAttempt = (e: CustomEvent<{ responseIdentifier: string; countAttempt: boolean }>) => {\n e.stopImmediatePropagation();\n const { responseIdentifier, countAttempt } = e.detail;\n this.validate();\n this.updateResponseVariable(responseIdentifier, 'true');\n this.#processResponse(countAttempt, true, responseIdentifier);\n };\n\n /**\n * Applies the outcomes of a processing run, ours or a backend's, in one update, with the\n * interactions' validity.\n *\n * completionStatus is response processing's to set (QTI info model §2.2.2). When an attempt of a\n * non-adaptive item ends and processing left it `unknown`, the item marks it `completed`: the\n * attempt is done. TAO does the same, and the 1EdTech results-reporting reference results expect\n * it. An adaptive item is completed only by its own processing.\n */\n #handleSetOutcomes = (e: CustomEvent<SetOutcomesDetails>) => {\n e.stopImmediatePropagation();\n const { outcomes = [], responseProcessed = false } = e.detail ?? {};\n const completion: OutcomeEntry[] = [];\n if (responseProcessed && this.adaptive === 'false') {\n const sent = outcomes.find(o => o.identifier === 'completionStatus')?.value;\n if ((sent ?? this.getVariable('completionStatus')?.value) === 'unknown') {\n completion.push({ identifier: 'completionStatus', value: 'completed' });\n }\n }\n this.#applyOutcomes([...outcomes, ...completion], this.#checkValidity(false));\n\n if (responseProcessed) {\n // `responseProcessed` marks this as the end of an attempt (vs. a plain\n // selection, which fires the same event without the flag). Lets listeners\n // react only when an attempt actually ends.\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext; responseProcessed: boolean }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context, responseProcessed: true }\n })\n );\n }\n };\n\n #handleSetTemplateValue = (e: CustomEvent<{ templateIdentifier: string; value: string | string[] | null }>) => {\n e.stopImmediatePropagation();\n const { templateIdentifier, value } = e.detail;\n if (!this.#restoredTemplateValues.has(templateIdentifier)) {\n this.updateTemplateVariable(templateIdentifier, value ?? undefined);\n }\n e.stopPropagation();\n };\n\n #handleSetCorrectResponse = (e: CustomEvent<{ responseIdentifier: string; value: string | string[] | null }>) => {\n e.stopImmediatePropagation();\n const { responseIdentifier, value } = e.detail;\n this.updateCorrectResponse(responseIdentifier, value ?? undefined);\n e.stopPropagation();\n };\n\n #handleUpdateResponseVariable = (e: CustomEvent<ResponseInteraction>) => {\n e.stopImmediatePropagation();\n\n const { responseIdentifier, response, state } = e.detail;\n this.updateResponseVariable(responseIdentifier, response);\n if (state !== undefined) {\n this._context = {\n ...this._context,\n state: {\n ...(this._context.state || {}),\n [responseIdentifier]: state\n }\n };\n }\n\n // The interaction has updated its own validity by now; publish the verdict with the answer.\n this.validate(false);\n\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n };\n\n async #processTemplates(): Promise<void> {\n const templateDeclarations = Array.from(this.querySelectorAll<LitElement>('qti-template-declaration'));\n if (templateDeclarations.length > 0) {\n await Promise.all(templateDeclarations.map(declaration => declaration.updateComplete));\n await Promise.resolve();\n }\n\n this.#templateProcessing = this.querySelector<QtiTemplateProcessing>('qti-template-processing');\n this.#templatesProcessed = true;\n // Run template processing before first presentation\n this.#runTemplateProcessing();\n }\n\n #runTemplateProcessing(): void {\n if (!this.#templateProcessing) return;\n this.#templateProcessing.process();\n this.#initialContext = { ...this._context, variables: this._context.variables };\n }\n\n /**\n * Scores the attempt. The item does not score itself: its response processing does, like a\n * backend, and sends the outcomes back in `cito-item-set-outcomes`, which the item applies in one\n * update. The item keeps the attempt count (QTI info model §2.2.2: the delivery engine maintains\n * numAttempts) and hands the new count to the run, whose rules may read it. Without response\n * processing the item only counts the attempt. A run that throws still counts the attempt and applies\n * no outcomes.\n *\n * @returns whether the item has response processing.\n */\n public processResponse(countNumAttempts = true, reportValidityAfterScoring = true): boolean {\n return this.#processResponse(countNumAttempts, reportValidityAfterScoring);\n }\n\n #processResponse(countNumAttempts: boolean, reportValidity: boolean, endedBy?: string): boolean {\n this.#checkValidity(reportValidity);\n this.#resetOtherEndAttempts(endedBy);\n\n const numAttempts = +this.getVariable('numAttempts')?.value + (countNumAttempts ? 1 : 0);\n const processor = this.querySelector<HTMLElement & Partial<ResponseProcessor>>('qti-response-processing');\n if (typeof processor?.processResponse === 'function') {\n try {\n processor.processResponse({ numAttempts });\n } catch (error) {\n // The attempt started, so it counts (QTI info model §2.2.2), as in TAO and Amp-up; the\n // outcomes of a run that failed are not applied.\n this.#applyOutcomes([{ identifier: 'numAttempts', value: String(numAttempts) }]);\n throw error;\n }\n return true;\n }\n dispatchPublic(this, 'cito-item-set-outcomes', {\n outcomes: [{ identifier: 'numAttempts', value: String(numAttempts) }],\n responseProcessed: true\n });\n return false;\n }\n\n /**\n * QTI info model §2.4.5: when an attempt ends any other way than through an end-attempt\n * interaction, that interaction's response is `false`.\n */\n #resetOtherEndAttempts(endedBy: string | undefined): void {\n const others = Array.from(this.querySelectorAll('qti-end-attempt-interaction'), el =>\n el.getAttribute('response-identifier')\n ).filter(id => id && id !== endedBy);\n if (others.length === 0) return;\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v => (others.includes(v.identifier) ? { ...v, value: 'false' } : v))\n };\n }\n\n public resetResponses() {\n this._context = this.#initialContext;\n if (this.isConnected) this.#beginFirstAttempt();\n }\n\n /**\n * QTI info model §2.2.2: completionStatus is `not_attempted` until the first attempt starts, then\n * `unknown`. The attempt starts when the candidate gets the item, so an item on screen is never\n * `not_attempted` — also not after a reset or a restore that says so.\n */\n #beginFirstAttempt(): void {\n if (this.getVariable('completionStatus')?.value !== 'not_attempted') return;\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v =>\n v.identifier === 'completionStatus' ? { ...v, value: 'unknown' } : v\n )\n };\n }\n\n protected getResponse(identifier: string): Readonly<ResponseVariable> {\n return this.getVariable(identifier) as ResponseVariable;\n }\n\n public getOutcome(identifier: string): Readonly<OutcomeVariable> {\n return this.getVariable(identifier) as OutcomeVariable;\n }\n\n protected getVariable(identifier: string): Readonly<VariableDeclaration<string | string[] | null>> {\n return this._context.variables.find(v => v.identifier === identifier) || null;\n }\n\n /** Extension point for presentation packages reacting to a candidate response change. */\n protected afterResponseVariableUpdated(): void {}\n\n // saving privates here: ------------------------------------------------------------------------------\n\n /**\n * Updates the response variable with the specified identifier to the given value.\n *\n * @protected\n *\n * This method is intended for internal use within the class and subclasses.\n * It should not be called externally by third parties.\n *\n * @param identifier - The identifier of the response variable to update.\n * @param value - The new value for the response variable.\n */\n public updateResponseVariable(identifier: string, value: string | string[] | undefined) {\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v => (v.identifier !== identifier ? v : { ...v, value: value }))\n };\n\n this.afterResponseVariableUpdated();\n\n dispatchPublic(this, 'cito-item-response-changed', {\n item: this.identifier,\n responseIdentifier: identifier,\n response: Array.isArray(value) ? [...value] : value\n });\n\n // A different answer can mean a different verdict.\n this.validate(false);\n }\n\n /**\n * Updates the template variable with the specified identifier to the given value.\n */\n public updateTemplateVariable(identifier: string, value: string | string[] | undefined) {\n const templateVariable = this._context.variables.find(v => v.identifier === identifier && v.type === 'template') as\n | TemplateVariable\n | undefined;\n\n if (!templateVariable) {\n console.warn(`Can not set qti-template-identifier: ${identifier}, it is not available`);\n return;\n }\n\n const normalizedValue =\n templateVariable.cardinality === 'single'\n ? Array.isArray(value)\n ? value[0]\n : (value ?? null)\n : value === undefined || value === null\n ? null\n : Array.isArray(value)\n ? value\n : [value];\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v =>\n v.identifier !== identifier\n ? v\n : {\n ...v,\n value: normalizedValue\n }\n )\n };\n\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n\n /**\n * Updates the correct response for the specified response variable.\n */\n public updateCorrectResponse(identifier: string, value: string | string[] | undefined) {\n const responseVariable = this.getResponse(identifier);\n\n if (!responseVariable) {\n console.warn(`Can not set correct response for identifier: ${identifier}, it is not available`);\n return;\n }\n\n const normalizedValue =\n responseVariable.cardinality === 'single'\n ? Array.isArray(value)\n ? value[0]\n : (value ?? null)\n : value === undefined || value === null\n ? null\n : Array.isArray(value)\n ? value\n : [value];\n\n this._context = {\n ...this._context,\n variables: this._context.variables.map(v =>\n v.identifier !== identifier\n ? v\n : {\n ...v,\n correctResponse: normalizedValue\n }\n )\n };\n\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n\n public setOutcomeVariable(identifier: string, value: string | string[] | undefined) {\n this.updateOutcomeVariable(identifier, value);\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n\n /**\n * Updates the outcome variable with the specified identifier to the given value.\n *\n * @protected\n *\n * This method is intended for internal use within the class and subclasses.\n * It should not be called externally by third parties.\n *\n * @param identifier - The identifier of the response variable to update.\n * @param value - The new value for the response variable.\n */\n public updateOutcomeVariable(identifier: string, value: string | string[] | undefined) {\n this.#applyOutcomes([{ identifier, value }]);\n }\n\n /**\n * Publishes the outcomes (and the validity) in one update, then a `cito-item-outcome-changed` per\n * outcome with its final value and one `cito-item-outcomes-changed` with all of them.\n * An identifier the item does not declare is skipped with a warning; a later entry for the same\n * identifier wins.\n */\n #applyOutcomes(outcomes: readonly OutcomeEntry[], valid?: boolean) {\n const values = new Map<string, OutcomeEntry['value']>();\n for (const { identifier, value } of outcomes) {\n const variable = this.getVariable(identifier);\n if (!variable) {\n console.warn(`Can not set qti-outcome-identifier: ${identifier}, it is not available`);\n continue;\n }\n values.set(identifier, variable.cardinality === 'single' ? value : Array.isArray(value) ? value : [value]);\n }\n const validChanged = valid !== undefined && valid !== this._context.valid;\n if (values.size === 0 && !validChanged) return;\n\n this._context = {\n ...this._context,\n ...(validChanged ? { valid } : {}),\n variables: this._context.variables.map(v =>\n values.has(v.identifier) ? { ...v, value: values.get(v.identifier) as string | string[] | null } : v\n )\n };\n\n if (values.size > 0) {\n for (const [outcomeIdentifier, value] of values) {\n dispatchPublic(this, 'cito-item-outcome-changed', {\n item: this.identifier,\n outcomeIdentifier,\n value: value as string | string[]\n });\n }\n dispatchPublic(this, 'cito-item-outcomes-changed', {\n item: this.identifier,\n outcomes: [...values].map(([identifier, value]) => ({ identifier, value }))\n });\n }\n }\n\n public validate(reportValidity = true): boolean {\n const isValid = this.#checkValidity(reportValidity);\n this.#publishValidity(isValid);\n return isValid;\n }\n\n #checkValidity(reportValidity: boolean): boolean {\n const isValid = this.interactionElements.every(interactionElement => interactionElement.validate());\n if (reportValidity) {\n this.reportValidity();\n }\n return isValid;\n }\n\n /** Puts the verdict in the item context, where the test and anything else can read it. */\n #publishValidity(valid: boolean): void {\n if (this._context.valid !== valid) {\n this._context = { ...this._context, valid };\n }\n }\n\n /**\n * Checks again after something the interactions' validity depends on changed without them saying\n * so (an answer handed in through `variables`), and tells the test if the verdict moved.\n */\n #revalidate(): void {\n const before = this._context.valid;\n this.validate(false);\n if (this._context.valid !== before) {\n this.dispatchEvent(\n new CustomEvent<{ itemContext: ItemContext }>('qti-item-context-updated', {\n bubbles: true,\n composed: true,\n detail: { itemContext: this._context }\n })\n );\n }\n }\n\n public reportValidity() {\n for (const interactionElement of this.interactionElements) {\n interactionElement.reportValidity();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-assessment-item': QtiAssessmentItem;\n }\n}\n\ndeclare module '../../public-events' {\n interface PublicItemEventMap {\n /** The item rendered: its interactions are in the DOM and its validity is known. */\n 'cito-item-rendered': CustomEvent<QtiAssessmentItem>;\n }\n}\n"]}
@@ -75,16 +75,6 @@ export class QtiCustomOperator extends LitElement {
75
75
  value
76
76
  }
77
77
  }));
78
- },
79
- updateResponseVariable: (responseIdentifier, response) => {
80
- this.dispatchEvent(new CustomEvent('qti-interaction-response', {
81
- bubbles: true,
82
- composed: true,
83
- detail: {
84
- responseIdentifier,
85
- response
86
- }
87
- }));
88
78
  }
89
79
  };
90
80
  return this.#operatorFunction(this._context, fn, item);
@@ -1 +1 @@
1
- {"version":3,"file":"qti-custom-operator.js","sourceRoot":"","sources":["../../../src/components/qti-custom-operator/qti-custom-operator.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { LitElement, html } from 'lit';\nimport { state } from 'lit/decorators.js';\n\nimport { getCustomOperator, itemContext } from '@qti-components/base';\n\nimport type { CustomOperatorValue, ResponseVariable } from '@qti-components/base';\nimport type { Calculate } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\n/**\n * https://www.imsglobal.org/spec/qti/v3p0/impl#h.fi29q8dubjgw\n *\n * A custom operator is outside the QTI vocabulary, so what it does is supplied\n * by the delivery engine rather than by the item. Two ways to supply it:\n *\n * `definition` names an operator the host registered with\n * `registerCustomOperator`, or one of the built-ins (`Trim`, `ToAscii`,\n * `ParseCommaDecimal` under the `depcp:`, `questify:` and `qade:` prefixes).\n * The operator is handed the calculated values of the child expressions:\n *\n * ```xml\n * <qti-custom-operator definition=\"depcp:Trim\">\n * <qti-variable identifier=\"RESPONSE\"/>\n * </qti-custom-operator>\n * ```\n *\n * `class=\"js.org\"` instead runs inline JavaScript out of a CDATA section:\n *\n * ```xml\n * <qti-custom-operator class=\"js.org\">\n * <qti-base-value base-type=\"string\"><![CDATA[\n * console.log(context.variables);\n * return 'B'\n * ]]></qti-base-value>\n * </qti-custom-operator>\n * ```\n *\n * `definition` is checked first; the two do not interact.\n */\n\nexport class QtiCustomOperator extends LitElement implements Calculate {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n #operatorFunction: Function;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n private _context?: ItemContext;\n\n override render() {\n return html`<slot @slotchange=${this.handleSlotChange}></slot>`;\n }\n\n handleSlotChange() {\n // expecting <[!CDATA[ ... ]]> is converted into <!-- ... --> with qti-transform: cDataToComment\n const commentNode = Array.from(this.firstElementChild?.childNodes ?? []).find(\n node => node.nodeType === Node.COMMENT_NODE\n );\n try {\n this.#operatorFunction = new Function('context', 'fn', 'item', commentNode?.textContent ?? '');\n } catch (e) {\n console.error('custom-operator contains invalid javascript code', e);\n }\n }\n\n public calculate() {\n const definition = this.getAttribute('definition');\n if (definition) {\n return this.#applyDefinedOperator(definition);\n }\n\n const fn = {\n variable: (responseIdentifier: string) =>\n this._context?.variables.find(v => v.identifier === responseIdentifier)?.value ?? '',\n correct: (responseIdentifier: string) =>\n (this._context?.variables.find(v => v.identifier === responseIdentifier) as ResponseVariable)\n ?.correctResponse ?? ''\n };\n const item = {\n getVariable: (variableIdentifier: string) =>\n this._context?.variables.find(v => v.identifier === variableIdentifier),\n updateOutcomeVariable: (outcomeIdentifier: string, value: string | string[]) => {\n this.dispatchEvent(\n new CustomEvent<{ outcomeIdentifier: string; value: string | string[] }>('qti-set-outcome-value', {\n bubbles: true,\n composed: true,\n detail: {\n outcomeIdentifier,\n value\n }\n })\n );\n },\n updateResponseVariable: (responseIdentifier: string, response: string | string[]) => {\n this.dispatchEvent(\n new CustomEvent<{ responseIdentifier: string; response: string | string[] }>('qti-interaction-response', {\n bubbles: true,\n composed: true,\n detail: {\n responseIdentifier,\n response\n }\n })\n );\n }\n };\n\n return this.#operatorFunction(this._context, fn, item);\n }\n\n /**\n * An operator the host registered, or a built-in. An unknown `definition` is\n * NULL rather than an error: the item names an operator this engine does not\n * provide, which the surrounding expression can still test with\n * `qti-is-null`.\n */\n #applyDefinedOperator(definition: string) {\n const operator = getCustomOperator(definition);\n if (!operator) {\n console.warn(`qti-custom-operator: no operator registered for definition \"${definition}\"`);\n return null;\n }\n\n const values = Array.from(this.children).map(child => {\n const calculate = (child as Partial<Calculate>).calculate;\n return typeof calculate === 'function' ? (calculate.call(child) as CustomOperatorValue) : null;\n });\n\n return operator(values);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-custom-operator': QtiCustomOperator;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-custom-operator.js","sourceRoot":"","sources":["../../../src/components/qti-custom-operator/qti-custom-operator.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { LitElement, html } from 'lit';\nimport { state } from 'lit/decorators.js';\n\nimport { getCustomOperator, itemContext } from '@qti-components/base';\n\nimport type { CustomOperatorValue, ResponseVariable } from '@qti-components/base';\nimport type { Calculate } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\n/**\n * https://www.imsglobal.org/spec/qti/v3p0/impl#h.fi29q8dubjgw\n *\n * A custom operator is outside the QTI vocabulary, so what it does is supplied\n * by the delivery engine rather than by the item. Two ways to supply it:\n *\n * `definition` names an operator the host registered with\n * `registerCustomOperator`, or one of the built-ins (`Trim`, `ToAscii`,\n * `ParseCommaDecimal` under the `depcp:`, `questify:` and `qade:` prefixes).\n * The operator is handed the calculated values of the child expressions:\n *\n * ```xml\n * <qti-custom-operator definition=\"depcp:Trim\">\n * <qti-variable identifier=\"RESPONSE\"/>\n * </qti-custom-operator>\n * ```\n *\n * `class=\"js.org\"` instead runs inline JavaScript out of a CDATA section:\n *\n * ```xml\n * <qti-custom-operator class=\"js.org\">\n * <qti-base-value base-type=\"string\"><![CDATA[\n * console.log(context.variables);\n * return 'B'\n * ]]></qti-base-value>\n * </qti-custom-operator>\n * ```\n *\n * `definition` is checked first; the two do not interact.\n */\n\nexport class QtiCustomOperator extends LitElement implements Calculate {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n #operatorFunction: Function;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n private _context?: ItemContext;\n\n override render() {\n return html`<slot @slotchange=${this.handleSlotChange}></slot>`;\n }\n\n handleSlotChange() {\n // expecting <[!CDATA[ ... ]]> is converted into <!-- ... --> with qti-transform: cDataToComment\n const commentNode = Array.from(this.firstElementChild?.childNodes ?? []).find(\n node => node.nodeType === Node.COMMENT_NODE\n );\n try {\n this.#operatorFunction = new Function('context', 'fn', 'item', commentNode?.textContent ?? '');\n } catch (e) {\n console.error('custom-operator contains invalid javascript code', e);\n }\n }\n\n public calculate() {\n const definition = this.getAttribute('definition');\n if (definition) {\n return this.#applyDefinedOperator(definition);\n }\n\n const fn = {\n variable: (responseIdentifier: string) =>\n this._context?.variables.find(v => v.identifier === responseIdentifier)?.value ?? '',\n correct: (responseIdentifier: string) =>\n (this._context?.variables.find(v => v.identifier === responseIdentifier) as ResponseVariable)\n ?.correctResponse ?? ''\n };\n const item = {\n getVariable: (variableIdentifier: string) =>\n this._context?.variables.find(v => v.identifier === variableIdentifier),\n updateOutcomeVariable: (outcomeIdentifier: string, value: string | string[]) => {\n this.dispatchEvent(\n new CustomEvent<{ outcomeIdentifier: string; value: string | string[] }>('qti-set-outcome-value', {\n bubbles: true,\n composed: true,\n detail: {\n outcomeIdentifier,\n value\n }\n })\n );\n }\n };\n\n return this.#operatorFunction(this._context, fn, item);\n }\n\n /**\n * An operator the host registered, or a built-in. An unknown `definition` is\n * NULL rather than an error: the item names an operator this engine does not\n * provide, which the surrounding expression can still test with\n * `qti-is-null`.\n */\n #applyDefinedOperator(definition: string) {\n const operator = getCustomOperator(definition);\n if (!operator) {\n console.warn(`qti-custom-operator: no operator registered for definition \"${definition}\"`);\n return null;\n }\n\n const values = Array.from(this.children).map(child => {\n const calculate = (child as Partial<Calculate>).calculate;\n return typeof calculate === 'function' ? (calculate.call(child) as CustomOperatorValue) : null;\n });\n\n return operator(values);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-custom-operator': QtiCustomOperator;\n }\n}\n"]}
@@ -8,7 +8,7 @@ import { consume } from '@lit/context';
8
8
  import { css, html } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { itemContext } from '@qti-components/base';
11
- import { QtiVariableDeclaration } from '@qti-components/base';
11
+ import { QtiVariableDeclaration, readRecordFieldValues } from '@qti-components/base';
12
12
  export class QtiResponseDeclaration extends QtiVariableDeclaration {
13
13
  static { this.styles = [
14
14
  css `
@@ -23,11 +23,16 @@ export class QtiResponseDeclaration extends QtiVariableDeclaration {
23
23
  }
24
24
  connectedCallback() {
25
25
  super.connectedCallback();
26
- const defaultValue = this.defaultValues(this.cardinality);
26
+ // A record value is its field map; the variable types only spell out string | string[].
27
+ const defaultValue = this.cardinality === 'record'
28
+ ? this.#recordDefaultValue
29
+ : this.defaultValues(this.cardinality);
30
+ const { correctResponse, fieldBaseTypes } = this.#readCorrectResponse();
27
31
  const responseVariable = {
28
32
  baseType: this.baseType,
29
33
  identifier: this.identifier,
30
- correctResponse: this.#correctResponse,
34
+ correctResponse,
35
+ ...(fieldBaseTypes ? { fieldBaseTypes } : {}),
31
36
  cardinality: this.cardinality || 'single',
32
37
  mapping: this.#mapping,
33
38
  defaultValue,
@@ -43,6 +48,38 @@ export class QtiResponseDeclaration extends QtiVariableDeclaration {
43
48
  detail: { variable: responseVariable }
44
49
  }));
45
50
  }
51
+ /**
52
+ * The correct response, and for `record` cardinality the declared base type of each field.
53
+ *
54
+ * A record's correct response is a field map, `{ time: '4.0', point: '460 245' }`, read from
55
+ * `qti-value field-identifier="…" base-type="…"`. The field base types are kept beside it, since
56
+ * the record declaration itself has no `base-type` to say how a field is to be compared.
57
+ */
58
+ #readCorrectResponse() {
59
+ if (this.cardinality !== 'record') {
60
+ return { correctResponse: this.#correctResponse };
61
+ }
62
+ const correctResponse = this.querySelector('qti-correct-response');
63
+ if (!correctResponse) {
64
+ return { correctResponse: undefined };
65
+ }
66
+ const values = Array.from(correctResponse.querySelectorAll('qti-value'));
67
+ const { value, baseTypes } = readRecordFieldValues(values);
68
+ values.forEach(element => element.remove());
69
+ return {
70
+ // The variable types hold string | string[]; a record is stored as its field map.
71
+ correctResponse: (Object.keys(value).length ? value : undefined),
72
+ fieldBaseTypes: baseTypes
73
+ };
74
+ }
75
+ /** A record's `qti-default-value`, as a field map like its correct response. */
76
+ get #recordDefaultValue() {
77
+ const values = this.querySelectorAll('qti-default-value > qti-value');
78
+ if (values.length === 0)
79
+ return null;
80
+ const { value } = readRecordFieldValues(values);
81
+ return Object.keys(value).length ? value : null;
82
+ }
46
83
  get #correctResponse() {
47
84
  let result;
48
85
  const correctResponse = this.querySelector('qti-correct-response');
@@ -1 +1 @@
1
- {"version":3,"file":"qti-response-declaration.js","sourceRoot":"","sources":["../../../src/components/qti-response-declaration/qti-response-declaration.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAQ9D,MAAM,OAAO,sBAAuB,SAAQ,sBAAsB;aAWhD,WAAM,GAAG;QACvB,GAAG,CAAA;;;;KAIF;KACF,CAAC;IAEO,MAAM;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;QAC7F,OAAO,IAAI,CAAA,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IACjD,CAAC;IAEe,iBAAiB;QAC/B,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE1D,MAAM,gBAAgB,GAAqB;YACzC,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,eAAe,EAAE,IAAI,CAAC,gBAAgB;YACtC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,QAAQ;YACzC,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,YAAY;YACZ,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,UAAU;YAChB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QACF,gBAAgB,CAAC,KAAK,GAAG,YAAY,CAAC;QAEtC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,uBAAuB,EAAE;YACvC,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,EAAE,QAAQ,EAAE,gBAAgB,EAAE;SACvC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,gBAAgB;QAClB,IAAI,MAAyB,CAAC;QAC9B,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;QACnE,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,MAAM,GAAG,eAAe,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;YAC7D,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvD,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;gBACtC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YACrB,CAAC;iBAAM,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;gBACzC,MAAM,GAAG,EAAE,CAAC;gBACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACvC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC1C,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,QAAQ;QACV,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QACzD,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,UAAU,CAAC,cAAc,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;QAC5E,MAAM,YAAY,GAAG;YACnB,YAAY,EAAE,MAAM,CAAC,cAAc,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC;YACxE,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU;YACjD,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW;YACnD,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBACzF,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,EAAE;gBACxC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC;gBACzD,aAAa,EAAE,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK;aACzG,CAAC,CAAC;SACJ,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,IAAI,YAAY;QACd,MAAM,kBAAkB,GAAG,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAgB,CAAC;QAEjF,MAAM,YAAY,GAAG,MAAM,CAAC,kBAAkB,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,UAAU,GAAG,UAAU,CAAC,kBAAkB,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;QAC/E,MAAM,WAAW,GAAG,UAAU,CAAC,kBAAkB,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;QAEhF,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CACrG,CAAC,EAAe,EAAE,EAAE,CAAC,CAAC;YACpB,KAAK,EAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAwD,IAAI,SAAS;YACpG,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE;YACvC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC;YACzD,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC;SAC5D,CAAC,CACH,CAAC;QAEF,OAAO;YACL,YAAY;YACZ,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU;YACjD,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW;YACnD,cAAc;SACf,CAAC;IACJ,CAAC;;AA5GmD;IAAnD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;wDAAoB;AAE3C;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;0DAAoB;AAEnB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;2DAA0B;AAI9C;IAFN,OAAO,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAClD,KAAK,EAAE;2DACyB","sourcesContent":["import { consume } from '@lit/context';\nimport { css, html } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { itemContext } from '@qti-components/base';\nimport { QtiVariableDeclaration } from '@qti-components/base';\n\nimport type { QtiAreaMapping } from '@qti-components/base';\nimport type { BaseType, Cardinality } from '@qti-components/base';\nimport type { ResponseVariable } from '@qti-components/base';\nimport type { QtiMapping } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\nexport class QtiResponseDeclaration extends QtiVariableDeclaration {\n @property({ type: String, attribute: 'base-type' }) baseType: BaseType;\n\n @property({ type: String }) identifier: string;\n\n @property({ type: String }) cardinality: Cardinality;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n public itemContext?: ItemContext;\n\n static override styles = [\n css`\n :host {\n display: none;\n }\n `\n ];\n\n override render() {\n const value = this.itemContext?.variables.find(v => v.identifier === this.identifier)?.value;\n return html`${JSON.stringify(value, null, 2)}`;\n }\n\n public override connectedCallback() {\n super.connectedCallback();\n const defaultValue = this.defaultValues(this.cardinality);\n\n const responseVariable: ResponseVariable = {\n baseType: this.baseType,\n identifier: this.identifier,\n correctResponse: this.#correctResponse,\n cardinality: this.cardinality || 'single',\n mapping: this.#mapping,\n defaultValue,\n areaMapping: this.#areaMapping,\n value: null,\n type: 'response',\n candidateResponse: null\n };\n responseVariable.value = defaultValue;\n\n this.dispatchEvent(\n new CustomEvent('qti-register-variable', {\n bubbles: true,\n composed: true,\n detail: { variable: responseVariable }\n })\n );\n }\n\n get #correctResponse(): string | string[] {\n let result: string | string[];\n const correctResponse = this.querySelector('qti-correct-response');\n if (correctResponse) {\n const values = correctResponse.querySelectorAll('qti-value');\n if (this.cardinality === 'single' && values.length > 0) {\n result = values[0].textContent.trim();\n values[0].remove();\n } else if (this.cardinality !== 'single') {\n result = [];\n for (let i = 0; i < values.length; i++) {\n result.push(values[i].textContent.trim());\n values[i].remove();\n }\n }\n }\n return result;\n }\n\n get #mapping(): QtiMapping {\n const mappingElement = this.querySelector('qti-mapping');\n const lowerBound = parseFloat(mappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(mappingElement?.getAttribute('upper-bound'));\n const mappingValue = {\n defaultValue: Number(mappingElement?.getAttribute('default-value')) || 0,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n mapEntries: Array.from(mappingElement?.querySelectorAll('qti-map-entry') || []).map(el => ({\n mapKey: el.getAttribute('map-key') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n caseSensitive: el.hasAttribute('case-sensitive') ? el.getAttribute('case-sensitive') !== 'false' : false\n }))\n };\n return mappingValue;\n }\n\n get #areaMapping(): QtiAreaMapping {\n const areaMappingElement = this.querySelector('qti-area-mapping') as HTMLElement;\n\n const defaultValue = Number(areaMappingElement?.getAttribute('default-value')) || 0;\n const lowerBound = parseFloat(areaMappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(areaMappingElement?.getAttribute('upper-bound'));\n\n const areaMapEntries = Array.from(areaMappingElement?.querySelectorAll('qti-area-map-entry') || []).map(\n (el: HTMLElement) => ({\n shape: (el.getAttribute('shape') as 'default' | 'circle' | 'rect' | 'ellipse' | 'poly') || 'default',\n coords: el.getAttribute('coords') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n defaultValue: Number(el.getAttribute('default-value')) || 0\n })\n );\n\n return {\n defaultValue,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n areaMapEntries\n };\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-response-declaration': QtiResponseDeclaration;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-response-declaration.js","sourceRoot":"","sources":["../../../src/components/qti-response-declaration/qti-response-declaration.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { css, html } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { itemContext } from '@qti-components/base';\nimport { QtiVariableDeclaration, readRecordFieldValues } from '@qti-components/base';\n\nimport type { QtiAreaMapping } from '@qti-components/base';\nimport type { BaseType, Cardinality } from '@qti-components/base';\nimport type { RecordValue, ResponseVariable } from '@qti-components/base';\nimport type { QtiMapping } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\nexport class QtiResponseDeclaration extends QtiVariableDeclaration {\n @property({ type: String, attribute: 'base-type' }) baseType: BaseType;\n\n @property({ type: String }) identifier: string;\n\n @property({ type: String }) cardinality: Cardinality;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n public itemContext?: ItemContext;\n\n static override styles = [\n css`\n :host {\n display: none;\n }\n `\n ];\n\n override render() {\n const value = this.itemContext?.variables.find(v => v.identifier === this.identifier)?.value;\n return html`${JSON.stringify(value, null, 2)}`;\n }\n\n public override connectedCallback() {\n super.connectedCallback();\n // A record value is its field map; the variable types only spell out string | string[].\n const defaultValue =\n this.cardinality === 'record'\n ? (this.#recordDefaultValue as unknown as string | string[] | null)\n : this.defaultValues(this.cardinality);\n const { correctResponse, fieldBaseTypes } = this.#readCorrectResponse();\n\n const responseVariable: ResponseVariable = {\n baseType: this.baseType,\n identifier: this.identifier,\n correctResponse,\n ...(fieldBaseTypes ? { fieldBaseTypes } : {}),\n cardinality: this.cardinality || 'single',\n mapping: this.#mapping,\n defaultValue,\n areaMapping: this.#areaMapping,\n value: null,\n type: 'response',\n candidateResponse: null\n };\n responseVariable.value = defaultValue;\n\n this.dispatchEvent(\n new CustomEvent('qti-register-variable', {\n bubbles: true,\n composed: true,\n detail: { variable: responseVariable }\n })\n );\n }\n\n /**\n * The correct response, and for `record` cardinality the declared base type of each field.\n *\n * A record's correct response is a field map, `{ time: '4.0', point: '460 245' }`, read from\n * `qti-value field-identifier=\"…\" base-type=\"…\"`. The field base types are kept beside it, since\n * the record declaration itself has no `base-type` to say how a field is to be compared.\n */\n #readCorrectResponse(): { correctResponse: string | string[]; fieldBaseTypes?: ResponseVariable['fieldBaseTypes'] } {\n if (this.cardinality !== 'record') {\n return { correctResponse: this.#correctResponse };\n }\n const correctResponse = this.querySelector('qti-correct-response');\n if (!correctResponse) {\n return { correctResponse: undefined };\n }\n const values = Array.from(correctResponse.querySelectorAll('qti-value'));\n const { value, baseTypes } = readRecordFieldValues(values);\n values.forEach(element => element.remove());\n return {\n // The variable types hold string | string[]; a record is stored as its field map.\n correctResponse: (Object.keys(value).length ? value : undefined) as unknown as string | string[],\n fieldBaseTypes: baseTypes\n };\n }\n\n /** A record's `qti-default-value`, as a field map like its correct response. */\n get #recordDefaultValue(): RecordValue | null {\n const values = this.querySelectorAll('qti-default-value > qti-value');\n if (values.length === 0) return null;\n const { value } = readRecordFieldValues(values);\n return Object.keys(value).length ? value : null;\n }\n\n get #correctResponse(): string | string[] {\n let result: string | string[];\n const correctResponse = this.querySelector('qti-correct-response');\n if (correctResponse) {\n const values = correctResponse.querySelectorAll('qti-value');\n if (this.cardinality === 'single' && values.length > 0) {\n result = values[0].textContent.trim();\n values[0].remove();\n } else if (this.cardinality !== 'single') {\n result = [];\n for (let i = 0; i < values.length; i++) {\n result.push(values[i].textContent.trim());\n values[i].remove();\n }\n }\n }\n return result;\n }\n\n get #mapping(): QtiMapping {\n const mappingElement = this.querySelector('qti-mapping');\n const lowerBound = parseFloat(mappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(mappingElement?.getAttribute('upper-bound'));\n const mappingValue = {\n defaultValue: Number(mappingElement?.getAttribute('default-value')) || 0,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n mapEntries: Array.from(mappingElement?.querySelectorAll('qti-map-entry') || []).map(el => ({\n mapKey: el.getAttribute('map-key') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n caseSensitive: el.hasAttribute('case-sensitive') ? el.getAttribute('case-sensitive') !== 'false' : false\n }))\n };\n return mappingValue;\n }\n\n get #areaMapping(): QtiAreaMapping {\n const areaMappingElement = this.querySelector('qti-area-mapping') as HTMLElement;\n\n const defaultValue = Number(areaMappingElement?.getAttribute('default-value')) || 0;\n const lowerBound = parseFloat(areaMappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(areaMappingElement?.getAttribute('upper-bound'));\n\n const areaMapEntries = Array.from(areaMappingElement?.querySelectorAll('qti-area-map-entry') || []).map(\n (el: HTMLElement) => ({\n shape: (el.getAttribute('shape') as 'default' | 'circle' | 'rect' | 'ellipse' | 'poly') || 'default',\n coords: el.getAttribute('coords') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n defaultValue: Number(el.getAttribute('default-value')) || 0\n })\n );\n\n return {\n defaultValue,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n areaMapEntries\n };\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-response-declaration': QtiResponseDeclaration;\n }\n}\n"]}
@@ -1,9 +1,25 @@
1
1
  import { LitElement } from 'lit';
2
2
  import type { PropertyValueMap } from 'lit';
3
+ /**
4
+ * Response processing as the item's own backend: it reads the item context, runs its rules once and
5
+ * sends the outcomes and the attempt count back in one `cito-item-set-outcomes`. The item applies
6
+ * them; a host that scores elsewhere sends the same event instead.
7
+ */
3
8
  export declare class QtiResponseProcessing extends LitElement {
9
+ #private;
4
10
  static styles: import("lit").CSSResult[];
11
+ constructor();
5
12
  render(): import("lit").TemplateResult<1>;
6
- process(): void;
13
+ /**
14
+ * Scores the attempt on the copy: sets `numAttempts` to the count the item hands over, resets a
15
+ * non-adaptive item's outcomes to their defaults (QTI info model §2.5), runs the rules and sends
16
+ * every outcome and `numAttempts` in one `cito-item-set-outcomes`. The item applies them and publishes, which gives this element a
17
+ * fresh copy. A rule that throws ends the run and the error propagates; nothing is sent, and the
18
+ * run's writes stay in the copy until the item's next update.
19
+ */
20
+ processResponse({ numAttempts }: {
21
+ numAttempts: number;
22
+ }): void;
7
23
  firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
8
24
  }
9
25
  declare global {
@@ -1,12 +1,19 @@
1
+ import { ContextConsumer, ContextProvider } from '@lit/context';
1
2
  import { css, html, LitElement } from 'lit';
2
- import { QtiExitResponseSignal } from '@qti-components/base';
3
+ import { itemContext, QtiExitResponseSignal } from '@qti-components/base';
3
4
  import { mapResponse, mapResponsePoint, matchCorrect } from '../../internal/template-strings';
5
+ import { dispatchPublic } from '../../public-events';
4
6
  /** The response processing templates we substitute for, keyed by the file name in `template`. */
5
7
  const responseProcessingTemplates = {
6
8
  map_response: mapResponse,
7
9
  map_response_point: mapResponsePoint,
8
10
  match_correct: matchCorrect
9
11
  };
12
+ /**
13
+ * Response processing as the item's own backend: it reads the item context, runs its rules once and
14
+ * sends the outcomes and the attempt count back in one `cito-item-set-outcomes`. The item applies
15
+ * them; a host that scores elsewhere sends the same event instead.
16
+ */
10
17
  export class QtiResponseProcessing extends LitElement {
11
18
  static { this.styles = [
12
19
  css `
@@ -15,13 +22,70 @@ export class QtiResponseProcessing extends LitElement {
15
22
  }
16
23
  `
17
24
  ]; }
25
+ /**
26
+ * The rules inside read and write this element's copy of the item context, never the item's. It
27
+ * is provided under the same `itemContext`: the nearest provider wins, so every expression in
28
+ * here gets the copy without knowing about it.
29
+ */
30
+ #provider = new ContextProvider(this, { context: itemContext });
31
+ #copy;
32
+ constructor() {
33
+ super();
34
+ new ContextConsumer(this, {
35
+ context: itemContext,
36
+ subscribe: true,
37
+ // Every variable its own object, so a write changes the copy and not the item's context.
38
+ callback: context => {
39
+ this.#copy = { ...context, variables: context.variables.map(v => ({ ...v })) };
40
+ this.#provider.setValue(this.#copy);
41
+ }
42
+ });
43
+ // Every rule that sets an outcome sits in here: the write lands in the copy, so a later rule
44
+ // reads it, and never reaches the item (or the test above it, whose outcome processing listens
45
+ // for the same name).
46
+ this.addEventListener('qti-set-outcome-value', (e) => {
47
+ e.stopImmediatePropagation();
48
+ const variable = this.#copy?.variables.find(v => v.identifier === e.detail.outcomeIdentifier);
49
+ if (!variable) {
50
+ console.warn(`Can not set qti-outcome-identifier: ${e.detail.outcomeIdentifier}, it is not available`);
51
+ return;
52
+ }
53
+ const { value } = e.detail;
54
+ variable.value = variable.cardinality === 'single' ? value : Array.isArray(value) ? value : [value];
55
+ });
56
+ }
18
57
  render() {
19
58
  return html `<slot></slot>`;
20
59
  }
21
- process() {
22
- const assessmentItem = this.closest('qti-assessment-item');
23
- if (!assessmentItem)
60
+ /**
61
+ * Scores the attempt on the copy: sets `numAttempts` to the count the item hands over, resets a
62
+ * non-adaptive item's outcomes to their defaults (QTI info model §2.5), runs the rules and sends
63
+ * every outcome and `numAttempts` in one `cito-item-set-outcomes`. The item applies them and publishes, which gives this element a
64
+ * fresh copy. A rule that throws ends the run and the error propagates; nothing is sent, and the
65
+ * run's writes stay in the copy until the item's next update.
66
+ */
67
+ processResponse({ numAttempts }) {
68
+ const copy = this.#copy;
69
+ if (!copy)
24
70
  return;
71
+ const reset = this.closest('qti-assessment-item')?.getAttribute('adaptive') !== 'true';
72
+ for (const variable of copy.variables) {
73
+ if (variable.identifier === 'numAttempts')
74
+ variable.value = String(numAttempts);
75
+ // completionStatus is built in: it has no default to reset to, and keeps its value (as in TAO).
76
+ if (variable.type === 'outcome' && reset && variable.identifier !== 'completionStatus') {
77
+ variable.value = defaultValueOf(variable);
78
+ }
79
+ }
80
+ this.#runRules();
81
+ dispatchPublic(this, 'cito-item-set-outcomes', {
82
+ outcomes: copy.variables
83
+ .filter(v => v.type === 'outcome' || v.identifier === 'numAttempts')
84
+ .map(({ identifier, value }) => ({ identifier, value })),
85
+ responseProcessed: true
86
+ });
87
+ }
88
+ #runRules() {
25
89
  const rules = [...this.children];
26
90
  try {
27
91
  for (const rule of rules) {
@@ -53,4 +117,11 @@ export class QtiResponseProcessing extends LitElement {
53
117
  this.innerHTML = rules;
54
118
  }
55
119
  }
120
+ /** The declared default; a numeric outcome without one starts at 0. */
121
+ function defaultValueOf(variable) {
122
+ const value = ('defaultValue' in variable ? variable.defaultValue : null) ?? null;
123
+ if (value === null && (variable.baseType === 'integer' || variable.baseType === 'float'))
124
+ return '0';
125
+ return value;
126
+ }
56
127
  //# sourceMappingURL=qti-response-processing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"qti-response-processing.js","sourceRoot":"","sources":["../../../src/components/qti-response-processing/qti-response-processing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAoB,MAAM,sBAAsB,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAI9F,iGAAiG;AACjG,MAAM,2BAA2B,GAA2B;IAC1D,YAAY,EAAE,WAAW;IACzB,kBAAkB,EAAE,gBAAgB;IACpC,aAAa,EAAE,YAAY;CAC5B,CAAC;AAEF,MAAM,OAAO,qBAAsB,SAAQ,UAAU;aACnC,WAAM,GAAG;QACvB,GAAG,CAAA;;;;KAIF;KACF,CAAC;IAEO,MAAM;QACb,OAAO,IAAI,CAAA,eAAe,CAAC;IAC7B,CAAC;IAEM,OAAO;QACZ,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAC3D,IAAI,CAAC,cAAc;YAAE,OAAO;QAC5B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAA6B,CAAC;QAC7D,IAAI,CAAC;YACH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,qEAAqE;YACrE,wEAAwE;YACxE,yEAAyE;YACzE,IAAI,CAAC,CAAC,KAAK,YAAY,qBAAqB,CAAC;gBAAE,MAAM,KAAK,CAAC;QAC7D,CAAC;IACH,CAAC;IAEe,YAAY,CAAC,kBAAqE;QAChG,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,2BAA2B,CAAC,YAAY,CAAC,CAAC;QACxD,uFAAuF;QACvF,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,+FAA+F;QAC/F,yFAAyF;QACzF,8FAA8F;QAC9F,6FAA6F;QAC7F,4EAA4E;QAC5E,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC","sourcesContent":["import { css, html, LitElement } from 'lit';\n\nimport { QtiExitResponseSignal, type QtiRuleBase } from '@qti-components/base';\n\nimport { mapResponse, mapResponsePoint, matchCorrect } from '../../internal/template-strings';\n\nimport type { PropertyValueMap } from 'lit';\n\n/** The response processing templates we substitute for, keyed by the file name in `template`. */\nconst responseProcessingTemplates: Record<string, string> = {\n map_response: mapResponse,\n map_response_point: mapResponsePoint,\n match_correct: matchCorrect\n};\n\nexport class QtiResponseProcessing extends LitElement {\n static override styles = [\n css`\n :host {\n display: none;\n }\n `\n ];\n\n override render() {\n return html`<slot></slot>`;\n }\n\n public process() {\n const assessmentItem = this.closest('qti-assessment-item');\n if (!assessmentItem) return;\n const rules = [...this.children] as unknown as QtiRuleBase[];\n try {\n for (const rule of rules) {\n rule.process();\n }\n } catch (error) {\n // `qti-exit-response` ends the attempt's processing from wherever it\n // sits, however deeply nested. Reaching here is the rule doing its job,\n // so the run simply stops; anything else is a real failure and rethrows.\n if (!(error instanceof QtiExitResponseSignal)) throw error;\n }\n }\n\n public override firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {\n const template = this.getAttribute('template');\n if (!template) return;\n\n const templateName = template.split('/').pop()!.replace('.xml', '');\n const rules = responseProcessingTemplates[templateName];\n // Leave an unknown template's authored children alone; there is nothing to substitute.\n if (!rules) return;\n\n // Parsed through this element's own `innerHTML` rather than a fragment created off `document`:\n // the HTML fragment parsing algorithm takes its custom element registry from the context\n // element, so the rules are upgraded with the registry this element lives in. A fragment from\n // `document.createRange()` is parsed against the global registry, which leaves every rule an\n // unupgraded HTMLElement when the item was rendered into a scoped registry.\n this.innerHTML = rules;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-response-processing': QtiResponseProcessing;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-response-processing.js","sourceRoot":"","sources":["../../../src/components/qti-response-processing/qti-response-processing.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextConsumer, ContextProvider } from '@lit/context';\nimport { css, html, LitElement } from 'lit';\n\nimport { itemContext, QtiExitResponseSignal, type QtiRuleBase } from '@qti-components/base';\n\nimport { mapResponse, mapResponsePoint, matchCorrect } from '../../internal/template-strings';\nimport { dispatchPublic } from '../../public-events';\n\nimport type { ItemContext, OutcomeVariable } from '@qti-components/base';\nimport type { PropertyValueMap } from 'lit';\n\ntype OutcomeValue = string | string[] | null | undefined;\n\n/** The response processing templates we substitute for, keyed by the file name in `template`. */\nconst responseProcessingTemplates: Record<string, string> = {\n map_response: mapResponse,\n map_response_point: mapResponsePoint,\n match_correct: matchCorrect\n};\n\n/**\n * Response processing as the item's own backend: it reads the item context, runs its rules once and\n * sends the outcomes and the attempt count back in one `cito-item-set-outcomes`. The item applies\n * them; a host that scores elsewhere sends the same event instead.\n */\nexport class QtiResponseProcessing extends LitElement {\n static override styles = [\n css`\n :host {\n display: none;\n }\n `\n ];\n\n /**\n * The rules inside read and write this element's copy of the item context, never the item's. It\n * is provided under the same `itemContext`: the nearest provider wins, so every expression in\n * here gets the copy without knowing about it.\n */\n #provider = new ContextProvider(this, { context: itemContext });\n #copy?: ItemContext;\n\n constructor() {\n super();\n new ContextConsumer(this, {\n context: itemContext,\n subscribe: true,\n // Every variable its own object, so a write changes the copy and not the item's context.\n callback: context => {\n this.#copy = { ...context, variables: context.variables.map(v => ({ ...v })) };\n this.#provider.setValue(this.#copy);\n }\n });\n // Every rule that sets an outcome sits in here: the write lands in the copy, so a later rule\n // reads it, and never reaches the item (or the test above it, whose outcome processing listens\n // for the same name).\n this.addEventListener(\n 'qti-set-outcome-value',\n (e: CustomEvent<{ outcomeIdentifier: string; value: OutcomeValue }>) => {\n e.stopImmediatePropagation();\n const variable = this.#copy?.variables.find(v => v.identifier === e.detail.outcomeIdentifier);\n if (!variable) {\n console.warn(`Can not set qti-outcome-identifier: ${e.detail.outcomeIdentifier}, it is not available`);\n return;\n }\n const { value } = e.detail;\n variable.value = variable.cardinality === 'single' ? value : Array.isArray(value) ? value : [value];\n }\n );\n }\n\n override render() {\n return html`<slot></slot>`;\n }\n\n /**\n * Scores the attempt on the copy: sets `numAttempts` to the count the item hands over, resets a\n * non-adaptive item's outcomes to their defaults (QTI info model §2.5), runs the rules and sends\n * every outcome and `numAttempts` in one `cito-item-set-outcomes`. The item applies them and publishes, which gives this element a\n * fresh copy. A rule that throws ends the run and the error propagates; nothing is sent, and the\n * run's writes stay in the copy until the item's next update.\n */\n public processResponse({ numAttempts }: { numAttempts: number }): void {\n const copy = this.#copy;\n if (!copy) return;\n const reset = this.closest('qti-assessment-item')?.getAttribute('adaptive') !== 'true';\n for (const variable of copy.variables) {\n if (variable.identifier === 'numAttempts') variable.value = String(numAttempts);\n // completionStatus is built in: it has no default to reset to, and keeps its value (as in TAO).\n if (variable.type === 'outcome' && reset && variable.identifier !== 'completionStatus') {\n variable.value = defaultValueOf(variable as OutcomeVariable);\n }\n }\n\n this.#runRules();\n\n dispatchPublic(this, 'cito-item-set-outcomes', {\n outcomes: copy.variables\n .filter(v => v.type === 'outcome' || v.identifier === 'numAttempts')\n .map(({ identifier, value }) => ({ identifier, value })),\n responseProcessed: true\n });\n }\n\n #runRules() {\n const rules = [...this.children] as unknown as QtiRuleBase[];\n try {\n for (const rule of rules) {\n rule.process();\n }\n } catch (error) {\n // `qti-exit-response` ends the attempt's processing from wherever it\n // sits, however deeply nested. Reaching here is the rule doing its job,\n // so the run simply stops; anything else is a real failure and rethrows.\n if (!(error instanceof QtiExitResponseSignal)) throw error;\n }\n }\n\n public override firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {\n const template = this.getAttribute('template');\n if (!template) return;\n\n const templateName = template.split('/').pop()!.replace('.xml', '');\n const rules = responseProcessingTemplates[templateName];\n // Leave an unknown template's authored children alone; there is nothing to substitute.\n if (!rules) return;\n\n // Parsed through this element's own `innerHTML` rather than a fragment created off `document`:\n // the HTML fragment parsing algorithm takes its custom element registry from the context\n // element, so the rules are upgraded with the registry this element lives in. A fragment from\n // `document.createRange()` is parsed against the global registry, which leaves every rule an\n // unupgraded HTMLElement when the item was rendered into a scoped registry.\n this.innerHTML = rules;\n }\n}\n\n/** The declared default; a numeric outcome without one starts at 0. */\nfunction defaultValueOf(variable: OutcomeVariable): OutcomeValue {\n const value = ('defaultValue' in variable ? variable.defaultValue : null) ?? null;\n if (value === null && (variable.baseType === 'integer' || variable.baseType === 'float')) return '0';\n return value;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-response-processing': QtiResponseProcessing;\n }\n}\n"]}
@@ -1,9 +1,27 @@
1
1
  import type { ResponseInteraction } from '@qti-components/base';
2
+ export interface OutcomeEntry {
3
+ identifier: string;
4
+ value: Readonly<string | string[] | null | undefined>;
5
+ }
6
+ /** One outcome an update applied, with its final value. */
2
7
  export interface OutcomeChangedDetails {
3
8
  item: string;
4
9
  outcomeIdentifier: string;
5
10
  value: Readonly<string | string[]>;
6
11
  }
12
+ /** The outcomes one update applied, sent after the item context is published. */
13
+ export interface OutcomesChangedDetails {
14
+ item: string;
15
+ outcomes: OutcomeEntry[];
16
+ }
17
+ /**
18
+ * Outcomes for the item to apply in one update: the result of its own response processing, or what
19
+ * a backend returned. `responseProcessed` says an attempt ended, as it does after `processResponse`.
20
+ */
21
+ export interface SetOutcomesDetails {
22
+ outcomes: OutcomeEntry[];
23
+ responseProcessed?: boolean;
24
+ }
7
25
  export type InteractionChangedDetails = ResponseInteraction & {
8
26
  item: string;
9
27
  };
@@ -1 +1 @@
1
- {"version":3,"file":"event-types.js","sourceRoot":"","sources":["../../src/internal/event-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ResponseInteraction } from '@qti-components/base';\n\nexport interface OutcomeChangedDetails {\n item: string;\n outcomeIdentifier: string;\n value: Readonly<string | string[]>;\n}\nexport type InteractionChangedDetails = ResponseInteraction & { item: string };\n"]}
1
+ {"version":3,"file":"event-types.js","sourceRoot":"","sources":["../../src/internal/event-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ResponseInteraction } from '@qti-components/base';\n\nexport interface OutcomeEntry {\n identifier: string;\n value: Readonly<string | string[] | null | undefined>;\n}\n\n/** One outcome an update applied, with its final value. */\nexport interface OutcomeChangedDetails {\n item: string;\n outcomeIdentifier: string;\n value: Readonly<string | string[]>;\n}\n\n/** The outcomes one update applied, sent after the item context is published. */\nexport interface OutcomesChangedDetails {\n item: string;\n outcomes: OutcomeEntry[];\n}\n\n/**\n * Outcomes for the item to apply in one update: the result of its own response processing, or what\n * a backend returned. `responseProcessed` says an attempt ended, as it does after `processResponse`.\n */\nexport interface SetOutcomesDetails {\n outcomes: OutcomeEntry[];\n responseProcessed?: boolean;\n}\nexport type InteractionChangedDetails = ResponseInteraction & { item: string };\n"]}
@@ -1,4 +1,4 @@
1
- import type { InteractionChangedDetails, OutcomeChangedDetails } from './internal/event-types';
1
+ import type { InteractionChangedDetails, OutcomeChangedDetails, OutcomesChangedDetails, SetOutcomesDetails } from './internal/event-types';
2
2
  /**
3
3
  * The public events of the item. The name tells you it is public: everything declared here starts
4
4
  * with `cito-`; every other event is internal plumbing and may change in any release.
@@ -20,7 +20,15 @@ export interface RubricPlacement {
20
20
  }
21
21
  export interface PublicItemEventMap {
22
22
  'cito-item-response-changed': CustomEvent<InteractionChangedDetails>;
23
+ /**
24
+ * Request: the item applies these outcomes in one update. Response processing sends it; a host
25
+ * that scores on a backend sends it on the item with the outcomes the backend returned.
26
+ */
27
+ 'cito-item-set-outcomes': CustomEvent<SetOutcomesDetails>;
28
+ /** One per outcome an update applied, with its final value, after the item context is published. */
23
29
  'cito-item-outcome-changed': CustomEvent<OutcomeChangedDetails>;
30
+ /** All the outcomes one update applied, after the item context is published. */
31
+ 'cito-item-outcomes-changed': CustomEvent<OutcomesChangedDetails>;
24
32
  /** Cancelable: `preventDefault()` keeps the rubric block where it is. */
25
33
  'cito-item-rubric-placement': CustomEvent<RubricPlacement>;
26
34
  }
@@ -2,7 +2,9 @@ export const PUBLIC_ITEM_EVENTS = {
2
2
  // The old name `qti-assessment-item-connected` stays as internal plumbing: it is sent separately.
3
3
  'cito-item-rendered': { kind: 'notification', scope: 'item' },
4
4
  'cito-item-response-changed': { kind: 'notification', scope: 'item', deprecated: 'qti-interaction-changed' },
5
+ 'cito-item-set-outcomes': { kind: 'request', scope: 'item' },
5
6
  'cito-item-outcome-changed': { kind: 'notification', scope: 'item', deprecated: 'qti-outcome-changed' },
7
+ 'cito-item-outcomes-changed': { kind: 'notification', scope: 'item' },
6
8
  'cito-item-rubric-placement': {
7
9
  kind: 'notification',
8
10
  scope: 'item',
@@ -1 +1 @@
1
- {"version":3,"file":"public-events.js","sourceRoot":"","sources":["../src/public-events.ts"],"names":[],"mappings":"AAkCA,MAAM,CAAC,MAAM,kBAAkB,GAAwD;IACrF,kGAAkG;IAClG,oBAAoB,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE;IAC7D,4BAA4B,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,yBAAyB,EAAE;IAC5G,2BAA2B,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,qBAAqB,EAAE;IACvG,4BAA4B,EAAE;QAC5B,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,oCAAoC;KACjD;CACF,CAAC;AAMF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAmB,EACnB,YAAoD,EACpD,IAAY,EACZ,MAAe,EACf,UAAU,GAAG,KAAK;IAElB,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IACjF,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACvE,MAAM,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU;QAAE,OAAO,YAAY,CAAC;IACrC,OAAO,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,IAAI,YAAY,CAAC;AACjF,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,YAAoD,EACpD,IAAY,EACZ,OAA2B;IAE3B,IAAI,WAAoB,CAAC;IACzB,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE;QAChC,WAAW,GAAI,CAAiB,CAAC,MAAM,CAAC;QACxC,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,CAAC,CAAC,CAAC;IACb,CAAC,CAAC,CAAC;IACH,MAAM,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE;YACtC,IAAI,WAAW,KAAK,SAAS,IAAK,CAAiB,CAAC,MAAM,KAAK,WAAW;gBAAE,OAAO;YACnF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC,+BAA+B,UAAU,8BAA8B,IAAI,YAAY,CAAC,CAAC;YACxG,CAAC;YACD,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,IAAO,EACP,MAAsE,EACtE,UAAU,GAAG,KAAK;IAElB,OAAO,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAAmB,EACnB,IAAO,EACP,OAA2C;IAE3C,cAAc,CAAC,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,OAA6B,CAAC,CAAC;AAClF,CAAC","sourcesContent":["import type { InteractionChangedDetails, OutcomeChangedDetails } from './internal/event-types';\n\n/**\n * The public events of the item. The name tells you it is public: everything declared here starts\n * with `cito-`; every other event is internal plumbing and may change in any release.\n *\n * Requests are verbs, notifications are past tense. All bubble and are composed.\n *\n * `deprecated` is the name the event had before it became public. `dispatchPublic` sends and\n * `listenPublic` handles both until the next major, then the field goes.\n */\nexport interface PublicEventDeclaration {\n kind: 'request' | 'notification';\n scope: 'test' | 'item';\n /** The name before it was public. Dispatched and handled alongside the new one. */\n deprecated?: string;\n}\n\nexport interface RubricPlacement {\n className: string;\n element: Element;\n}\n\nexport interface PublicItemEventMap {\n // `cito-item-rendered` (detail: the `QtiAssessmentItem`) is added by qti-assessment-item.ts, so this\n // module imports no element: that import would be a cycle.\n 'cito-item-response-changed': CustomEvent<InteractionChangedDetails>;\n 'cito-item-outcome-changed': CustomEvent<OutcomeChangedDetails>;\n /** Cancelable: `preventDefault()` keeps the rubric block where it is. */\n 'cito-item-rubric-placement': CustomEvent<RubricPlacement>;\n}\n\nexport type PublicItemEventName = keyof PublicItemEventMap;\n\nexport const PUBLIC_ITEM_EVENTS: Record<PublicItemEventName, PublicEventDeclaration> = {\n // The old name `qti-assessment-item-connected` stays as internal plumbing: it is sent separately.\n 'cito-item-rendered': { kind: 'notification', scope: 'item' },\n 'cito-item-response-changed': { kind: 'notification', scope: 'item', deprecated: 'qti-interaction-changed' },\n 'cito-item-outcome-changed': { kind: 'notification', scope: 'item', deprecated: 'qti-outcome-changed' },\n 'cito-item-rubric-placement': {\n kind: 'notification',\n scope: 'item',\n deprecated: 'qti-rubric:discretionary-placement'\n }\n};\n\ndeclare global {\n interface HTMLElementEventMap extends PublicItemEventMap {}\n}\n\n/**\n * Dispatch `name` and its deprecated name with the same `detail` object, new name first. Returns\n * `false` when a listener of either cancelled the event. The detail is always an object.\n * Shared by the item and test tiers; use `dispatchPublic` (item) or the test's own wrapper.\n */\nexport function dispatchDeclared(\n target: EventTarget,\n declarations: Record<string, PublicEventDeclaration>,\n name: string,\n detail: unknown,\n cancelable = false\n): boolean {\n const init = { detail: detail ?? {}, bubbles: true, composed: true, cancelable };\n const notCancelled = target.dispatchEvent(new CustomEvent(name, init));\n const { deprecated } = declarations[name];\n if (!deprecated) return notCancelled;\n return target.dispatchEvent(new CustomEvent(deprecated, init)) && notCancelled;\n}\n\nconst warned = new Set<string>();\n\n/**\n * Listen for `name` and its deprecated name. `dispatchDeclared` sends the old name straight after\n * the new one, synchronously, with the same `detail`: that one is skipped, so the handler runs\n * once. A host that sends only the old name is handled too, and warned once per name in the\n * console: only a sender outside our own components gets here without the new name before it.\n */\nexport function listenDeclared(\n target: EventTarget,\n declarations: Record<string, PublicEventDeclaration>,\n name: string,\n handler: (e: Event) => void\n): void {\n let justHandled: unknown;\n target.addEventListener(name, e => {\n justHandled = (e as CustomEvent).detail;\n queueMicrotask(() => (justHandled = undefined));\n handler(e);\n });\n const { deprecated } = declarations[name];\n if (deprecated) {\n target.addEventListener(deprecated, e => {\n if (justHandled !== undefined && (e as CustomEvent).detail === justHandled) return;\n if (!warned.has(deprecated)) {\n warned.add(deprecated);\n console.warn(`[qti-components] The event \"${deprecated}\" is deprecated; dispatch \"${name}\" instead.`);\n }\n handler(e);\n });\n }\n}\n\nexport function dispatchPublic<K extends PublicItemEventName>(\n target: EventTarget,\n name: K,\n detail: PublicItemEventMap[K] extends CustomEvent<infer D> ? D : never,\n cancelable = false\n): boolean {\n return dispatchDeclared(target, PUBLIC_ITEM_EVENTS, name, detail, cancelable);\n}\n\nexport function listenPublic<K extends PublicItemEventName>(\n target: EventTarget,\n name: K,\n handler: (e: PublicItemEventMap[K]) => void\n): void {\n listenDeclared(target, PUBLIC_ITEM_EVENTS, name, handler as (e: Event) => void);\n}\n"]}
1
+ {"version":3,"file":"public-events.js","sourceRoot":"","sources":["../src/public-events.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n InteractionChangedDetails,\n OutcomeChangedDetails,\n OutcomesChangedDetails,\n SetOutcomesDetails\n} from './internal/event-types';\n\n/**\n * The public events of the item. The name tells you it is public: everything declared here starts\n * with `cito-`; every other event is internal plumbing and may change in any release.\n *\n * Requests are verbs, notifications are past tense. All bubble and are composed.\n *\n * `deprecated` is the name the event had before it became public. `dispatchPublic` sends and\n * `listenPublic` handles both until the next major, then the field goes.\n */\nexport interface PublicEventDeclaration {\n kind: 'request' | 'notification';\n scope: 'test' | 'item';\n /** The name before it was public. Dispatched and handled alongside the new one. */\n deprecated?: string;\n}\n\nexport interface RubricPlacement {\n className: string;\n element: Element;\n}\n\nexport interface PublicItemEventMap {\n // `cito-item-rendered` (detail: the `QtiAssessmentItem`) is added by qti-assessment-item.ts, so this\n // module imports no element: that import would be a cycle.\n 'cito-item-response-changed': CustomEvent<InteractionChangedDetails>;\n /**\n * Request: the item applies these outcomes in one update. Response processing sends it; a host\n * that scores on a backend sends it on the item with the outcomes the backend returned.\n */\n 'cito-item-set-outcomes': CustomEvent<SetOutcomesDetails>;\n /** One per outcome an update applied, with its final value, after the item context is published. */\n 'cito-item-outcome-changed': CustomEvent<OutcomeChangedDetails>;\n /** All the outcomes one update applied, after the item context is published. */\n 'cito-item-outcomes-changed': CustomEvent<OutcomesChangedDetails>;\n /** Cancelable: `preventDefault()` keeps the rubric block where it is. */\n 'cito-item-rubric-placement': CustomEvent<RubricPlacement>;\n}\n\nexport type PublicItemEventName = keyof PublicItemEventMap;\n\nexport const PUBLIC_ITEM_EVENTS: Record<PublicItemEventName, PublicEventDeclaration> = {\n // The old name `qti-assessment-item-connected` stays as internal plumbing: it is sent separately.\n 'cito-item-rendered': { kind: 'notification', scope: 'item' },\n 'cito-item-response-changed': { kind: 'notification', scope: 'item', deprecated: 'qti-interaction-changed' },\n 'cito-item-set-outcomes': { kind: 'request', scope: 'item' },\n 'cito-item-outcome-changed': { kind: 'notification', scope: 'item', deprecated: 'qti-outcome-changed' },\n 'cito-item-outcomes-changed': { kind: 'notification', scope: 'item' },\n 'cito-item-rubric-placement': {\n kind: 'notification',\n scope: 'item',\n deprecated: 'qti-rubric:discretionary-placement'\n }\n};\n\ndeclare global {\n interface HTMLElementEventMap extends PublicItemEventMap {}\n}\n\n/**\n * Dispatch `name` and its deprecated name with the same `detail` object, new name first. Returns\n * `false` when a listener of either cancelled the event. The detail is always an object.\n * Shared by the item and test tiers; use `dispatchPublic` (item) or the test's own wrapper.\n */\nexport function dispatchDeclared(\n target: EventTarget,\n declarations: Record<string, PublicEventDeclaration>,\n name: string,\n detail: unknown,\n cancelable = false\n): boolean {\n const init = { detail: detail ?? {}, bubbles: true, composed: true, cancelable };\n const notCancelled = target.dispatchEvent(new CustomEvent(name, init));\n const { deprecated } = declarations[name];\n if (!deprecated) return notCancelled;\n return target.dispatchEvent(new CustomEvent(deprecated, init)) && notCancelled;\n}\n\nconst warned = new Set<string>();\n\n/**\n * Listen for `name` and its deprecated name. `dispatchDeclared` sends the old name straight after\n * the new one, synchronously, with the same `detail`: that one is skipped, so the handler runs\n * once. A host that sends only the old name is handled too, and warned once per name in the\n * console: only a sender outside our own components gets here without the new name before it.\n */\nexport function listenDeclared(\n target: EventTarget,\n declarations: Record<string, PublicEventDeclaration>,\n name: string,\n handler: (e: Event) => void\n): void {\n let justHandled: unknown;\n target.addEventListener(name, e => {\n justHandled = (e as CustomEvent).detail;\n queueMicrotask(() => (justHandled = undefined));\n handler(e);\n });\n const { deprecated } = declarations[name];\n if (deprecated) {\n target.addEventListener(deprecated, e => {\n if (justHandled !== undefined && (e as CustomEvent).detail === justHandled) return;\n if (!warned.has(deprecated)) {\n warned.add(deprecated);\n console.warn(`[qti-components] The event \"${deprecated}\" is deprecated; dispatch \"${name}\" instead.`);\n }\n handler(e);\n });\n }\n}\n\nexport function dispatchPublic<K extends PublicItemEventName>(\n target: EventTarget,\n name: K,\n detail: PublicItemEventMap[K] extends CustomEvent<infer D> ? D : never,\n cancelable = false\n): boolean {\n return dispatchDeclared(target, PUBLIC_ITEM_EVENTS, name, detail, cancelable);\n}\n\nexport function listenPublic<K extends PublicItemEventName>(\n target: EventTarget,\n name: K,\n handler: (e: PublicItemEventMap[K]) => void\n): void {\n listenDeclared(target, PUBLIC_ITEM_EVENTS, name, handler as (e: Event) => void);\n}\n"]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@qti-components/elements",
3
3
  "description": "QTI elements utilities",
4
- "version": "1.10.0",
4
+ "version": "2.0.0-next.5",
5
5
  "author": "",
6
6
  "dependencies": {
7
- "@qti-components/base": "^2.4.1",
7
+ "@qti-components/base": "^3.0.0-next.4",
8
8
  "@qti-components/transformers": "^1.7.3",
9
9
  "@qti-components/utilities": "^1.3.3"
10
10
  },