@intellectif/lk-react 15.0.0 → 16.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/README.md +3 -3
- package/dist/{WrittenResponse-BPbhERgk.d.cts → WrittenResponse-6tSj6U3G.d.cts} +1 -1
- package/dist/{WrittenResponse-ZAxLR_AU.d.ts → WrittenResponse-xJ7I_5yq.d.ts} +1 -1
- package/dist/{chunk-53YOZOGN.cjs → chunk-2S3MK4VX.cjs} +5 -5
- package/dist/{chunk-53YOZOGN.cjs.map → chunk-2S3MK4VX.cjs.map} +1 -1
- package/dist/{chunk-4H3JTX74.cjs → chunk-2TFNDHJ5.cjs} +13 -13
- package/dist/{chunk-4H3JTX74.cjs.map → chunk-2TFNDHJ5.cjs.map} +1 -1
- package/dist/{chunk-AF72QUBI.js → chunk-2XNCPLHU.js} +5 -5
- package/dist/{chunk-AF72QUBI.js.map → chunk-2XNCPLHU.js.map} +1 -1
- package/dist/{chunk-RT5FTHUU.js → chunk-3UXRX4KU.js} +5 -5
- package/dist/{chunk-RT5FTHUU.js.map → chunk-3UXRX4KU.js.map} +1 -1
- package/dist/{chunk-CE6OGJE6.js → chunk-5QHVYOME.js} +5 -5
- package/dist/{chunk-CE6OGJE6.js.map → chunk-5QHVYOME.js.map} +1 -1
- package/dist/{chunk-EKBEKDWO.cjs → chunk-B6S5ADHR.cjs} +13 -13
- package/dist/{chunk-EKBEKDWO.cjs.map → chunk-B6S5ADHR.cjs.map} +1 -1
- package/dist/{chunk-QM4AV6EG.js → chunk-BI547RIA.js} +4 -4
- package/dist/{chunk-QM4AV6EG.js.map → chunk-BI547RIA.js.map} +1 -1
- package/dist/{chunk-WKOV25NP.cjs → chunk-DFMBVA7D.cjs} +8 -8
- package/dist/{chunk-WKOV25NP.cjs.map → chunk-DFMBVA7D.cjs.map} +1 -1
- package/dist/{chunk-ZJMTHPHY.js → chunk-DKZWUIQD.js} +3 -3
- package/dist/{chunk-ZJMTHPHY.js.map → chunk-DKZWUIQD.js.map} +1 -1
- package/dist/{chunk-2HAEQDHQ.js → chunk-DVOPHC2Q.js} +755 -201
- package/dist/chunk-DVOPHC2Q.js.map +1 -0
- package/dist/{chunk-DQ3VVU5T.cjs → chunk-FZWWVJE4.cjs} +24 -24
- package/dist/{chunk-DQ3VVU5T.cjs.map → chunk-FZWWVJE4.cjs.map} +1 -1
- package/dist/{chunk-I2QGW4GD.js → chunk-GDMM2WEG.js} +11 -11
- package/dist/{chunk-I2QGW4GD.js.map → chunk-GDMM2WEG.js.map} +1 -1
- package/dist/{chunk-QEB6O6GU.js → chunk-GUPBIEUA.js} +6 -6
- package/dist/{chunk-QEB6O6GU.js.map → chunk-GUPBIEUA.js.map} +1 -1
- package/dist/{chunk-33BQNWAQ.js → chunk-IQI5I2X5.js} +5 -5
- package/dist/{chunk-33BQNWAQ.js.map → chunk-IQI5I2X5.js.map} +1 -1
- package/dist/{chunk-YWQH35VO.js → chunk-JPDR6TTA.js} +4 -4
- package/dist/{chunk-YWQH35VO.js.map → chunk-JPDR6TTA.js.map} +1 -1
- package/dist/{chunk-UCKA22ZJ.cjs → chunk-K63VPFOV.cjs} +9 -2
- package/dist/chunk-K63VPFOV.cjs.map +1 -0
- package/dist/{chunk-52GXJNQ3.js → chunk-KSAINXRG.js} +9 -2
- package/dist/chunk-KSAINXRG.js.map +1 -0
- package/dist/{chunk-ZH5WT2IE.cjs → chunk-ML6L4LJH.cjs} +25 -25
- package/dist/{chunk-ZH5WT2IE.cjs.map → chunk-ML6L4LJH.cjs.map} +1 -1
- package/dist/{chunk-Z6TVOAO5.cjs → chunk-MQAZJQRE.cjs} +9 -2
- package/dist/chunk-MQAZJQRE.cjs.map +1 -0
- package/dist/{chunk-UAYWLYFW.cjs → chunk-OO24JGYH.cjs} +18 -18
- package/dist/{chunk-UAYWLYFW.cjs.map → chunk-OO24JGYH.cjs.map} +1 -1
- package/dist/{chunk-26GQO5I2.cjs → chunk-PTJTC6A3.cjs} +12 -12
- package/dist/{chunk-26GQO5I2.cjs.map → chunk-PTJTC6A3.cjs.map} +1 -1
- package/dist/{chunk-DP3DM7FR.cjs → chunk-PXGJHZKJ.cjs} +6 -6
- package/dist/{chunk-DP3DM7FR.cjs.map → chunk-PXGJHZKJ.cjs.map} +1 -1
- package/dist/{chunk-XMJE34FY.cjs → chunk-RSQFGF5W.cjs} +13 -13
- package/dist/{chunk-XMJE34FY.cjs.map → chunk-RSQFGF5W.cjs.map} +1 -1
- package/dist/{chunk-FKGUKFC7.cjs → chunk-TRP5Z3XW.cjs} +5 -5
- package/dist/{chunk-FKGUKFC7.cjs.map → chunk-TRP5Z3XW.cjs.map} +1 -1
- package/dist/{chunk-EVBYEWHO.js → chunk-UHKZXOXD.js} +9 -2
- package/dist/chunk-UHKZXOXD.js.map +1 -0
- package/dist/{chunk-IE5ZNCWN.js → chunk-VC2MPB2Y.js} +5 -5
- package/dist/{chunk-IE5ZNCWN.js.map → chunk-VC2MPB2Y.js.map} +1 -1
- package/dist/{chunk-SRD4KVAP.cjs → chunk-WOYCT5M6.cjs} +764 -209
- package/dist/chunk-WOYCT5M6.cjs.map +1 -0
- package/dist/{chunk-H6I2JDNW.js → chunk-ZEDMFTKD.js} +10 -10
- package/dist/{chunk-H6I2JDNW.js.map → chunk-ZEDMFTKD.js.map} +1 -1
- package/dist/{chunk-HA3VRF6E.js → chunk-ZPCO3CEB.js} +3 -3
- package/dist/{chunk-HA3VRF6E.js.map → chunk-ZPCO3CEB.js.map} +1 -1
- package/dist/{chunk-L7NE7PR4.cjs → chunk-ZSY4VANQ.cjs} +13 -13
- package/dist/{chunk-L7NE7PR4.cjs.map → chunk-ZSY4VANQ.cjs.map} +1 -1
- package/dist/components/ActivityPreview.cjs +12 -12
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +11 -11
- package/dist/components/ActivitySequence.cjs +13 -13
- package/dist/components/ActivitySequence.d.cts +2 -2
- package/dist/components/ActivitySequence.d.ts +2 -2
- package/dist/components/ActivitySequence.js +12 -12
- package/dist/components/Dictation.cjs +5 -5
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +4 -4
- package/dist/components/FillInTheBlanks.cjs +5 -5
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +4 -4
- package/dist/components/GapSelect.cjs +5 -5
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +4 -4
- package/dist/components/InteractiveVideo.cjs +15 -11
- package/dist/components/InteractiveVideo.d.cts +164 -7
- package/dist/components/InteractiveVideo.d.ts +164 -7
- package/dist/components/InteractiveVideo.js +10 -10
- package/dist/components/MultipleChoice.cjs +5 -5
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +4 -4
- package/dist/components/PronunciationFeedback.cjs +4 -4
- package/dist/components/PronunciationFeedback.d.cts +1 -1
- package/dist/components/PronunciationFeedback.d.ts +1 -1
- package/dist/components/PronunciationFeedback.js +3 -3
- package/dist/components/ReadAloud.cjs +6 -6
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +5 -5
- package/dist/components/StimulusPanel.cjs +4 -4
- package/dist/components/StimulusPanel.d.cts +1 -1
- package/dist/components/StimulusPanel.d.ts +1 -1
- package/dist/components/StimulusPanel.js +3 -3
- package/dist/components/WrittenResponse.cjs +5 -5
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +4 -4
- package/dist/i18n/LkIntlProvider.cjs +7 -7
- package/dist/i18n/LkIntlProvider.d.cts +2 -2
- package/dist/i18n/LkIntlProvider.d.ts +2 -2
- package/dist/i18n/LkIntlProvider.js +1 -1
- package/dist/index.cjs +41 -37
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -15
- package/dist/{strings-5588Q4Te.d.cts → strings-C6wM5aUQ.d.cts} +23 -2
- package/dist/{strings-D0IWcsUm.d.ts → strings-Cd_2F-Bw.d.ts} +23 -2
- package/dist/theme/ThemeProvider.cjs +6 -6
- package/dist/theme/ThemeProvider.js +1 -1
- package/dist/theme/defaults.css +7 -0
- package/dist/theme/skin.css +60 -6
- package/package.json +3 -3
- package/dist/chunk-2HAEQDHQ.js.map +0 -1
- package/dist/chunk-52GXJNQ3.js.map +0 -1
- package/dist/chunk-EVBYEWHO.js.map +0 -1
- package/dist/chunk-SRD4KVAP.cjs.map +0 -1
- package/dist/chunk-UCKA22ZJ.cjs.map +0 -1
- package/dist/chunk-Z6TVOAO5.cjs.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
4
|
+
var chunkZSY4VANQ_cjs = require('./chunk-ZSY4VANQ.cjs');
|
|
5
|
+
var chunk2TFNDHJ5_cjs = require('./chunk-2TFNDHJ5.cjs');
|
|
6
|
+
var chunkB6S5ADHR_cjs = require('./chunk-B6S5ADHR.cjs');
|
|
7
|
+
var chunkRSQFGF5W_cjs = require('./chunk-RSQFGF5W.cjs');
|
|
8
|
+
var chunkML6L4LJH_cjs = require('./chunk-ML6L4LJH.cjs');
|
|
9
|
+
var chunk2S3MK4VX_cjs = require('./chunk-2S3MK4VX.cjs');
|
|
10
|
+
var chunkMQAZJQRE_cjs = require('./chunk-MQAZJQRE.cjs');
|
|
11
11
|
var lkCore = require('@intellectif/lk-core');
|
|
12
12
|
var react = require('react');
|
|
13
13
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -109,6 +109,7 @@ var SHORTCUT_KEYS = [
|
|
|
109
109
|
{ action: "chapter", keys: "[ / ]" },
|
|
110
110
|
{ action: "mute", keys: "M" },
|
|
111
111
|
{ action: "captions", keys: "C" },
|
|
112
|
+
{ action: "secondary-captions", keys: "Shift + C" },
|
|
112
113
|
{ action: "transcript", keys: "T" },
|
|
113
114
|
{ action: "picture-in-picture", keys: "P" },
|
|
114
115
|
{ action: "fullscreen", keys: "F" },
|
|
@@ -116,17 +117,87 @@ var SHORTCUT_KEYS = [
|
|
|
116
117
|
];
|
|
117
118
|
var JUMP_SECONDS = 10;
|
|
118
119
|
var FRAME_SECONDS = 1 / 30;
|
|
120
|
+
|
|
121
|
+
// src/components/InteractiveVideo/tracks.ts
|
|
122
|
+
var primarySubtag = (tag) => (tag.split("-")[0] ?? tag).toLowerCase();
|
|
123
|
+
function preferCaptions(tracks) {
|
|
124
|
+
return tracks.find((track) => track.kind === "captions") ?? tracks[0];
|
|
125
|
+
}
|
|
126
|
+
function matchLanguage(tracks, language) {
|
|
127
|
+
const wanted = language.toLowerCase();
|
|
128
|
+
const exact = tracks.filter((track) => track.srclang.toLowerCase() === wanted);
|
|
129
|
+
if (exact.length > 0) {
|
|
130
|
+
return preferCaptions(exact);
|
|
131
|
+
}
|
|
132
|
+
const family = primarySubtag(wanted);
|
|
133
|
+
return preferCaptions(tracks.filter((track) => primarySubtag(track.srclang) === family));
|
|
134
|
+
}
|
|
135
|
+
function drawable(tracks) {
|
|
136
|
+
return tracks.filter((track) => track.kind === "captions" || track.kind === "subtitles");
|
|
137
|
+
}
|
|
138
|
+
function secondaryCandidates(tracks, primary) {
|
|
139
|
+
const taken = primary?.srclang.toLowerCase();
|
|
140
|
+
return drawable(tracks).filter((track) => track.srclang.toLowerCase() !== taken);
|
|
141
|
+
}
|
|
142
|
+
function resolveCaptionTracks(tracks, preferences) {
|
|
143
|
+
const usable = drawable(tracks);
|
|
144
|
+
if (usable.length === 0) {
|
|
145
|
+
return {};
|
|
146
|
+
}
|
|
147
|
+
const primary = (preferences.captionLanguage === null ? void 0 : matchLanguage(usable, preferences.captionLanguage)) ?? usable.find((track) => track.default === true) ?? usable[0];
|
|
148
|
+
const second = preferences.secondaryCaptionLanguage;
|
|
149
|
+
const secondary = second === null ? void 0 : matchLanguage(secondaryCandidates(usable, primary), second);
|
|
150
|
+
return secondary === void 0 ? { primary } : { primary, secondary };
|
|
151
|
+
}
|
|
152
|
+
var overlap = (a, b) => Math.min(a.end, b.end) - Math.max(a.start, b.start);
|
|
153
|
+
var LOOKBACK = 8;
|
|
154
|
+
function pairSecondaryCues(primary, secondary) {
|
|
155
|
+
const rows = primary.map(() => []);
|
|
156
|
+
for (const cue of secondary) {
|
|
157
|
+
let low = 0;
|
|
158
|
+
let high = primary.length;
|
|
159
|
+
while (low < high) {
|
|
160
|
+
const middle = low + high >> 1;
|
|
161
|
+
if (primary[middle].start < cue.end) {
|
|
162
|
+
low = middle + 1;
|
|
163
|
+
} else {
|
|
164
|
+
high = middle;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let best = -1;
|
|
168
|
+
let bestShare = 0;
|
|
169
|
+
let misses = 0;
|
|
170
|
+
for (let index = low - 1; index >= 0 && misses <= LOOKBACK; index -= 1) {
|
|
171
|
+
const share = overlap(primary[index], cue);
|
|
172
|
+
if (share > 0) {
|
|
173
|
+
misses = 0;
|
|
174
|
+
if (share >= bestShare) {
|
|
175
|
+
best = index;
|
|
176
|
+
bestShare = share;
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
misses += 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (best !== -1) {
|
|
183
|
+
rows[best].push(cue);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return rows;
|
|
187
|
+
}
|
|
119
188
|
function Menu({
|
|
120
189
|
label,
|
|
121
190
|
onClose,
|
|
122
|
-
children
|
|
191
|
+
children,
|
|
192
|
+
returnTo
|
|
123
193
|
}) {
|
|
124
194
|
const ref = react.useRef(null);
|
|
125
195
|
react.useEffect(() => {
|
|
126
196
|
const items = ref.current?.querySelectorAll('[role^="menuitem"]');
|
|
197
|
+
const back = returnTo === void 0 ? null : ref.current?.querySelector(`[data-menu-key="${returnTo}"]`);
|
|
127
198
|
const checked = ref.current?.querySelector('[aria-checked="true"]');
|
|
128
|
-
(checked ?? items?.[0])?.focus();
|
|
129
|
-
}, []);
|
|
199
|
+
(back ?? checked ?? items?.[0])?.focus();
|
|
200
|
+
}, [returnTo]);
|
|
130
201
|
const onKeyDown = (event) => {
|
|
131
202
|
const items = Array.from(
|
|
132
203
|
ref.current?.querySelectorAll('[role^="menuitem"]') ?? []
|
|
@@ -178,66 +249,136 @@ function SpeedMenu({
|
|
|
178
249
|
function SettingsMenu({
|
|
179
250
|
preferences,
|
|
180
251
|
tracks,
|
|
181
|
-
|
|
252
|
+
primaryTrack,
|
|
253
|
+
secondaryTrack,
|
|
182
254
|
captionsFailed,
|
|
183
255
|
strings,
|
|
184
256
|
onChange,
|
|
257
|
+
onCaptionTrack,
|
|
258
|
+
onSecondaryTrack,
|
|
185
259
|
onShowShortcuts,
|
|
186
260
|
onClose
|
|
187
261
|
}) {
|
|
188
262
|
const [page, setPage] = react.useState("main");
|
|
189
|
-
const
|
|
263
|
+
const [returnTo, setReturnTo] = react.useState(void 0);
|
|
264
|
+
const open = (next) => {
|
|
265
|
+
setReturnTo(void 0);
|
|
266
|
+
setPage(next);
|
|
267
|
+
};
|
|
268
|
+
const leave = () => {
|
|
269
|
+
setReturnTo(page);
|
|
270
|
+
setPage(page === "second" ? "captions" : "main");
|
|
271
|
+
};
|
|
272
|
+
const captionsValue = !preferences.captions || primaryTrack === void 0 ? strings.videoOff : secondaryTrack === void 0 ? primaryTrack.label : strings.videoCaptionPair(primaryTrack.label, secondaryTrack.label);
|
|
273
|
+
const partners = secondaryCandidates(tracks, primaryTrack);
|
|
274
|
+
const failedNotes = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
275
|
+
captionsFailed.primary && page === "captions" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-iv-menu-note", role: "status", children: strings.videoCaptionsFailed }) : null,
|
|
276
|
+
captionsFailed.secondary ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-iv-menu-note", role: "status", children: strings.videoSecondaryCaptionsFailed }) : null
|
|
277
|
+
] });
|
|
190
278
|
const back = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
191
279
|
"button",
|
|
192
280
|
{
|
|
193
281
|
type: "button",
|
|
194
282
|
role: "menuitem",
|
|
195
283
|
className: "lk-iv-menu-item lk-iv-menu-back",
|
|
196
|
-
onClick:
|
|
284
|
+
onClick: leave,
|
|
197
285
|
children: [
|
|
198
286
|
/* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { direction: "left" }),
|
|
199
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: page === "captions" ? strings.videoCaptionLanguage : strings.videoCaptionSize })
|
|
287
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: page === "captions" ? strings.videoCaptionLanguage : page === "second" ? strings.videoSecondCaptionLanguage : strings.videoCaptionSize })
|
|
200
288
|
]
|
|
201
289
|
}
|
|
202
290
|
);
|
|
291
|
+
const trackRadio = (track, on, choose) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
292
|
+
"button",
|
|
293
|
+
{
|
|
294
|
+
type: "button",
|
|
295
|
+
role: "menuitemradio",
|
|
296
|
+
"aria-checked": on,
|
|
297
|
+
className: "lk-iv-menu-item",
|
|
298
|
+
lang: track.srclang,
|
|
299
|
+
onClick: choose,
|
|
300
|
+
children: [
|
|
301
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { dir: "auto", children: track.label }),
|
|
302
|
+
on ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) : null
|
|
303
|
+
]
|
|
304
|
+
},
|
|
305
|
+
`${track.kind}:${track.srclang}`
|
|
306
|
+
);
|
|
203
307
|
if (page === "captions") {
|
|
204
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
308
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
309
|
+
Menu,
|
|
310
|
+
{
|
|
311
|
+
label: strings.videoCaptionLanguage,
|
|
312
|
+
onClose,
|
|
313
|
+
returnTo,
|
|
314
|
+
children: [
|
|
315
|
+
back,
|
|
316
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
317
|
+
"button",
|
|
318
|
+
{
|
|
319
|
+
type: "button",
|
|
320
|
+
role: "menuitemradio",
|
|
321
|
+
"aria-checked": !preferences.captions,
|
|
322
|
+
className: "lk-iv-menu-item",
|
|
323
|
+
onClick: () => onChange({ captions: false }),
|
|
324
|
+
children: [
|
|
325
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: strings.videoOff }),
|
|
326
|
+
!preferences.captions ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) : null
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
),
|
|
330
|
+
tracks.map(
|
|
331
|
+
(track) => trackRadio(
|
|
332
|
+
track,
|
|
333
|
+
preferences.captions && primaryTrack === track,
|
|
334
|
+
() => onCaptionTrack(track)
|
|
335
|
+
)
|
|
336
|
+
),
|
|
337
|
+
partners.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
338
|
+
"button",
|
|
339
|
+
{
|
|
340
|
+
type: "button",
|
|
341
|
+
role: "menuitem",
|
|
342
|
+
className: "lk-iv-menu-item",
|
|
343
|
+
"data-menu-key": "second",
|
|
344
|
+
onClick: () => open("second"),
|
|
345
|
+
children: [
|
|
346
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: strings.videoSecondCaptionLanguage }),
|
|
347
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lk-iv-menu-value", children: [
|
|
348
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { dir: "auto", lang: secondaryTrack?.srclang, children: secondaryTrack === void 0 ? strings.videoOff : secondaryTrack.label }),
|
|
349
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { direction: "right" })
|
|
350
|
+
] })
|
|
351
|
+
]
|
|
352
|
+
}
|
|
353
|
+
) : null,
|
|
354
|
+
failedNotes
|
|
355
|
+
]
|
|
356
|
+
},
|
|
357
|
+
"captions"
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
if (page === "second") {
|
|
361
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Menu, { label: strings.videoSecondCaptionLanguage, onClose, children: [
|
|
205
362
|
back,
|
|
206
363
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
207
364
|
"button",
|
|
208
365
|
{
|
|
209
366
|
type: "button",
|
|
210
367
|
role: "menuitemradio",
|
|
211
|
-
"aria-checked":
|
|
368
|
+
"aria-checked": secondaryTrack === void 0,
|
|
212
369
|
className: "lk-iv-menu-item",
|
|
213
|
-
onClick: () =>
|
|
370
|
+
onClick: () => onSecondaryTrack(null),
|
|
214
371
|
children: [
|
|
215
372
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: strings.videoOff }),
|
|
216
|
-
|
|
373
|
+
secondaryTrack === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) : null
|
|
217
374
|
]
|
|
218
375
|
}
|
|
219
376
|
),
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
type: "button",
|
|
226
|
-
role: "menuitemradio",
|
|
227
|
-
"aria-checked": on,
|
|
228
|
-
className: "lk-iv-menu-item",
|
|
229
|
-
lang: track.srclang,
|
|
230
|
-
onClick: () => onChange({ captions: true, captionLanguage: track.srclang }),
|
|
231
|
-
children: [
|
|
232
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: track.label }),
|
|
233
|
-
on ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) : null
|
|
234
|
-
]
|
|
235
|
-
},
|
|
236
|
-
`${track.kind}:${track.srclang}`
|
|
237
|
-
);
|
|
238
|
-
}),
|
|
239
|
-
captionsFailed ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-iv-menu-note", role: "status", children: strings.videoCaptionsFailed }) : null
|
|
240
|
-
] }, "captions");
|
|
377
|
+
partners.map(
|
|
378
|
+
(track) => trackRadio(track, secondaryTrack === track, () => onSecondaryTrack(track))
|
|
379
|
+
),
|
|
380
|
+
failedNotes
|
|
381
|
+
] }, "second");
|
|
241
382
|
}
|
|
242
383
|
if (page === "size") {
|
|
243
384
|
return /* @__PURE__ */ jsxRuntime.jsxs(Menu, { label: strings.videoCaptionSize, onClose, children: [
|
|
@@ -259,7 +400,7 @@ function SettingsMenu({
|
|
|
259
400
|
))
|
|
260
401
|
] }, "size");
|
|
261
402
|
}
|
|
262
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Menu, { label: strings.videoSettings, onClose, children: [
|
|
403
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Menu, { label: strings.videoSettings, onClose, returnTo, children: [
|
|
263
404
|
tracks.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
264
405
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
265
406
|
"button",
|
|
@@ -267,11 +408,12 @@ function SettingsMenu({
|
|
|
267
408
|
type: "button",
|
|
268
409
|
role: "menuitem",
|
|
269
410
|
className: "lk-iv-menu-item",
|
|
270
|
-
|
|
411
|
+
"data-menu-key": "captions",
|
|
412
|
+
onClick: () => open("captions"),
|
|
271
413
|
children: [
|
|
272
414
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: strings.videoCaptionLanguage }),
|
|
273
415
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lk-iv-menu-value", children: [
|
|
274
|
-
captionsValue,
|
|
416
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { dir: "auto", children: captionsValue }),
|
|
275
417
|
/* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { direction: "right" })
|
|
276
418
|
] })
|
|
277
419
|
]
|
|
@@ -283,7 +425,8 @@ function SettingsMenu({
|
|
|
283
425
|
type: "button",
|
|
284
426
|
role: "menuitem",
|
|
285
427
|
className: "lk-iv-menu-item",
|
|
286
|
-
|
|
428
|
+
"data-menu-key": "size",
|
|
429
|
+
onClick: () => open("size"),
|
|
287
430
|
children: [
|
|
288
431
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: strings.videoCaptionSize }),
|
|
289
432
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lk-iv-menu-value", children: [
|
|
@@ -377,6 +520,9 @@ function ContentsPanel({
|
|
|
377
520
|
chapters,
|
|
378
521
|
quizzes,
|
|
379
522
|
cues,
|
|
523
|
+
primaryLanguage,
|
|
524
|
+
secondaryCues,
|
|
525
|
+
secondaryLanguage,
|
|
380
526
|
activeCue,
|
|
381
527
|
limit,
|
|
382
528
|
strings,
|
|
@@ -388,10 +534,18 @@ function ContentsPanel({
|
|
|
388
534
|
const followUntil = react.useRef(0);
|
|
389
535
|
const hasTranscript = cues.length > 0;
|
|
390
536
|
const shownTab = hasTranscript ? tab : "contents";
|
|
537
|
+
const paired = react.useMemo(() => pairSecondaryCues(cues, secondaryCues), [cues, secondaryCues]);
|
|
391
538
|
const lines = react.useMemo(() => {
|
|
392
539
|
const trimmed = query.trim().toLowerCase();
|
|
393
|
-
return cues.map((cue, index) => ({
|
|
394
|
-
|
|
540
|
+
return cues.map((cue, index) => ({
|
|
541
|
+
cue,
|
|
542
|
+
index,
|
|
543
|
+
// The spoiler limit holds for the second language as for the first.
|
|
544
|
+
second: (paired[index] ?? []).filter((line) => line.start <= limit).map((line) => line.text).join(" ")
|
|
545
|
+
})).filter(({ cue }) => cue.start <= limit).filter(
|
|
546
|
+
({ cue, second }) => trimmed === "" || cue.text.toLowerCase().includes(trimmed) || second.toLowerCase().includes(trimmed)
|
|
547
|
+
);
|
|
548
|
+
}, [cues, paired, limit, query]);
|
|
395
549
|
react.useEffect(() => {
|
|
396
550
|
if (shownTab !== "transcript" || query.trim() !== "" || Date.now() < followUntil.current) {
|
|
397
551
|
return;
|
|
@@ -514,7 +668,7 @@ function ContentsPanel({
|
|
|
514
668
|
onTouchMove: () => {
|
|
515
669
|
followUntil.current = Date.now() + 4e3;
|
|
516
670
|
},
|
|
517
|
-
children: lines.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-iv-empty", children: strings.videoTranscriptNoMatch }) : lines.map(({ cue, index }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
671
|
+
children: lines.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-iv-empty", children: strings.videoTranscriptNoMatch }) : lines.map(({ cue, index, second }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
518
672
|
"button",
|
|
519
673
|
{
|
|
520
674
|
type: "button",
|
|
@@ -523,7 +677,10 @@ function ContentsPanel({
|
|
|
523
677
|
onClick: () => onSeek(cue.start),
|
|
524
678
|
children: [
|
|
525
679
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-iv-row-time", children: clock(cue.start) }),
|
|
526
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: highlight(cue.text) })
|
|
680
|
+
secondaryCues.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { lang: primaryLanguage, dir: "auto", children: highlight(cue.text) }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lk-iv-line-text", children: [
|
|
681
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { lang: primaryLanguage, dir: "auto", children: highlight(cue.text) }),
|
|
682
|
+
second === "" ? null : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-iv-line-secondary", lang: secondaryLanguage, dir: "auto", children: highlight(second) })
|
|
683
|
+
] })
|
|
527
684
|
]
|
|
528
685
|
},
|
|
529
686
|
`${cue.start}-${index}`
|
|
@@ -545,8 +702,10 @@ function EndScreen({
|
|
|
545
702
|
strings,
|
|
546
703
|
onOpenQuiz,
|
|
547
704
|
onWatchAgain,
|
|
705
|
+
pending,
|
|
548
706
|
onFinish
|
|
549
707
|
}) {
|
|
708
|
+
const pendingId = `${idPrefix}-end-pending`;
|
|
550
709
|
const headingRef = react.useRef(null);
|
|
551
710
|
react.useEffect(() => {
|
|
552
711
|
headingRef.current?.focus();
|
|
@@ -571,12 +730,23 @@ function EndScreen({
|
|
|
571
730
|
]
|
|
572
731
|
}
|
|
573
732
|
) }, quiz.id)) }) : null,
|
|
733
|
+
onFinish !== void 0 && pending ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: pendingId, className: "lk-iv-end-pending", role: "status", children: strings.videoAnswerPending }) : null,
|
|
574
734
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-end-actions", children: [
|
|
575
735
|
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "lk-iv-action", onClick: onWatchAgain, children: [
|
|
576
736
|
/* @__PURE__ */ jsxRuntime.jsx(ReplayIcon, {}),
|
|
577
737
|
strings.videoWatchAgain
|
|
578
738
|
] }),
|
|
579
|
-
onFinish !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
739
|
+
onFinish !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
740
|
+
"button",
|
|
741
|
+
{
|
|
742
|
+
type: "button",
|
|
743
|
+
className: "lk-iv-action lk-iv-action-primary",
|
|
744
|
+
"aria-disabled": pending || void 0,
|
|
745
|
+
"aria-describedby": pending ? pendingId : void 0,
|
|
746
|
+
onClick: onFinish,
|
|
747
|
+
children: strings.videoFinish
|
|
748
|
+
}
|
|
749
|
+
) : null
|
|
580
750
|
] })
|
|
581
751
|
] }) });
|
|
582
752
|
}
|
|
@@ -588,45 +758,73 @@ var DEFAULT_PREFERENCES = {
|
|
|
588
758
|
muted: false,
|
|
589
759
|
captions: true,
|
|
590
760
|
captionLanguage: null,
|
|
761
|
+
secondaryCaptionLanguage: null,
|
|
591
762
|
captionSize: "medium",
|
|
592
763
|
captionBackground: true,
|
|
593
764
|
panel: false,
|
|
594
765
|
shortcuts: true
|
|
595
766
|
};
|
|
596
767
|
var STORAGE_KEY = "lk.video.v1";
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
768
|
+
var LANGUAGE_TAG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
769
|
+
var BOOLEAN_FIELDS = ["muted", "captions", "captionBackground", "panel", "shortcuts"];
|
|
770
|
+
var LANGUAGE_FIELDS = ["captionLanguage", "secondaryCaptionLanguage"];
|
|
771
|
+
function sanitizePartialPreferences(raw) {
|
|
772
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
773
|
+
return {};
|
|
600
774
|
}
|
|
601
775
|
const value = raw;
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
776
|
+
const has = (key) => Object.hasOwn(value, key);
|
|
777
|
+
const out = {};
|
|
778
|
+
if (has("speed") && SPEEDS.includes(value.speed)) {
|
|
779
|
+
out.speed = value.speed;
|
|
780
|
+
}
|
|
781
|
+
if (has("volume") && typeof value.volume === "number" && Number.isFinite(value.volume)) {
|
|
782
|
+
out.volume = Math.min(1, Math.max(0, value.volume));
|
|
783
|
+
}
|
|
784
|
+
for (const field of BOOLEAN_FIELDS) {
|
|
785
|
+
if (has(field) && typeof value[field] === "boolean") {
|
|
786
|
+
out[field] = value[field];
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
for (const field of LANGUAGE_FIELDS) {
|
|
790
|
+
const tag = value[field];
|
|
791
|
+
if (has(field) && (tag === null || typeof tag === "string" && LANGUAGE_TAG.test(tag))) {
|
|
792
|
+
out[field] = tag;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (has("captionSize") && (value.captionSize === "small" || value.captionSize === "medium" || value.captionSize === "large")) {
|
|
796
|
+
out.captionSize = value.captionSize;
|
|
797
|
+
}
|
|
798
|
+
return out;
|
|
799
|
+
}
|
|
800
|
+
function normalised(preferences) {
|
|
801
|
+
const { captionLanguage: first, secondaryCaptionLanguage: second } = preferences;
|
|
802
|
+
return first !== null && second !== null && first.toLowerCase() === second.toLowerCase() ? { ...preferences, secondaryCaptionLanguage: null } : preferences;
|
|
803
|
+
}
|
|
804
|
+
function resolvePreferences(layers) {
|
|
805
|
+
return normalised({
|
|
806
|
+
...DEFAULT_PREFERENCES,
|
|
807
|
+
...sanitizePartialPreferences(layers.defaults),
|
|
808
|
+
...sanitizePartialPreferences(layers.stored),
|
|
809
|
+
...sanitizePartialPreferences(layers.force),
|
|
810
|
+
...sanitizePartialPreferences(layers.chosen)
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
function applyPreferenceChange(previous, change) {
|
|
814
|
+
return normalised({ ...previous, ...sanitizePartialPreferences(change) });
|
|
618
815
|
}
|
|
619
|
-
function
|
|
816
|
+
function readStoredPreferences() {
|
|
620
817
|
try {
|
|
621
818
|
const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
622
|
-
return stored ?
|
|
819
|
+
return stored ? sanitizePartialPreferences(JSON.parse(stored)) : {};
|
|
623
820
|
} catch {
|
|
624
|
-
return
|
|
821
|
+
return {};
|
|
625
822
|
}
|
|
626
823
|
}
|
|
627
|
-
function
|
|
824
|
+
function rememberPreferences(change) {
|
|
628
825
|
try {
|
|
629
|
-
|
|
826
|
+
const next = { ...readStoredPreferences(), ...sanitizePartialPreferences(change) };
|
|
827
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
630
828
|
} catch {
|
|
631
829
|
}
|
|
632
830
|
}
|
|
@@ -946,6 +1144,55 @@ function onNextFrame(run) {
|
|
|
946
1144
|
const timer = setTimeout(run, 33);
|
|
947
1145
|
return () => clearTimeout(timer);
|
|
948
1146
|
}
|
|
1147
|
+
function trackKey(track) {
|
|
1148
|
+
return `${track.kind}:${track.srclang}:${track.src}`;
|
|
1149
|
+
}
|
|
1150
|
+
var NO_CUES = [];
|
|
1151
|
+
function useCaptionLine(track, load, peek) {
|
|
1152
|
+
const key = track === void 0 ? void 0 : trackKey(track);
|
|
1153
|
+
const [line, setLine] = react.useState({
|
|
1154
|
+
cues: NO_CUES,
|
|
1155
|
+
failed: false
|
|
1156
|
+
});
|
|
1157
|
+
const trackRef = react.useRef(track);
|
|
1158
|
+
trackRef.current = track;
|
|
1159
|
+
react.useEffect(() => {
|
|
1160
|
+
const wanted = trackRef.current;
|
|
1161
|
+
if (key === void 0 || wanted === void 0) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
let live = true;
|
|
1165
|
+
load(wanted).then(
|
|
1166
|
+
(cues) => {
|
|
1167
|
+
if (live) {
|
|
1168
|
+
setLine({ key, cues, failed: false });
|
|
1169
|
+
}
|
|
1170
|
+
},
|
|
1171
|
+
() => {
|
|
1172
|
+
if (live) {
|
|
1173
|
+
setLine({ key, cues: NO_CUES, failed: true });
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
);
|
|
1177
|
+
return () => {
|
|
1178
|
+
live = false;
|
|
1179
|
+
};
|
|
1180
|
+
}, [key, load]);
|
|
1181
|
+
if (key === void 0) {
|
|
1182
|
+
return { cues: NO_CUES, failed: false };
|
|
1183
|
+
}
|
|
1184
|
+
if (line.key === key) {
|
|
1185
|
+
return line;
|
|
1186
|
+
}
|
|
1187
|
+
return { cues: peek(key) ?? NO_CUES, failed: false };
|
|
1188
|
+
}
|
|
1189
|
+
function captionsShown(tracks, preferences) {
|
|
1190
|
+
if (!preferences.captions) {
|
|
1191
|
+
return { srclang: null, secondary: null };
|
|
1192
|
+
}
|
|
1193
|
+
const shown = resolveCaptionTracks(tracks, preferences);
|
|
1194
|
+
return { srclang: shown.primary?.srclang ?? null, secondary: shown.secondary?.srclang ?? null };
|
|
1195
|
+
}
|
|
949
1196
|
var playerCount = 0;
|
|
950
1197
|
var ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
951
1198
|
"multiple-choice",
|
|
@@ -971,13 +1218,13 @@ function Player(props) {
|
|
|
971
1218
|
theme,
|
|
972
1219
|
sanitizeHtml
|
|
973
1220
|
} = props;
|
|
974
|
-
const strings =
|
|
1221
|
+
const strings = chunkMQAZJQRE_cjs.useLkStrings(props.strings);
|
|
975
1222
|
const group = react.useRef(props.group).current;
|
|
976
1223
|
const timeline = group.timeline ?? { cues: [] };
|
|
977
1224
|
const media = group.stimulus.media;
|
|
978
1225
|
const chapters = timeline.chapters ?? [];
|
|
979
1226
|
const devError = react.useMemo(() => {
|
|
980
|
-
if (!
|
|
1227
|
+
if (!chunk2S3MK4VX_cjs.isDevelopment()) {
|
|
981
1228
|
return null;
|
|
982
1229
|
}
|
|
983
1230
|
if (group.timeline === void 0) {
|
|
@@ -1023,21 +1270,25 @@ function Player(props) {
|
|
|
1023
1270
|
playerCount += 1;
|
|
1024
1271
|
return `lk-iv-${playerCount}`;
|
|
1025
1272
|
});
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
})
|
|
1030
|
-
const
|
|
1273
|
+
const forced = react.useRef(props.preferences).current;
|
|
1274
|
+
const defaultsKey = JSON.stringify(props.defaultPreferences ?? null);
|
|
1275
|
+
const defaults = react.useMemo(() => JSON.parse(defaultsKey), [defaultsKey]);
|
|
1276
|
+
const chosen = react.useRef({});
|
|
1277
|
+
const [preferences, setPreferences] = react.useState(
|
|
1278
|
+
() => resolvePreferences({ force: forced, defaults: props.defaultPreferences })
|
|
1279
|
+
);
|
|
1280
|
+
const preferencesRef = react.useRef(preferences);
|
|
1281
|
+
preferencesRef.current = preferences;
|
|
1031
1282
|
react.useEffect(() => {
|
|
1032
|
-
setPreferences(
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
}, []);
|
|
1283
|
+
setPreferences(
|
|
1284
|
+
resolvePreferences({
|
|
1285
|
+
force: forced,
|
|
1286
|
+
stored: readStoredPreferences(),
|
|
1287
|
+
defaults,
|
|
1288
|
+
chosen: chosen.current
|
|
1289
|
+
})
|
|
1290
|
+
);
|
|
1291
|
+
}, [forced, defaults]);
|
|
1041
1292
|
const [submitted] = react.useState(() => new Set(props.submittedSlotIds ?? []));
|
|
1042
1293
|
const [answered, setAnswered] = react.useState(() => {
|
|
1043
1294
|
const seeded = /* @__PURE__ */ new Set();
|
|
@@ -1050,18 +1301,61 @@ function Player(props) {
|
|
|
1050
1301
|
});
|
|
1051
1302
|
const answeredRef = react.useRef(answered);
|
|
1052
1303
|
answeredRef.current = answered;
|
|
1053
|
-
const
|
|
1054
|
-
const markAnswered = react.useCallback((slotId) => {
|
|
1304
|
+
const setSlotAnswered = react.useCallback((slotId, on) => {
|
|
1055
1305
|
setAnswered((previous) => {
|
|
1056
|
-
if (previous.has(slotId)) {
|
|
1306
|
+
if (previous.has(slotId) === on) {
|
|
1057
1307
|
return previous;
|
|
1058
1308
|
}
|
|
1059
1309
|
const next = new Set(previous);
|
|
1060
|
-
|
|
1310
|
+
if (on) {
|
|
1311
|
+
next.add(slotId);
|
|
1312
|
+
} else {
|
|
1313
|
+
next.delete(slotId);
|
|
1314
|
+
}
|
|
1061
1315
|
answeredRef.current = next;
|
|
1062
1316
|
return next;
|
|
1063
1317
|
});
|
|
1064
1318
|
}, []);
|
|
1319
|
+
const [scores, setScores] = react.useState(() => /* @__PURE__ */ new Map());
|
|
1320
|
+
const setSlotScore = react.useCallback((slotId, score) => {
|
|
1321
|
+
setScores((previous) => {
|
|
1322
|
+
if (previous.get(slotId) === score) {
|
|
1323
|
+
return previous;
|
|
1324
|
+
}
|
|
1325
|
+
const next = new Map(previous);
|
|
1326
|
+
if (score === void 0) {
|
|
1327
|
+
next.delete(slotId);
|
|
1328
|
+
} else {
|
|
1329
|
+
next.set(slotId, score);
|
|
1330
|
+
}
|
|
1331
|
+
return next;
|
|
1332
|
+
});
|
|
1333
|
+
}, []);
|
|
1334
|
+
const [pending, setPendingState] = react.useState(() => /* @__PURE__ */ new Set());
|
|
1335
|
+
const pendingRef = react.useRef(pending);
|
|
1336
|
+
const setPendingKey = react.useCallback((key, on) => {
|
|
1337
|
+
setPendingState((previous) => {
|
|
1338
|
+
if (previous.has(key) === on) {
|
|
1339
|
+
return previous;
|
|
1340
|
+
}
|
|
1341
|
+
const next = new Set(previous);
|
|
1342
|
+
if (on) {
|
|
1343
|
+
next.add(key);
|
|
1344
|
+
} else {
|
|
1345
|
+
next.delete(key);
|
|
1346
|
+
}
|
|
1347
|
+
pendingRef.current = next;
|
|
1348
|
+
return next;
|
|
1349
|
+
});
|
|
1350
|
+
}, []);
|
|
1351
|
+
const latestTakes = react.useRef(/* @__PURE__ */ new Map());
|
|
1352
|
+
const alive = react.useRef(true);
|
|
1353
|
+
react.useEffect(() => {
|
|
1354
|
+
alive.current = true;
|
|
1355
|
+
return () => {
|
|
1356
|
+
alive.current = false;
|
|
1357
|
+
};
|
|
1358
|
+
}, []);
|
|
1065
1359
|
const finished = react.useCallback(
|
|
1066
1360
|
(cueId) => (slotsByCue.get(cueId) ?? []).every((slot) => answeredRef.current.has(slot.slotId)),
|
|
1067
1361
|
[slotsByCue]
|
|
@@ -1070,6 +1364,7 @@ function Player(props) {
|
|
|
1070
1364
|
const videoRef = react.useRef(null);
|
|
1071
1365
|
const playButtonRef = react.useRef(null);
|
|
1072
1366
|
const quizHeadingRef = react.useRef(null);
|
|
1367
|
+
const [portalContainer, setPortalContainer] = react.useState(null);
|
|
1073
1368
|
const [playing, setPlaying] = react.useState(false);
|
|
1074
1369
|
const [waiting, setWaiting] = react.useState(false);
|
|
1075
1370
|
const [errorCode, setErrorCode] = react.useState(null);
|
|
@@ -1306,43 +1601,124 @@ function Player(props) {
|
|
|
1306
1601
|
}
|
|
1307
1602
|
] : [];
|
|
1308
1603
|
}, [media, locale, strings.videoCaptionLanguage]);
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1604
|
+
const { primary: primaryTrack, secondary: secondaryTrack } = react.useMemo(
|
|
1605
|
+
() => resolveCaptionTracks(tracks, {
|
|
1606
|
+
captionLanguage: preferences.captionLanguage,
|
|
1607
|
+
secondaryCaptionLanguage: preferences.secondaryCaptionLanguage
|
|
1608
|
+
}),
|
|
1609
|
+
[tracks, preferences.captionLanguage, preferences.secondaryCaptionLanguage]
|
|
1610
|
+
);
|
|
1312
1611
|
const loaderRef = react.useRef(captionsLoader);
|
|
1313
1612
|
loaderRef.current = captionsLoader;
|
|
1314
|
-
react.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1613
|
+
const loading = react.useRef(/* @__PURE__ */ new Map());
|
|
1614
|
+
const loaded = react.useRef(/* @__PURE__ */ new Map());
|
|
1615
|
+
const loadCues = react.useCallback((track) => {
|
|
1616
|
+
const key = trackKey(track);
|
|
1617
|
+
const pending2 = loading.current.get(key);
|
|
1618
|
+
if (pending2 !== void 0) {
|
|
1619
|
+
return pending2;
|
|
1318
1620
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1621
|
+
const request = Promise.resolve().then(
|
|
1622
|
+
() => loaderRef.current ? loaderRef.current(track) : fetch(track.src, { credentials: "same-origin" }).then((response) => {
|
|
1623
|
+
if (!response.ok) {
|
|
1624
|
+
throw new Error(String(response.status));
|
|
1625
|
+
}
|
|
1626
|
+
return response.text();
|
|
1627
|
+
})
|
|
1628
|
+
).then((text) => {
|
|
1629
|
+
const parsed = parseWebVtt(text);
|
|
1630
|
+
if (parsed.length === 0) {
|
|
1631
|
+
throw new Error("The caption file held no cues.");
|
|
1324
1632
|
}
|
|
1325
|
-
|
|
1633
|
+
loaded.current.set(key, parsed);
|
|
1634
|
+
return parsed;
|
|
1635
|
+
});
|
|
1636
|
+
loading.current.set(key, request);
|
|
1637
|
+
request.catch(() => {
|
|
1638
|
+
loading.current.delete(key);
|
|
1326
1639
|
});
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1640
|
+
return request;
|
|
1641
|
+
}, []);
|
|
1642
|
+
const peekCues = react.useCallback((key) => loaded.current.get(key), []);
|
|
1643
|
+
const primaryLine = useCaptionLine(primaryTrack, loadCues, peekCues);
|
|
1644
|
+
const secondaryLine = useCaptionLine(secondaryTrack, loadCues, peekCues);
|
|
1645
|
+
const primaryCues = primaryLine.cues;
|
|
1646
|
+
const secondaryCues = secondaryLine.cues;
|
|
1647
|
+
const captionsFailed = { primary: primaryLine.failed, secondary: secondaryLine.failed };
|
|
1648
|
+
const hasCaptions = primaryCues.length > 0 || secondaryCues.length > 0;
|
|
1649
|
+
const hasTranscript = primaryCues.length > 0;
|
|
1650
|
+
const activeCueIndex = react.useMemo(() => cueIndexAt(primaryCues, current), [primaryCues, current]);
|
|
1651
|
+
const secondaryCueIndex = react.useMemo(
|
|
1652
|
+
() => cueIndexAt(secondaryCues, current),
|
|
1653
|
+
[secondaryCues, current]
|
|
1654
|
+
);
|
|
1655
|
+
const spoilerLimit = timeline.navigation === "no-skip-ahead" && renderMode !== "review" ? Math.max(furthestShown, current) : Number.POSITIVE_INFINITY;
|
|
1656
|
+
const onPreferencesChangeRef = react.useRef(props.onPreferencesChange);
|
|
1657
|
+
onPreferencesChangeRef.current = props.onPreferencesChange;
|
|
1658
|
+
const tracksRef = react.useRef(tracks);
|
|
1659
|
+
tracksRef.current = tracks;
|
|
1660
|
+
const lastSecondary = react.useRef(null);
|
|
1661
|
+
const changePreferences = react.useCallback(
|
|
1662
|
+
(change) => {
|
|
1663
|
+
const previous = preferencesRef.current;
|
|
1664
|
+
const next = applyPreferenceChange(previous, change);
|
|
1665
|
+
if (previous.secondaryCaptionLanguage !== null) {
|
|
1666
|
+
lastSecondary.current = previous.secondaryCaptionLanguage;
|
|
1332
1667
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1668
|
+
chosen.current = { ...chosen.current, ...change };
|
|
1669
|
+
preferencesRef.current = next;
|
|
1670
|
+
setPreferences(next);
|
|
1671
|
+
rememberPreferences(change);
|
|
1672
|
+
onPreferencesChangeRef.current?.(next, change);
|
|
1673
|
+
const before = captionsShown(tracksRef.current, previous);
|
|
1674
|
+
const after = captionsShown(tracksRef.current, next);
|
|
1675
|
+
if (before.srclang !== after.srclang || before.secondary !== after.secondary) {
|
|
1676
|
+
emit("video-captions-changed", after);
|
|
1337
1677
|
}
|
|
1678
|
+
},
|
|
1679
|
+
[emit]
|
|
1680
|
+
);
|
|
1681
|
+
const chooseCaptionTrack = (track) => {
|
|
1682
|
+
const swaps = primaryTrack !== void 0 && secondaryTrack !== void 0 && track.srclang.toLowerCase() === secondaryTrack.srclang.toLowerCase();
|
|
1683
|
+
changePreferences({
|
|
1684
|
+
captions: true,
|
|
1685
|
+
captionLanguage: track.srclang,
|
|
1686
|
+
...swaps ? { secondaryCaptionLanguage: primaryTrack.srclang } : {}
|
|
1338
1687
|
});
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
const
|
|
1688
|
+
};
|
|
1689
|
+
const chooseSecondaryTrack = (track) => {
|
|
1690
|
+
changePreferences(
|
|
1691
|
+
track === null ? { secondaryCaptionLanguage: null } : { secondaryCaptionLanguage: track.srclang, captions: true }
|
|
1692
|
+
);
|
|
1693
|
+
};
|
|
1694
|
+
const toggleSecondary = () => {
|
|
1695
|
+
if (secondaryTrack !== void 0) {
|
|
1696
|
+
changePreferences(
|
|
1697
|
+
preferences.captions ? { secondaryCaptionLanguage: null } : { captions: true }
|
|
1698
|
+
);
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
const wanted = [
|
|
1702
|
+
lastSecondary.current,
|
|
1703
|
+
preferences.secondaryCaptionLanguage,
|
|
1704
|
+
sanitizePartialPreferences(defaults).secondaryCaptionLanguage ?? null,
|
|
1705
|
+
secondaryCandidates(tracks, primaryTrack)[0]?.srclang ?? null
|
|
1706
|
+
];
|
|
1707
|
+
for (const language of wanted) {
|
|
1708
|
+
if (language === null) {
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
const { secondary } = resolveCaptionTracks(tracks, {
|
|
1712
|
+
captionLanguage: preferences.captionLanguage,
|
|
1713
|
+
secondaryCaptionLanguage: language
|
|
1714
|
+
});
|
|
1715
|
+
if (secondary !== void 0) {
|
|
1716
|
+
changePreferences({ secondaryCaptionLanguage: secondary.srclang, captions: true });
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
announce(strings.videoNoSecondLanguage);
|
|
1721
|
+
};
|
|
1346
1722
|
react.useEffect(() => {
|
|
1347
1723
|
const video = videoRef.current;
|
|
1348
1724
|
if (video !== null) {
|
|
@@ -1509,14 +1885,17 @@ function Player(props) {
|
|
|
1509
1885
|
return;
|
|
1510
1886
|
case "c":
|
|
1511
1887
|
case "C":
|
|
1512
|
-
if (
|
|
1888
|
+
if (event.shiftKey) {
|
|
1889
|
+
done();
|
|
1890
|
+
toggleSecondary();
|
|
1891
|
+
} else if (hasCaptions) {
|
|
1513
1892
|
done();
|
|
1514
1893
|
changePreferences({ captions: !preferences.captions });
|
|
1515
1894
|
}
|
|
1516
1895
|
return;
|
|
1517
1896
|
case "t":
|
|
1518
1897
|
case "T":
|
|
1519
|
-
if (
|
|
1898
|
+
if (hasTranscript) {
|
|
1520
1899
|
done();
|
|
1521
1900
|
setPanelTab("transcript");
|
|
1522
1901
|
changePreferences({ panel: !(preferences.panel && panelTab === "transcript") });
|
|
@@ -1566,7 +1945,7 @@ function Player(props) {
|
|
|
1566
1945
|
}));
|
|
1567
1946
|
const totalQuestions = quizzes.reduce((sum, quiz) => sum + quiz.total, 0);
|
|
1568
1947
|
const answeredCount = quizzes.reduce((sum, quiz) => sum + quiz.answered, 0);
|
|
1569
|
-
const graded = [...scores.
|
|
1948
|
+
const graded = [...scores.values()];
|
|
1570
1949
|
const percent = graded.length === 0 ? null : Math.round(graded.reduce((a, b) => a + b, 0) / graded.length * 100);
|
|
1571
1950
|
const summary = react.useCallback(() => {
|
|
1572
1951
|
return {
|
|
@@ -1588,11 +1967,12 @@ function Player(props) {
|
|
|
1588
1967
|
finishedOnce.current = true;
|
|
1589
1968
|
onFinished?.(summary());
|
|
1590
1969
|
}, [onFinished, summary]);
|
|
1970
|
+
const answerPending = pending.size > 0;
|
|
1591
1971
|
react.useEffect(() => {
|
|
1592
|
-
if (ended && totalQuestions > 0 && answeredCount === totalQuestions && renderMode !== "review") {
|
|
1972
|
+
if (ended && !answerPending && totalQuestions > 0 && answeredCount === totalQuestions && renderMode !== "review") {
|
|
1593
1973
|
finish();
|
|
1594
1974
|
}
|
|
1595
|
-
}, [ended, totalQuestions, answeredCount, renderMode, finish]);
|
|
1975
|
+
}, [ended, answerPending, totalQuestions, answeredCount, renderMode, finish]);
|
|
1596
1976
|
const slotBindings = react.useMemo(() => {
|
|
1597
1977
|
const bindings = /* @__PURE__ */ new Map();
|
|
1598
1978
|
if (recordingBinding === void 0) {
|
|
@@ -1613,70 +1993,172 @@ function Player(props) {
|
|
|
1613
1993
|
}
|
|
1614
1994
|
return bindings;
|
|
1615
1995
|
}, [slots, recordingBinding]);
|
|
1616
|
-
const
|
|
1617
|
-
const
|
|
1618
|
-
const
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1996
|
+
const places = react.useMemo(() => {
|
|
1997
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1998
|
+
for (const [cueId, cueSlots] of slotsByCue) {
|
|
1999
|
+
for (const slot of cueSlots) {
|
|
2000
|
+
byId.set(slot.slotId, {
|
|
2001
|
+
slotId: slot.slotId,
|
|
2002
|
+
index: slot.index,
|
|
2003
|
+
activityId: slot.activity.id,
|
|
2004
|
+
cueId
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return byId;
|
|
2009
|
+
}, [slotsByCue]);
|
|
2010
|
+
const reportTo = react.useRef({ onSubmit, onActivityComplete, onInteraction, renderMode });
|
|
2011
|
+
reportTo.current = { onSubmit, onActivityComplete, onInteraction, renderMode };
|
|
2012
|
+
const calls = react.useRef(/* @__PURE__ */ new Map());
|
|
2013
|
+
const callsFor = (place) => {
|
|
2014
|
+
const made = calls.current.get(place.slotId);
|
|
2015
|
+
if (made !== void 0) {
|
|
2016
|
+
return made;
|
|
2017
|
+
}
|
|
2018
|
+
const { slotId } = place;
|
|
2019
|
+
const warned = /* @__PURE__ */ new Set();
|
|
2020
|
+
const refused = (call) => {
|
|
2021
|
+
if (reportTo.current.renderMode !== "review") {
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
if (chunk2S3MK4VX_cjs.isDevelopment() && !warned.has(call)) {
|
|
2025
|
+
warned.add(call);
|
|
2026
|
+
console.warn(
|
|
2027
|
+
`InteractiveVideo: \`${call}()\` was called for question "${slotId}" in renderMode "review", where nothing is submittable. It was ignored.`
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
return true;
|
|
2031
|
+
};
|
|
2032
|
+
const next = {
|
|
2033
|
+
submit: (response) => {
|
|
2034
|
+
if (!alive.current || refused("submit")) {
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
setSlotAnswered(slotId, true);
|
|
2038
|
+
reportTo.current.onSubmit?.(response, place);
|
|
2039
|
+
},
|
|
2040
|
+
complete: (result) => {
|
|
2041
|
+
if (!alive.current || refused("complete")) {
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
setSlotAnswered(slotId, true);
|
|
2045
|
+
setSlotScore(slotId, result.maxScore > 0 ? result.score / result.maxScore : void 0);
|
|
2046
|
+
reportTo.current.onActivityComplete?.(result, place);
|
|
2047
|
+
},
|
|
2048
|
+
clear: () => {
|
|
2049
|
+
if (!alive.current || refused("clear")) {
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
setSlotAnswered(slotId, false);
|
|
2053
|
+
setSlotScore(slotId, void 0);
|
|
2054
|
+
},
|
|
2055
|
+
setPending: (on) => {
|
|
2056
|
+
if (!alive.current) {
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
setPendingKey(`host:${slotId}`, on === true);
|
|
2060
|
+
},
|
|
2061
|
+
emit: (type, payload) => {
|
|
2062
|
+
if (!alive.current) {
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
reportTo.current.onInteraction?.({
|
|
2066
|
+
type,
|
|
2067
|
+
activityId: place.activityId,
|
|
2068
|
+
timestamp: Date.now(),
|
|
2069
|
+
...payload !== void 0 ? { payload } : {}
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
1623
2072
|
};
|
|
1624
|
-
|
|
1625
|
-
|
|
2073
|
+
calls.current.set(slotId, next);
|
|
2074
|
+
return next;
|
|
2075
|
+
};
|
|
2076
|
+
const reportTake = (slotId, take, state) => {
|
|
2077
|
+
if (!alive.current) {
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
const latest = latestTakes.current.get(slotId);
|
|
2081
|
+
if (latest !== void 0 && take < latest) {
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
latestTakes.current.set(slotId, take);
|
|
2085
|
+
setPendingKey(`take:${slotId}`, state === "in-flight");
|
|
2086
|
+
};
|
|
2087
|
+
const channels = react.useRef(/* @__PURE__ */ new Map());
|
|
2088
|
+
const channelFor = (slotId) => {
|
|
2089
|
+
let channel = channels.current.get(slotId);
|
|
2090
|
+
if (channel === void 0) {
|
|
2091
|
+
channel = {
|
|
2092
|
+
captureGroup: `${playerId}::${slotId}`,
|
|
2093
|
+
takeState: (take, state) => reportTake(slotId, take, state)
|
|
2094
|
+
};
|
|
2095
|
+
channels.current.set(slotId, channel);
|
|
2096
|
+
}
|
|
2097
|
+
return channel;
|
|
2098
|
+
};
|
|
2099
|
+
const seedsFor = (slotId) => ({
|
|
2100
|
+
restored: props.responses !== void 0 && Object.hasOwn(props.responses, slotId) ? props.responses[slotId] : void 0,
|
|
2101
|
+
outcome: props.outcomes !== void 0 && Object.hasOwn(props.outcomes, slotId) ? props.outcomes[slotId] : void 0
|
|
2102
|
+
});
|
|
2103
|
+
const questionFor = (slot, place, active) => {
|
|
2104
|
+
const { restored, outcome } = seedsFor(slot.slotId);
|
|
2105
|
+
return {
|
|
2106
|
+
activity: slot.activity,
|
|
2107
|
+
slot: place,
|
|
2108
|
+
renderMode,
|
|
2109
|
+
active,
|
|
2110
|
+
...locale !== void 0 ? { locale } : {},
|
|
2111
|
+
...restored !== void 0 ? { defaultValue: restored } : {},
|
|
2112
|
+
defaultSubmitted: submitted.has(slot.slotId),
|
|
2113
|
+
...outcome !== void 0 ? { outcome } : {},
|
|
2114
|
+
portalContainer,
|
|
2115
|
+
...callsFor(place)
|
|
2116
|
+
};
|
|
2117
|
+
};
|
|
2118
|
+
const sdkQuestion = (slot, place) => {
|
|
2119
|
+
const activity = slot.activity;
|
|
2120
|
+
const { restored, outcome } = seedsFor(slot.slotId);
|
|
2121
|
+
const reported = callsFor(place);
|
|
1626
2122
|
const common = {
|
|
1627
2123
|
renderMode,
|
|
1628
2124
|
...restored !== void 0 ? { defaultValue: restored } : {},
|
|
1629
2125
|
...submitted.has(slot.slotId) ? { defaultSubmitted: true } : {},
|
|
1630
2126
|
...outcome !== void 0 ? { outcome } : {},
|
|
1631
|
-
onSubmit:
|
|
1632
|
-
|
|
1633
|
-
onSubmit?.(response, place);
|
|
1634
|
-
},
|
|
1635
|
-
onComplete: (result) => {
|
|
1636
|
-
markAnswered(slot.slotId);
|
|
1637
|
-
if (result.maxScore > 0) {
|
|
1638
|
-
scores.current.set(slot.slotId, result.score / result.maxScore);
|
|
1639
|
-
}
|
|
1640
|
-
onActivityComplete?.(result, place);
|
|
1641
|
-
},
|
|
2127
|
+
onSubmit: reported.submit,
|
|
2128
|
+
onComplete: reported.complete,
|
|
1642
2129
|
...onInteraction !== void 0 ? { onInteraction } : {},
|
|
1643
2130
|
...sanitizeHtml !== void 0 ? { sanitizeHtml } : {},
|
|
1644
2131
|
...props.strings !== void 0 ? { strings: props.strings } : {},
|
|
1645
2132
|
...locale !== void 0 ? { locale } : {}
|
|
1646
2133
|
};
|
|
1647
|
-
let question;
|
|
1648
2134
|
switch (activity.type) {
|
|
1649
2135
|
case "multiple-choice":
|
|
1650
|
-
|
|
1651
|
-
|
|
2136
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2137
|
+
chunkZSY4VANQ_cjs.MultipleChoice,
|
|
1652
2138
|
{
|
|
1653
2139
|
data: activity,
|
|
1654
2140
|
...common,
|
|
1655
2141
|
...shuffleSeed !== void 0 ? { shuffleSeed } : {}
|
|
1656
2142
|
}
|
|
1657
2143
|
);
|
|
1658
|
-
break;
|
|
1659
2144
|
case "fill-in-the-blanks":
|
|
1660
|
-
|
|
1661
|
-
break;
|
|
2145
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunk2TFNDHJ5_cjs.FillInTheBlanks, { data: activity, ...common });
|
|
1662
2146
|
case "gap-select":
|
|
1663
|
-
|
|
1664
|
-
|
|
2147
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2148
|
+
chunkB6S5ADHR_cjs.GapSelect,
|
|
1665
2149
|
{
|
|
1666
2150
|
data: activity,
|
|
1667
2151
|
...common,
|
|
1668
2152
|
...shuffleSeed !== void 0 ? { shuffleSeed } : {}
|
|
1669
2153
|
}
|
|
1670
2154
|
);
|
|
1671
|
-
break;
|
|
1672
2155
|
case "dictation":
|
|
1673
|
-
|
|
1674
|
-
break;
|
|
2156
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkRSQFGF5W_cjs.Dictation, { data: activity, ...common });
|
|
1675
2157
|
case "read-aloud": {
|
|
1676
2158
|
const binding = slotBindings.get(slot.slotId);
|
|
1677
2159
|
const assessment = props.assessments !== void 0 && Object.hasOwn(props.assessments, slot.slotId) ? props.assessments[slot.slotId] : void 0;
|
|
1678
|
-
|
|
1679
|
-
|
|
2160
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2161
|
+
chunkML6L4LJH_cjs.ReadAloud,
|
|
1680
2162
|
{
|
|
1681
2163
|
data: activity,
|
|
1682
2164
|
...common,
|
|
@@ -1685,21 +2167,10 @@ function Player(props) {
|
|
|
1685
2167
|
...workletUrl !== void 0 ? { workletUrl } : {}
|
|
1686
2168
|
}
|
|
1687
2169
|
);
|
|
1688
|
-
break;
|
|
1689
2170
|
}
|
|
1690
2171
|
default:
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
return question;
|
|
1694
|
-
};
|
|
1695
|
-
const channels = react.useRef(/* @__PURE__ */ new Map());
|
|
1696
|
-
const channelFor = (slotId) => {
|
|
1697
|
-
let channel = channels.current.get(slotId);
|
|
1698
|
-
if (channel === void 0) {
|
|
1699
|
-
channel = { captureGroup: `${playerId}::${slotId}`, takeState: () => void 0 };
|
|
1700
|
-
channels.current.set(slotId, channel);
|
|
2172
|
+
return null;
|
|
1701
2173
|
}
|
|
1702
|
-
return channel;
|
|
1703
2174
|
};
|
|
1704
2175
|
if (devError !== null) {
|
|
1705
2176
|
throw devError;
|
|
@@ -1720,7 +2191,9 @@ function Player(props) {
|
|
|
1720
2191
|
emit("video-quiz-question-shown", { cueId: openCue.id, slotId: slot.slotId });
|
|
1721
2192
|
}
|
|
1722
2193
|
};
|
|
1723
|
-
const
|
|
2194
|
+
const captionsHidden = !preferences.captions || openQuiz !== null || ended || errorCode !== null;
|
|
2195
|
+
const primaryText = captionsHidden ? void 0 : primaryCues[activeCueIndex]?.text;
|
|
2196
|
+
const secondaryText = captionsHidden ? void 0 : secondaryCues[secondaryCueIndex]?.text;
|
|
1724
2197
|
const chromeVisible = !playing || chromeShown || menu !== null || scrubbing || focusInChrome || sheet || openQuiz !== null;
|
|
1725
2198
|
const volumeLevel = preferences.muted || preferences.volume === 0 ? "muted" : preferences.volume < 0.5 ? "low" : "high";
|
|
1726
2199
|
return (
|
|
@@ -1832,15 +2305,43 @@ function Player(props) {
|
|
|
1832
2305
|
}
|
|
1833
2306
|
),
|
|
1834
2307
|
label !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-iv-label", children: label }) : null,
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
"
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
2308
|
+
primaryText !== void 0 || secondaryText !== void 0 ? (
|
|
2309
|
+
// The lines keep `.lk-iv-caption`, `data-size` and `data-background`
|
|
2310
|
+
// as well, so a stylesheet written against 15.x still matches them.
|
|
2311
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2312
|
+
"div",
|
|
2313
|
+
{
|
|
2314
|
+
className: "lk-iv-captions",
|
|
2315
|
+
"data-size": preferences.captionSize,
|
|
2316
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2317
|
+
children: [
|
|
2318
|
+
primaryText !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2319
|
+
"div",
|
|
2320
|
+
{
|
|
2321
|
+
className: "lk-iv-caption",
|
|
2322
|
+
"data-role": "primary",
|
|
2323
|
+
"data-size": preferences.captionSize,
|
|
2324
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2325
|
+
lang: primaryTrack?.srclang,
|
|
2326
|
+
dir: "auto",
|
|
2327
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: primaryText })
|
|
2328
|
+
}
|
|
2329
|
+
) : null,
|
|
2330
|
+
secondaryText !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2331
|
+
"div",
|
|
2332
|
+
{
|
|
2333
|
+
className: "lk-iv-caption",
|
|
2334
|
+
"data-role": "secondary",
|
|
2335
|
+
"data-size": preferences.captionSize,
|
|
2336
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2337
|
+
lang: secondaryTrack?.srclang,
|
|
2338
|
+
dir: "auto",
|
|
2339
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: secondaryText })
|
|
2340
|
+
}
|
|
2341
|
+
) : null
|
|
2342
|
+
]
|
|
2343
|
+
}
|
|
2344
|
+
)
|
|
1844
2345
|
) : null,
|
|
1845
2346
|
waiting && errorCode === null && openQuiz === null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-iv-spinner", "aria-hidden": "true" }) : null,
|
|
1846
2347
|
errorCode !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-error", role: "alert", children: [
|
|
@@ -1889,7 +2390,7 @@ function Player(props) {
|
|
|
1889
2390
|
buffered,
|
|
1890
2391
|
chapters,
|
|
1891
2392
|
markers,
|
|
1892
|
-
cues:
|
|
2393
|
+
cues: primaryCues,
|
|
1893
2394
|
previewLimit: spoilerLimit,
|
|
1894
2395
|
strings,
|
|
1895
2396
|
onSeek: seek,
|
|
@@ -1974,7 +2475,7 @@ function Player(props) {
|
|
|
1974
2475
|
)
|
|
1975
2476
|
] }),
|
|
1976
2477
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-bar-group", children: [
|
|
1977
|
-
quizzes.length > 0 || chapters.length > 0 ||
|
|
2478
|
+
quizzes.length > 0 || chapters.length > 0 || hasTranscript ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1978
2479
|
"button",
|
|
1979
2480
|
{
|
|
1980
2481
|
type: "button",
|
|
@@ -1992,12 +2493,7 @@ function Player(props) {
|
|
|
1992
2493
|
className: "lk-iv-button",
|
|
1993
2494
|
"aria-label": preferences.captions ? strings.videoCaptionsHide : strings.videoCaptionsShow,
|
|
1994
2495
|
"aria-pressed": preferences.captions,
|
|
1995
|
-
onClick: () => {
|
|
1996
|
-
changePreferences({ captions: !preferences.captions });
|
|
1997
|
-
emit("video-captions-changed", {
|
|
1998
|
-
srclang: preferences.captions ? null : activeTrack?.srclang ?? null
|
|
1999
|
-
});
|
|
2000
|
-
},
|
|
2496
|
+
onClick: () => changePreferences({ captions: !preferences.captions }),
|
|
2001
2497
|
children: /* @__PURE__ */ jsxRuntime.jsx(CaptionsIcon, { on: preferences.captions })
|
|
2002
2498
|
}
|
|
2003
2499
|
) : null,
|
|
@@ -2045,8 +2541,11 @@ function Player(props) {
|
|
|
2045
2541
|
{
|
|
2046
2542
|
preferences,
|
|
2047
2543
|
tracks,
|
|
2048
|
-
|
|
2544
|
+
primaryTrack,
|
|
2545
|
+
secondaryTrack,
|
|
2049
2546
|
captionsFailed,
|
|
2547
|
+
onCaptionTrack: chooseCaptionTrack,
|
|
2548
|
+
onSecondaryTrack: chooseSecondaryTrack,
|
|
2050
2549
|
strings,
|
|
2051
2550
|
onChange: changePreferences,
|
|
2052
2551
|
onShowShortcuts: () => {
|
|
@@ -2092,7 +2591,7 @@ function Player(props) {
|
|
|
2092
2591
|
"aria-labelledby": `${playerId}-quiz-title`,
|
|
2093
2592
|
hidden: openQuiz === null,
|
|
2094
2593
|
onKeyDown: (event) => {
|
|
2095
|
-
if (event.key === "Escape" && !required && openQuiz !== null) {
|
|
2594
|
+
if (event.key === "Escape" && !event.defaultPrevented && !required && openQuiz !== null) {
|
|
2096
2595
|
event.preventDefault();
|
|
2097
2596
|
closeQuiz("skip");
|
|
2098
2597
|
}
|
|
@@ -2139,15 +2638,29 @@ function Player(props) {
|
|
|
2139
2638
|
] }) : null
|
|
2140
2639
|
] }) : null,
|
|
2141
2640
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lk-iv-quiz-body", children: cues.filter((cue) => mounted.has(cue.id)).map(
|
|
2142
|
-
(cue) => (slotsByCue.get(cue.id) ?? []).map((slot, index) =>
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2641
|
+
(cue) => (slotsByCue.get(cue.id) ?? []).map((slot, index) => {
|
|
2642
|
+
const shown = openQuiz?.cueId === cue.id && step === index;
|
|
2643
|
+
const place = places.get(slot.slotId);
|
|
2644
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2645
|
+
QuestionPane,
|
|
2646
|
+
{
|
|
2647
|
+
hidden: !shown,
|
|
2648
|
+
channel: channelFor(slot.slotId),
|
|
2649
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2650
|
+
SlotQuestion,
|
|
2651
|
+
{
|
|
2652
|
+
title: slot.activity.title,
|
|
2653
|
+
strings,
|
|
2654
|
+
render: props.renderQuestion,
|
|
2655
|
+
question: props.renderQuestion === void 0 ? void 0 : questionFor(slot, place, shown),
|
|
2656
|
+
sdk: () => sdkQuestion(slot, place),
|
|
2657
|
+
setHostPending: callsFor(place).setPending
|
|
2658
|
+
}
|
|
2659
|
+
)
|
|
2660
|
+
},
|
|
2661
|
+
slot.slotId
|
|
2662
|
+
);
|
|
2663
|
+
})
|
|
2151
2664
|
) }),
|
|
2152
2665
|
openCue !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-quiz-foot", children: [
|
|
2153
2666
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-quiz-foot-start", children: [
|
|
@@ -2203,7 +2716,14 @@ function Player(props) {
|
|
|
2203
2716
|
}
|
|
2204
2717
|
},
|
|
2205
2718
|
onWatchAgain: togglePlay,
|
|
2206
|
-
|
|
2719
|
+
pending: answerPending,
|
|
2720
|
+
onFinish: renderMode === "review" ? void 0 : () => {
|
|
2721
|
+
if (pendingRef.current.size > 0) {
|
|
2722
|
+
announce(strings.videoAnswerPending);
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
finish();
|
|
2726
|
+
}
|
|
2207
2727
|
}
|
|
2208
2728
|
) : null,
|
|
2209
2729
|
sheet ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2227,7 +2747,10 @@ function Player(props) {
|
|
|
2227
2747
|
onTab: setPanelTab,
|
|
2228
2748
|
chapters,
|
|
2229
2749
|
quizzes,
|
|
2230
|
-
cues:
|
|
2750
|
+
cues: primaryCues,
|
|
2751
|
+
primaryLanguage: primaryTrack?.srclang,
|
|
2752
|
+
secondaryCues: secondaryTrack === void 0 ? NO_CUES : secondaryCues,
|
|
2753
|
+
secondaryLanguage: secondaryTrack?.srclang,
|
|
2231
2754
|
activeCue: activeCueIndex,
|
|
2232
2755
|
limit: spoilerLimit,
|
|
2233
2756
|
strings,
|
|
@@ -2251,12 +2774,43 @@ function Player(props) {
|
|
|
2251
2774
|
}
|
|
2252
2775
|
}
|
|
2253
2776
|
) : null,
|
|
2254
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lk-iv-live", "aria-live": "polite", "aria-atomic": "true", children: announcement })
|
|
2777
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lk-iv-live", "aria-live": "polite", "aria-atomic": "true", children: announcement }),
|
|
2778
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: setPortalContainer, className: "lk-iv-portal" })
|
|
2255
2779
|
]
|
|
2256
2780
|
}
|
|
2257
2781
|
)
|
|
2258
2782
|
);
|
|
2259
2783
|
}
|
|
2784
|
+
function SlotQuestion({
|
|
2785
|
+
title,
|
|
2786
|
+
strings,
|
|
2787
|
+
...content
|
|
2788
|
+
}) {
|
|
2789
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2790
|
+
chunk2S3MK4VX_cjs.ActivityErrorBoundary,
|
|
2791
|
+
{
|
|
2792
|
+
...title !== void 0 ? { activityTitle: title } : {},
|
|
2793
|
+
strings,
|
|
2794
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(SlotContent, { ...content })
|
|
2795
|
+
}
|
|
2796
|
+
);
|
|
2797
|
+
}
|
|
2798
|
+
function SlotContent({
|
|
2799
|
+
render,
|
|
2800
|
+
question,
|
|
2801
|
+
sdk,
|
|
2802
|
+
setHostPending
|
|
2803
|
+
}) {
|
|
2804
|
+
const hosted = render !== void 0 && question !== void 0 ? render(question) : void 0;
|
|
2805
|
+
const hosting = hosted !== void 0;
|
|
2806
|
+
react.useEffect(() => {
|
|
2807
|
+
if (!hosting) {
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
return () => setHostPending(false);
|
|
2811
|
+
}, [hosting, setHostPending]);
|
|
2812
|
+
return hosting ? hosted : sdk();
|
|
2813
|
+
}
|
|
2260
2814
|
function QuestionPane({
|
|
2261
2815
|
hidden,
|
|
2262
2816
|
channel,
|
|
@@ -2268,7 +2822,7 @@ function QuestionPane({
|
|
|
2268
2822
|
if (!hidden) {
|
|
2269
2823
|
return;
|
|
2270
2824
|
}
|
|
2271
|
-
|
|
2825
|
+
chunkML6L4LJH_cjs.stopCaptureGroup(captureGroup);
|
|
2272
2826
|
for (const media of ref.current?.querySelectorAll("audio, video") ?? []) {
|
|
2273
2827
|
const element = media;
|
|
2274
2828
|
if (!element.paused) {
|
|
@@ -2276,7 +2830,7 @@ function QuestionPane({
|
|
|
2276
2830
|
}
|
|
2277
2831
|
}
|
|
2278
2832
|
}, [hidden, captureGroup]);
|
|
2279
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: "lk-iv-question", hidden, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2833
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: "lk-iv-question", hidden, children: /* @__PURE__ */ jsxRuntime.jsx(chunkML6L4LJH_cjs.SequenceSlotContext.Provider, { value: channel, children }) });
|
|
2280
2834
|
}
|
|
2281
2835
|
function bufferedAhead(video) {
|
|
2282
2836
|
const ranges = video.buffered;
|
|
@@ -2316,9 +2870,9 @@ function markerState(quizSlots, answered, renderMode, outcomes) {
|
|
|
2316
2870
|
return "unreached";
|
|
2317
2871
|
}
|
|
2318
2872
|
function InteractiveVideo2(props) {
|
|
2319
|
-
const strings =
|
|
2873
|
+
const strings = chunkMQAZJQRE_cjs.useLkStrings(props.strings);
|
|
2320
2874
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2321
|
-
|
|
2875
|
+
chunk2S3MK4VX_cjs.ActivityErrorBoundary,
|
|
2322
2876
|
{
|
|
2323
2877
|
...props.group?.title !== void 0 ? { activityTitle: props.group.title } : {},
|
|
2324
2878
|
strings,
|
|
@@ -2328,5 +2882,6 @@ function InteractiveVideo2(props) {
|
|
|
2328
2882
|
}
|
|
2329
2883
|
|
|
2330
2884
|
exports.InteractiveVideo = InteractiveVideo2;
|
|
2331
|
-
|
|
2332
|
-
//# sourceMappingURL=chunk-
|
|
2885
|
+
exports.resolveCaptionTracks = resolveCaptionTracks;
|
|
2886
|
+
//# sourceMappingURL=chunk-WOYCT5M6.cjs.map
|
|
2887
|
+
//# sourceMappingURL=chunk-WOYCT5M6.cjs.map
|