@longsightgroup/qti3-player 0.2.0 → 0.2.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.
- package/README.md +19 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +240 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +281 -27
package/README.md
CHANGED
|
@@ -39,6 +39,25 @@ player?.addEventListener("qti-statechange", (event) => {
|
|
|
39
39
|
});
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Player Chrome Messages
|
|
43
|
+
|
|
44
|
+
The player keeps authored QTI content language separate from player chrome such as
|
|
45
|
+
remove buttons. Chrome language defaults to the player `languageOfInterface` property,
|
|
46
|
+
the `language-of-interface` attribute, browser language, document language, then
|
|
47
|
+
English. Hosts can override it when delivery settings require a fixed interface language.
|
|
48
|
+
|
|
49
|
+
Built-in catalogs are currently available for English, Spanish (`es-MX`, `es-ES`),
|
|
50
|
+
Swedish (`sv-SE`), German (`de-DE`), Portuguese (`pt-BR`, `pt-PT`), and French
|
|
51
|
+
(`fr-FR`, `fr-CA`).
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
player.languageOfInterface = "es-MX";
|
|
55
|
+
player.messages = {
|
|
56
|
+
remove: () => "Eliminar",
|
|
57
|
+
removePair: ({ label }) => `Eliminar ${label}`,
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
42
61
|
## Portable Custom Interactions
|
|
43
62
|
|
|
44
63
|
For `qti-portable-custom-interaction`, the player renders a
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,14 @@ export interface QtiPlayerLoadOptions {
|
|
|
15
15
|
fetchXml?: QtiPlayerFetchXml | undefined;
|
|
16
16
|
resolveAsset?: QtiPlayerResolveAsset | undefined;
|
|
17
17
|
}
|
|
18
|
+
export interface QtiPlayerRemoveMessageParams {
|
|
19
|
+
label: string;
|
|
20
|
+
}
|
|
21
|
+
export interface QtiPlayerMessages {
|
|
22
|
+
remove: () => string;
|
|
23
|
+
removePair: (params: QtiPlayerRemoveMessageParams) => string;
|
|
24
|
+
}
|
|
25
|
+
export type QtiPlayerMessageOverrides = Partial<QtiPlayerMessages>;
|
|
18
26
|
export interface QtiReadyEventDetail {
|
|
19
27
|
item: QtiAssessmentItem;
|
|
20
28
|
}
|
|
@@ -61,11 +69,21 @@ export type QtiAssessmentItemPlayerCustomEventMap = {
|
|
|
61
69
|
};
|
|
62
70
|
declare const HTMLElementBase: typeof HTMLElement;
|
|
63
71
|
export declare class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
72
|
+
static get observedAttributes(): string[];
|
|
64
73
|
private documentModel?;
|
|
65
74
|
private session?;
|
|
66
75
|
private resolveAsset;
|
|
67
76
|
private validationMessages;
|
|
77
|
+
private languageOfInterfaceOverride;
|
|
78
|
+
private messageOverrides;
|
|
68
79
|
private sessionControl;
|
|
80
|
+
get languageOfInterface(): string;
|
|
81
|
+
set languageOfInterface(value: string | undefined);
|
|
82
|
+
get locale(): string;
|
|
83
|
+
set locale(value: string | undefined);
|
|
84
|
+
get messages(): QtiPlayerMessageOverrides;
|
|
85
|
+
set messages(value: QtiPlayerMessageOverrides | undefined);
|
|
86
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
69
87
|
loadXml(xml: string, options?: QtiPlayerLoadOptions): Promise<void>;
|
|
70
88
|
loadUrl(url: string, options?: QtiPlayerLoadOptions): Promise<void>;
|
|
71
89
|
scoreAttempt(options?: QtiScoreAttemptOptions): QtiScoreResult | undefined;
|
|
@@ -78,6 +96,8 @@ export declare class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
|
78
96
|
getCatalogSupportResolution(options?: QtiCatalogSupportResolutionOptions): QtiCatalogSupportResolution | undefined;
|
|
79
97
|
private emitStateChange;
|
|
80
98
|
private dispatchPlayerEvent;
|
|
99
|
+
private playerMessages;
|
|
100
|
+
private rerenderIfLoaded;
|
|
81
101
|
private render;
|
|
82
102
|
private renderInteraction;
|
|
83
103
|
private renderPortableCustomResponse;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAGtB,KAAK,aAAa,EAElB,KAAK,cAAc,EAGnB,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,cAAc,EACnB,KAAK,2BAA2B,EAChC,KAAK,kCAAkC,EACvC,KAAK,wBAAwB,EAC7B,KAAK,QAAQ,EACd,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,uBAAuB;IACtC,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AACjE,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACtC,cAAc,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACzC,YAAY,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CAClD;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,MAAM,CAAC;IACrB,UAAU,EAAE,CAAC,MAAM,EAAE,4BAA4B,KAAK,MAAM,CAAC;CAC9D;AAED,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA4B;IAC3C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,iCAAiC;IAChD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,cAAc,CAAC;IAC5B,UAAU,EAAE,2BAA2B,CAAC;IACxC,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;CACjD;AAED,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAEjD,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,EAAE,aAAa,EAAE,CAAC;IACpC,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,qCAAqC;IACpD,WAAW,EAAE,mBAAmB,CAAC;IACjC,iBAAiB,EAAE,yBAAyB,CAAC;IAC7C,oBAAoB,EAAE,4BAA4B,CAAC;IACnD,2BAA2B,EAAE,iCAAiC,CAAC;IAC/D,WAAW,EAAE,mBAAmB,CAAC;IACjC,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,aAAa,EAAE,qBAAqB,CAAC;IACrC,gBAAgB,EAAE,wBAAwB,CAAC;CAC5C;AAED,MAAM,MAAM,gCAAgC,GAAG,MAAM,qCAAqC,CAAC;AAE3F,MAAM,MAAM,4BAA4B,CACtC,CAAC,SAAS,gCAAgC,GAAG,gCAAgC,IAC3E,WAAW,CAAC,qCAAqC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D,MAAM,MAAM,qCAAqC,GAAG;KACjD,CAAC,IAAI,gCAAgC,GAAG,WAAW,CAAC,qCAAqC,CAAC,CAAC,CAAC,CAAC;CAC/F,CAAC;AAEF,QAAA,MAAM,eAAe,EAAE,OAAO,WAOO,CAAC;AAEtC,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,OAAO,CAAC,aAAa,CAAC,CAAc;IACpC,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,kBAAkB,CAAuB;IACjD,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,cAAc,CAGpB;IAEF,IAAI,mBAAmB,IAAI,MAAM,CAOhC;IAED,IAAI,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAGhD;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEnC;IAED,IAAI,QAAQ,IAAI,yBAAyB,CAExC;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,yBAAyB,GAAG,SAAS,EAGxD;IAED,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAOxF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BvE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7E,YAAY,CAAC,OAAO,GAAE,sBAA2B,GAAG,cAAc,GAAG,SAAS;IA6B9E,KAAK,IAAI,IAAI;IAUb,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAmBvC,OAAO,IAAI,IAAI;IASf,UAAU,CAAC,OAAO,GAAE,sBAA2B,GAAG,IAAI;IAgBtD,SAAS,IAAI,iBAAiB,GAAG,SAAS;IAM1C,wBAAwB,IAAI,wBAAwB,GAAG,SAAS;IAKhE,2BAA2B,CACzB,OAAO,GAAE,kCAAuC,GAC/C,2BAA2B,GAAG,SAAS;IAK1C,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,MAAM;IA0Cd,OAAO,CAAC,iBAAiB;IAqJzB,OAAO,CAAC,4BAA4B;IA6FpC,OAAO,CAAC,yBAAyB;IAiCjC,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,iBAAiB;IA0CzB,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,sBAAsB;IA+B9B,OAAO,CAAC,yBAAyB;IAgCjC,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,yBAAyB;IAsBjC,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,iBAAiB;IAyDzB,OAAO,CAAC,wBAAwB;IAgChC,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,cAAc;CAgBvB;AAED,wBAAgB,6BAA6B,IAAI,IAAI,CAIpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,4BAA4B,EAAE,uBAAuB,CAAC;KACvD;CACF"}
|
package/dist/index.js
CHANGED
|
@@ -7,14 +7,48 @@ const HTMLElementBase = globalThis.HTMLElement ??
|
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
export class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
10
|
+
static get observedAttributes() {
|
|
11
|
+
return ["language-of-interface", "locale"];
|
|
12
|
+
}
|
|
10
13
|
documentModel;
|
|
11
14
|
session;
|
|
12
15
|
resolveAsset;
|
|
13
16
|
validationMessages = [];
|
|
17
|
+
languageOfInterfaceOverride;
|
|
18
|
+
messageOverrides = {};
|
|
14
19
|
sessionControl = {
|
|
15
20
|
validateResponses: true,
|
|
16
21
|
showFeedback: true,
|
|
17
22
|
};
|
|
23
|
+
get languageOfInterface() {
|
|
24
|
+
return (this.languageOfInterfaceOverride ??
|
|
25
|
+
this.getAttribute?.("language-of-interface") ??
|
|
26
|
+
this.getAttribute?.("locale") ??
|
|
27
|
+
defaultPlayerLocale(this));
|
|
28
|
+
}
|
|
29
|
+
set languageOfInterface(value) {
|
|
30
|
+
this.languageOfInterfaceOverride = normalizedLocale(value);
|
|
31
|
+
this.rerenderIfLoaded();
|
|
32
|
+
}
|
|
33
|
+
get locale() {
|
|
34
|
+
return this.languageOfInterface;
|
|
35
|
+
}
|
|
36
|
+
set locale(value) {
|
|
37
|
+
this.languageOfInterface = value;
|
|
38
|
+
}
|
|
39
|
+
get messages() {
|
|
40
|
+
return this.messageOverrides;
|
|
41
|
+
}
|
|
42
|
+
set messages(value) {
|
|
43
|
+
this.messageOverrides = value ?? {};
|
|
44
|
+
this.rerenderIfLoaded();
|
|
45
|
+
}
|
|
46
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
47
|
+
if ((name !== "language-of-interface" && name !== "locale") || oldValue === newValue) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.rerenderIfLoaded();
|
|
51
|
+
}
|
|
18
52
|
async loadXml(xml, options = {}) {
|
|
19
53
|
this.sessionControl = {
|
|
20
54
|
validateResponses: options.sessionControl?.validateResponses ?? true,
|
|
@@ -147,6 +181,16 @@ export class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
|
147
181
|
dispatchPlayerEvent(type, detail) {
|
|
148
182
|
this.dispatchEvent(new CustomEvent(type, { detail }));
|
|
149
183
|
}
|
|
184
|
+
playerMessages() {
|
|
185
|
+
return resolvePlayerMessages(this.languageOfInterface, this.messageOverrides);
|
|
186
|
+
}
|
|
187
|
+
rerenderIfLoaded() {
|
|
188
|
+
if (!this.documentModel)
|
|
189
|
+
return;
|
|
190
|
+
this.render();
|
|
191
|
+
this.renderValidationMessages();
|
|
192
|
+
this.updateAttemptAvailability();
|
|
193
|
+
}
|
|
150
194
|
render() {
|
|
151
195
|
const documentModel = this.documentModel;
|
|
152
196
|
if (!documentModel)
|
|
@@ -186,6 +230,7 @@ export class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
|
186
230
|
this.replaceChildren(root);
|
|
187
231
|
}
|
|
188
232
|
renderInteraction(interaction) {
|
|
233
|
+
const messages = this.playerMessages();
|
|
189
234
|
const field = document.createElement("section");
|
|
190
235
|
field.className = `qti3-interaction qti3-${interaction.type}`;
|
|
191
236
|
field.classList.add(...qtiSharedClassNames(interaction.attributes.class));
|
|
@@ -212,7 +257,7 @@ export class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
|
212
257
|
};
|
|
213
258
|
const currentValue = responseIdentifier ? this.currentResponseValue(responseIdentifier) : null;
|
|
214
259
|
if (interaction.type === "graphicOrder") {
|
|
215
|
-
field.append(renderGraphicOrderResponse(interaction, update, currentValue));
|
|
260
|
+
field.append(renderGraphicOrderResponse(interaction, update, currentValue, messages));
|
|
216
261
|
return field;
|
|
217
262
|
}
|
|
218
263
|
if (usesOrderedResponse(interaction)) {
|
|
@@ -224,15 +269,15 @@ export class QtiAssessmentItemPlayer extends HTMLElementBase {
|
|
|
224
269
|
return field;
|
|
225
270
|
}
|
|
226
271
|
if (interaction.type === "graphicAssociate") {
|
|
227
|
-
field.append(renderGraphicAssociateResponse(interaction, update, currentValue));
|
|
272
|
+
field.append(renderGraphicAssociateResponse(interaction, update, currentValue, messages));
|
|
228
273
|
return field;
|
|
229
274
|
}
|
|
230
275
|
if (interaction.type === "match") {
|
|
231
|
-
field.append(renderMatchResponse(interaction, update, currentValue));
|
|
276
|
+
field.append(renderMatchResponse(interaction, update, currentValue, messages));
|
|
232
277
|
return field;
|
|
233
278
|
}
|
|
234
279
|
if (usesPairResponse(interaction)) {
|
|
235
|
-
field.append(renderPairResponse(interaction, update, currentValue));
|
|
280
|
+
field.append(renderPairResponse(interaction, update, currentValue, messages));
|
|
236
281
|
return field;
|
|
237
282
|
}
|
|
238
283
|
if (interaction.type === "hotspot" && interaction.object) {
|
|
@@ -727,6 +772,137 @@ export function defineQtiAssessmentItemPlayer() {
|
|
|
727
772
|
customElements.define("qti-assessment-item-player", QtiAssessmentItemPlayer);
|
|
728
773
|
}
|
|
729
774
|
}
|
|
775
|
+
const defaultEnglishPlayerMessages = {
|
|
776
|
+
remove: () => "Remove",
|
|
777
|
+
removePair: ({ label }) => `Remove ${label}`,
|
|
778
|
+
};
|
|
779
|
+
const playerMessages = {
|
|
780
|
+
defaultEnglish: defaultEnglishPlayerMessages,
|
|
781
|
+
spanish: playerMessageCatalog("Quitar", ({ label }) => `Quitar ${label}`),
|
|
782
|
+
swedish: playerMessageCatalog("Ta bort", ({ label }) => `Ta bort ${label}`),
|
|
783
|
+
german: playerMessageCatalog("Entfernen", ({ label }) => `${label} entfernen`),
|
|
784
|
+
portuguese: playerMessageCatalog("Remover", ({ label }) => `Remover ${label}`),
|
|
785
|
+
french: playerMessageCatalog("Supprimer", ({ label }) => `Supprimer ${label}`),
|
|
786
|
+
};
|
|
787
|
+
const builtInPlayerMessageCatalogs = new Map([
|
|
788
|
+
["en", playerMessages.defaultEnglish],
|
|
789
|
+
["es", playerMessages.spanish],
|
|
790
|
+
["es-es", playerMessages.spanish],
|
|
791
|
+
["es-mx", playerMessages.spanish],
|
|
792
|
+
["sv", playerMessages.swedish],
|
|
793
|
+
["sv-se", playerMessages.swedish],
|
|
794
|
+
["de", playerMessages.german],
|
|
795
|
+
["de-de", playerMessages.german],
|
|
796
|
+
["pt", playerMessages.portuguese],
|
|
797
|
+
["pt-br", playerMessages.portuguese],
|
|
798
|
+
["pt-pt", playerMessages.portuguese],
|
|
799
|
+
["fr", playerMessages.french],
|
|
800
|
+
["fr-ca", playerMessages.french],
|
|
801
|
+
["fr-fr", playerMessages.french],
|
|
802
|
+
]);
|
|
803
|
+
function playerMessageCatalog(remove, removePair) {
|
|
804
|
+
return {
|
|
805
|
+
remove: () => remove,
|
|
806
|
+
removePair,
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function resolvePlayerMessages(locale, overrides) {
|
|
810
|
+
const catalog = builtInPlayerMessageCatalog(locale);
|
|
811
|
+
return {
|
|
812
|
+
remove: overrides.remove ?? catalog?.remove ?? defaultEnglishPlayerMessages.remove,
|
|
813
|
+
removePair: overrides.removePair ?? catalog?.removePair ?? defaultEnglishPlayerMessages.removePair,
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function builtInPlayerMessageCatalog(locale) {
|
|
817
|
+
for (const candidate of localeFallbacks(locale)) {
|
|
818
|
+
const catalog = builtInPlayerMessageCatalogs.get(candidate);
|
|
819
|
+
if (catalog)
|
|
820
|
+
return catalog;
|
|
821
|
+
}
|
|
822
|
+
return undefined;
|
|
823
|
+
}
|
|
824
|
+
function localeFallbacks(locale) {
|
|
825
|
+
const normalized = normalizedLocale(locale)?.toLowerCase();
|
|
826
|
+
if (!normalized)
|
|
827
|
+
return ["en"];
|
|
828
|
+
const parts = normalized.split("-");
|
|
829
|
+
const fallbacks = [];
|
|
830
|
+
for (let length = parts.length; length > 0; length -= 1) {
|
|
831
|
+
fallbacks.push(parts.slice(0, length).join("-"));
|
|
832
|
+
}
|
|
833
|
+
return fallbacks.includes("en") ? fallbacks : [...fallbacks, "en"];
|
|
834
|
+
}
|
|
835
|
+
function normalizedLocale(value) {
|
|
836
|
+
const trimmed = value?.trim();
|
|
837
|
+
if (!trimmed)
|
|
838
|
+
return undefined;
|
|
839
|
+
try {
|
|
840
|
+
return Intl.getCanonicalLocales(trimmed)[0] ?? trimmed;
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
return trimmed;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function defaultPlayerLocale(host) {
|
|
847
|
+
const elementLanguage = normalizedLocale(host?.getAttribute("lang"));
|
|
848
|
+
if (elementLanguage)
|
|
849
|
+
return elementLanguage;
|
|
850
|
+
const navigatorLanguages = globalThis.navigator?.languages ?? [];
|
|
851
|
+
for (const language of navigatorLanguages) {
|
|
852
|
+
const normalized = normalizedLocale(language);
|
|
853
|
+
if (normalized)
|
|
854
|
+
return normalized;
|
|
855
|
+
}
|
|
856
|
+
return (normalizedLocale(globalThis.navigator?.language) ??
|
|
857
|
+
normalizedLocale(host?.closest("[lang]")?.getAttribute("lang")) ??
|
|
858
|
+
normalizedLocale(host?.ownerDocument?.documentElement.lang) ??
|
|
859
|
+
normalizedLocale(globalThis.document?.documentElement.lang) ??
|
|
860
|
+
"en");
|
|
861
|
+
}
|
|
862
|
+
function removeButton(label, messages) {
|
|
863
|
+
const safeLabel = label?.trim() || messages.remove();
|
|
864
|
+
const button = document.createElement("button");
|
|
865
|
+
button.type = "button";
|
|
866
|
+
button.className = "qti3-icon-button qti3-remove-button";
|
|
867
|
+
button.title = messages.remove();
|
|
868
|
+
button.setAttribute("aria-label", messages.removePair({ label: safeLabel }));
|
|
869
|
+
button.append(trashIcon());
|
|
870
|
+
return button;
|
|
871
|
+
}
|
|
872
|
+
function inlineIcon(className, paths) {
|
|
873
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
874
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
875
|
+
svg.setAttribute("width", "24");
|
|
876
|
+
svg.setAttribute("height", "24");
|
|
877
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
878
|
+
svg.setAttribute("fill", "none");
|
|
879
|
+
svg.setAttribute("stroke", "currentColor");
|
|
880
|
+
svg.setAttribute("stroke-width", "2");
|
|
881
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
882
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
883
|
+
svg.setAttribute("aria-hidden", "true");
|
|
884
|
+
svg.setAttribute("focusable", "false");
|
|
885
|
+
svg.setAttribute("class", className);
|
|
886
|
+
for (const entry of paths) {
|
|
887
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
888
|
+
if (typeof entry === "string") {
|
|
889
|
+
path.setAttribute("d", entry);
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
path.setAttribute("d", entry.d);
|
|
893
|
+
if (entry.stroke) {
|
|
894
|
+
path.setAttribute("stroke", entry.stroke);
|
|
895
|
+
path.style.stroke = entry.stroke;
|
|
896
|
+
}
|
|
897
|
+
if (entry.fill) {
|
|
898
|
+
path.setAttribute("fill", entry.fill);
|
|
899
|
+
path.style.fill = entry.fill;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
svg.append(path);
|
|
903
|
+
}
|
|
904
|
+
return svg;
|
|
905
|
+
}
|
|
730
906
|
function renderChoice(interaction, update, currentValue) {
|
|
731
907
|
const group = responseGroup("qti3-choice-group");
|
|
732
908
|
const multiple = interaction.responseCardinality === "multiple" || interaction.responseCardinality === "ordered";
|
|
@@ -790,19 +966,32 @@ function responseGroup(className) {
|
|
|
790
966
|
group.className = ["qti3-response-group", className].filter(Boolean).join(" ");
|
|
791
967
|
return group;
|
|
792
968
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
969
|
+
function trashIcon() {
|
|
970
|
+
return inlineIcon("qti3-trash-icon", [
|
|
971
|
+
{ d: "M0 0h24v24H0z", stroke: "none", fill: "none" },
|
|
972
|
+
"M4 7l16 0",
|
|
973
|
+
"M10 11l0 6",
|
|
974
|
+
"M14 11l0 6",
|
|
975
|
+
"M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12",
|
|
976
|
+
"M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3",
|
|
977
|
+
]);
|
|
978
|
+
}
|
|
979
|
+
const movementIconPaths = {
|
|
980
|
+
up: ["M12 5l0 14", "M18 11l-6 -6", "M6 11l6 -6"],
|
|
981
|
+
down: ["M12 5l0 14", "M18 13l-6 6", "M6 13l6 6"],
|
|
982
|
+
left: ["M5 12l14 0", "M5 12l6 6", "M5 12l6 -6"],
|
|
983
|
+
right: ["M5 12l14 0", "M13 18l6 -6", "M13 6l6 6"],
|
|
798
984
|
};
|
|
985
|
+
function movementIcon(direction) {
|
|
986
|
+
return inlineIcon("qti3-movement-icon", movementIconPaths[direction]);
|
|
987
|
+
}
|
|
799
988
|
function movementButton(direction, accessibleName, onClick) {
|
|
800
989
|
const button = document.createElement("button");
|
|
801
990
|
button.type = "button";
|
|
802
|
-
button.className = "qti3-icon-button";
|
|
991
|
+
button.className = "qti3-icon-button qti3-move-button";
|
|
803
992
|
button.dataset.moveDirection = direction;
|
|
804
|
-
button.textContent = movementGlyphs[direction];
|
|
805
993
|
button.setAttribute("aria-label", accessibleName);
|
|
994
|
+
button.append(movementIcon(direction));
|
|
806
995
|
button.addEventListener("click", onClick);
|
|
807
996
|
return button;
|
|
808
997
|
}
|
|
@@ -1007,7 +1196,7 @@ function renderOrderedResponse(interaction, update, currentValue) {
|
|
|
1007
1196
|
group.append(list);
|
|
1008
1197
|
return group;
|
|
1009
1198
|
}
|
|
1010
|
-
function renderPairResponse(interaction, update, currentValue) {
|
|
1199
|
+
function renderPairResponse(interaction, update, currentValue, messages) {
|
|
1011
1200
|
const group = responseGroup();
|
|
1012
1201
|
appendGraphicContext(group, interaction);
|
|
1013
1202
|
const sources = sourceChoices(interaction);
|
|
@@ -1066,10 +1255,7 @@ function renderPairResponse(interaction, update, currentValue) {
|
|
|
1066
1255
|
item.className = "qti3-pair-chip";
|
|
1067
1256
|
const text = document.createElement("span");
|
|
1068
1257
|
text.textContent = `${choiceText(sources, source)} to ${choiceText(targets, target)}`;
|
|
1069
|
-
const remove =
|
|
1070
|
-
remove.type = "button";
|
|
1071
|
-
remove.textContent = "Remove";
|
|
1072
|
-
remove.setAttribute("aria-label", `Remove ${text.textContent}`);
|
|
1258
|
+
const remove = removeButton(text.textContent, messages);
|
|
1073
1259
|
remove.addEventListener("click", () => {
|
|
1074
1260
|
const index = selectedPairs.indexOf(pair);
|
|
1075
1261
|
if (index >= 0)
|
|
@@ -1124,7 +1310,7 @@ function renderPairResponse(interaction, update, currentValue) {
|
|
|
1124
1310
|
group.append(selector, pairList);
|
|
1125
1311
|
return group;
|
|
1126
1312
|
}
|
|
1127
|
-
function renderMatchResponse(interaction, update, currentValue) {
|
|
1313
|
+
function renderMatchResponse(interaction, update, currentValue, messages) {
|
|
1128
1314
|
const group = responseGroup();
|
|
1129
1315
|
const sources = sourceChoices(interaction);
|
|
1130
1316
|
const targets = targetChoices(interaction);
|
|
@@ -1176,10 +1362,7 @@ function renderMatchResponse(interaction, update, currentValue) {
|
|
|
1176
1362
|
item.className = "qti3-pair-chip";
|
|
1177
1363
|
const text = document.createElement("span");
|
|
1178
1364
|
text.textContent = label;
|
|
1179
|
-
const remove =
|
|
1180
|
-
remove.type = "button";
|
|
1181
|
-
remove.textContent = "Remove";
|
|
1182
|
-
remove.setAttribute("aria-label", `Remove ${label}`);
|
|
1365
|
+
const remove = removeButton(label, messages);
|
|
1183
1366
|
remove.addEventListener("click", () => {
|
|
1184
1367
|
removePair(pair);
|
|
1185
1368
|
syncPressed();
|
|
@@ -1310,7 +1493,7 @@ function pairRegionLabels(interaction) {
|
|
|
1310
1493
|
return { source: "Prompt", target: "Match" };
|
|
1311
1494
|
return { source: "Source", target: "Target" };
|
|
1312
1495
|
}
|
|
1313
|
-
function renderGraphicOrderResponse(interaction, update, currentValue) {
|
|
1496
|
+
function renderGraphicOrderResponse(interaction, update, currentValue, messages) {
|
|
1314
1497
|
const group = responseGroup();
|
|
1315
1498
|
const width = objectWidth(interaction);
|
|
1316
1499
|
const height = objectHeight(interaction);
|
|
@@ -1471,10 +1654,7 @@ function renderGraphicOrderResponse(interaction, update, currentValue) {
|
|
|
1471
1654
|
up.disabled = index === 0;
|
|
1472
1655
|
const down = movementButton("down", movementLabel(choiceLabel, "down"), () => moveHotspot(choice.identifier, 1));
|
|
1473
1656
|
down.disabled = index === currentChoices.length - 1;
|
|
1474
|
-
const remove =
|
|
1475
|
-
remove.type = "button";
|
|
1476
|
-
remove.textContent = "Remove";
|
|
1477
|
-
remove.setAttribute("aria-label", `Remove ${choiceLabel}`);
|
|
1657
|
+
const remove = removeButton(choiceLabel, messages);
|
|
1478
1658
|
remove.addEventListener("click", () => removeHotspot(choice.identifier));
|
|
1479
1659
|
item.append(label, up, down, remove);
|
|
1480
1660
|
return item;
|
|
@@ -1518,7 +1698,7 @@ function renderGraphicOrderResponse(interaction, update, currentValue) {
|
|
|
1518
1698
|
group.append(surface, summary, list);
|
|
1519
1699
|
return group;
|
|
1520
1700
|
}
|
|
1521
|
-
function renderGraphicAssociateResponse(interaction, update, currentValue) {
|
|
1701
|
+
function renderGraphicAssociateResponse(interaction, update, currentValue, messages) {
|
|
1522
1702
|
const group = responseGroup();
|
|
1523
1703
|
const width = objectWidth(interaction);
|
|
1524
1704
|
const height = objectHeight(interaction);
|
|
@@ -1721,10 +1901,7 @@ function renderGraphicAssociateResponse(interaction, update, currentValue) {
|
|
|
1721
1901
|
item.className = "qti3-pair-chip";
|
|
1722
1902
|
const text = document.createElement("span");
|
|
1723
1903
|
text.textContent = pairLabel;
|
|
1724
|
-
const remove =
|
|
1725
|
-
remove.type = "button";
|
|
1726
|
-
remove.textContent = "Remove";
|
|
1727
|
-
remove.setAttribute("aria-label", `Remove ${pairLabel}`);
|
|
1904
|
+
const remove = removeButton(pairLabel, messages);
|
|
1728
1905
|
remove.addEventListener("click", () => removePair(pair));
|
|
1729
1906
|
item.append(text, remove);
|
|
1730
1907
|
return item;
|
|
@@ -2041,6 +2218,7 @@ function renderGraphicGapMatchResponse(interaction, update, currentValue) {
|
|
|
2041
2218
|
renderTargets();
|
|
2042
2219
|
commit();
|
|
2043
2220
|
});
|
|
2221
|
+
button.style.position = "absolute";
|
|
2044
2222
|
placeHotspotButton(button, gap, width, height);
|
|
2045
2223
|
if (assigned) {
|
|
2046
2224
|
const assignedLabel = document.createElement("span");
|
|
@@ -4118,6 +4296,37 @@ function playerStyleElement() {
|
|
|
4118
4296
|
line-height: 1;
|
|
4119
4297
|
}
|
|
4120
4298
|
|
|
4299
|
+
.qti3-remove-button {
|
|
4300
|
+
border: 1px solid currentColor;
|
|
4301
|
+
background: transparent;
|
|
4302
|
+
color: inherit;
|
|
4303
|
+
cursor: pointer;
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
.qti3-remove-button:hover {
|
|
4307
|
+
background: color-mix(in srgb, currentColor 14%, transparent);
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
.qti3-trash-icon {
|
|
4311
|
+
inline-size: 1.125rem;
|
|
4312
|
+
block-size: 1.125rem;
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
.qti3-movement-icon {
|
|
4316
|
+
inline-size: 1rem;
|
|
4317
|
+
block-size: 1rem;
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
.qti3-trash-icon path,
|
|
4321
|
+
.qti3-movement-icon path {
|
|
4322
|
+
fill: none;
|
|
4323
|
+
stroke: currentColor;
|
|
4324
|
+
stroke-width: 2;
|
|
4325
|
+
stroke-linecap: round;
|
|
4326
|
+
stroke-linejoin: round;
|
|
4327
|
+
vector-effect: non-scaling-stroke;
|
|
4328
|
+
}
|
|
4329
|
+
|
|
4121
4330
|
.qti3-token[aria-pressed="true"],
|
|
4122
4331
|
.qti3-pair-chip {
|
|
4123
4332
|
background: Highlight;
|