@longsightgroup/qti3-player 0.4.0 → 0.5.4
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.
- package/README.md +146 -13
- package/dist/content/content-dom.d.ts +11 -0
- package/dist/content/content-dom.d.ts.map +1 -0
- package/dist/content/content-dom.js +262 -0
- package/dist/content/content-dom.js.map +1 -0
- package/dist/content/content-renderer.d.ts +17 -0
- package/dist/content/content-renderer.d.ts.map +1 -0
- package/dist/content/content-renderer.js +82 -0
- package/dist/content/content-renderer.js.map +1 -0
- package/dist/controls/remove-button.d.ts +3 -0
- package/dist/controls/remove-button.d.ts.map +1 -0
- package/dist/controls/remove-button.js +12 -0
- package/dist/controls/remove-button.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/interactions/choice-interaction.d.ts +4 -0
- package/dist/interactions/choice-interaction.d.ts.map +1 -0
- package/dist/interactions/choice-interaction.js +81 -0
- package/dist/interactions/choice-interaction.js.map +1 -0
- package/dist/interactions/drawing-interaction.d.ts +6 -0
- package/dist/interactions/drawing-interaction.d.ts.map +1 -0
- package/dist/interactions/drawing-interaction.js +401 -0
- package/dist/interactions/drawing-interaction.js.map +1 -0
- package/dist/interactions/end-attempt-interaction.d.ts +4 -0
- package/dist/interactions/end-attempt-interaction.d.ts.map +1 -0
- package/dist/interactions/end-attempt-interaction.js +13 -0
- package/dist/interactions/end-attempt-interaction.js.map +1 -0
- package/dist/interactions/gap-match-interaction.d.ts +4 -0
- package/dist/interactions/gap-match-interaction.d.ts.map +1 -0
- package/dist/interactions/gap-match-interaction.js +277 -0
- package/dist/interactions/gap-match-interaction.js.map +1 -0
- package/dist/interactions/graphic-associate-interaction.d.ts +4 -0
- package/dist/interactions/graphic-associate-interaction.d.ts.map +1 -0
- package/dist/interactions/graphic-associate-interaction.js +297 -0
- package/dist/interactions/graphic-associate-interaction.js.map +1 -0
- package/dist/interactions/graphic-context.d.ts +3 -0
- package/dist/interactions/graphic-context.d.ts.map +1 -0
- package/dist/interactions/graphic-context.js +35 -0
- package/dist/interactions/graphic-context.js.map +1 -0
- package/dist/interactions/hotspot-interaction.d.ts +4 -0
- package/dist/interactions/hotspot-interaction.d.ts.map +1 -0
- package/dist/interactions/hotspot-interaction.js +68 -0
- package/dist/interactions/hotspot-interaction.js.map +1 -0
- package/dist/interactions/hottext-interaction.d.ts +3 -0
- package/dist/interactions/hottext-interaction.d.ts.map +1 -0
- package/dist/interactions/hottext-interaction.js +66 -0
- package/dist/interactions/hottext-interaction.js.map +1 -0
- package/dist/interactions/inline-choice-interaction.d.ts +4 -0
- package/dist/interactions/inline-choice-interaction.d.ts.map +1 -0
- package/dist/interactions/inline-choice-interaction.js +31 -0
- package/dist/interactions/inline-choice-interaction.js.map +1 -0
- package/dist/interactions/inline-controls.d.ts +6 -0
- package/dist/interactions/inline-controls.d.ts.map +1 -0
- package/dist/interactions/inline-controls.js +15 -0
- package/dist/interactions/inline-controls.js.map +1 -0
- package/dist/interactions/interaction-diagnostics.d.ts +7 -0
- package/dist/interactions/interaction-diagnostics.d.ts.map +1 -0
- package/dist/interactions/interaction-diagnostics.js +137 -0
- package/dist/interactions/interaction-diagnostics.js.map +1 -0
- package/dist/interactions/interaction-dispatch.d.ts +2 -0
- package/dist/interactions/interaction-dispatch.d.ts.map +1 -0
- package/dist/interactions/interaction-dispatch.js +2 -0
- package/dist/interactions/interaction-dispatch.js.map +1 -0
- package/dist/interactions/interaction-label.d.ts +4 -0
- package/dist/interactions/interaction-label.d.ts.map +1 -0
- package/dist/interactions/interaction-label.js +8 -0
- package/dist/interactions/interaction-label.js.map +1 -0
- package/dist/interactions/interaction-registry.d.ts +24 -0
- package/dist/interactions/interaction-registry.d.ts.map +1 -0
- package/dist/interactions/interaction-registry.js +138 -0
- package/dist/interactions/interaction-registry.js.map +1 -0
- package/dist/interactions/match-interaction.d.ts +4 -0
- package/dist/interactions/match-interaction.d.ts.map +1 -0
- package/dist/interactions/match-interaction.js +188 -0
- package/dist/interactions/match-interaction.js.map +1 -0
- package/dist/interactions/object-asset.d.ts +8 -0
- package/dist/interactions/object-asset.d.ts.map +1 -0
- package/dist/interactions/object-asset.js +182 -0
- package/dist/interactions/object-asset.js.map +1 -0
- package/dist/interactions/pair-interaction.d.ts +4 -0
- package/dist/interactions/pair-interaction.d.ts.map +1 -0
- package/dist/interactions/pair-interaction.js +125 -0
- package/dist/interactions/pair-interaction.js.map +1 -0
- package/dist/interactions/point-value.d.ts +16 -0
- package/dist/interactions/point-value.d.ts.map +1 -0
- package/dist/interactions/point-value.js +37 -0
- package/dist/interactions/point-value.js.map +1 -0
- package/dist/interactions/portable-custom-interaction.d.ts +16 -0
- package/dist/interactions/portable-custom-interaction.d.ts.map +1 -0
- package/dist/interactions/portable-custom-interaction.js +97 -0
- package/dist/interactions/portable-custom-interaction.js.map +1 -0
- package/dist/interactions/position-object-interaction.d.ts +4 -0
- package/dist/interactions/position-object-interaction.d.ts.map +1 -0
- package/dist/interactions/position-object-interaction.js +177 -0
- package/dist/interactions/position-object-interaction.js.map +1 -0
- package/dist/interactions/routing.d.ts +5 -0
- package/dist/interactions/routing.d.ts.map +1 -0
- package/dist/interactions/routing.js +24 -0
- package/dist/interactions/routing.js.map +1 -0
- package/dist/interactions/select-point-interaction.d.ts +4 -0
- package/dist/interactions/select-point-interaction.d.ts.map +1 -0
- package/dist/interactions/select-point-interaction.js +166 -0
- package/dist/interactions/select-point-interaction.js.map +1 -0
- package/dist/interactions/shared.d.ts +12 -0
- package/dist/interactions/shared.d.ts.map +1 -0
- package/dist/interactions/shared.js +65 -0
- package/dist/interactions/shared.js.map +1 -0
- package/dist/interactions/text-interaction.d.ts +6 -0
- package/dist/interactions/text-interaction.d.ts.map +1 -0
- package/dist/interactions/text-interaction.js +110 -0
- package/dist/interactions/text-interaction.js.map +1 -0
- package/dist/interactions/unsupported-interaction.d.ts +4 -0
- package/dist/interactions/unsupported-interaction.d.ts.map +1 -0
- package/dist/interactions/unsupported-interaction.js +23 -0
- package/dist/interactions/unsupported-interaction.js.map +1 -0
- package/dist/interactions/upload-interaction.d.ts +4 -0
- package/dist/interactions/upload-interaction.d.ts.map +1 -0
- package/dist/interactions/upload-interaction.js +10 -0
- package/dist/interactions/upload-interaction.js.map +1 -0
- package/dist/player/attempt-availability.d.ts +6 -0
- package/dist/player/attempt-availability.d.ts.map +1 -0
- package/dist/player/attempt-availability.js +27 -0
- package/dist/player/attempt-availability.js.map +1 -0
- package/dist/player/content-state.d.ts +19 -0
- package/dist/player/content-state.d.ts.map +1 -0
- package/dist/player/content-state.js +39 -0
- package/dist/player/content-state.js.map +1 -0
- package/dist/player/dynamic-body.d.ts +7 -0
- package/dist/player/dynamic-body.d.ts.map +1 -0
- package/dist/player/dynamic-body.js +28 -0
- package/dist/player/dynamic-body.js.map +1 -0
- package/dist/player/feedback-panel.d.ts +3 -0
- package/dist/player/feedback-panel.d.ts.map +1 -0
- package/dist/player/feedback-panel.js +14 -0
- package/dist/player/feedback-panel.js.map +1 -0
- package/dist/player/fetch-xml.d.ts +2 -0
- package/dist/player/fetch-xml.d.ts.map +1 -0
- package/dist/player/fetch-xml.js +10 -0
- package/dist/player/fetch-xml.js.map +1 -0
- package/dist/player/interaction-render.d.ts +14 -0
- package/dist/player/interaction-render.d.ts.map +1 -0
- package/dist/player/interaction-render.js +51 -0
- package/dist/player/interaction-render.js.map +1 -0
- package/dist/player/render-shell.d.ts +8 -0
- package/dist/player/render-shell.d.ts.map +1 -0
- package/dist/player/render-shell.js +37 -0
- package/dist/player/render-shell.js.map +1 -0
- package/dist/player/resolve-assets.d.ts +3 -0
- package/dist/player/resolve-assets.d.ts.map +1 -0
- package/dist/player/resolve-assets.js +12 -0
- package/dist/player/resolve-assets.js.map +1 -0
- package/dist/player/validation-messages.d.ts +17 -0
- package/dist/player/validation-messages.d.ts.map +1 -0
- package/dist/player/validation-messages.js +33 -0
- package/dist/player/validation-messages.js.map +1 -0
- package/dist/player-adapter.d.ts +62 -0
- package/dist/player-adapter.d.ts.map +1 -0
- package/dist/player-adapter.js +119 -0
- package/dist/player-adapter.js.map +1 -0
- package/dist/player-dev.d.ts +4 -0
- package/dist/player-dev.d.ts.map +1 -0
- package/dist/player-dev.js +14 -0
- package/dist/player-dev.js.map +1 -0
- package/dist/player-element.d.ts +14 -1
- package/dist/player-element.d.ts.map +1 -1
- package/dist/player-element.js +57 -5
- package/dist/player-element.js.map +1 -1
- package/dist/player-locale.d.ts +8 -3
- package/dist/player-locale.d.ts.map +1 -1
- package/dist/player-locale.js +16 -175
- package/dist/player-locale.js.map +1 -1
- package/dist/player-message-catalog-default.d.ts +4 -0
- package/dist/player-message-catalog-default.d.ts.map +1 -0
- package/dist/player-message-catalog-default.js +118 -0
- package/dist/player-message-catalog-default.js.map +1 -0
- package/dist/player-message-catalog-validate.d.ts +31 -0
- package/dist/player-message-catalog-validate.d.ts.map +1 -0
- package/dist/player-message-catalog-validate.js +327 -0
- package/dist/player-message-catalog-validate.js.map +1 -0
- package/dist/player-message-catalog.d.ts +18 -0
- package/dist/player-message-catalog.d.ts.map +1 -0
- package/dist/player-message-catalog.js +40 -0
- package/dist/player-message-catalog.js.map +1 -0
- package/dist/player-message-keys.d.ts +6 -0
- package/dist/player-message-keys.d.ts.map +1 -0
- package/dist/player-message-keys.js +7 -0
- package/dist/player-message-keys.js.map +1 -0
- package/dist/player-message-manifest.d.ts +272 -0
- package/dist/player-message-manifest.d.ts.map +1 -0
- package/dist/player-message-manifest.js +83 -0
- package/dist/player-message-manifest.js.map +1 -0
- package/dist/player-message-overrides.d.ts +3 -0
- package/dist/player-message-overrides.d.ts.map +1 -0
- package/dist/player-message-overrides.js +28 -0
- package/dist/player-message-overrides.js.map +1 -0
- package/dist/player-message-resolver.d.ts +31 -0
- package/dist/player-message-resolver.d.ts.map +1 -0
- package/dist/player-message-resolver.js +110 -0
- package/dist/player-message-resolver.js.map +1 -0
- package/dist/player-messages.d.ts +0 -38
- package/dist/player-messages.d.ts.map +1 -1
- package/dist/player-types.d.ts +12 -2
- package/dist/player-types.d.ts.map +1 -1
- package/dist/reorder/a11y.d.ts +7 -0
- package/dist/reorder/a11y.d.ts.map +1 -0
- package/dist/reorder/a11y.js +34 -0
- package/dist/reorder/a11y.js.map +1 -0
- package/dist/reorder/graphic-order-interaction.d.ts +4 -0
- package/dist/reorder/graphic-order-interaction.d.ts.map +1 -0
- package/dist/reorder/graphic-order-interaction.js +205 -0
- package/dist/reorder/graphic-order-interaction.js.map +1 -0
- package/dist/reorder/list-controls.d.ts +22 -0
- package/dist/reorder/list-controls.d.ts.map +1 -0
- package/dist/reorder/list-controls.js +75 -0
- package/dist/reorder/list-controls.js.map +1 -0
- package/dist/reorder/order-interaction.d.ts +4 -0
- package/dist/reorder/order-interaction.d.ts.map +1 -0
- package/dist/reorder/order-interaction.js +54 -0
- package/dist/reorder/order-interaction.js.map +1 -0
- package/dist/styles/base-styles.d.ts +2 -0
- package/dist/styles/base-styles.d.ts.map +1 -0
- package/dist/styles/base-styles.js +133 -0
- package/dist/styles/base-styles.js.map +1 -0
- package/dist/styles/choice-hottext-styles.d.ts +2 -0
- package/dist/styles/choice-hottext-styles.d.ts.map +1 -0
- package/dist/styles/choice-hottext-styles.js +76 -0
- package/dist/styles/choice-hottext-styles.js.map +1 -0
- package/dist/styles/control-styles.d.ts +2 -0
- package/dist/styles/control-styles.d.ts.map +1 -0
- package/dist/styles/control-styles.js +114 -0
- package/dist/styles/control-styles.js.map +1 -0
- package/dist/styles/drawing-styles.d.ts +2 -0
- package/dist/styles/drawing-styles.d.ts.map +1 -0
- package/dist/styles/drawing-styles.js +30 -0
- package/dist/styles/drawing-styles.js.map +1 -0
- package/dist/styles/gap-match-styles.d.ts +2 -0
- package/dist/styles/gap-match-styles.d.ts.map +1 -0
- package/dist/styles/gap-match-styles.js +33 -0
- package/dist/styles/gap-match-styles.js.map +1 -0
- package/dist/styles/graphic-styles.d.ts +2 -0
- package/dist/styles/graphic-styles.d.ts.map +1 -0
- package/dist/styles/graphic-styles.js +289 -0
- package/dist/styles/graphic-styles.js.map +1 -0
- package/dist/styles/match-pair-styles.d.ts +2 -0
- package/dist/styles/match-pair-styles.d.ts.map +1 -0
- package/dist/styles/match-pair-styles.js +62 -0
- package/dist/styles/match-pair-styles.js.map +1 -0
- package/dist/styles/text-slider-styles.d.ts +2 -0
- package/dist/styles/text-slider-styles.d.ts.map +1 -0
- package/dist/styles/text-slider-styles.js +35 -0
- package/dist/styles/text-slider-styles.js.map +1 -0
- package/package.json +8 -8
- package/src/controls/remove-button.ts +8 -5
- package/src/index.ts +61 -5
- package/src/interactions/choice-interaction.ts +6 -2
- package/src/interactions/drawing-interaction.ts +14 -9
- package/src/interactions/end-attempt-interaction.ts +3 -3
- package/src/interactions/gap-match-interaction.ts +32 -13
- package/src/interactions/graphic-associate-interaction.ts +15 -10
- package/src/interactions/hotspot-interaction.ts +10 -6
- package/src/interactions/inline-choice-interaction.ts +4 -4
- package/src/interactions/interaction-registry.ts +12 -12
- package/src/interactions/match-interaction.ts +9 -6
- package/src/interactions/pair-interaction.ts +22 -14
- package/src/interactions/position-object-interaction.ts +22 -13
- package/src/interactions/select-point-interaction.ts +25 -13
- package/src/interactions/shared.ts +21 -4
- package/src/interactions/text-interaction.ts +14 -4
- package/src/interactions/upload-interaction.ts +6 -3
- package/src/player/interaction-render.ts +4 -4
- package/src/player-adapter.ts +253 -0
- package/src/player-dev.ts +14 -0
- package/src/player-element.ts +78 -8
- package/src/player-locale.ts +28 -199
- package/src/player-message-catalog-default.ts +119 -0
- package/src/player-message-catalog-validate.ts +425 -0
- package/src/player-message-catalog.ts +72 -0
- package/src/player-message-keys.ts +12 -0
- package/src/player-message-manifest.ts +103 -0
- package/src/player-message-overrides.ts +38 -0
- package/src/player-message-resolver.ts +205 -0
- package/src/player-messages.ts +0 -30
- package/src/player-types.ts +15 -4
- package/src/reorder/a11y.ts +22 -7
- package/src/reorder/graphic-order-interaction.ts +23 -16
- package/src/reorder/list-controls.ts +8 -6
- package/src/reorder/order-interaction.ts +7 -5
- package/src/styles/base-styles.ts +20 -5
- package/src/styles/graphic-styles.ts +0 -6
|
@@ -5,11 +5,10 @@ import {
|
|
|
5
5
|
appendGraphicObjectImage,
|
|
6
6
|
objectHeight,
|
|
7
7
|
objectWidth,
|
|
8
|
-
readableType,
|
|
9
8
|
responseGroup,
|
|
10
9
|
} from "../interaction-support.js";
|
|
11
10
|
import { movementButton } from "../movement.js";
|
|
12
|
-
import type {
|
|
11
|
+
import type { PlayerMessageResolver } from "../player-message-resolver.js";
|
|
13
12
|
import { maximumAllowedResponses } from "../response-limits.js";
|
|
14
13
|
import { parsePointValues, pointToString } from "./point-value.js";
|
|
15
14
|
|
|
@@ -17,11 +16,14 @@ export function renderSelectPointResponse(
|
|
|
17
16
|
interaction: QtiInteraction,
|
|
18
17
|
update: (value: QtiValue) => void,
|
|
19
18
|
currentValue: QtiValue,
|
|
20
|
-
messages:
|
|
19
|
+
messages: PlayerMessageResolver,
|
|
21
20
|
): HTMLElement {
|
|
22
21
|
const group = responseGroup();
|
|
23
22
|
group.role = "group";
|
|
24
|
-
group.setAttribute(
|
|
23
|
+
group.setAttribute(
|
|
24
|
+
"aria-label",
|
|
25
|
+
messages.message("interactionCoordinateResponse", { type: interaction.type }),
|
|
26
|
+
);
|
|
25
27
|
const isMultiple = interaction.responseCardinality === "multiple";
|
|
26
28
|
const maxPoints = isMultiple ? maximumAllowedResponses(interaction) : 1;
|
|
27
29
|
|
|
@@ -33,7 +35,10 @@ export function renderSelectPointResponse(
|
|
|
33
35
|
objectHeight(interaction),
|
|
34
36
|
"qti3-point-surface",
|
|
35
37
|
);
|
|
36
|
-
surface.setAttribute(
|
|
38
|
+
surface.setAttribute(
|
|
39
|
+
"aria-label",
|
|
40
|
+
messages.message("interactionCoordinateArea", { type: interaction.type }),
|
|
41
|
+
);
|
|
37
42
|
|
|
38
43
|
const object = interaction.object;
|
|
39
44
|
if (object) {
|
|
@@ -45,7 +50,8 @@ export function renderSelectPointResponse(
|
|
|
45
50
|
let points = parsePointValues(currentValue);
|
|
46
51
|
let activeIndex = points.length > 0 ? points.length - 1 : -1;
|
|
47
52
|
const coordinate = document.createElement("output");
|
|
48
|
-
coordinate.className = "qti3-coordinate-output";
|
|
53
|
+
coordinate.className = "qti3-coordinate-output qti-visually-hidden";
|
|
54
|
+
coordinate.setAttribute("aria-live", "polite");
|
|
49
55
|
const initialPoint = () => ({
|
|
50
56
|
x: Math.round(width / 2),
|
|
51
57
|
y: Math.round(height / 2),
|
|
@@ -62,8 +68,11 @@ export function renderSelectPointResponse(
|
|
|
62
68
|
surface.querySelectorAll(".qti3-point-marker").forEach((marker) => marker.remove());
|
|
63
69
|
if (points.length === 0) {
|
|
64
70
|
coordinate.value = "";
|
|
65
|
-
coordinate.textContent = messages.noPointSelected
|
|
66
|
-
surface.setAttribute(
|
|
71
|
+
coordinate.textContent = messages.message("noPointSelected");
|
|
72
|
+
surface.setAttribute(
|
|
73
|
+
"aria-label",
|
|
74
|
+
messages.message("interactionCoordinateArea", { type: interaction.type }),
|
|
75
|
+
);
|
|
67
76
|
return;
|
|
68
77
|
}
|
|
69
78
|
points.forEach((point, index) => {
|
|
@@ -83,11 +92,14 @@ export function renderSelectPointResponse(
|
|
|
83
92
|
? points.map(pointToString).join(" | ")
|
|
84
93
|
: pointToString(points[0]);
|
|
85
94
|
coordinate.textContent = isMultiple
|
|
86
|
-
?
|
|
87
|
-
:
|
|
95
|
+
? messages.message("selectedPointsSummary", { count: points.length, coordinates: text })
|
|
96
|
+
: messages.message("selectedPointAt", { coordinates: pointToString(points[0]) });
|
|
88
97
|
surface.setAttribute(
|
|
89
98
|
"aria-label",
|
|
90
|
-
|
|
99
|
+
messages.message("interactionCoordinateAreaSelected", {
|
|
100
|
+
type: interaction.type,
|
|
101
|
+
coordinates: text,
|
|
102
|
+
}),
|
|
91
103
|
);
|
|
92
104
|
};
|
|
93
105
|
const clampPoint = (point: { x: number; y: number }) => {
|
|
@@ -158,7 +170,7 @@ export function renderSelectPointResponse(
|
|
|
158
170
|
["down", 0, 1],
|
|
159
171
|
] as const) {
|
|
160
172
|
controls.append(
|
|
161
|
-
movementButton(direction, messages.movePoint
|
|
173
|
+
movementButton(direction, messages.message("movePoint", { direction }), () => {
|
|
162
174
|
const point = mutableActivePoint();
|
|
163
175
|
point.x += dx;
|
|
164
176
|
point.y += dy;
|
|
@@ -171,7 +183,7 @@ export function renderSelectPointResponse(
|
|
|
171
183
|
if (isMultiple) {
|
|
172
184
|
const clear = document.createElement("button");
|
|
173
185
|
clear.type = "button";
|
|
174
|
-
clear.textContent = messages.clearPoints
|
|
186
|
+
clear.textContent = messages.message("clearPoints");
|
|
175
187
|
clear.addEventListener("click", () => {
|
|
176
188
|
points = [];
|
|
177
189
|
activeIndex = -1;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { QtiChoice, QtiInteraction } from "@longsightgroup/qti3-core";
|
|
2
|
+
import type { PlayerMessageResolver } from "../player-message-resolver.js";
|
|
2
3
|
import { interactionChoices } from "../interaction-support.js";
|
|
3
4
|
|
|
4
5
|
export function tokenRegion(label: string, visibleLabel?: string): HTMLElement {
|
|
@@ -49,8 +50,24 @@ export function targetChoices(interaction: QtiInteraction): QtiChoice[] {
|
|
|
49
50
|
return targets.length > 0 ? targets : choices;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
export function pairRegionLabels(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
export function pairRegionLabels(
|
|
54
|
+
interaction: QtiInteraction,
|
|
55
|
+
messages: PlayerMessageResolver,
|
|
56
|
+
): { source: string; target: string } {
|
|
57
|
+
if (interaction.type === "associate") {
|
|
58
|
+
return {
|
|
59
|
+
source: messages.message("associateFirstConceptRegion"),
|
|
60
|
+
target: messages.message("associatePairWithRegion"),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (interaction.type === "match") {
|
|
64
|
+
return {
|
|
65
|
+
source: messages.message("matchPromptRegion"),
|
|
66
|
+
target: messages.message("matchMatchRegion"),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
source: messages.message("genericSourceRegion"),
|
|
71
|
+
target: messages.message("genericTargetRegion"),
|
|
72
|
+
};
|
|
56
73
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { QtiInteraction, QtiValue } from "@longsightgroup/qti3-core";
|
|
2
|
+
import type { PlayerMessageResolver } from "../player-message-resolver.js";
|
|
2
3
|
|
|
3
4
|
function scalarString(value: QtiValue): string {
|
|
4
5
|
if (value === null || Array.isArray(value) || typeof value === "object") return "";
|
|
@@ -32,6 +33,7 @@ export function renderTextResponse(
|
|
|
32
33
|
update: (value: QtiValue) => void,
|
|
33
34
|
mode: "entry" | "extended",
|
|
34
35
|
currentValue: QtiValue,
|
|
36
|
+
messages: PlayerMessageResolver,
|
|
35
37
|
): HTMLElement {
|
|
36
38
|
const group = document.createElement("div");
|
|
37
39
|
group.className = "qti3-text-response";
|
|
@@ -43,7 +45,10 @@ export function renderTextResponse(
|
|
|
43
45
|
control.value = scalarString(currentValue);
|
|
44
46
|
control.setAttribute(
|
|
45
47
|
"aria-label",
|
|
46
|
-
interaction.prompt ??
|
|
48
|
+
interaction.prompt ??
|
|
49
|
+
(mode === "extended"
|
|
50
|
+
? messages.message("extendedTextResponseLabel")
|
|
51
|
+
: messages.message("textResponseLabel")),
|
|
47
52
|
);
|
|
48
53
|
if (mode === "extended" && expectedLines > 0) {
|
|
49
54
|
(control as HTMLTextAreaElement).rows = expectedLines;
|
|
@@ -60,7 +65,10 @@ export function renderTextResponse(
|
|
|
60
65
|
const value = control.value;
|
|
61
66
|
if (counter) {
|
|
62
67
|
const words = value.trim().length > 0 ? value.trim().split(/\s+/).length : 0;
|
|
63
|
-
counter.textContent =
|
|
68
|
+
counter.textContent = messages.message("extendedTextCounter", {
|
|
69
|
+
characters: value.length,
|
|
70
|
+
words,
|
|
71
|
+
});
|
|
64
72
|
}
|
|
65
73
|
if (emitResponse) update(value);
|
|
66
74
|
};
|
|
@@ -76,6 +84,7 @@ export function renderInlineTextEntry(
|
|
|
76
84
|
interaction: QtiInteraction,
|
|
77
85
|
update: (value: QtiValue) => void,
|
|
78
86
|
currentValue: QtiValue,
|
|
87
|
+
messages: PlayerMessageResolver,
|
|
79
88
|
): HTMLElement {
|
|
80
89
|
const group = document.createElement("span");
|
|
81
90
|
group.className = "qti3-inline-text-response";
|
|
@@ -84,7 +93,7 @@ export function renderInlineTextEntry(
|
|
|
84
93
|
input.value = scalarString(currentValue);
|
|
85
94
|
input.setAttribute(
|
|
86
95
|
"aria-label",
|
|
87
|
-
interaction.prompt ?? interaction.contextText ?? "
|
|
96
|
+
interaction.prompt ?? interaction.contextText ?? messages.message("textResponseLabel"),
|
|
88
97
|
);
|
|
89
98
|
const expectedLength = Number(interaction.attributes["expected-length"] ?? 0);
|
|
90
99
|
applyExpectedTextEntryWidth(input, expectedLength);
|
|
@@ -102,6 +111,7 @@ export function renderSliderResponse(
|
|
|
102
111
|
interaction: QtiInteraction,
|
|
103
112
|
update: (value: QtiValue) => void,
|
|
104
113
|
currentValue: QtiValue,
|
|
114
|
+
messages: PlayerMessageResolver,
|
|
105
115
|
): HTMLElement {
|
|
106
116
|
const group = document.createElement("div");
|
|
107
117
|
group.className = "qti3-slider-response";
|
|
@@ -111,7 +121,7 @@ export function renderSliderResponse(
|
|
|
111
121
|
input.max = interaction.attributes["upper-bound"] ?? "100";
|
|
112
122
|
input.step = interaction.attributes.step ?? "1";
|
|
113
123
|
input.value = scalarString(currentValue) || interaction.attributes["lower-bound"] || "0";
|
|
114
|
-
input.setAttribute("aria-label", interaction.prompt ?? "
|
|
124
|
+
input.setAttribute("aria-label", interaction.prompt ?? messages.message("sliderResponseLabel"));
|
|
115
125
|
const output = document.createElement("output");
|
|
116
126
|
output.className = "qti3-slider-output";
|
|
117
127
|
output.value = input.value;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { QtiInteraction, QtiValue } from "@longsightgroup/qti3-core";
|
|
2
|
-
import type {
|
|
2
|
+
import type { PlayerMessageResolver } from "../player-message-resolver.js";
|
|
3
3
|
import { interactionLabel } from "./interaction-label.js";
|
|
4
4
|
|
|
5
5
|
export function renderUploadResponse(
|
|
6
6
|
interaction: QtiInteraction,
|
|
7
7
|
update: (value: QtiValue) => void,
|
|
8
|
-
messages:
|
|
8
|
+
messages: PlayerMessageResolver,
|
|
9
9
|
): HTMLElement {
|
|
10
10
|
const input = document.createElement("input");
|
|
11
11
|
input.type = "file";
|
|
12
12
|
input.className = "qti3-upload-input";
|
|
13
|
-
input.setAttribute(
|
|
13
|
+
input.setAttribute(
|
|
14
|
+
"aria-label",
|
|
15
|
+
interactionLabel(interaction) || messages.message("uploadResponse"),
|
|
16
|
+
);
|
|
14
17
|
input.addEventListener("change", () => update(input.files?.[0]?.name ?? ""));
|
|
15
18
|
return input;
|
|
16
19
|
}
|
|
@@ -5,12 +5,12 @@ import { interactionLabel, qtiSharedClassNames } from "../interactions/interacti
|
|
|
5
5
|
import { renderSelect } from "../interactions/inline-choice-interaction.js";
|
|
6
6
|
import { renderInlineTextEntry } from "../interactions/text-interaction.js";
|
|
7
7
|
import { renderUnsupportedEmbeddedInteraction } from "../interactions/unsupported-interaction.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { PlayerMessageResolver } from "../player-message-resolver.js";
|
|
9
9
|
import { inlineValidationMessageElement, validationMessageElement } from "../player-validation.js";
|
|
10
10
|
|
|
11
11
|
export interface BlockInteractionRenderOptions {
|
|
12
12
|
interaction: QtiInteraction;
|
|
13
|
-
messages:
|
|
13
|
+
messages: PlayerMessageResolver;
|
|
14
14
|
update: (value: QtiValue) => void;
|
|
15
15
|
currentValue: QtiValue;
|
|
16
16
|
isCompleted: () => boolean;
|
|
@@ -65,7 +65,7 @@ export function renderEmbeddedInteractionSection(
|
|
|
65
65
|
interaction: QtiInteraction,
|
|
66
66
|
update: (value: QtiValue) => void,
|
|
67
67
|
currentValue: QtiValue,
|
|
68
|
-
messages:
|
|
68
|
+
messages: PlayerMessageResolver,
|
|
69
69
|
): HTMLElement {
|
|
70
70
|
if (interaction.type !== "inlineChoice" && interaction.type !== "textEntry") {
|
|
71
71
|
return renderUnsupportedEmbeddedInteraction(interaction);
|
|
@@ -83,7 +83,7 @@ export function renderEmbeddedInteractionSection(
|
|
|
83
83
|
wrapper.append(
|
|
84
84
|
interaction.type === "inlineChoice"
|
|
85
85
|
? renderSelect(interaction, update, currentValue, messages)
|
|
86
|
-
: renderInlineTextEntry(interaction, update, currentValue),
|
|
86
|
+
: renderInlineTextEntry(interaction, update, currentValue, messages),
|
|
87
87
|
);
|
|
88
88
|
return wrapper;
|
|
89
89
|
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
QtiAttemptStateV1,
|
|
3
|
+
QtiCatalogSupportResolution,
|
|
4
|
+
QtiCatalogSupportResolutionOptions,
|
|
5
|
+
QtiScoreResult,
|
|
6
|
+
QtiTextToSpeechTraversal,
|
|
7
|
+
} from "@longsightgroup/qti3-core";
|
|
8
|
+
import type { PlayerMessageCatalog } from "./player-message-catalog.js";
|
|
9
|
+
import type { QtiAssessmentItemPlayer } from "./player-element.js";
|
|
10
|
+
import type { QtiPlayerMessageOverrides } from "./player-message-resolver.js";
|
|
11
|
+
import type {
|
|
12
|
+
QtiAssessmentItemPlayerEventDetailMap,
|
|
13
|
+
QtiAssessmentItemPlayerEventName,
|
|
14
|
+
QtiPlayerFetchXml,
|
|
15
|
+
QtiPlayerLoadOptions,
|
|
16
|
+
QtiPlayerResolveAsset,
|
|
17
|
+
QtiScoreAttemptOptions,
|
|
18
|
+
} from "./player-types.js";
|
|
19
|
+
|
|
20
|
+
export type QtiAssessmentItemPlayerAdapterEventPropName =
|
|
21
|
+
| "onReady"
|
|
22
|
+
| "onResponseChange"
|
|
23
|
+
| "onStateChange"
|
|
24
|
+
| "onScore"
|
|
25
|
+
| "onValidation"
|
|
26
|
+
| "onSuspend"
|
|
27
|
+
| "onEndAttempt"
|
|
28
|
+
| "onPortableCustomMount"
|
|
29
|
+
| "onDiagnostics"
|
|
30
|
+
| "onReset"
|
|
31
|
+
| "onRestore";
|
|
32
|
+
|
|
33
|
+
export type QtiAssessmentItemPlayerAdapterPropName =
|
|
34
|
+
| "xml"
|
|
35
|
+
| "loadOptions"
|
|
36
|
+
| "languageOfInterface"
|
|
37
|
+
| "messageCatalog"
|
|
38
|
+
| "messages"
|
|
39
|
+
| "onLoadError"
|
|
40
|
+
| QtiAssessmentItemPlayerAdapterEventPropName;
|
|
41
|
+
|
|
42
|
+
export type QtiAssessmentItemPlayerAdapterEventCallback<
|
|
43
|
+
T extends QtiAssessmentItemPlayerEventName,
|
|
44
|
+
> = (detail: QtiAssessmentItemPlayerEventDetailMap[T]) => void;
|
|
45
|
+
|
|
46
|
+
export type QtiAssessmentItemPlayerAdapterEventHandlerProps = {
|
|
47
|
+
[K in (typeof qtiAssessmentItemPlayerAdapterEventEntries)[number] as K[1]]?: QtiAssessmentItemPlayerAdapterEventCallback<
|
|
48
|
+
K[0]
|
|
49
|
+
>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export interface QtiAssessmentItemPlayerAdapterProps extends QtiAssessmentItemPlayerAdapterEventHandlerProps {
|
|
53
|
+
/** Already-prepared candidate-safe XML for delivery, or authoring/preview XML in preview tools. */
|
|
54
|
+
xml?: string | undefined;
|
|
55
|
+
loadOptions?: QtiPlayerLoadOptions | undefined;
|
|
56
|
+
languageOfInterface?: string | undefined;
|
|
57
|
+
messageCatalog?: PlayerMessageCatalog | undefined;
|
|
58
|
+
messages?: QtiPlayerMessageOverrides | undefined;
|
|
59
|
+
onLoadError?: ((error: Error) => void) | undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface QtiAssessmentItemPlayerHandle {
|
|
63
|
+
readonly element: QtiAssessmentItemPlayer;
|
|
64
|
+
loadXml(xml: string, options?: QtiPlayerLoadOptions): Promise<void>;
|
|
65
|
+
loadUrl(url: string, options?: QtiPlayerLoadOptions): Promise<void>;
|
|
66
|
+
scoreAttempt(options?: QtiScoreAttemptOptions): QtiScoreResult | undefined;
|
|
67
|
+
restore(state: QtiAttemptStateV1): void;
|
|
68
|
+
suspend(): void;
|
|
69
|
+
endAttempt(options?: QtiScoreAttemptOptions): void;
|
|
70
|
+
reset(): void;
|
|
71
|
+
clearItem(): void;
|
|
72
|
+
serialize(): QtiAttemptStateV1 | undefined;
|
|
73
|
+
getTextToSpeechTraversal(): QtiTextToSpeechTraversal | undefined;
|
|
74
|
+
getCatalogSupportResolution(
|
|
75
|
+
options?: QtiCatalogSupportResolutionOptions,
|
|
76
|
+
): QtiCatalogSupportResolution | undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const qtiAssessmentItemPlayerAdapterEventEntries = [
|
|
80
|
+
["qti-ready", "onReady"],
|
|
81
|
+
["qti-responsechange", "onResponseChange"],
|
|
82
|
+
["qti-statechange", "onStateChange"],
|
|
83
|
+
["qti-score", "onScore"],
|
|
84
|
+
["qti-validation", "onValidation"],
|
|
85
|
+
["qti-suspend", "onSuspend"],
|
|
86
|
+
["qti-endattempt", "onEndAttempt"],
|
|
87
|
+
["qti-portable-custom-mount", "onPortableCustomMount"],
|
|
88
|
+
["qti-diagnostics", "onDiagnostics"],
|
|
89
|
+
["qti-reset", "onReset"],
|
|
90
|
+
["qti-restore", "onRestore"],
|
|
91
|
+
] as const satisfies readonly (readonly [
|
|
92
|
+
QtiAssessmentItemPlayerEventName,
|
|
93
|
+
QtiAssessmentItemPlayerAdapterEventPropName,
|
|
94
|
+
])[];
|
|
95
|
+
|
|
96
|
+
export const qtiAssessmentItemPlayerAdapterPropNames = [
|
|
97
|
+
"xml",
|
|
98
|
+
"loadOptions",
|
|
99
|
+
"languageOfInterface",
|
|
100
|
+
"messageCatalog",
|
|
101
|
+
"messages",
|
|
102
|
+
"onLoadError",
|
|
103
|
+
...qtiAssessmentItemPlayerAdapterEventEntries.map((entry) => entry[1]),
|
|
104
|
+
] as const satisfies readonly QtiAssessmentItemPlayerAdapterPropName[];
|
|
105
|
+
|
|
106
|
+
const qtiAssessmentItemPlayerAdapterPropNameSet = new Set<string>(
|
|
107
|
+
qtiAssessmentItemPlayerAdapterPropNames,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
export function isQtiAssessmentItemPlayerAdapterPropName(name: string): boolean {
|
|
111
|
+
return qtiAssessmentItemPlayerAdapterPropNameSet.has(name);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type QtiAssessmentItemPlayerLoadDependencies = readonly [
|
|
115
|
+
string | undefined,
|
|
116
|
+
QtiPlayerLoadOptions["status"] | undefined,
|
|
117
|
+
boolean | undefined,
|
|
118
|
+
boolean | undefined,
|
|
119
|
+
QtiPlayerFetchXml | undefined,
|
|
120
|
+
QtiPlayerResolveAsset | undefined,
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
export function qtiAssessmentItemPlayerLoadStateKey(
|
|
124
|
+
state: QtiAttemptStateV1 | undefined,
|
|
125
|
+
): string | undefined {
|
|
126
|
+
if (!state) return undefined;
|
|
127
|
+
// JSON key order follows insertion order; in-place mutation without reload is not detected.
|
|
128
|
+
return JSON.stringify(state);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function qtiAssessmentItemPlayerLoadDependencies(
|
|
132
|
+
loadOptions: QtiPlayerLoadOptions | undefined,
|
|
133
|
+
): QtiAssessmentItemPlayerLoadDependencies {
|
|
134
|
+
return [
|
|
135
|
+
qtiAssessmentItemPlayerLoadStateKey(loadOptions?.state),
|
|
136
|
+
loadOptions?.status,
|
|
137
|
+
loadOptions?.sessionControl?.validateResponses,
|
|
138
|
+
loadOptions?.sessionControl?.showFeedback,
|
|
139
|
+
loadOptions?.fetchXml,
|
|
140
|
+
loadOptions?.resolveAsset,
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function bindQtiAssessmentItemPlayerAdapterEvents(
|
|
145
|
+
element: QtiAssessmentItemPlayer,
|
|
146
|
+
getProps: () => QtiAssessmentItemPlayerAdapterProps,
|
|
147
|
+
): () => void {
|
|
148
|
+
const removers = qtiAssessmentItemPlayerAdapterEventEntries.map(([eventName, propName]) => {
|
|
149
|
+
const listener = (event: Event) => {
|
|
150
|
+
const callback = getProps()[propName] as
|
|
151
|
+
| QtiAssessmentItemPlayerAdapterEventCallback<typeof eventName>
|
|
152
|
+
| undefined;
|
|
153
|
+
callback?.(
|
|
154
|
+
(event as CustomEvent<QtiAssessmentItemPlayerEventDetailMap[typeof eventName]>).detail,
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
element.addEventListener(eventName, listener);
|
|
158
|
+
return () => element.removeEventListener(eventName, listener);
|
|
159
|
+
});
|
|
160
|
+
return () => {
|
|
161
|
+
for (const remove of removers) remove();
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function syncQtiAssessmentItemPlayerAdapterChrome(
|
|
166
|
+
element: QtiAssessmentItemPlayer,
|
|
167
|
+
props: Pick<
|
|
168
|
+
QtiAssessmentItemPlayerAdapterProps,
|
|
169
|
+
"languageOfInterface" | "messageCatalog" | "messages"
|
|
170
|
+
>,
|
|
171
|
+
): void {
|
|
172
|
+
element.languageOfInterface = props.languageOfInterface;
|
|
173
|
+
element.messageCatalog = props.messageCatalog;
|
|
174
|
+
element.messages = props.messages;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** @deprecated Use {@link syncQtiAssessmentItemPlayerAdapterChrome}. */
|
|
178
|
+
export const syncQtiAssessmentItemPlayerAdapterMessages = syncQtiAssessmentItemPlayerAdapterChrome;
|
|
179
|
+
|
|
180
|
+
export interface QtiAssessmentItemPlayerAdapterLoadSyncInput {
|
|
181
|
+
xml?: string | undefined;
|
|
182
|
+
loadOptions?: QtiPlayerLoadOptions | undefined;
|
|
183
|
+
onLoadError?: ((error: Error) => void) | undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function createQtiAssessmentItemPlayerAdapterLoadSync(): {
|
|
187
|
+
run(
|
|
188
|
+
element: QtiAssessmentItemPlayer,
|
|
189
|
+
input: QtiAssessmentItemPlayerAdapterLoadSyncInput,
|
|
190
|
+
): () => void;
|
|
191
|
+
} {
|
|
192
|
+
let loadSequence = 0;
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
run(element, input) {
|
|
196
|
+
if (input.xml === undefined) {
|
|
197
|
+
loadSequence += 1;
|
|
198
|
+
element.clearItem();
|
|
199
|
+
return () => {};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let active = true;
|
|
203
|
+
const sequence = (loadSequence += 1);
|
|
204
|
+
void element
|
|
205
|
+
.loadXml(input.xml, input.loadOptions)
|
|
206
|
+
.then(() => {
|
|
207
|
+
if (!active || sequence !== loadSequence) return;
|
|
208
|
+
})
|
|
209
|
+
.catch((error: unknown) => {
|
|
210
|
+
if (!active || sequence !== loadSequence) return;
|
|
211
|
+
input.onLoadError?.(normalizeQtiAssessmentItemPlayerLoadError(error));
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return () => {
|
|
215
|
+
active = false;
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function createQtiAssessmentItemPlayerHandle(
|
|
222
|
+
getElement: () => QtiAssessmentItemPlayer | null,
|
|
223
|
+
): QtiAssessmentItemPlayerHandle {
|
|
224
|
+
return {
|
|
225
|
+
get element() {
|
|
226
|
+
return requiredElement(getElement);
|
|
227
|
+
},
|
|
228
|
+
loadXml: (xml, options) => requiredElement(getElement).loadXml(xml, options),
|
|
229
|
+
loadUrl: (url, options) => requiredElement(getElement).loadUrl(url, options),
|
|
230
|
+
scoreAttempt: (options) => requiredElement(getElement).scoreAttempt(options),
|
|
231
|
+
restore: (state) => requiredElement(getElement).restore(state),
|
|
232
|
+
suspend: () => requiredElement(getElement).suspend(),
|
|
233
|
+
endAttempt: (options) => requiredElement(getElement).endAttempt(options),
|
|
234
|
+
reset: () => requiredElement(getElement).reset(),
|
|
235
|
+
clearItem: () => requiredElement(getElement).clearItem(),
|
|
236
|
+
serialize: () => requiredElement(getElement).serialize(),
|
|
237
|
+
getTextToSpeechTraversal: () => requiredElement(getElement).getTextToSpeechTraversal(),
|
|
238
|
+
getCatalogSupportResolution: (options) =>
|
|
239
|
+
requiredElement(getElement).getCatalogSupportResolution(options),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function normalizeQtiAssessmentItemPlayerLoadError(error: unknown): Error {
|
|
244
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function requiredElement(
|
|
248
|
+
getElement: () => QtiAssessmentItemPlayer | null,
|
|
249
|
+
): QtiAssessmentItemPlayer {
|
|
250
|
+
const element = getElement();
|
|
251
|
+
if (!element) throw new Error("QTI assessment item player element is not mounted.");
|
|
252
|
+
return element;
|
|
253
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** True when dev warnings are enabled (non-production Node, or browser without NODE_ENV). */
|
|
2
|
+
export function playerDevWarningsEnabled(): boolean {
|
|
3
|
+
const nodeEnv = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env
|
|
4
|
+
?.NODE_ENV;
|
|
5
|
+
return nodeEnv !== "production";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const warnedKeys = new Set<string>();
|
|
9
|
+
|
|
10
|
+
export function warnPlayerMessageOnce(code: string, message: string): void {
|
|
11
|
+
if (!playerDevWarningsEnabled() || warnedKeys.has(code)) return;
|
|
12
|
+
warnedKeys.add(code);
|
|
13
|
+
console.warn(`[qti3-player] ${message}`);
|
|
14
|
+
}
|