@intellectif/lk-react 15.0.0 → 16.0.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 +51 -0
- package/README.md +2 -2
- package/dist/{WrittenResponse-BPbhERgk.d.cts → WrittenResponse-C2zw-1jK.d.cts} +1 -1
- package/dist/{WrittenResponse-ZAxLR_AU.d.ts → WrittenResponse-CTiNpSCC.d.ts} +1 -1
- package/dist/{chunk-ZH5WT2IE.cjs → chunk-2WM2KSO7.cjs} +25 -25
- package/dist/{chunk-ZH5WT2IE.cjs.map → chunk-2WM2KSO7.cjs.map} +1 -1
- package/dist/{chunk-XMJE34FY.cjs → chunk-2YZZL5FV.cjs} +13 -13
- package/dist/{chunk-XMJE34FY.cjs.map → chunk-2YZZL5FV.cjs.map} +1 -1
- package/dist/{chunk-2HAEQDHQ.js → chunk-3IHVDQRC.js} +492 -139
- package/dist/chunk-3IHVDQRC.js.map +1 -0
- package/dist/{chunk-IE5ZNCWN.js → chunk-4AQ4SWJ3.js} +5 -5
- package/dist/{chunk-IE5ZNCWN.js.map → chunk-4AQ4SWJ3.js.map} +1 -1
- package/dist/{chunk-RT5FTHUU.js → chunk-4X7NFEDB.js} +5 -5
- package/dist/{chunk-RT5FTHUU.js.map → chunk-4X7NFEDB.js.map} +1 -1
- package/dist/{chunk-SRD4KVAP.cjs → chunk-A2CPCOJA.cjs} +503 -149
- package/dist/chunk-A2CPCOJA.cjs.map +1 -0
- package/dist/{chunk-AF72QUBI.js → chunk-AEUP63CZ.js} +5 -5
- package/dist/{chunk-AF72QUBI.js.map → chunk-AEUP63CZ.js.map} +1 -1
- package/dist/{chunk-DQ3VVU5T.cjs → chunk-ALDGRDYD.cjs} +24 -24
- package/dist/{chunk-DQ3VVU5T.cjs.map → chunk-ALDGRDYD.cjs.map} +1 -1
- package/dist/{chunk-WKOV25NP.cjs → chunk-BO64SLHL.cjs} +8 -8
- package/dist/{chunk-WKOV25NP.cjs.map → chunk-BO64SLHL.cjs.map} +1 -1
- package/dist/{chunk-L7NE7PR4.cjs → chunk-DMCFARGP.cjs} +13 -13
- package/dist/{chunk-L7NE7PR4.cjs.map → chunk-DMCFARGP.cjs.map} +1 -1
- package/dist/{chunk-QM4AV6EG.js → chunk-HJLVWZOM.js} +4 -4
- package/dist/{chunk-QM4AV6EG.js.map → chunk-HJLVWZOM.js.map} +1 -1
- package/dist/{chunk-ZJMTHPHY.js → chunk-ICAYWVNF.js} +3 -3
- package/dist/{chunk-ZJMTHPHY.js.map → chunk-ICAYWVNF.js.map} +1 -1
- package/dist/{chunk-26GQO5I2.cjs → chunk-JYC2VV3Y.cjs} +12 -12
- package/dist/{chunk-26GQO5I2.cjs.map → chunk-JYC2VV3Y.cjs.map} +1 -1
- package/dist/{chunk-UCKA22ZJ.cjs → chunk-K63VPFOV.cjs} +9 -2
- package/dist/chunk-K63VPFOV.cjs.map +1 -0
- package/dist/{chunk-YWQH35VO.js → chunk-NOF6HJDV.js} +4 -4
- package/dist/{chunk-YWQH35VO.js.map → chunk-NOF6HJDV.js.map} +1 -1
- package/dist/{chunk-I2QGW4GD.js → chunk-OOYKKO3V.js} +11 -11
- package/dist/{chunk-I2QGW4GD.js.map → chunk-OOYKKO3V.js.map} +1 -1
- package/dist/{chunk-FKGUKFC7.cjs → chunk-OXUUE3HX.cjs} +5 -5
- package/dist/{chunk-FKGUKFC7.cjs.map → chunk-OXUUE3HX.cjs.map} +1 -1
- package/dist/{chunk-53YOZOGN.cjs → chunk-P5FGJMMI.cjs} +5 -5
- package/dist/{chunk-53YOZOGN.cjs.map → chunk-P5FGJMMI.cjs.map} +1 -1
- package/dist/{chunk-UAYWLYFW.cjs → chunk-QADR2XMQ.cjs} +18 -18
- package/dist/{chunk-UAYWLYFW.cjs.map → chunk-QADR2XMQ.cjs.map} +1 -1
- package/dist/{chunk-52GXJNQ3.js → chunk-QIA5FAAW.js} +8 -2
- package/dist/chunk-QIA5FAAW.js.map +1 -0
- package/dist/{chunk-EKBEKDWO.cjs → chunk-RJGCT35F.cjs} +13 -13
- package/dist/{chunk-EKBEKDWO.cjs.map → chunk-RJGCT35F.cjs.map} +1 -1
- package/dist/{chunk-33BQNWAQ.js → chunk-T33AJYLW.js} +5 -5
- package/dist/{chunk-33BQNWAQ.js.map → chunk-T33AJYLW.js.map} +1 -1
- package/dist/{chunk-EVBYEWHO.js → chunk-UHKZXOXD.js} +9 -2
- package/dist/chunk-UHKZXOXD.js.map +1 -0
- package/dist/{chunk-HA3VRF6E.js → chunk-UIGSH2XB.js} +3 -3
- package/dist/{chunk-HA3VRF6E.js.map → chunk-UIGSH2XB.js.map} +1 -1
- package/dist/{chunk-Z6TVOAO5.cjs → chunk-UISZWFI7.cjs} +8 -2
- package/dist/chunk-UISZWFI7.cjs.map +1 -0
- package/dist/{chunk-CE6OGJE6.js → chunk-UYRQEIB7.js} +5 -5
- package/dist/{chunk-CE6OGJE6.js.map → chunk-UYRQEIB7.js.map} +1 -1
- package/dist/{chunk-QEB6O6GU.js → chunk-W32ND3ZR.js} +6 -6
- package/dist/{chunk-QEB6O6GU.js.map → chunk-W32ND3ZR.js.map} +1 -1
- package/dist/{chunk-H6I2JDNW.js → chunk-WBLRUJ77.js} +10 -10
- package/dist/{chunk-H6I2JDNW.js.map → chunk-WBLRUJ77.js.map} +1 -1
- package/dist/{chunk-DP3DM7FR.cjs → chunk-WGIMLXRW.cjs} +6 -6
- package/dist/{chunk-DP3DM7FR.cjs.map → chunk-WGIMLXRW.cjs.map} +1 -1
- package/dist/{chunk-4H3JTX74.cjs → chunk-ZA5QUQ2V.cjs} +13 -13
- package/dist/{chunk-4H3JTX74.cjs.map → chunk-ZA5QUQ2V.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 +50 -4
- package/dist/components/InteractiveVideo.d.ts +50 -4
- 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-D0IWcsUm.d.ts → strings-Dgc5-DeA.d.ts} +21 -2
- package/dist/{strings-5588Q4Te.d.cts → strings-zY2QQYpU.d.cts} +21 -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 +44 -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 chunkDMCFARGP_cjs = require('./chunk-DMCFARGP.cjs');
|
|
5
|
+
var chunkZA5QUQ2V_cjs = require('./chunk-ZA5QUQ2V.cjs');
|
|
6
|
+
var chunkRJGCT35F_cjs = require('./chunk-RJGCT35F.cjs');
|
|
7
|
+
var chunk2YZZL5FV_cjs = require('./chunk-2YZZL5FV.cjs');
|
|
8
|
+
var chunk2WM2KSO7_cjs = require('./chunk-2WM2KSO7.cjs');
|
|
9
|
+
var chunkP5FGJMMI_cjs = require('./chunk-P5FGJMMI.cjs');
|
|
10
|
+
var chunkUISZWFI7_cjs = require('./chunk-UISZWFI7.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}`
|
|
@@ -588,45 +745,73 @@ var DEFAULT_PREFERENCES = {
|
|
|
588
745
|
muted: false,
|
|
589
746
|
captions: true,
|
|
590
747
|
captionLanguage: null,
|
|
748
|
+
secondaryCaptionLanguage: null,
|
|
591
749
|
captionSize: "medium",
|
|
592
750
|
captionBackground: true,
|
|
593
751
|
panel: false,
|
|
594
752
|
shortcuts: true
|
|
595
753
|
};
|
|
596
754
|
var STORAGE_KEY = "lk.video.v1";
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
755
|
+
var LANGUAGE_TAG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
756
|
+
var BOOLEAN_FIELDS = ["muted", "captions", "captionBackground", "panel", "shortcuts"];
|
|
757
|
+
var LANGUAGE_FIELDS = ["captionLanguage", "secondaryCaptionLanguage"];
|
|
758
|
+
function sanitizePartialPreferences(raw) {
|
|
759
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
760
|
+
return {};
|
|
600
761
|
}
|
|
601
762
|
const value = raw;
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
763
|
+
const has = (key) => Object.hasOwn(value, key);
|
|
764
|
+
const out = {};
|
|
765
|
+
if (has("speed") && SPEEDS.includes(value.speed)) {
|
|
766
|
+
out.speed = value.speed;
|
|
767
|
+
}
|
|
768
|
+
if (has("volume") && typeof value.volume === "number" && Number.isFinite(value.volume)) {
|
|
769
|
+
out.volume = Math.min(1, Math.max(0, value.volume));
|
|
770
|
+
}
|
|
771
|
+
for (const field of BOOLEAN_FIELDS) {
|
|
772
|
+
if (has(field) && typeof value[field] === "boolean") {
|
|
773
|
+
out[field] = value[field];
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
for (const field of LANGUAGE_FIELDS) {
|
|
777
|
+
const tag = value[field];
|
|
778
|
+
if (has(field) && (tag === null || typeof tag === "string" && LANGUAGE_TAG.test(tag))) {
|
|
779
|
+
out[field] = tag;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (has("captionSize") && (value.captionSize === "small" || value.captionSize === "medium" || value.captionSize === "large")) {
|
|
783
|
+
out.captionSize = value.captionSize;
|
|
784
|
+
}
|
|
785
|
+
return out;
|
|
786
|
+
}
|
|
787
|
+
function normalised(preferences) {
|
|
788
|
+
const { captionLanguage: first, secondaryCaptionLanguage: second } = preferences;
|
|
789
|
+
return first !== null && second !== null && first.toLowerCase() === second.toLowerCase() ? { ...preferences, secondaryCaptionLanguage: null } : preferences;
|
|
790
|
+
}
|
|
791
|
+
function resolvePreferences(layers) {
|
|
792
|
+
return normalised({
|
|
793
|
+
...DEFAULT_PREFERENCES,
|
|
794
|
+
...sanitizePartialPreferences(layers.defaults),
|
|
795
|
+
...sanitizePartialPreferences(layers.stored),
|
|
796
|
+
...sanitizePartialPreferences(layers.force),
|
|
797
|
+
...sanitizePartialPreferences(layers.chosen)
|
|
798
|
+
});
|
|
618
799
|
}
|
|
619
|
-
function
|
|
800
|
+
function applyPreferenceChange(previous, change) {
|
|
801
|
+
return normalised({ ...previous, ...sanitizePartialPreferences(change) });
|
|
802
|
+
}
|
|
803
|
+
function readStoredPreferences() {
|
|
620
804
|
try {
|
|
621
805
|
const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
622
|
-
return stored ?
|
|
806
|
+
return stored ? sanitizePartialPreferences(JSON.parse(stored)) : {};
|
|
623
807
|
} catch {
|
|
624
|
-
return
|
|
808
|
+
return {};
|
|
625
809
|
}
|
|
626
810
|
}
|
|
627
|
-
function
|
|
811
|
+
function rememberPreferences(change) {
|
|
628
812
|
try {
|
|
629
|
-
|
|
813
|
+
const next = { ...readStoredPreferences(), ...sanitizePartialPreferences(change) };
|
|
814
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
630
815
|
} catch {
|
|
631
816
|
}
|
|
632
817
|
}
|
|
@@ -946,6 +1131,55 @@ function onNextFrame(run) {
|
|
|
946
1131
|
const timer = setTimeout(run, 33);
|
|
947
1132
|
return () => clearTimeout(timer);
|
|
948
1133
|
}
|
|
1134
|
+
function trackKey(track) {
|
|
1135
|
+
return `${track.kind}:${track.srclang}:${track.src}`;
|
|
1136
|
+
}
|
|
1137
|
+
var NO_CUES = [];
|
|
1138
|
+
function useCaptionLine(track, load, peek) {
|
|
1139
|
+
const key = track === void 0 ? void 0 : trackKey(track);
|
|
1140
|
+
const [line, setLine] = react.useState({
|
|
1141
|
+
cues: NO_CUES,
|
|
1142
|
+
failed: false
|
|
1143
|
+
});
|
|
1144
|
+
const trackRef = react.useRef(track);
|
|
1145
|
+
trackRef.current = track;
|
|
1146
|
+
react.useEffect(() => {
|
|
1147
|
+
const wanted = trackRef.current;
|
|
1148
|
+
if (key === void 0 || wanted === void 0) {
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
let live = true;
|
|
1152
|
+
load(wanted).then(
|
|
1153
|
+
(cues) => {
|
|
1154
|
+
if (live) {
|
|
1155
|
+
setLine({ key, cues, failed: false });
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
() => {
|
|
1159
|
+
if (live) {
|
|
1160
|
+
setLine({ key, cues: NO_CUES, failed: true });
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
);
|
|
1164
|
+
return () => {
|
|
1165
|
+
live = false;
|
|
1166
|
+
};
|
|
1167
|
+
}, [key, load]);
|
|
1168
|
+
if (key === void 0) {
|
|
1169
|
+
return { cues: NO_CUES, failed: false };
|
|
1170
|
+
}
|
|
1171
|
+
if (line.key === key) {
|
|
1172
|
+
return line;
|
|
1173
|
+
}
|
|
1174
|
+
return { cues: peek(key) ?? NO_CUES, failed: false };
|
|
1175
|
+
}
|
|
1176
|
+
function captionsShown(tracks, preferences) {
|
|
1177
|
+
if (!preferences.captions) {
|
|
1178
|
+
return { srclang: null, secondary: null };
|
|
1179
|
+
}
|
|
1180
|
+
const shown = resolveCaptionTracks(tracks, preferences);
|
|
1181
|
+
return { srclang: shown.primary?.srclang ?? null, secondary: shown.secondary?.srclang ?? null };
|
|
1182
|
+
}
|
|
949
1183
|
var playerCount = 0;
|
|
950
1184
|
var ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
951
1185
|
"multiple-choice",
|
|
@@ -971,13 +1205,13 @@ function Player(props) {
|
|
|
971
1205
|
theme,
|
|
972
1206
|
sanitizeHtml
|
|
973
1207
|
} = props;
|
|
974
|
-
const strings =
|
|
1208
|
+
const strings = chunkUISZWFI7_cjs.useLkStrings(props.strings);
|
|
975
1209
|
const group = react.useRef(props.group).current;
|
|
976
1210
|
const timeline = group.timeline ?? { cues: [] };
|
|
977
1211
|
const media = group.stimulus.media;
|
|
978
1212
|
const chapters = timeline.chapters ?? [];
|
|
979
1213
|
const devError = react.useMemo(() => {
|
|
980
|
-
if (!
|
|
1214
|
+
if (!chunkP5FGJMMI_cjs.isDevelopment()) {
|
|
981
1215
|
return null;
|
|
982
1216
|
}
|
|
983
1217
|
if (group.timeline === void 0) {
|
|
@@ -1023,21 +1257,25 @@ function Player(props) {
|
|
|
1023
1257
|
playerCount += 1;
|
|
1024
1258
|
return `lk-iv-${playerCount}`;
|
|
1025
1259
|
});
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
})
|
|
1030
|
-
const
|
|
1260
|
+
const forced = react.useRef(props.preferences).current;
|
|
1261
|
+
const defaultsKey = JSON.stringify(props.defaultPreferences ?? null);
|
|
1262
|
+
const defaults = react.useMemo(() => JSON.parse(defaultsKey), [defaultsKey]);
|
|
1263
|
+
const chosen = react.useRef({});
|
|
1264
|
+
const [preferences, setPreferences] = react.useState(
|
|
1265
|
+
() => resolvePreferences({ force: forced, defaults: props.defaultPreferences })
|
|
1266
|
+
);
|
|
1267
|
+
const preferencesRef = react.useRef(preferences);
|
|
1268
|
+
preferencesRef.current = preferences;
|
|
1031
1269
|
react.useEffect(() => {
|
|
1032
|
-
setPreferences(
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
}, []);
|
|
1270
|
+
setPreferences(
|
|
1271
|
+
resolvePreferences({
|
|
1272
|
+
force: forced,
|
|
1273
|
+
stored: readStoredPreferences(),
|
|
1274
|
+
defaults,
|
|
1275
|
+
chosen: chosen.current
|
|
1276
|
+
})
|
|
1277
|
+
);
|
|
1278
|
+
}, [forced, defaults]);
|
|
1041
1279
|
const [submitted] = react.useState(() => new Set(props.submittedSlotIds ?? []));
|
|
1042
1280
|
const [answered, setAnswered] = react.useState(() => {
|
|
1043
1281
|
const seeded = /* @__PURE__ */ new Set();
|
|
@@ -1306,43 +1544,124 @@ function Player(props) {
|
|
|
1306
1544
|
}
|
|
1307
1545
|
] : [];
|
|
1308
1546
|
}, [media, locale, strings.videoCaptionLanguage]);
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1547
|
+
const { primary: primaryTrack, secondary: secondaryTrack } = react.useMemo(
|
|
1548
|
+
() => resolveCaptionTracks(tracks, {
|
|
1549
|
+
captionLanguage: preferences.captionLanguage,
|
|
1550
|
+
secondaryCaptionLanguage: preferences.secondaryCaptionLanguage
|
|
1551
|
+
}),
|
|
1552
|
+
[tracks, preferences.captionLanguage, preferences.secondaryCaptionLanguage]
|
|
1553
|
+
);
|
|
1312
1554
|
const loaderRef = react.useRef(captionsLoader);
|
|
1313
1555
|
loaderRef.current = captionsLoader;
|
|
1314
|
-
react.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1556
|
+
const loading = react.useRef(/* @__PURE__ */ new Map());
|
|
1557
|
+
const loaded = react.useRef(/* @__PURE__ */ new Map());
|
|
1558
|
+
const loadCues = react.useCallback((track) => {
|
|
1559
|
+
const key = trackKey(track);
|
|
1560
|
+
const pending = loading.current.get(key);
|
|
1561
|
+
if (pending !== void 0) {
|
|
1562
|
+
return pending;
|
|
1318
1563
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1564
|
+
const request = Promise.resolve().then(
|
|
1565
|
+
() => loaderRef.current ? loaderRef.current(track) : fetch(track.src, { credentials: "same-origin" }).then((response) => {
|
|
1566
|
+
if (!response.ok) {
|
|
1567
|
+
throw new Error(String(response.status));
|
|
1568
|
+
}
|
|
1569
|
+
return response.text();
|
|
1570
|
+
})
|
|
1571
|
+
).then((text) => {
|
|
1572
|
+
const parsed = parseWebVtt(text);
|
|
1573
|
+
if (parsed.length === 0) {
|
|
1574
|
+
throw new Error("The caption file held no cues.");
|
|
1324
1575
|
}
|
|
1325
|
-
|
|
1576
|
+
loaded.current.set(key, parsed);
|
|
1577
|
+
return parsed;
|
|
1326
1578
|
});
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1579
|
+
loading.current.set(key, request);
|
|
1580
|
+
request.catch(() => {
|
|
1581
|
+
loading.current.delete(key);
|
|
1582
|
+
});
|
|
1583
|
+
return request;
|
|
1584
|
+
}, []);
|
|
1585
|
+
const peekCues = react.useCallback((key) => loaded.current.get(key), []);
|
|
1586
|
+
const primaryLine = useCaptionLine(primaryTrack, loadCues, peekCues);
|
|
1587
|
+
const secondaryLine = useCaptionLine(secondaryTrack, loadCues, peekCues);
|
|
1588
|
+
const primaryCues = primaryLine.cues;
|
|
1589
|
+
const secondaryCues = secondaryLine.cues;
|
|
1590
|
+
const captionsFailed = { primary: primaryLine.failed, secondary: secondaryLine.failed };
|
|
1591
|
+
const hasCaptions = primaryCues.length > 0 || secondaryCues.length > 0;
|
|
1592
|
+
const hasTranscript = primaryCues.length > 0;
|
|
1593
|
+
const activeCueIndex = react.useMemo(() => cueIndexAt(primaryCues, current), [primaryCues, current]);
|
|
1594
|
+
const secondaryCueIndex = react.useMemo(
|
|
1595
|
+
() => cueIndexAt(secondaryCues, current),
|
|
1596
|
+
[secondaryCues, current]
|
|
1597
|
+
);
|
|
1598
|
+
const spoilerLimit = timeline.navigation === "no-skip-ahead" && renderMode !== "review" ? Math.max(furthestShown, current) : Number.POSITIVE_INFINITY;
|
|
1599
|
+
const onPreferencesChangeRef = react.useRef(props.onPreferencesChange);
|
|
1600
|
+
onPreferencesChangeRef.current = props.onPreferencesChange;
|
|
1601
|
+
const tracksRef = react.useRef(tracks);
|
|
1602
|
+
tracksRef.current = tracks;
|
|
1603
|
+
const lastSecondary = react.useRef(null);
|
|
1604
|
+
const changePreferences = react.useCallback(
|
|
1605
|
+
(change) => {
|
|
1606
|
+
const previous = preferencesRef.current;
|
|
1607
|
+
const next = applyPreferenceChange(previous, change);
|
|
1608
|
+
if (previous.secondaryCaptionLanguage !== null) {
|
|
1609
|
+
lastSecondary.current = previous.secondaryCaptionLanguage;
|
|
1332
1610
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1611
|
+
chosen.current = { ...chosen.current, ...change };
|
|
1612
|
+
preferencesRef.current = next;
|
|
1613
|
+
setPreferences(next);
|
|
1614
|
+
rememberPreferences(change);
|
|
1615
|
+
onPreferencesChangeRef.current?.(next, change);
|
|
1616
|
+
const before = captionsShown(tracksRef.current, previous);
|
|
1617
|
+
const after = captionsShown(tracksRef.current, next);
|
|
1618
|
+
if (before.srclang !== after.srclang || before.secondary !== after.secondary) {
|
|
1619
|
+
emit("video-captions-changed", after);
|
|
1337
1620
|
}
|
|
1621
|
+
},
|
|
1622
|
+
[emit]
|
|
1623
|
+
);
|
|
1624
|
+
const chooseCaptionTrack = (track) => {
|
|
1625
|
+
const swaps = primaryTrack !== void 0 && secondaryTrack !== void 0 && track.srclang.toLowerCase() === secondaryTrack.srclang.toLowerCase();
|
|
1626
|
+
changePreferences({
|
|
1627
|
+
captions: true,
|
|
1628
|
+
captionLanguage: track.srclang,
|
|
1629
|
+
...swaps ? { secondaryCaptionLanguage: primaryTrack.srclang } : {}
|
|
1338
1630
|
});
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
const
|
|
1631
|
+
};
|
|
1632
|
+
const chooseSecondaryTrack = (track) => {
|
|
1633
|
+
changePreferences(
|
|
1634
|
+
track === null ? { secondaryCaptionLanguage: null } : { secondaryCaptionLanguage: track.srclang, captions: true }
|
|
1635
|
+
);
|
|
1636
|
+
};
|
|
1637
|
+
const toggleSecondary = () => {
|
|
1638
|
+
if (secondaryTrack !== void 0) {
|
|
1639
|
+
changePreferences(
|
|
1640
|
+
preferences.captions ? { secondaryCaptionLanguage: null } : { captions: true }
|
|
1641
|
+
);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
const wanted = [
|
|
1645
|
+
lastSecondary.current,
|
|
1646
|
+
preferences.secondaryCaptionLanguage,
|
|
1647
|
+
sanitizePartialPreferences(defaults).secondaryCaptionLanguage ?? null,
|
|
1648
|
+
secondaryCandidates(tracks, primaryTrack)[0]?.srclang ?? null
|
|
1649
|
+
];
|
|
1650
|
+
for (const language of wanted) {
|
|
1651
|
+
if (language === null) {
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
const { secondary } = resolveCaptionTracks(tracks, {
|
|
1655
|
+
captionLanguage: preferences.captionLanguage,
|
|
1656
|
+
secondaryCaptionLanguage: language
|
|
1657
|
+
});
|
|
1658
|
+
if (secondary !== void 0) {
|
|
1659
|
+
changePreferences({ secondaryCaptionLanguage: secondary.srclang, captions: true });
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
announce(strings.videoNoSecondLanguage);
|
|
1664
|
+
};
|
|
1346
1665
|
react.useEffect(() => {
|
|
1347
1666
|
const video = videoRef.current;
|
|
1348
1667
|
if (video !== null) {
|
|
@@ -1509,14 +1828,17 @@ function Player(props) {
|
|
|
1509
1828
|
return;
|
|
1510
1829
|
case "c":
|
|
1511
1830
|
case "C":
|
|
1512
|
-
if (
|
|
1831
|
+
if (event.shiftKey) {
|
|
1832
|
+
done();
|
|
1833
|
+
toggleSecondary();
|
|
1834
|
+
} else if (hasCaptions) {
|
|
1513
1835
|
done();
|
|
1514
1836
|
changePreferences({ captions: !preferences.captions });
|
|
1515
1837
|
}
|
|
1516
1838
|
return;
|
|
1517
1839
|
case "t":
|
|
1518
1840
|
case "T":
|
|
1519
|
-
if (
|
|
1841
|
+
if (hasTranscript) {
|
|
1520
1842
|
done();
|
|
1521
1843
|
setPanelTab("transcript");
|
|
1522
1844
|
changePreferences({ panel: !(preferences.panel && panelTab === "transcript") });
|
|
@@ -1648,7 +1970,7 @@ function Player(props) {
|
|
|
1648
1970
|
switch (activity.type) {
|
|
1649
1971
|
case "multiple-choice":
|
|
1650
1972
|
question = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1651
|
-
|
|
1973
|
+
chunkDMCFARGP_cjs.MultipleChoice,
|
|
1652
1974
|
{
|
|
1653
1975
|
data: activity,
|
|
1654
1976
|
...common,
|
|
@@ -1657,11 +1979,11 @@ function Player(props) {
|
|
|
1657
1979
|
);
|
|
1658
1980
|
break;
|
|
1659
1981
|
case "fill-in-the-blanks":
|
|
1660
|
-
question = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1982
|
+
question = /* @__PURE__ */ jsxRuntime.jsx(chunkZA5QUQ2V_cjs.FillInTheBlanks, { data: activity, ...common });
|
|
1661
1983
|
break;
|
|
1662
1984
|
case "gap-select":
|
|
1663
1985
|
question = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1664
|
-
|
|
1986
|
+
chunkRJGCT35F_cjs.GapSelect,
|
|
1665
1987
|
{
|
|
1666
1988
|
data: activity,
|
|
1667
1989
|
...common,
|
|
@@ -1670,13 +1992,13 @@ function Player(props) {
|
|
|
1670
1992
|
);
|
|
1671
1993
|
break;
|
|
1672
1994
|
case "dictation":
|
|
1673
|
-
question = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1995
|
+
question = /* @__PURE__ */ jsxRuntime.jsx(chunk2YZZL5FV_cjs.Dictation, { data: activity, ...common });
|
|
1674
1996
|
break;
|
|
1675
1997
|
case "read-aloud": {
|
|
1676
1998
|
const binding = slotBindings.get(slot.slotId);
|
|
1677
1999
|
const assessment = props.assessments !== void 0 && Object.hasOwn(props.assessments, slot.slotId) ? props.assessments[slot.slotId] : void 0;
|
|
1678
2000
|
question = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1679
|
-
|
|
2001
|
+
chunk2WM2KSO7_cjs.ReadAloud,
|
|
1680
2002
|
{
|
|
1681
2003
|
data: activity,
|
|
1682
2004
|
...common,
|
|
@@ -1720,7 +2042,9 @@ function Player(props) {
|
|
|
1720
2042
|
emit("video-quiz-question-shown", { cueId: openCue.id, slotId: slot.slotId });
|
|
1721
2043
|
}
|
|
1722
2044
|
};
|
|
1723
|
-
const
|
|
2045
|
+
const captionsHidden = !preferences.captions || openQuiz !== null || ended || errorCode !== null;
|
|
2046
|
+
const primaryText = captionsHidden ? void 0 : primaryCues[activeCueIndex]?.text;
|
|
2047
|
+
const secondaryText = captionsHidden ? void 0 : secondaryCues[secondaryCueIndex]?.text;
|
|
1724
2048
|
const chromeVisible = !playing || chromeShown || menu !== null || scrubbing || focusInChrome || sheet || openQuiz !== null;
|
|
1725
2049
|
const volumeLevel = preferences.muted || preferences.volume === 0 ? "muted" : preferences.volume < 0.5 ? "low" : "high";
|
|
1726
2050
|
return (
|
|
@@ -1832,15 +2156,43 @@ function Player(props) {
|
|
|
1832
2156
|
}
|
|
1833
2157
|
),
|
|
1834
2158
|
label !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-iv-label", children: label }) : null,
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
"
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
2159
|
+
primaryText !== void 0 || secondaryText !== void 0 ? (
|
|
2160
|
+
// The lines keep `.lk-iv-caption`, `data-size` and `data-background`
|
|
2161
|
+
// as well, so a stylesheet written against 15.x still matches them.
|
|
2162
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2163
|
+
"div",
|
|
2164
|
+
{
|
|
2165
|
+
className: "lk-iv-captions",
|
|
2166
|
+
"data-size": preferences.captionSize,
|
|
2167
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2168
|
+
children: [
|
|
2169
|
+
primaryText !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2170
|
+
"div",
|
|
2171
|
+
{
|
|
2172
|
+
className: "lk-iv-caption",
|
|
2173
|
+
"data-role": "primary",
|
|
2174
|
+
"data-size": preferences.captionSize,
|
|
2175
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2176
|
+
lang: primaryTrack?.srclang,
|
|
2177
|
+
dir: "auto",
|
|
2178
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: primaryText })
|
|
2179
|
+
}
|
|
2180
|
+
) : null,
|
|
2181
|
+
secondaryText !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2182
|
+
"div",
|
|
2183
|
+
{
|
|
2184
|
+
className: "lk-iv-caption",
|
|
2185
|
+
"data-role": "secondary",
|
|
2186
|
+
"data-size": preferences.captionSize,
|
|
2187
|
+
"data-background": preferences.captionBackground || void 0,
|
|
2188
|
+
lang: secondaryTrack?.srclang,
|
|
2189
|
+
dir: "auto",
|
|
2190
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: secondaryText })
|
|
2191
|
+
}
|
|
2192
|
+
) : null
|
|
2193
|
+
]
|
|
2194
|
+
}
|
|
2195
|
+
)
|
|
1844
2196
|
) : null,
|
|
1845
2197
|
waiting && errorCode === null && openQuiz === null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-iv-spinner", "aria-hidden": "true" }) : null,
|
|
1846
2198
|
errorCode !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-error", role: "alert", children: [
|
|
@@ -1889,7 +2241,7 @@ function Player(props) {
|
|
|
1889
2241
|
buffered,
|
|
1890
2242
|
chapters,
|
|
1891
2243
|
markers,
|
|
1892
|
-
cues:
|
|
2244
|
+
cues: primaryCues,
|
|
1893
2245
|
previewLimit: spoilerLimit,
|
|
1894
2246
|
strings,
|
|
1895
2247
|
onSeek: seek,
|
|
@@ -1974,7 +2326,7 @@ function Player(props) {
|
|
|
1974
2326
|
)
|
|
1975
2327
|
] }),
|
|
1976
2328
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-iv-bar-group", children: [
|
|
1977
|
-
quizzes.length > 0 || chapters.length > 0 ||
|
|
2329
|
+
quizzes.length > 0 || chapters.length > 0 || hasTranscript ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1978
2330
|
"button",
|
|
1979
2331
|
{
|
|
1980
2332
|
type: "button",
|
|
@@ -1992,12 +2344,7 @@ function Player(props) {
|
|
|
1992
2344
|
className: "lk-iv-button",
|
|
1993
2345
|
"aria-label": preferences.captions ? strings.videoCaptionsHide : strings.videoCaptionsShow,
|
|
1994
2346
|
"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
|
-
},
|
|
2347
|
+
onClick: () => changePreferences({ captions: !preferences.captions }),
|
|
2001
2348
|
children: /* @__PURE__ */ jsxRuntime.jsx(CaptionsIcon, { on: preferences.captions })
|
|
2002
2349
|
}
|
|
2003
2350
|
) : null,
|
|
@@ -2045,8 +2392,11 @@ function Player(props) {
|
|
|
2045
2392
|
{
|
|
2046
2393
|
preferences,
|
|
2047
2394
|
tracks,
|
|
2048
|
-
|
|
2395
|
+
primaryTrack,
|
|
2396
|
+
secondaryTrack,
|
|
2049
2397
|
captionsFailed,
|
|
2398
|
+
onCaptionTrack: chooseCaptionTrack,
|
|
2399
|
+
onSecondaryTrack: chooseSecondaryTrack,
|
|
2050
2400
|
strings,
|
|
2051
2401
|
onChange: changePreferences,
|
|
2052
2402
|
onShowShortcuts: () => {
|
|
@@ -2227,7 +2577,10 @@ function Player(props) {
|
|
|
2227
2577
|
onTab: setPanelTab,
|
|
2228
2578
|
chapters,
|
|
2229
2579
|
quizzes,
|
|
2230
|
-
cues:
|
|
2580
|
+
cues: primaryCues,
|
|
2581
|
+
primaryLanguage: primaryTrack?.srclang,
|
|
2582
|
+
secondaryCues: secondaryTrack === void 0 ? NO_CUES : secondaryCues,
|
|
2583
|
+
secondaryLanguage: secondaryTrack?.srclang,
|
|
2231
2584
|
activeCue: activeCueIndex,
|
|
2232
2585
|
limit: spoilerLimit,
|
|
2233
2586
|
strings,
|
|
@@ -2268,7 +2621,7 @@ function QuestionPane({
|
|
|
2268
2621
|
if (!hidden) {
|
|
2269
2622
|
return;
|
|
2270
2623
|
}
|
|
2271
|
-
|
|
2624
|
+
chunk2WM2KSO7_cjs.stopCaptureGroup(captureGroup);
|
|
2272
2625
|
for (const media of ref.current?.querySelectorAll("audio, video") ?? []) {
|
|
2273
2626
|
const element = media;
|
|
2274
2627
|
if (!element.paused) {
|
|
@@ -2276,7 +2629,7 @@ function QuestionPane({
|
|
|
2276
2629
|
}
|
|
2277
2630
|
}
|
|
2278
2631
|
}, [hidden, captureGroup]);
|
|
2279
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: "lk-iv-question", hidden, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2632
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: "lk-iv-question", hidden, children: /* @__PURE__ */ jsxRuntime.jsx(chunk2WM2KSO7_cjs.SequenceSlotContext.Provider, { value: channel, children }) });
|
|
2280
2633
|
}
|
|
2281
2634
|
function bufferedAhead(video) {
|
|
2282
2635
|
const ranges = video.buffered;
|
|
@@ -2316,9 +2669,9 @@ function markerState(quizSlots, answered, renderMode, outcomes) {
|
|
|
2316
2669
|
return "unreached";
|
|
2317
2670
|
}
|
|
2318
2671
|
function InteractiveVideo2(props) {
|
|
2319
|
-
const strings =
|
|
2672
|
+
const strings = chunkUISZWFI7_cjs.useLkStrings(props.strings);
|
|
2320
2673
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2321
|
-
|
|
2674
|
+
chunkP5FGJMMI_cjs.ActivityErrorBoundary,
|
|
2322
2675
|
{
|
|
2323
2676
|
...props.group?.title !== void 0 ? { activityTitle: props.group.title } : {},
|
|
2324
2677
|
strings,
|
|
@@ -2328,5 +2681,6 @@ function InteractiveVideo2(props) {
|
|
|
2328
2681
|
}
|
|
2329
2682
|
|
|
2330
2683
|
exports.InteractiveVideo = InteractiveVideo2;
|
|
2331
|
-
|
|
2332
|
-
//# sourceMappingURL=chunk-
|
|
2684
|
+
exports.resolveCaptionTracks = resolveCaptionTracks;
|
|
2685
|
+
//# sourceMappingURL=chunk-A2CPCOJA.cjs.map
|
|
2686
|
+
//# sourceMappingURL=chunk-A2CPCOJA.cjs.map
|