@longsightgroup/qti3-core 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +41 -10
  2. package/dist/content-text.d.ts +7 -1
  3. package/dist/content-text.d.ts.map +1 -1
  4. package/dist/content-text.js +14 -0
  5. package/dist/content-text.js.map +1 -1
  6. package/dist/index.d.ts +7 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +6 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/package-xml.d.ts +12 -0
  11. package/dist/package-xml.d.ts.map +1 -0
  12. package/dist/package-xml.js +17 -0
  13. package/dist/package-xml.js.map +1 -0
  14. package/dist/parser-custom-interactions.d.ts +8 -0
  15. package/dist/parser-custom-interactions.d.ts.map +1 -0
  16. package/dist/parser-custom-interactions.js +178 -0
  17. package/dist/parser-custom-interactions.js.map +1 -0
  18. package/dist/parser-declarations.d.ts +6 -0
  19. package/dist/parser-declarations.d.ts.map +1 -0
  20. package/dist/parser-declarations.js +136 -0
  21. package/dist/parser-declarations.js.map +1 -0
  22. package/dist/parser-item-metadata.d.ts +9 -0
  23. package/dist/parser-item-metadata.d.ts.map +1 -0
  24. package/dist/parser-item-metadata.js +158 -0
  25. package/dist/parser-item-metadata.js.map +1 -0
  26. package/dist/parser-processing.d.ts +5 -0
  27. package/dist/parser-processing.d.ts.map +1 -0
  28. package/dist/parser-processing.js +642 -0
  29. package/dist/parser-processing.js.map +1 -0
  30. package/dist/parser-values.d.ts +8 -0
  31. package/dist/parser-values.d.ts.map +1 -0
  32. package/dist/parser-values.js +49 -0
  33. package/dist/parser-values.js.map +1 -0
  34. package/dist/parser.d.ts.map +1 -1
  35. package/dist/parser.js +15 -1083
  36. package/dist/parser.js.map +1 -1
  37. package/dist/processing-expression-tags.d.ts +56 -0
  38. package/dist/processing-expression-tags.d.ts.map +1 -0
  39. package/dist/processing-expression-tags.js +52 -0
  40. package/dist/processing-expression-tags.js.map +1 -0
  41. package/dist/processing-rules.d.ts +3 -0
  42. package/dist/processing-rules.d.ts.map +1 -0
  43. package/dist/processing-rules.js +10 -0
  44. package/dist/processing-rules.js.map +1 -0
  45. package/dist/serializer-processing-expressions.d.ts +8 -0
  46. package/dist/serializer-processing-expressions.d.ts.map +1 -0
  47. package/dist/serializer-processing-expressions.js +198 -0
  48. package/dist/serializer-processing-expressions.js.map +1 -0
  49. package/dist/serializer-processing-xml.d.ts +15 -0
  50. package/dist/serializer-processing-xml.d.ts.map +1 -0
  51. package/dist/serializer-processing-xml.js +46 -0
  52. package/dist/serializer-processing-xml.js.map +1 -0
  53. package/dist/serializer-processing.d.ts +8 -0
  54. package/dist/serializer-processing.d.ts.map +1 -0
  55. package/dist/serializer-processing.js +106 -0
  56. package/dist/serializer-processing.js.map +1 -0
  57. package/dist/shared-vocabulary-authoring.d.ts +52 -0
  58. package/dist/shared-vocabulary-authoring.d.ts.map +1 -0
  59. package/dist/shared-vocabulary-authoring.js +327 -0
  60. package/dist/shared-vocabulary-authoring.js.map +1 -0
  61. package/dist/shared-vocabulary-interaction-sets.d.ts +6 -0
  62. package/dist/shared-vocabulary-interaction-sets.d.ts.map +1 -0
  63. package/dist/shared-vocabulary-interaction-sets.js +23 -0
  64. package/dist/shared-vocabulary-interaction-sets.js.map +1 -0
  65. package/dist/shared-vocabulary-interaction-validation.d.ts +3 -0
  66. package/dist/shared-vocabulary-interaction-validation.d.ts.map +1 -0
  67. package/dist/shared-vocabulary-interaction-validation.js +99 -0
  68. package/dist/shared-vocabulary-interaction-validation.js.map +1 -0
  69. package/dist/shared-vocabulary-registry-validation.d.ts +5 -0
  70. package/dist/shared-vocabulary-registry-validation.d.ts.map +1 -0
  71. package/dist/shared-vocabulary-registry-validation.js +147 -0
  72. package/dist/shared-vocabulary-registry-validation.js.map +1 -0
  73. package/dist/shared-vocabulary-support.d.ts.map +1 -1
  74. package/dist/shared-vocabulary-support.js +160 -67
  75. package/dist/shared-vocabulary-support.js.map +1 -1
  76. package/dist/shared-vocabulary.d.ts +6 -0
  77. package/dist/shared-vocabulary.d.ts.map +1 -1
  78. package/dist/shared-vocabulary.js +12 -0
  79. package/dist/shared-vocabulary.js.map +1 -1
  80. package/dist/support.d.ts +4 -1
  81. package/dist/support.d.ts.map +1 -1
  82. package/dist/support.js +130 -0
  83. package/dist/support.js.map +1 -1
  84. package/dist/types.d.ts +32 -7
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/validation.d.ts.map +1 -1
  87. package/dist/validation.js +29 -250
  88. package/dist/validation.js.map +1 -1
  89. package/dist/xml.d.ts +10 -0
  90. package/dist/xml.d.ts.map +1 -1
  91. package/dist/xml.js +321 -272
  92. package/dist/xml.js.map +1 -1
  93. package/package.json +1 -4
  94. package/src/content-text.ts +20 -1
  95. package/src/index.ts +68 -0
  96. package/src/interaction-test-fixtures.ts +44 -0
  97. package/src/package-xml.ts +30 -0
  98. package/src/parser-custom-interactions.ts +309 -0
  99. package/src/parser-declarations.ts +174 -0
  100. package/src/parser-item-metadata.ts +194 -0
  101. package/src/parser-processing.ts +701 -0
  102. package/src/parser-values.ts +61 -0
  103. package/src/parser.ts +36 -1227
  104. package/src/processing-expression-tags.ts +65 -0
  105. package/src/processing-rules.ts +11 -0
  106. package/src/serializer-processing-expressions.ts +456 -0
  107. package/src/serializer-processing-xml.ts +67 -0
  108. package/src/serializer-processing.fixtures.ts +469 -0
  109. package/src/serializer-processing.ts +196 -0
  110. package/src/shared-vocabulary-authoring.ts +438 -0
  111. package/src/shared-vocabulary-interaction-sets.ts +28 -0
  112. package/src/shared-vocabulary-interaction-validation.ts +143 -0
  113. package/src/shared-vocabulary-registry-validation.ts +210 -0
  114. package/src/shared-vocabulary-support.ts +200 -127
  115. package/src/shared-vocabulary.ts +18 -0
  116. package/src/support.ts +144 -0
  117. package/src/types.ts +42 -7
  118. package/src/validation.ts +33 -341
  119. package/src/xml.ts +376 -276
@@ -0,0 +1,99 @@
1
+ import { validateChoicesContainerWidthSharedVocabulary, validateMatchInteractionSharedVocabulary, validateRegistrySharedVocabularyClasses, } from "./shared-vocabulary-registry-validation.js";
2
+ import { validateSharedVocabularyExtendedText, validateSharedVocabularyInputWidth, validateSharedVocabularyMediaPlayerControls, } from "./shared-vocabulary-validation.js";
3
+ const SHARED_VOCABULARY_INTERACTION_TYPES = new Set([
4
+ "choice",
5
+ "match",
6
+ "gapMatch",
7
+ "graphicGapMatch",
8
+ "inlineChoice",
9
+ "textEntry",
10
+ "extendedText",
11
+ "media",
12
+ "order",
13
+ ]);
14
+ export function validateInteractionSharedVocabulary(interaction, diagnostics) {
15
+ if (!SHARED_VOCABULARY_INTERACTION_TYPES.has(interaction.type))
16
+ return;
17
+ const classNames = sharedClassNames(interaction.attributes);
18
+ if (interaction.type === "media") {
19
+ validateMediaInteractionSharedVocabulary(interaction, diagnostics);
20
+ return;
21
+ }
22
+ if (interaction.type === "inlineChoice" || interaction.type === "textEntry") {
23
+ validateInteractionInputWidthSharedVocabulary(interaction, classNames, diagnostics);
24
+ return;
25
+ }
26
+ if (interaction.type === "extendedText") {
27
+ diagnostics.push(...validateSharedVocabularyExtendedText({
28
+ classNames,
29
+ subjectQtiName: interaction.qtiName,
30
+ expectedLength: interaction.attributes["expected-length"],
31
+ path: interaction.source?.path,
32
+ source: interaction.source,
33
+ }));
34
+ return;
35
+ }
36
+ if (interaction.type === "match" ||
37
+ interaction.type === "gapMatch" ||
38
+ interaction.type === "graphicGapMatch") {
39
+ if (interaction.type === "match") {
40
+ validateMatchInteractionSharedVocabulary(interaction, classNames, diagnostics);
41
+ }
42
+ validateRegistrySharedVocabularyClasses(interaction, classNames, diagnostics);
43
+ validateChoicesContainerWidthSharedVocabulary(interaction, diagnostics);
44
+ if (interaction.type === "gapMatch") {
45
+ validateGapInputWidthSharedVocabulary(interaction, diagnostics);
46
+ }
47
+ return;
48
+ }
49
+ if (interaction.type === "order") {
50
+ validateRegistrySharedVocabularyClasses(interaction, classNames, diagnostics);
51
+ validateChoicesContainerWidthSharedVocabulary(interaction, diagnostics);
52
+ return;
53
+ }
54
+ if (interaction.type === "choice") {
55
+ validateRegistrySharedVocabularyClasses(interaction, classNames, diagnostics);
56
+ }
57
+ }
58
+ function validateGapInputWidthSharedVocabulary(interaction, diagnostics) {
59
+ for (const gap of interaction.choices.filter((choice) => choice.qtiName === "qti-gap")) {
60
+ diagnostics.push(...validateSharedVocabularyInputWidth({
61
+ classNames: sharedClassNames(gap.attributes),
62
+ subjectQtiName: "qti-gap",
63
+ path: gap.source?.path ?? interaction.source?.path,
64
+ source: gap.source ?? interaction.source,
65
+ conflictCode: "interaction.sharedVocabulary.gapInputWidthConflict",
66
+ invalidCode: "interaction.sharedVocabulary.gapInputWidthInvalid",
67
+ }));
68
+ }
69
+ }
70
+ function validateInteractionInputWidthSharedVocabulary(interaction, classNames, diagnostics) {
71
+ diagnostics.push(...validateSharedVocabularyInputWidth({
72
+ classNames,
73
+ subjectQtiName: interaction.qtiName,
74
+ path: interaction.source?.path,
75
+ source: interaction.source,
76
+ conflictCode: "interaction.sharedVocabulary.inputWidthConflict",
77
+ invalidCode: "interaction.sharedVocabulary.inputWidthInvalid",
78
+ }));
79
+ }
80
+ function validateMediaInteractionSharedVocabulary(interaction, diagnostics) {
81
+ diagnostics.push(...validateSharedVocabularyMediaPlayerControls({
82
+ value: interaction.attributes["data-qti-media-player-controls"],
83
+ subjectQtiName: interaction.qtiName,
84
+ path: interaction.source?.path,
85
+ source: interaction.source,
86
+ }));
87
+ if (!interaction.object)
88
+ return;
89
+ diagnostics.push(...validateSharedVocabularyMediaPlayerControls({
90
+ value: interaction.object.attributes["data-qti-media-player-controls"],
91
+ subjectQtiName: `${interaction.qtiName} media object`,
92
+ path: interaction.object.source?.path ?? interaction.source?.path,
93
+ source: interaction.object.source ?? interaction.source,
94
+ }));
95
+ }
96
+ function sharedClassNames(attributes) {
97
+ return (attributes.class ?? "").split(/\s+/).filter(Boolean);
98
+ }
99
+ //# sourceMappingURL=shared-vocabulary-interaction-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-vocabulary-interaction-validation.js","sourceRoot":"","sources":["../src/shared-vocabulary-interaction-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { QtiDiagnostic, QtiInteraction } from "./types.js";
2
+ export declare function validateRegistrySharedVocabularyClasses(interaction: QtiInteraction, classNames: string[], diagnostics: QtiDiagnostic[]): void;
3
+ export declare function validateChoicesContainerWidthSharedVocabulary(interaction: QtiInteraction, diagnostics: QtiDiagnostic[]): void;
4
+ export declare function validateMatchInteractionSharedVocabulary(interaction: QtiInteraction, classNames: string[], diagnostics: QtiDiagnostic[]): void;
5
+ //# sourceMappingURL=shared-vocabulary-registry-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-vocabulary-registry-validation.d.ts","sourceRoot":"","sources":["../src/shared-vocabulary-registry-validation.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAchE,wBAAgB,uCAAuC,CACrD,WAAW,EAAE,cAAc,EAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,aAAa,EAAE,GAC3B,IAAI,CAMN;AA4FD,wBAAgB,6CAA6C,CAC3D,WAAW,EAAE,cAAc,EAC3B,WAAW,EAAE,aAAa,EAAE,GAC3B,IAAI,CAeN;AAED,wBAAgB,wCAAwC,CACtD,WAAW,EAAE,cAAc,EAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,aAAa,EAAE,GAC3B,IAAI,CA0DN"}
@@ -0,0 +1,147 @@
1
+ import { describeSharedVocabularyPrecedence, formatSharedVocabularyClassValueRange, matchedSharedVocabularyClassNames, parseClassValue, parseSharedVocabularyAttributes, sharedVocabularyFieldById, sharedVocabularyFieldsForInteraction, sharedVocabularyFixedClassName, } from "./shared-vocabulary-authoring.js";
2
+ const LEGACY_CLASS_VALUE_CONFLICT_CODES = {
3
+ "labels-style": "interaction.sharedVocabulary.labelsConflict",
4
+ "labels-suffix": "interaction.sharedVocabulary.labelSuffixConflict",
5
+ orientation: "interaction.sharedVocabulary.orientationConflict",
6
+ "choices-stacking": "interaction.sharedVocabulary.stackingConflict",
7
+ "choices-position": "interaction.sharedVocabulary.orderChoicesPositionConflict",
8
+ };
9
+ const LEGACY_CLASS_VALUE_INVALID_CODES = {
10
+ "choices-stacking": "interaction.sharedVocabulary.stackingInvalid",
11
+ };
12
+ export function validateRegistrySharedVocabularyClasses(interaction, classNames, diagnostics) {
13
+ for (const field of sharedVocabularyFieldsForInteraction(interaction.type)) {
14
+ if (field.kind !== "class-value")
15
+ continue;
16
+ validateClassValueFieldConflicts(interaction, field, classNames, diagnostics);
17
+ validateUnsupportedClassValueClasses(interaction, field, classNames, diagnostics);
18
+ }
19
+ }
20
+ function validateClassValueFieldConflicts(interaction, field, classNames, diagnostics) {
21
+ const matched = matchedSharedVocabularyClassNames(field, classNames);
22
+ if (new Set(matched).size <= 1)
23
+ return;
24
+ diagnostics.push({
25
+ code: classValueConflictCode(field.id),
26
+ severity: "warning",
27
+ message: conflictMessage(interaction, field, matched),
28
+ path: interaction.source?.path,
29
+ source: interaction.source,
30
+ });
31
+ }
32
+ function classValueConflictCode(fieldId) {
33
+ return (LEGACY_CLASS_VALUE_CONFLICT_CODES[fieldId] ??
34
+ `interaction.sharedVocabulary.${fieldIdToDiagnosticSuffix(fieldId)}Conflict`);
35
+ }
36
+ function classValueInvalidCode(fieldId) {
37
+ return (LEGACY_CLASS_VALUE_INVALID_CODES[fieldId] ??
38
+ `interaction.sharedVocabulary.${fieldIdToDiagnosticSuffix(fieldId)}Invalid`);
39
+ }
40
+ function fieldIdToDiagnosticSuffix(fieldId) {
41
+ return fieldId.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
42
+ }
43
+ function conflictMessage(interaction, field, matchedClassNames) {
44
+ const uniqueMatched = [...new Set(matchedClassNames)];
45
+ const precedence = describeSharedVocabularyPrecedence(field, uniqueMatched);
46
+ switch (field.id) {
47
+ case "labels-style":
48
+ return `${interaction.qtiName} should not include multiple qti-labels-* style classes: ${uniqueMatched.join(", ")}. ${precedence} at runtime.`;
49
+ case "labels-suffix":
50
+ return `${interaction.qtiName} should not include multiple qti-labels-suffix-* classes: ${uniqueMatched.join(", ")}. ${precedence} at runtime.`;
51
+ case "orientation":
52
+ return `${interaction.qtiName} should not include both qti-orientation-horizontal and qti-orientation-vertical; ${precedence} at runtime.`;
53
+ case "choices-stacking":
54
+ return `qti-choice-interaction should not include multiple qti-choices-stacking-* classes: ${uniqueMatched.join(", ")}. The first valid stacking class in class attribute order takes precedence at runtime.`;
55
+ case "choices-position":
56
+ return `${interaction.qtiName} should not include multiple qti-choices-* position classes: ${uniqueMatched.join(", ")}. The first position class in class attribute order takes precedence at runtime.`;
57
+ default:
58
+ return `${interaction.qtiName} should not include multiple ${field.classPrefix}* classes: ${uniqueMatched.join(", ")}. ${precedence} at runtime.`;
59
+ }
60
+ }
61
+ function validateUnsupportedClassValueClasses(interaction, field, classNames, diagnostics) {
62
+ for (const className of classNames) {
63
+ if (!className.startsWith(field.classPrefix))
64
+ continue;
65
+ if (parseClassValue(field, className) !== undefined)
66
+ continue;
67
+ diagnostics.push({
68
+ code: classValueInvalidCode(field.id),
69
+ severity: "warning",
70
+ message: unsupportedClassValueMessage(interaction, field, className),
71
+ path: interaction.source?.path,
72
+ source: interaction.source,
73
+ });
74
+ }
75
+ }
76
+ function unsupportedClassValueMessage(interaction, field, className) {
77
+ if (field.id === "choices-stacking") {
78
+ return `qti-choice-interaction shared vocabulary class ${className} is not supported; expected ${formatSharedVocabularyClassValueRange(field)}.`;
79
+ }
80
+ return `${interaction.qtiName} shared vocabulary class ${className} is not supported; expected ${formatSharedVocabularyClassValueRange(field)}.`;
81
+ }
82
+ export function validateChoicesContainerWidthSharedVocabulary(interaction, diagnostics) {
83
+ const width = interaction.attributes["data-choices-container-width"];
84
+ if (width === undefined)
85
+ return;
86
+ const parsed = parseSharedVocabularyAttributes({ "data-choices-container-width": width }, interaction.type)["choices-container-width"];
87
+ if (parsed !== undefined)
88
+ return;
89
+ diagnostics.push({
90
+ code: "interaction.sharedVocabulary.orderChoicesContainerWidth",
91
+ severity: "warning",
92
+ message: `${interaction.qtiName} data-choices-container-width must be a positive pixel value; the invalid value is ignored at runtime.`,
93
+ path: interaction.source?.path,
94
+ source: interaction.source,
95
+ });
96
+ }
97
+ export function validateMatchInteractionSharedVocabulary(interaction, classNames, diagnostics) {
98
+ const matchTabularClass = sharedVocabularyFixedClassName("match-tabular");
99
+ const headerHiddenClass = sharedVocabularyFixedClassName("header-hidden");
100
+ const hasTabular = matchTabularClass === undefined ? false : classNames.includes(matchTabularClass);
101
+ const hasHeaderHidden = headerHiddenClass === undefined ? false : classNames.includes(headerHiddenClass);
102
+ const firstColumnHeader = interaction.attributes["data-first-column-header"];
103
+ if (!hasTabular && (hasHeaderHidden || firstColumnHeader !== undefined)) {
104
+ diagnostics.push({
105
+ code: "interaction.sharedVocabulary.matchTabularContext",
106
+ severity: "warning",
107
+ message: "qti-match-interaction shared vocabulary class qti-header-hidden and data-first-column-header are only relevant when qti-match-tabular is specified; they are ignored at runtime.",
108
+ path: interaction.source?.path,
109
+ source: interaction.source,
110
+ });
111
+ }
112
+ if (hasTabular && hasHeaderHidden && firstColumnHeader !== undefined) {
113
+ diagnostics.push({
114
+ code: "interaction.sharedVocabulary.matchTabularHeaderHidden",
115
+ severity: "warning",
116
+ message: "qti-match-interaction data-first-column-header is ignored when qti-header-hidden suppresses the tabular header row.",
117
+ path: interaction.source?.path,
118
+ source: interaction.source,
119
+ });
120
+ }
121
+ if (!hasTabular)
122
+ return;
123
+ const choicesPositionField = sharedVocabularyFieldById("choices-position");
124
+ const choicesPositionClasses = choicesPositionField?.kind === "class-value"
125
+ ? matchedSharedVocabularyClassNames(choicesPositionField, classNames)
126
+ : [];
127
+ if (choicesPositionClasses.length > 0 ||
128
+ interaction.attributes["data-choices-container-width"] !== undefined) {
129
+ diagnostics.push({
130
+ code: "interaction.sharedVocabulary.matchTabularChoicesConflict",
131
+ severity: "warning",
132
+ message: "qti-match-interaction qti-match-tabular uses a matrix layout; qti-choices-* position classes and data-choices-container-width are ignored at runtime.",
133
+ path: interaction.source?.path,
134
+ source: interaction.source,
135
+ });
136
+ }
137
+ if (!hasHeaderHidden && (firstColumnHeader === undefined || firstColumnHeader === "")) {
138
+ diagnostics.push({
139
+ code: "interaction.sharedVocabulary.matchTabularFirstColumnHeader",
140
+ severity: "warning",
141
+ message: "qti-match-interaction with qti-match-tabular should specify data-first-column-header for the top-left table header when the tabular header row is shown.",
142
+ path: interaction.source?.path,
143
+ source: interaction.source,
144
+ });
145
+ }
146
+ }
147
+ //# sourceMappingURL=shared-vocabulary-registry-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-vocabulary-registry-validation.js","sourceRoot":"","sources":["../src/shared-vocabulary-registry-validation.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared-vocabulary-support.d.ts","sourceRoot":"","sources":["../src/shared-vocabulary-support.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAsB,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAoHnF,eAAO,MAAM,4BAA4B,EAAE,4BAA4B,EAkNtE,CAAC"}
1
+ {"version":3,"file":"shared-vocabulary-support.d.ts","sourceRoot":"","sources":["../src/shared-vocabulary-support.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAsB,4BAA4B,EAAE,MAAM,YAAY,CAAC;AA0QnF,eAAO,MAAM,4BAA4B,EAAE,4BAA4B,EAgItE,CAAC"}
@@ -1,5 +1,7 @@
1
- import { SHARED_VOCABULARY_CHOICE_WRITING_ORIENTATIONS, SHARED_VOCABULARY_CONTENT_ALIGNMENTS, SHARED_VOCABULARY_CONTENT_FLOAT_SUFFIXES, SHARED_VOCABULARY_CONTENT_LIST_STYLE_TYPES, SHARED_VOCABULARY_CONTENT_TEXT_INDENT_SUFFIXES, SHARED_VOCABULARY_CONTENT_VALIGNS, SHARED_VOCABULARY_CONTENT_WRITING_MODES, SHARED_VOCABULARY_LAYOUT_COLUMN_SPAN_COUNT, SHARED_VOCABULARY_LAYOUT_OFFSET_COUNT, } from "./shared-vocabulary-generated-families.js";
1
+ import { SHARED_VOCABULARY_CONTENT_ALIGNMENTS, SHARED_VOCABULARY_CONTENT_FLOAT_SUFFIXES, SHARED_VOCABULARY_CONTENT_LIST_STYLE_TYPES, SHARED_VOCABULARY_CONTENT_TEXT_INDENT_SUFFIXES, SHARED_VOCABULARY_CONTENT_VALIGNS, SHARED_VOCABULARY_CONTENT_WRITING_MODES, SHARED_VOCABULARY_LAYOUT_COLUMN_SPAN_COUNT, SHARED_VOCABULARY_LAYOUT_OFFSET_COUNT, } from "./shared-vocabulary-generated-families.js";
2
+ import { sharedVocabularyFixedClassName, sharedVocabularyInteractionFields, } from "./shared-vocabulary-authoring.js";
2
3
  import { SHARED_VOCABULARY_EXTENDED_TEXT_COUNTER_CLASSES, SHARED_VOCABULARY_EXTENDED_TEXT_HEIGHT_LINES, SHARED_VOCABULARY_INPUT_WIDTHS, } from "./shared-vocabulary.js";
4
+ import { SHARED_VOCABULARY_CHOICE_AND_ORDER_INTERACTIONS } from "./shared-vocabulary-interaction-sets.js";
3
5
  const sharedVocabularyFixture = [
4
6
  "packages/fixtures/packages/basic-item-player/valid-item-only/items/shared-vocabulary.xml",
5
7
  ];
@@ -18,7 +20,10 @@ const sharedVocabularyUnitTests = [
18
20
  const sharedVocabularyValidationTests = [
19
21
  "packages/core/src/core.test.ts",
20
22
  "packages/core/src/shared-vocabulary-validation.test.ts",
23
+ "packages/core/src/shared-vocabulary-registry-validation.test.ts",
24
+ "packages/core/src/shared-vocabulary-interaction-validation.test.ts",
21
25
  ];
26
+ const choiceLayoutTests = ["packages/player/src/interactions/choice-layout.test.ts"];
22
27
  const browserBehaviorTests = [
23
28
  "tests/browser/player-dom-behavior.spec.ts",
24
29
  ...sharedVocabularyMatrixTests,
@@ -34,19 +39,154 @@ const interactionInputWidthFixtures = [
34
39
  "packages/fixtures/packages/sv-matrix/items/interaction-input-width-embedded.xml",
35
40
  ];
36
41
  const orderMinMaxMessagesFixture = "packages/fixtures/packages/sv-matrix/items/order-min-max-messages.xml";
37
- const choiceAndOrder = ["choice", "order"];
38
- const choicesLayoutInteractions = [
39
- "match",
40
- "gapMatch",
41
- "graphicGapMatch",
42
- "order",
43
- ];
44
- const selectionPresentationInteractions = [
45
- "choice",
46
- "hottext",
47
- "hotspot",
48
- "graphicGapMatch",
42
+ const choiceAndOrder = SHARED_VOCABULARY_CHOICE_AND_ORDER_INTERACTIONS;
43
+ function registryClassValueSupportProfile(fieldId) {
44
+ switch (fieldId) {
45
+ case "labels-style":
46
+ case "labels-suffix":
47
+ return { level: "full", tests: [...sharedVocabularyUnitTests, ...browserBehaviorTests] };
48
+ case "orientation":
49
+ return {
50
+ level: "full",
51
+ tests: [...choiceLayoutTests, ...sharedVocabularyUnitTests, ...browserBehaviorTests],
52
+ };
53
+ case "choices-stacking":
54
+ return {
55
+ level: "full",
56
+ tests: [...choiceLayoutTests, "packages/core/src/core.test.ts", ...browserBehaviorTests],
57
+ };
58
+ case "choices-position":
59
+ return {
60
+ level: "full",
61
+ tests: [...sharedVocabularyUnitTests, ...browserBehaviorTests, ...graphicBrowserTests],
62
+ };
63
+ case "selections-tone":
64
+ return { level: "stylesheet", tests: [...browserBehaviorTests, ...graphicBrowserTests] };
65
+ case "writing-orientation":
66
+ return { level: "stylesheet", tests: [...browserBehaviorTests] };
67
+ default:
68
+ return { level: "full", tests: [...sharedVocabularyUnitTests, ...browserBehaviorTests] };
69
+ }
70
+ }
71
+ function registryClassValueSupportEntries() {
72
+ return sharedVocabularyInteractionFields.flatMap((field) => {
73
+ if (field.kind !== "class-value")
74
+ return [];
75
+ const profile = registryClassValueSupportProfile(field.id);
76
+ const createEntry = profile.level === "stylesheet" ? interactionStylesheetEntry : interactionFullEntry;
77
+ return field.values.map((value) => createEntry(`${field.classPrefix}${String(value)}`, field.interactions, profile.tests));
78
+ });
79
+ }
80
+ function registryClassFixedSupportProfile(fieldId) {
81
+ switch (fieldId) {
82
+ case "input-control-hidden":
83
+ return {
84
+ level: "stylesheet",
85
+ tests: [...browserBehaviorTests, "tests/browser/player-keyboard-a11y.spec.ts"],
86
+ };
87
+ case "unselected-hidden":
88
+ return { level: "stylesheet", tests: [...browserBehaviorTests, ...graphicBrowserTests] };
89
+ case "match-tabular":
90
+ return {
91
+ level: "full",
92
+ tests: [
93
+ "packages/core/src/core.test.ts",
94
+ ...browserBehaviorTests,
95
+ "tests/browser/player-keyboard-a11y.spec.ts",
96
+ ],
97
+ };
98
+ case "gap-placement":
99
+ return {
100
+ level: "full",
101
+ tests: [
102
+ ...sharedVocabularyUnitTests,
103
+ "packages/core/src/core.test.ts",
104
+ ...browserBehaviorTests,
105
+ ],
106
+ };
107
+ default:
108
+ return { level: "full", tests: [...browserBehaviorTests] };
109
+ }
110
+ }
111
+ const registryClassFixedSupportIds = [
112
+ "input-control-hidden",
113
+ "unselected-hidden",
114
+ "match-tabular",
115
+ "gap-placement",
49
116
  ];
117
+ function registryClassFixedSupportEntries() {
118
+ return registryClassFixedSupportIds.flatMap((fieldId) => {
119
+ const field = sharedVocabularyInteractionFields.find((candidate) => candidate.kind === "class-fixed" && candidate.id === fieldId);
120
+ if (field === undefined)
121
+ return [];
122
+ const profile = registryClassFixedSupportProfile(field.id);
123
+ const createEntry = profile.level === "stylesheet" ? interactionStylesheetEntry : interactionFullEntry;
124
+ return [createEntry(field.className, field.interactions, profile.tests)];
125
+ });
126
+ }
127
+ function registryAttributeSupportProfile(fieldId) {
128
+ switch (fieldId) {
129
+ case "choices-container-width":
130
+ return {
131
+ fixtures: sharedVocabularyFixture,
132
+ tests: [
133
+ "packages/core/src/core.test.ts",
134
+ "packages/player/src/interactions/shared-vocabulary.test.ts",
135
+ ...browserBehaviorTests,
136
+ ...graphicBrowserTests,
137
+ ],
138
+ notes: "Sets the authored choices-bank width for interactions that support qti-choices-* layout classes.",
139
+ };
140
+ case "first-column-header":
141
+ return {
142
+ fixtures: sharedVocabularyFixture,
143
+ tests: [
144
+ "packages/core/src/core.test.ts",
145
+ ...browserBehaviorTests,
146
+ "tests/browser/player-keyboard-a11y.spec.ts",
147
+ ],
148
+ notes: "Provides the top-left header text for qti-match-tabular table rendering.",
149
+ };
150
+ case "media-player-controls":
151
+ return {
152
+ fixtures: [mediaPlayerFixture],
153
+ tests: [...mediaBrowserTests, "packages/core/src/shared-vocabulary-validation.test.ts"],
154
+ notes: "Supports tokens none, default, play, rewind, captions, and audioDescription on media interactions and rendered media assets.",
155
+ };
156
+ case "media-player-pause-delay":
157
+ return {
158
+ fixtures: [mediaPlayerFixture],
159
+ tests: mediaBrowserTests,
160
+ notes: "Reflects authored pause-delay values on rendered media assets. Pause timer behavior is covered in tests/browser/player.spec.ts.",
161
+ };
162
+ case "media-player-pause-duration":
163
+ return {
164
+ fixtures: [mediaPlayerFixture],
165
+ tests: mediaBrowserTests,
166
+ notes: "Reflects authored pause-duration values on rendered media assets. Pause timer behavior is covered in tests/browser/player.spec.ts.",
167
+ };
168
+ default:
169
+ return {
170
+ fixtures: sharedVocabularyFixture,
171
+ tests: [...sharedVocabularyUnitTests, ...browserBehaviorTests],
172
+ };
173
+ }
174
+ }
175
+ function registryAttributeSupportEntries() {
176
+ return sharedVocabularyInteractionFields.flatMap((field) => {
177
+ if (field.kind !== "attribute")
178
+ return [];
179
+ const profile = registryAttributeSupportProfile(field.id);
180
+ return [
181
+ svEntry(field.attributeName, "interaction", "full", {
182
+ interactions: [...field.interactions],
183
+ fixtures: profile.fixtures,
184
+ tests: profile.tests,
185
+ ...(profile.notes === undefined ? {} : { notes: profile.notes }),
186
+ }),
187
+ ];
188
+ });
189
+ }
50
190
  function svEntry(className, scope, level, options = {}) {
51
191
  return {
52
192
  className,
@@ -64,7 +204,7 @@ function contentStylesheetEntry(className, notes) {
64
204
  }
65
205
  function interactionFullEntry(className, interactions, tests, notes) {
66
206
  return svEntry(className, "interaction", "full", {
67
- interactions,
207
+ interactions: [...interactions],
68
208
  fixtures: sharedVocabularyFixture,
69
209
  tests,
70
210
  notes,
@@ -72,20 +212,12 @@ function interactionFullEntry(className, interactions, tests, notes) {
72
212
  }
73
213
  function interactionStylesheetEntry(className, interactions, tests, notes) {
74
214
  return svEntry(className, "interaction", "stylesheet", {
75
- interactions,
215
+ interactions: [...interactions],
76
216
  fixtures: sharedVocabularyFixture,
77
217
  tests,
78
218
  notes,
79
219
  });
80
220
  }
81
- function mediaPlayerSvEntry(className, notes, tests = mediaBrowserTests) {
82
- return svEntry(className, "interaction", "full", {
83
- interactions: ["media"],
84
- fixtures: [mediaPlayerFixture],
85
- tests,
86
- notes,
87
- });
88
- }
89
221
  export const sharedVocabularyClassSupport = [
90
222
  contentStylesheetEntry("qti-layout-row", "Twelve-column content layout row; validated for supported child column spans and offsets."),
91
223
  ...Array.from({ length: SHARED_VOCABULARY_LAYOUT_COLUMN_SPAN_COUNT }, (_, index) => index + 1).flatMap((span) => [
@@ -118,39 +250,9 @@ export const sharedVocabularyClassSupport = [
118
250
  contentStylesheetEntry("qti-underline"),
119
251
  contentStylesheetEntry("qti-italic"),
120
252
  contentStylesheetEntry("qti-display-inline-block"),
121
- ...["none", "decimal", "cjk-ideographic", "lower-alpha", "upper-alpha"].map((labelStyle) => interactionFullEntry(`qti-labels-${labelStyle}`, choiceAndOrder, [
122
- ...sharedVocabularyUnitTests,
123
- ...browserBehaviorTests,
124
- ])),
125
- ...["none", "period", "parenthesis"].map((suffix) => interactionFullEntry(`qti-labels-suffix-${suffix}`, choiceAndOrder, [
126
- ...sharedVocabularyUnitTests,
127
- ...browserBehaviorTests,
128
- ])),
129
- ...["horizontal", "vertical"].map((orientation) => interactionFullEntry(`qti-orientation-${orientation}`, ["choice", "order"], [
130
- "packages/player/src/interactions/choice-layout.test.ts",
131
- ...sharedVocabularyUnitTests,
132
- ...browserBehaviorTests,
133
- ])),
134
- ...Array.from({ length: 5 }, (_, index) => index + 1).map((stacking) => interactionFullEntry(`qti-choices-stacking-${stacking}`, ["choice"], [
135
- "packages/player/src/interactions/choice-layout.test.ts",
136
- "packages/core/src/core.test.ts",
137
- ...browserBehaviorTests,
138
- ])),
139
- interactionStylesheetEntry("qti-input-control-hidden", ["choice", "hottext"], [...browserBehaviorTests, "tests/browser/player-keyboard-a11y.spec.ts"]),
140
- ...SHARED_VOCABULARY_CHOICE_WRITING_ORIENTATIONS.map((orientation) => interactionStylesheetEntry(`qti-writing-orientation-${orientation}`, ["choice", "inlineChoice"], [...browserBehaviorTests])),
141
- ...["light", "dark"].map((tone) => interactionStylesheetEntry(`qti-selections-${tone}`, selectionPresentationInteractions, [
142
- ...browserBehaviorTests,
143
- ...graphicBrowserTests,
144
- ])),
145
- interactionStylesheetEntry("qti-unselected-hidden", selectionPresentationInteractions, [
146
- ...browserBehaviorTests,
147
- ...graphicBrowserTests,
148
- ]),
149
- ...["top", "bottom", "left", "right"].map((position) => interactionFullEntry(`qti-choices-${position}`, choicesLayoutInteractions, [
150
- ...sharedVocabularyUnitTests,
151
- ...browserBehaviorTests,
152
- ...graphicBrowserTests,
153
- ])),
253
+ ...registryClassValueSupportEntries(),
254
+ ...registryClassFixedSupportEntries(),
255
+ ...registryAttributeSupportEntries(),
154
256
  svEntry("data-min-selections-message", "interaction", "full", {
155
257
  interactions: ["order"],
156
258
  fixtures: [orderMinMaxMessagesFixture],
@@ -158,23 +260,17 @@ export const sharedVocabularyClassSupport = [
158
260
  notes: "Overrides minimum response validation text for order interactions.",
159
261
  }),
160
262
  svEntry("data-max-selections-message", "interaction", "full", {
161
- interactions: ["choice", "order"],
263
+ interactions: [...choiceAndOrder],
162
264
  fixtures: [orderMinMaxMessagesFixture],
163
265
  tests: ["tests/browser/player-validation.spec.ts", ...sharedVocabularyMatrixTests],
164
266
  notes: "Overrides maximum response validation text for choice and order interactions.",
165
267
  }),
166
- interactionFullEntry("qti-match-tabular", ["match"], [
167
- "packages/core/src/core.test.ts",
168
- ...browserBehaviorTests,
169
- "tests/browser/player-keyboard-a11y.spec.ts",
170
- ]),
171
- svEntry("qti-header-hidden", "interaction", "conditional", {
268
+ svEntry(sharedVocabularyFixedClassName("header-hidden") ?? "qti-header-hidden", "interaction", "conditional", {
172
269
  interactions: ["match"],
173
270
  fixtures: sharedVocabularyFixture,
174
271
  tests: ["packages/core/src/core.test.ts", ...browserBehaviorTests],
175
272
  notes: "Applied when qti-match-tabular selects the tabular match renderer.",
176
273
  }),
177
- interactionFullEntry("qti-gap-placement", ["gapMatch"], [...sharedVocabularyUnitTests, "packages/core/src/core.test.ts", ...browserBehaviorTests]),
178
274
  ...SHARED_VOCABULARY_INPUT_WIDTHS.map((width) => svEntry(`qti-input-width-${width}`, "interaction", "full", {
179
275
  interactions: ["textEntry", "inlineChoice"],
180
276
  fixtures: interactionInputWidthFixtures,
@@ -204,8 +300,5 @@ export const sharedVocabularyClassSupport = [
204
300
  "packages/core/src/shared-vocabulary.test.ts",
205
301
  ...browserBehaviorTests,
206
302
  ])),
207
- mediaPlayerSvEntry("data-qti-media-player-controls", "Supports tokens none, default, play, rewind, captions, and audioDescription on media interactions and rendered media assets.", [...mediaBrowserTests, "packages/core/src/shared-vocabulary-validation.test.ts"]),
208
- mediaPlayerSvEntry("data-qti-media-player-pause-delay", "Reflects authored pause-delay values on rendered media assets. Pause timer behavior is covered in tests/browser/player.spec.ts."),
209
- mediaPlayerSvEntry("data-qti-media-player-pause-duration", "Reflects authored pause-duration values on rendered media assets. Pause timer behavior is covered in tests/browser/player.spec.ts."),
210
303
  ];
211
304
  //# sourceMappingURL=shared-vocabulary-support.js.map