vidply 1.0.27 → 1.0.29
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/dist/dev/{vidply.TranscriptManager-GZKY44ON.js → vidply.TranscriptManager-T677KF4N.js} +5 -6
- package/dist/dev/vidply.TranscriptManager-T677KF4N.js.map +7 -0
- package/dist/dev/{vidply.chunk-UH5MTGKF.js → vidply.chunk-GS2JX5RQ.js} +149 -100
- package/dist/dev/vidply.chunk-GS2JX5RQ.js.map +7 -0
- package/dist/dev/{vidply.de-THBIMP4S.js → vidply.de-SNL6AJ4D.js} +10 -2
- package/dist/dev/{vidply.de-THBIMP4S.js.map → vidply.de-SNL6AJ4D.js.map} +2 -2
- package/dist/dev/{vidply.es-6VWDNNNL.js → vidply.es-2QCQKZ4U.js} +10 -2
- package/dist/dev/{vidply.es-6VWDNNNL.js.map → vidply.es-2QCQKZ4U.js.map} +2 -2
- package/dist/dev/vidply.esm.js +1681 -317
- package/dist/dev/vidply.esm.js.map +4 -4
- package/dist/dev/{vidply.fr-WHTWCHWT.js → vidply.fr-FJAZRL4L.js} +10 -2
- package/dist/dev/{vidply.fr-WHTWCHWT.js.map → vidply.fr-FJAZRL4L.js.map} +2 -2
- package/dist/dev/{vidply.ja-BFQNPOFI.js → vidply.ja-2XQOW53T.js} +10 -2
- package/dist/dev/vidply.ja-2XQOW53T.js.map +7 -0
- package/dist/legacy/vidply.js +1829 -361
- package/dist/legacy/vidply.js.map +4 -4
- package/dist/legacy/vidply.min.js +1 -1
- package/dist/legacy/vidply.min.meta.json +103 -35
- package/dist/prod/vidply.TranscriptManager-WFZSW6NR.min.js +6 -0
- package/dist/prod/vidply.chunk-LGTJRPUL.min.js +6 -0
- package/dist/prod/vidply.de-FR3XX54P.min.js +6 -0
- package/dist/prod/vidply.es-3IJCQLJ7.min.js +6 -0
- package/dist/prod/vidply.esm.min.js +8 -8
- package/dist/prod/vidply.fr-NC4VEAPH.min.js +6 -0
- package/dist/prod/vidply.ja-4ZC6ZQLV.min.js +6 -0
- package/dist/vidply.esm.min.meta.json +115 -47
- package/package.json +1 -1
- package/src/controls/ControlBar.js +3 -7
- package/src/controls/TranscriptManager.js +8 -8
- package/src/core/AudioDescriptionManager.js +701 -0
- package/src/core/Player.js +4776 -4921
- package/src/core/SignLanguageManager.js +1134 -0
- package/src/features/PlaylistManager.js +12 -12
- package/src/i18n/languages/de.js +9 -1
- package/src/i18n/languages/en.js +9 -1
- package/src/i18n/languages/es.js +9 -1
- package/src/i18n/languages/fr.js +9 -1
- package/src/i18n/languages/ja.js +9 -1
- package/src/utils/DOMUtils.js +153 -114
- package/src/utils/MenuFactory.js +374 -0
- package/dist/dev/vidply.TranscriptManager-GZKY44ON.js.map +0 -7
- package/dist/dev/vidply.chunk-UH5MTGKF.js.map +0 -7
- package/dist/dev/vidply.ja-BFQNPOFI.js.map +0 -7
- package/dist/prod/vidply.TranscriptManager-UZ6DUFB6.min.js +0 -6
- package/dist/prod/vidply.chunk-MBUR3U5L.min.js +0 -6
- package/dist/prod/vidply.de-SWFW4HYT.min.js +0 -6
- package/dist/prod/vidply.es-7BJ2DJAY.min.js +0 -6
- package/dist/prod/vidply.fr-DPVR5DFY.min.js +0 -6
- package/dist/prod/vidply.ja-PEBVWKVH.min.js +0 -6
package/dist/legacy/vidply.js
CHANGED
|
@@ -34,15 +34,21 @@
|
|
|
34
34
|
var init_DOMUtils = __esm({
|
|
35
35
|
"src/utils/DOMUtils.js"() {
|
|
36
36
|
DOMUtils = {
|
|
37
|
+
/**
|
|
38
|
+
* Create an element with options
|
|
39
|
+
* @param {string} tag - HTML tag name
|
|
40
|
+
* @param {Object} options - Element options
|
|
41
|
+
* @returns {HTMLElement}
|
|
42
|
+
*/
|
|
37
43
|
createElement(tag, options = {}) {
|
|
38
44
|
const element = document.createElement(tag);
|
|
39
45
|
if (options.className) {
|
|
40
46
|
element.className = options.className;
|
|
41
47
|
}
|
|
42
48
|
if (options.attributes) {
|
|
43
|
-
Object.entries(options.attributes)
|
|
49
|
+
for (const [key, value] of Object.entries(options.attributes)) {
|
|
44
50
|
element.setAttribute(key, value);
|
|
45
|
-
}
|
|
51
|
+
}
|
|
46
52
|
}
|
|
47
53
|
if (options.innerHTML) {
|
|
48
54
|
element.innerHTML = options.innerHTML;
|
|
@@ -54,150 +60,190 @@
|
|
|
54
60
|
Object.assign(element.style, options.style);
|
|
55
61
|
}
|
|
56
62
|
if (options.children) {
|
|
57
|
-
options.children
|
|
63
|
+
for (const child of options.children) {
|
|
58
64
|
if (child) element.appendChild(child);
|
|
59
|
-
}
|
|
65
|
+
}
|
|
60
66
|
}
|
|
61
67
|
return element;
|
|
62
68
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
removeClass(element, className) {
|
|
69
|
-
if (element && className) {
|
|
70
|
-
element.classList.remove(className);
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
toggleClass(element, className) {
|
|
74
|
-
if (element && className) {
|
|
75
|
-
element.classList.toggle(className);
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
hasClass(element, className) {
|
|
79
|
-
return element && element.classList.contains(className);
|
|
80
|
-
},
|
|
69
|
+
/**
|
|
70
|
+
* Show element (remove display:none)
|
|
71
|
+
* @param {HTMLElement} element
|
|
72
|
+
*/
|
|
81
73
|
show(element) {
|
|
82
|
-
|
|
83
|
-
element.style.display = "";
|
|
84
|
-
}
|
|
74
|
+
(element == null ? void 0 : element.style) && (element.style.display = "");
|
|
85
75
|
},
|
|
76
|
+
/**
|
|
77
|
+
* Hide element
|
|
78
|
+
* @param {HTMLElement} element
|
|
79
|
+
*/
|
|
86
80
|
hide(element) {
|
|
87
|
-
|
|
88
|
-
element.style.display = "none";
|
|
89
|
-
}
|
|
81
|
+
(element == null ? void 0 : element.style) && (element.style.display = "none");
|
|
90
82
|
},
|
|
91
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Fade in element using CSS transitions (GPU accelerated)
|
|
85
|
+
* @param {HTMLElement} element
|
|
86
|
+
* @param {number} duration - Duration in ms
|
|
87
|
+
* @param {Function} [onComplete] - Callback when complete
|
|
88
|
+
*/
|
|
89
|
+
fadeIn(element, duration = 300, onComplete) {
|
|
92
90
|
if (!element) return;
|
|
93
91
|
element.style.opacity = "0";
|
|
94
92
|
element.style.display = "";
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
93
|
+
element.style.transition = "opacity ".concat(duration, "ms ease");
|
|
94
|
+
element.offsetHeight;
|
|
95
|
+
element.style.opacity = "1";
|
|
96
|
+
if (onComplete) {
|
|
97
|
+
const cleanup = () => {
|
|
98
|
+
element.removeEventListener("transitionend", cleanup);
|
|
99
|
+
onComplete();
|
|
100
|
+
};
|
|
101
|
+
element.addEventListener("transitionend", cleanup, { once: true });
|
|
102
|
+
setTimeout(cleanup, duration + 50);
|
|
103
|
+
}
|
|
106
104
|
},
|
|
107
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Fade out element using CSS transitions (GPU accelerated)
|
|
107
|
+
* @param {HTMLElement} element
|
|
108
|
+
* @param {number} duration - Duration in ms
|
|
109
|
+
* @param {Function} [onComplete] - Callback when complete
|
|
110
|
+
*/
|
|
111
|
+
fadeOut(element, duration = 300, onComplete) {
|
|
108
112
|
if (!element) return;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
element.style.opacity = opacity;
|
|
116
|
-
if (progress < duration) {
|
|
117
|
-
requestAnimationFrame(animate);
|
|
118
|
-
} else {
|
|
119
|
-
element.style.display = "none";
|
|
120
|
-
}
|
|
113
|
+
element.style.transition = "opacity ".concat(duration, "ms ease");
|
|
114
|
+
element.style.opacity = "0";
|
|
115
|
+
const cleanup = () => {
|
|
116
|
+
element.removeEventListener("transitionend", cleanup);
|
|
117
|
+
element.style.display = "none";
|
|
118
|
+
if (onComplete) onComplete();
|
|
121
119
|
};
|
|
122
|
-
|
|
120
|
+
element.addEventListener("transitionend", cleanup, { once: true });
|
|
121
|
+
setTimeout(cleanup, duration + 50);
|
|
123
122
|
},
|
|
123
|
+
/**
|
|
124
|
+
* Get element's offset position and dimensions
|
|
125
|
+
* @param {HTMLElement} element
|
|
126
|
+
* @returns {Object} { top, left, width, height }
|
|
127
|
+
*/
|
|
124
128
|
offset(element) {
|
|
125
|
-
if (!element) return { top: 0, left: 0 };
|
|
129
|
+
if (!element) return { top: 0, left: 0, width: 0, height: 0 };
|
|
126
130
|
const rect = element.getBoundingClientRect();
|
|
127
131
|
return {
|
|
128
|
-
top: rect.top + window.
|
|
129
|
-
left: rect.left + window.
|
|
132
|
+
top: rect.top + window.scrollY,
|
|
133
|
+
left: rect.left + window.scrollX,
|
|
130
134
|
width: rect.width,
|
|
131
135
|
height: rect.height
|
|
132
136
|
};
|
|
133
137
|
},
|
|
138
|
+
/**
|
|
139
|
+
* Escape HTML special characters
|
|
140
|
+
* @param {string} str - String to escape
|
|
141
|
+
* @returns {string} Escaped string
|
|
142
|
+
*/
|
|
134
143
|
escapeHTML(str) {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
const escapeMap = {
|
|
145
|
+
"&": "&",
|
|
146
|
+
"<": "<",
|
|
147
|
+
">": ">",
|
|
148
|
+
'"': """,
|
|
149
|
+
"'": "'"
|
|
150
|
+
};
|
|
151
|
+
return str.replace(/[&<>"']/g, (char) => escapeMap[char]);
|
|
138
152
|
},
|
|
153
|
+
/**
|
|
154
|
+
* Basic HTML sanitization for VTT captions
|
|
155
|
+
* Allows safe formatting tags, removes dangerous content
|
|
156
|
+
* @param {string} html - HTML string to sanitize
|
|
157
|
+
* @returns {string} Sanitized HTML
|
|
158
|
+
*/
|
|
139
159
|
sanitizeHTML(html) {
|
|
140
|
-
const temp = document.createElement("div");
|
|
141
160
|
const safeHtml = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, "").replace(/on\w+\s*=/gi, "").replace(/javascript:/gi, "");
|
|
161
|
+
const temp = document.createElement("div");
|
|
142
162
|
temp.innerHTML = safeHtml;
|
|
143
163
|
return temp.innerHTML;
|
|
144
164
|
},
|
|
145
165
|
/**
|
|
146
|
-
* Create a tooltip element
|
|
166
|
+
* Create a tooltip element (aria-hidden)
|
|
147
167
|
* @param {string} text - Tooltip text
|
|
148
|
-
* @param {string} classPrefix - Class prefix
|
|
149
|
-
* @returns {HTMLElement}
|
|
168
|
+
* @param {string} classPrefix - Class prefix
|
|
169
|
+
* @returns {HTMLElement}
|
|
150
170
|
*/
|
|
151
171
|
createTooltip(text, classPrefix = "vidply") {
|
|
152
|
-
|
|
172
|
+
return this.createElement("span", {
|
|
153
173
|
className: "".concat(classPrefix, "-tooltip"),
|
|
154
174
|
textContent: text,
|
|
155
|
-
attributes: {
|
|
156
|
-
"aria-hidden": "true"
|
|
157
|
-
}
|
|
175
|
+
attributes: { "aria-hidden": "true" }
|
|
158
176
|
});
|
|
159
|
-
return tooltip;
|
|
160
177
|
},
|
|
161
178
|
/**
|
|
162
|
-
* Attach a tooltip to an element
|
|
163
|
-
* @param {HTMLElement} element -
|
|
179
|
+
* Attach a tooltip to an element with hover/focus behavior
|
|
180
|
+
* @param {HTMLElement} element - Target element
|
|
164
181
|
* @param {string} text - Tooltip text
|
|
165
|
-
* @param {string} classPrefix - Class prefix
|
|
182
|
+
* @param {string} classPrefix - Class prefix
|
|
166
183
|
*/
|
|
167
184
|
attachTooltip(element, text, classPrefix = "vidply") {
|
|
185
|
+
var _a;
|
|
168
186
|
if (!element || !text) return;
|
|
169
|
-
|
|
170
|
-
if (existingTooltip) {
|
|
171
|
-
existingTooltip.remove();
|
|
172
|
-
}
|
|
187
|
+
(_a = element.querySelector(".".concat(classPrefix, "-tooltip"))) == null ? void 0 : _a.remove();
|
|
173
188
|
const tooltip = this.createTooltip(text, classPrefix);
|
|
174
189
|
element.appendChild(tooltip);
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
element.addEventListener("
|
|
182
|
-
element.addEventListener("mouseleave", hideTooltip);
|
|
183
|
-
element.addEventListener("focus", showTooltip);
|
|
184
|
-
element.addEventListener("blur", hideTooltip);
|
|
190
|
+
const visibleClass = "".concat(classPrefix, "-tooltip-visible");
|
|
191
|
+
const show = () => tooltip.classList.add(visibleClass);
|
|
192
|
+
const hide = () => tooltip.classList.remove(visibleClass);
|
|
193
|
+
element.addEventListener("mouseenter", show);
|
|
194
|
+
element.addEventListener("mouseleave", hide);
|
|
195
|
+
element.addEventListener("focus", show);
|
|
196
|
+
element.addEventListener("blur", hide);
|
|
185
197
|
},
|
|
186
198
|
/**
|
|
187
|
-
* Create
|
|
199
|
+
* Create button text element (visible when CSS disabled)
|
|
188
200
|
* @param {string} text - Button text
|
|
189
|
-
* @param {string} classPrefix - Class prefix
|
|
190
|
-
* @returns {HTMLElement}
|
|
201
|
+
* @param {string} classPrefix - Class prefix
|
|
202
|
+
* @returns {HTMLElement}
|
|
191
203
|
*/
|
|
192
204
|
createButtonText(text, classPrefix = "vidply") {
|
|
193
|
-
|
|
205
|
+
return this.createElement("span", {
|
|
194
206
|
className: "".concat(classPrefix, "-button-text"),
|
|
195
207
|
textContent: text,
|
|
196
|
-
attributes: {
|
|
197
|
-
"aria-hidden": "true"
|
|
198
|
-
}
|
|
208
|
+
attributes: { "aria-hidden": "true" }
|
|
199
209
|
});
|
|
200
|
-
|
|
210
|
+
},
|
|
211
|
+
/**
|
|
212
|
+
* Add class to element (null-safe)
|
|
213
|
+
* @param {HTMLElement} element
|
|
214
|
+
* @param {string} className
|
|
215
|
+
*/
|
|
216
|
+
addClass(element, className) {
|
|
217
|
+
var _a;
|
|
218
|
+
(_a = element == null ? void 0 : element.classList) == null ? void 0 : _a.add(className);
|
|
219
|
+
},
|
|
220
|
+
/**
|
|
221
|
+
* Remove class from element (null-safe)
|
|
222
|
+
* @param {HTMLElement} element
|
|
223
|
+
* @param {string} className
|
|
224
|
+
*/
|
|
225
|
+
removeClass(element, className) {
|
|
226
|
+
var _a;
|
|
227
|
+
(_a = element == null ? void 0 : element.classList) == null ? void 0 : _a.remove(className);
|
|
228
|
+
},
|
|
229
|
+
/**
|
|
230
|
+
* Toggle class on element (null-safe)
|
|
231
|
+
* @param {HTMLElement} element
|
|
232
|
+
* @param {string} className
|
|
233
|
+
*/
|
|
234
|
+
toggleClass(element, className) {
|
|
235
|
+
var _a;
|
|
236
|
+
(_a = element == null ? void 0 : element.classList) == null ? void 0 : _a.toggle(className);
|
|
237
|
+
},
|
|
238
|
+
/**
|
|
239
|
+
* Check if element has class (null-safe)
|
|
240
|
+
* @param {HTMLElement} element
|
|
241
|
+
* @param {string} className
|
|
242
|
+
* @returns {boolean}
|
|
243
|
+
*/
|
|
244
|
+
hasClass(element, className) {
|
|
245
|
+
var _a, _b;
|
|
246
|
+
return (_b = (_a = element == null ? void 0 : element.classList) == null ? void 0 : _a.contains(className)) != null ? _b : false;
|
|
201
247
|
}
|
|
202
248
|
};
|
|
203
249
|
}
|
|
@@ -307,6 +353,7 @@
|
|
|
307
353
|
},
|
|
308
354
|
transcript: {
|
|
309
355
|
title: "Transcript",
|
|
356
|
+
ariaLabel: "Video Transcript",
|
|
310
357
|
close: "Close transcript",
|
|
311
358
|
loading: "Loading transcript...",
|
|
312
359
|
noTranscript: "No transcript available for this video.",
|
|
@@ -373,7 +420,14 @@
|
|
|
373
420
|
currentlyPlaying: "Currently playing",
|
|
374
421
|
notPlaying: "Not playing",
|
|
375
422
|
pressEnterPlay: "Press Enter to play",
|
|
376
|
-
pressEnterRestart: "Press Enter to restart"
|
|
423
|
+
pressEnterRestart: "Press Enter to restart",
|
|
424
|
+
keyboardInstructions: "Playlist navigation: Use Up and Down arrow keys to move between tracks. Press Page Up or Page Down to skip 5 tracks. Press Home to go to first track, End to go to last track. Press Enter or Space to play the selected track.",
|
|
425
|
+
endOfPlaylist: "End of playlist. {current} of {total}.",
|
|
426
|
+
beginningOfPlaylist: "Beginning of playlist. 1 of {total}.",
|
|
427
|
+
jumpedToLastTrack: "Jumped to last track. {current} of {total}.",
|
|
428
|
+
jumpedToFirstTrack: "Jumped to first track. 1 of {total}.",
|
|
429
|
+
firstTrack: "First track. 1 of {total}.",
|
|
430
|
+
lastTrack: "Last track. {current} of {total}."
|
|
377
431
|
}
|
|
378
432
|
};
|
|
379
433
|
}
|
|
@@ -487,6 +541,7 @@
|
|
|
487
541
|
},
|
|
488
542
|
transcript: {
|
|
489
543
|
title: "Transkript",
|
|
544
|
+
ariaLabel: "Video-Transkript",
|
|
490
545
|
close: "Transkript schließen",
|
|
491
546
|
loading: "Transkript wird geladen...",
|
|
492
547
|
noTranscript: "Kein Transkript für dieses Video verfügbar.",
|
|
@@ -553,7 +608,14 @@
|
|
|
553
608
|
currentlyPlaying: "Wird gerade abgespielt",
|
|
554
609
|
notPlaying: "Nicht aktiv",
|
|
555
610
|
pressEnterPlay: "Eingabetaste zum Abspielen",
|
|
556
|
-
pressEnterRestart: "Eingabetaste zum Neustart"
|
|
611
|
+
pressEnterRestart: "Eingabetaste zum Neustart",
|
|
612
|
+
keyboardInstructions: "Wiedergabelisten-Navigation: Verwenden Sie die Pfeiltasten nach oben und unten, um zwischen Titeln zu wechseln. Drücken Sie Bild auf oder Bild ab, um 5 Titel zu überspringen. Drücken Sie Pos1, um zum ersten Titel zu springen, Ende für den letzten Titel. Drücken Sie die Eingabetaste oder Leertaste, um den ausgewählten Titel abzuspielen.",
|
|
613
|
+
endOfPlaylist: "Ende der Wiedergabeliste. {current} von {total}.",
|
|
614
|
+
beginningOfPlaylist: "Anfang der Wiedergabeliste. 1 von {total}.",
|
|
615
|
+
jumpedToLastTrack: "Zum letzten Titel gesprungen. {current} von {total}.",
|
|
616
|
+
jumpedToFirstTrack: "Zum ersten Titel gesprungen. 1 von {total}.",
|
|
617
|
+
firstTrack: "Erster Titel. 1 von {total}.",
|
|
618
|
+
lastTrack: "Letzter Titel. {current} von {total}."
|
|
557
619
|
}
|
|
558
620
|
};
|
|
559
621
|
}
|
|
@@ -667,6 +729,7 @@
|
|
|
667
729
|
},
|
|
668
730
|
transcript: {
|
|
669
731
|
title: "Transcripción",
|
|
732
|
+
ariaLabel: "Transcripción de video",
|
|
670
733
|
close: "Cerrar transcripción",
|
|
671
734
|
loading: "Cargando transcripción...",
|
|
672
735
|
noTranscript: "No hay transcripción disponible para este video.",
|
|
@@ -733,7 +796,14 @@
|
|
|
733
796
|
currentlyPlaying: "Reproduciendo actualmente",
|
|
734
797
|
notPlaying: "Sin reproducir",
|
|
735
798
|
pressEnterPlay: "Pulsa Enter para reproducir",
|
|
736
|
-
pressEnterRestart: "Pulsa Enter para reiniciar"
|
|
799
|
+
pressEnterRestart: "Pulsa Enter para reiniciar",
|
|
800
|
+
keyboardInstructions: "Navegación de lista de reproducción: Use las teclas de flecha arriba y abajo para moverse entre pistas. Pulse Retroceder página o Avanzar página para saltar 5 pistas. Pulse Inicio para ir a la primera pista, Fin para la última pista. Pulse Intro o Espacio para reproducir la pista seleccionada.",
|
|
801
|
+
endOfPlaylist: "Fin de la lista de reproducción. {current} de {total}.",
|
|
802
|
+
beginningOfPlaylist: "Inicio de la lista de reproducción. 1 de {total}.",
|
|
803
|
+
jumpedToLastTrack: "Saltó a la última pista. {current} de {total}.",
|
|
804
|
+
jumpedToFirstTrack: "Saltó a la primera pista. 1 de {total}.",
|
|
805
|
+
firstTrack: "Primera pista. 1 de {total}.",
|
|
806
|
+
lastTrack: "Última pista. {current} de {total}."
|
|
737
807
|
}
|
|
738
808
|
};
|
|
739
809
|
}
|
|
@@ -847,6 +917,7 @@
|
|
|
847
917
|
},
|
|
848
918
|
transcript: {
|
|
849
919
|
title: "Transcription",
|
|
920
|
+
ariaLabel: "Transcription vidéo",
|
|
850
921
|
close: "Fermer la transcription",
|
|
851
922
|
loading: "Chargement de la transcription...",
|
|
852
923
|
noTranscript: "Aucune transcription disponible pour cette vidéo.",
|
|
@@ -913,7 +984,14 @@
|
|
|
913
984
|
currentlyPlaying: "En cours de lecture",
|
|
914
985
|
notPlaying: "Non en lecture",
|
|
915
986
|
pressEnterPlay: "Appuyez sur Entrée pour lire",
|
|
916
|
-
pressEnterRestart: "Appuyez sur Entrée pour recommencer"
|
|
987
|
+
pressEnterRestart: "Appuyez sur Entrée pour recommencer",
|
|
988
|
+
keyboardInstructions: "Navigation de la liste de lecture : Utilisez les touches fléchées haut et bas pour naviguer entre les pistes. Appuyez sur Page précédente ou Page suivante pour sauter 5 pistes. Appuyez sur Début pour aller à la première piste, Fin pour la dernière piste. Appuyez sur Entrée ou Espace pour lire la piste sélectionnée.",
|
|
989
|
+
endOfPlaylist: "Fin de la liste de lecture. {current} sur {total}.",
|
|
990
|
+
beginningOfPlaylist: "Début de la liste de lecture. 1 sur {total}.",
|
|
991
|
+
jumpedToLastTrack: "Sauté à la dernière piste. {current} sur {total}.",
|
|
992
|
+
jumpedToFirstTrack: "Sauté à la première piste. 1 sur {total}.",
|
|
993
|
+
firstTrack: "Première piste. 1 sur {total}.",
|
|
994
|
+
lastTrack: "Dernière piste. {current} sur {total}."
|
|
917
995
|
}
|
|
918
996
|
};
|
|
919
997
|
}
|
|
@@ -1027,6 +1105,7 @@
|
|
|
1027
1105
|
},
|
|
1028
1106
|
transcript: {
|
|
1029
1107
|
title: "文字起こし",
|
|
1108
|
+
ariaLabel: "ビデオ文字起こし",
|
|
1030
1109
|
close: "文字起こしを閉じる",
|
|
1031
1110
|
loading: "文字起こしを読み込み中...",
|
|
1032
1111
|
noTranscript: "このビデオの文字起こしはありません。",
|
|
@@ -1093,7 +1172,14 @@
|
|
|
1093
1172
|
currentlyPlaying: "再生中",
|
|
1094
1173
|
notPlaying: "停止中",
|
|
1095
1174
|
pressEnterPlay: "Enterキーで再生",
|
|
1096
|
-
pressEnterRestart: "Enterキーで最初から再生"
|
|
1175
|
+
pressEnterRestart: "Enterキーで最初から再生",
|
|
1176
|
+
keyboardInstructions: "プレイリストナビゲーション:上下の矢印キーでトラック間を移動します。Page UpまたはPage Downで5トラックをスキップします。Homeで最初のトラックへ、Endで最後のトラックへ移動します。EnterまたはSpaceで選択したトラックを再生します。",
|
|
1177
|
+
endOfPlaylist: "プレイリストの終わりです。{current}/{total}。",
|
|
1178
|
+
beginningOfPlaylist: "プレイリストの始めです。1/{total}。",
|
|
1179
|
+
jumpedToLastTrack: "最後のトラックにジャンプしました。{current}/{total}。",
|
|
1180
|
+
jumpedToFirstTrack: "最初のトラックにジャンプしました。1/{total}。",
|
|
1181
|
+
firstTrack: "最初のトラックです。1/{total}。",
|
|
1182
|
+
lastTrack: "最後のトラックです。{current}/{total}。"
|
|
1097
1183
|
}
|
|
1098
1184
|
};
|
|
1099
1185
|
}
|
|
@@ -2824,7 +2910,7 @@
|
|
|
2824
2910
|
className: "".concat(this.player.options.classPrefix, "-transcript-window"),
|
|
2825
2911
|
attributes: {
|
|
2826
2912
|
"role": "dialog",
|
|
2827
|
-
"aria-label": "
|
|
2913
|
+
"aria-label": i18n.t("transcript.ariaLabel"),
|
|
2828
2914
|
"tabindex": "-1"
|
|
2829
2915
|
}
|
|
2830
2916
|
});
|
|
@@ -3228,8 +3314,7 @@
|
|
|
3228
3314
|
descriptionTrack = textTracks.find((track) => track.kind === "descriptions");
|
|
3229
3315
|
}
|
|
3230
3316
|
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
3231
|
-
|
|
3232
|
-
if (!captionTrack && !hasDescriptionTrack && !metadataTrack) {
|
|
3317
|
+
if (!captionTrack && !descriptionTrack && !metadataTrack) {
|
|
3233
3318
|
this.showNoTranscriptMessage();
|
|
3234
3319
|
return;
|
|
3235
3320
|
}
|
|
@@ -3267,7 +3352,7 @@
|
|
|
3267
3352
|
allCues.push({ cue, type: "caption" });
|
|
3268
3353
|
});
|
|
3269
3354
|
}
|
|
3270
|
-
if (descriptionTrack && descriptionTrack.cues
|
|
3355
|
+
if (descriptionTrack && descriptionTrack.cues) {
|
|
3271
3356
|
Array.from(descriptionTrack.cues).forEach((cue) => {
|
|
3272
3357
|
allCues.push({ cue, type: "description" });
|
|
3273
3358
|
});
|
|
@@ -5461,6 +5546,35 @@
|
|
|
5461
5546
|
init_Icons();
|
|
5462
5547
|
init_i18n();
|
|
5463
5548
|
init_FocusUtils();
|
|
5549
|
+
|
|
5550
|
+
// src/utils/PerformanceUtils.js
|
|
5551
|
+
function debounce(func, wait = 100) {
|
|
5552
|
+
let timeout;
|
|
5553
|
+
return function executedFunction(...args) {
|
|
5554
|
+
const later = () => {
|
|
5555
|
+
clearTimeout(timeout);
|
|
5556
|
+
func(...args);
|
|
5557
|
+
};
|
|
5558
|
+
clearTimeout(timeout);
|
|
5559
|
+
timeout = setTimeout(later, wait);
|
|
5560
|
+
};
|
|
5561
|
+
}
|
|
5562
|
+
function isMobile(breakpoint = 768) {
|
|
5563
|
+
return window.innerWidth < breakpoint;
|
|
5564
|
+
}
|
|
5565
|
+
function rafWithTimeout(callback, timeout = 100) {
|
|
5566
|
+
let called = false;
|
|
5567
|
+
const execute = () => {
|
|
5568
|
+
if (!called) {
|
|
5569
|
+
called = true;
|
|
5570
|
+
callback();
|
|
5571
|
+
}
|
|
5572
|
+
};
|
|
5573
|
+
requestAnimationFrame(execute);
|
|
5574
|
+
setTimeout(execute, timeout);
|
|
5575
|
+
}
|
|
5576
|
+
|
|
5577
|
+
// src/controls/ControlBar.js
|
|
5464
5578
|
var ControlBar = class {
|
|
5465
5579
|
constructor(player) {
|
|
5466
5580
|
this.player = player;
|
|
@@ -5480,17 +5594,13 @@
|
|
|
5480
5594
|
this.setupAutoHide();
|
|
5481
5595
|
this.setupOverflowDetection();
|
|
5482
5596
|
}
|
|
5483
|
-
// Helper method to check if we're on a mobile device
|
|
5484
|
-
isMobile() {
|
|
5485
|
-
return window.innerWidth < 768;
|
|
5486
|
-
}
|
|
5487
5597
|
// Helper method to detect touch devices
|
|
5488
5598
|
isTouchDevice() {
|
|
5489
5599
|
return "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
|
5490
5600
|
}
|
|
5491
5601
|
// Smart menu positioning to avoid overflow
|
|
5492
5602
|
positionMenu(menu, button, immediate = false) {
|
|
5493
|
-
const
|
|
5603
|
+
const mobile = isMobile();
|
|
5494
5604
|
const isOverflowMenu = menu.classList.contains("".concat(this.player.options.classPrefix, "-overflow-menu-list"));
|
|
5495
5605
|
const isFullscreen = this.player.state.fullscreen;
|
|
5496
5606
|
if (isFullscreen && menu.parentElement === this.player.container) {
|
|
@@ -5531,7 +5641,7 @@
|
|
|
5531
5641
|
}
|
|
5532
5642
|
return;
|
|
5533
5643
|
}
|
|
5534
|
-
if (
|
|
5644
|
+
if (mobile) {
|
|
5535
5645
|
const isVolumeMenu = menu.classList.contains("".concat(this.player.options.classPrefix, "-volume-menu"));
|
|
5536
5646
|
const doMobilePositioning = () => {
|
|
5537
5647
|
const parentContainer = button.parentElement;
|
|
@@ -7904,35 +8014,6 @@
|
|
|
7904
8014
|
init_DOMUtils();
|
|
7905
8015
|
init_i18n();
|
|
7906
8016
|
init_StorageManager();
|
|
7907
|
-
|
|
7908
|
-
// src/utils/PerformanceUtils.js
|
|
7909
|
-
function debounce(func, wait = 100) {
|
|
7910
|
-
let timeout;
|
|
7911
|
-
return function executedFunction(...args) {
|
|
7912
|
-
const later = () => {
|
|
7913
|
-
clearTimeout(timeout);
|
|
7914
|
-
func(...args);
|
|
7915
|
-
};
|
|
7916
|
-
clearTimeout(timeout);
|
|
7917
|
-
timeout = setTimeout(later, wait);
|
|
7918
|
-
};
|
|
7919
|
-
}
|
|
7920
|
-
function isMobile(breakpoint = 768) {
|
|
7921
|
-
return window.innerWidth < breakpoint;
|
|
7922
|
-
}
|
|
7923
|
-
function rafWithTimeout(callback, timeout = 100) {
|
|
7924
|
-
let called = false;
|
|
7925
|
-
const execute = () => {
|
|
7926
|
-
if (!called) {
|
|
7927
|
-
called = true;
|
|
7928
|
-
callback();
|
|
7929
|
-
}
|
|
7930
|
-
};
|
|
7931
|
-
requestAnimationFrame(execute);
|
|
7932
|
-
setTimeout(execute, timeout);
|
|
7933
|
-
}
|
|
7934
|
-
|
|
7935
|
-
// src/controls/CaptionManager.js
|
|
7936
8017
|
var CaptionManager = class {
|
|
7937
8018
|
constructor(player) {
|
|
7938
8019
|
this.player = player;
|
|
@@ -8464,37 +8545,1511 @@
|
|
|
8464
8545
|
init_DraggableResizable();
|
|
8465
8546
|
init_MenuUtils();
|
|
8466
8547
|
init_FormUtils();
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
this.
|
|
8472
|
-
|
|
8473
|
-
|
|
8548
|
+
|
|
8549
|
+
// src/core/AudioDescriptionManager.js
|
|
8550
|
+
var AudioDescriptionManager = class {
|
|
8551
|
+
constructor(player) {
|
|
8552
|
+
this.player = player;
|
|
8553
|
+
this.enabled = false;
|
|
8554
|
+
this.desiredState = false;
|
|
8555
|
+
this.src = player.options.audioDescriptionSrc;
|
|
8556
|
+
this.sourceElement = null;
|
|
8557
|
+
this.originalSource = null;
|
|
8558
|
+
this.captionTracks = [];
|
|
8559
|
+
}
|
|
8560
|
+
/**
|
|
8561
|
+
* Initialize audio description from source elements
|
|
8562
|
+
* Called during player initialization
|
|
8563
|
+
*/
|
|
8564
|
+
initFromSourceElements(sourceElements, trackElements) {
|
|
8565
|
+
for (const sourceEl of sourceElements) {
|
|
8566
|
+
const descSrc = sourceEl.getAttribute("data-desc-src");
|
|
8567
|
+
const origSrc = sourceEl.getAttribute("data-orig-src");
|
|
8568
|
+
if (descSrc || origSrc) {
|
|
8569
|
+
if (!this.sourceElement) {
|
|
8570
|
+
this.sourceElement = sourceEl;
|
|
8571
|
+
}
|
|
8572
|
+
if (origSrc) {
|
|
8573
|
+
if (!this.originalSource) {
|
|
8574
|
+
this.originalSource = origSrc;
|
|
8575
|
+
}
|
|
8576
|
+
if (!this.player.originalSrc) {
|
|
8577
|
+
this.player.originalSrc = origSrc;
|
|
8578
|
+
}
|
|
8579
|
+
} else {
|
|
8580
|
+
const currentSrcAttr = sourceEl.getAttribute("src");
|
|
8581
|
+
if (!this.originalSource && currentSrcAttr) {
|
|
8582
|
+
this.originalSource = currentSrcAttr;
|
|
8583
|
+
}
|
|
8584
|
+
if (!this.player.originalSrc && currentSrcAttr) {
|
|
8585
|
+
this.player.originalSrc = currentSrcAttr;
|
|
8586
|
+
}
|
|
8587
|
+
}
|
|
8588
|
+
if (descSrc && !this.src) {
|
|
8589
|
+
this.src = descSrc;
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8474
8592
|
}
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8593
|
+
trackElements.forEach((trackEl) => {
|
|
8594
|
+
const trackKind = trackEl.getAttribute("kind");
|
|
8595
|
+
const trackDescSrc = trackEl.getAttribute("data-desc-src");
|
|
8596
|
+
if ((trackKind === "captions" || trackKind === "subtitles" || trackKind === "chapters" || trackKind === "descriptions") && trackDescSrc) {
|
|
8597
|
+
this.captionTracks.push({
|
|
8598
|
+
trackElement: trackEl,
|
|
8599
|
+
originalSrc: trackEl.getAttribute("src"),
|
|
8600
|
+
describedSrc: trackDescSrc,
|
|
8601
|
+
originalTrackSrc: trackEl.getAttribute("data-orig-src") || trackEl.getAttribute("src"),
|
|
8602
|
+
explicit: true
|
|
8603
|
+
});
|
|
8604
|
+
this.player.log("Found explicit described ".concat(trackKind, " track: ").concat(trackEl.getAttribute("src"), " -> ").concat(trackDescSrc));
|
|
8605
|
+
}
|
|
8606
|
+
});
|
|
8607
|
+
}
|
|
8608
|
+
/**
|
|
8609
|
+
* Check if audio description is available
|
|
8610
|
+
*/
|
|
8611
|
+
isAvailable() {
|
|
8612
|
+
const hasSourceElementsWithDesc = this.player.sourceElements.some(
|
|
8613
|
+
(el) => el.getAttribute("data-desc-src")
|
|
8614
|
+
);
|
|
8615
|
+
return !!(this.src || hasSourceElementsWithDesc || this.captionTracks.length > 0);
|
|
8616
|
+
}
|
|
8617
|
+
/**
|
|
8618
|
+
* Enable audio description
|
|
8619
|
+
*/
|
|
8620
|
+
async enable() {
|
|
8621
|
+
const hasSourceElementsWithDesc = this.player.sourceElements.some(
|
|
8622
|
+
(el) => el.getAttribute("data-desc-src")
|
|
8623
|
+
);
|
|
8624
|
+
const hasTracksWithDesc = this.captionTracks.length > 0;
|
|
8625
|
+
if (!this.src && !hasSourceElementsWithDesc && !hasTracksWithDesc) {
|
|
8626
|
+
console.warn("VidPly: No audio description source, source elements, or tracks provided");
|
|
8627
|
+
return;
|
|
8628
|
+
}
|
|
8629
|
+
this.desiredState = true;
|
|
8630
|
+
const currentTime = this.player.state.currentTime;
|
|
8631
|
+
const wasPlaying = this.player.state.playing;
|
|
8632
|
+
const posterValue = this.player.element.poster || this.player.element.getAttribute("poster") || this.player.options.poster;
|
|
8633
|
+
const shouldKeepPoster = currentTime < 0.1 && !wasPlaying;
|
|
8634
|
+
const currentCaptionText = this._getCurrentCaptionText();
|
|
8635
|
+
if (this.sourceElement) {
|
|
8636
|
+
await this._enableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText);
|
|
8637
|
+
} else {
|
|
8638
|
+
await this._enableWithDirectSrc(currentTime, wasPlaying, posterValue, shouldKeepPoster);
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
/**
|
|
8642
|
+
* Disable audio description
|
|
8643
|
+
*/
|
|
8644
|
+
async disable() {
|
|
8645
|
+
if (!this.player.originalSrc) {
|
|
8646
|
+
return;
|
|
8647
|
+
}
|
|
8648
|
+
this.desiredState = false;
|
|
8649
|
+
const currentTime = this.player.state.currentTime;
|
|
8650
|
+
const wasPlaying = this.player.state.playing;
|
|
8651
|
+
const posterValue = this.player.element.poster || this.player.element.getAttribute("poster") || this.player.options.poster;
|
|
8652
|
+
const shouldKeepPoster = currentTime < 0.1 && !wasPlaying;
|
|
8653
|
+
const currentCaptionText = this._getCurrentCaptionText();
|
|
8654
|
+
if (this.sourceElement) {
|
|
8655
|
+
await this._disableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText);
|
|
8656
|
+
} else {
|
|
8657
|
+
await this._disableWithDirectSrc(currentTime, wasPlaying, posterValue);
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
/**
|
|
8661
|
+
* Toggle audio description
|
|
8662
|
+
*/
|
|
8663
|
+
async toggle() {
|
|
8664
|
+
const descriptionTrack = this.player.findTextTrack("descriptions");
|
|
8665
|
+
const hasAudioDescriptionSrc = this.isAvailable();
|
|
8666
|
+
if (descriptionTrack && !hasAudioDescriptionSrc) {
|
|
8667
|
+
if (descriptionTrack.mode === "showing") {
|
|
8668
|
+
descriptionTrack.mode = "hidden";
|
|
8669
|
+
this.enabled = false;
|
|
8670
|
+
this.player.emit("audiodescriptiondisabled");
|
|
8671
|
+
} else {
|
|
8672
|
+
descriptionTrack.mode = "showing";
|
|
8673
|
+
this.enabled = true;
|
|
8674
|
+
this.player.emit("audiodescriptionenabled");
|
|
8675
|
+
}
|
|
8676
|
+
} else if (descriptionTrack && hasAudioDescriptionSrc) {
|
|
8677
|
+
if (this.enabled) {
|
|
8678
|
+
this.desiredState = false;
|
|
8679
|
+
await this.disable();
|
|
8680
|
+
} else {
|
|
8681
|
+
descriptionTrack.mode = "showing";
|
|
8682
|
+
this.desiredState = true;
|
|
8683
|
+
await this.enable();
|
|
8684
|
+
}
|
|
8685
|
+
} else if (hasAudioDescriptionSrc) {
|
|
8686
|
+
if (this.enabled) {
|
|
8687
|
+
this.desiredState = false;
|
|
8688
|
+
await this.disable();
|
|
8689
|
+
} else {
|
|
8690
|
+
this.desiredState = true;
|
|
8691
|
+
await this.enable();
|
|
8692
|
+
}
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
/**
|
|
8696
|
+
* Get current caption text for synchronization
|
|
8697
|
+
*/
|
|
8698
|
+
_getCurrentCaptionText() {
|
|
8699
|
+
if (this.player.captionManager && this.player.captionManager.currentTrack && this.player.captionManager.currentCue) {
|
|
8700
|
+
return this.player.captionManager.currentCue.text;
|
|
8701
|
+
}
|
|
8702
|
+
return null;
|
|
8703
|
+
}
|
|
8704
|
+
/**
|
|
8705
|
+
* Validate that a track URL exists
|
|
8706
|
+
*/
|
|
8707
|
+
async _validateTrackExists(url) {
|
|
8708
|
+
try {
|
|
8709
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
8710
|
+
return response.ok;
|
|
8711
|
+
} catch (e) {
|
|
8712
|
+
return false;
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
/**
|
|
8716
|
+
* Swap caption tracks to described versions
|
|
8717
|
+
*/
|
|
8718
|
+
async _swapCaptionTracks(toDescribed = true) {
|
|
8719
|
+
if (this.captionTracks.length === 0) return [];
|
|
8720
|
+
const swappedTracks = [];
|
|
8721
|
+
const validationPromises = this.captionTracks.map(async (trackInfo) => {
|
|
8722
|
+
if (trackInfo.trackElement && trackInfo.describedSrc) {
|
|
8723
|
+
if (trackInfo.explicit === true) {
|
|
8724
|
+
try {
|
|
8725
|
+
const exists = await this._validateTrackExists(
|
|
8726
|
+
toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc
|
|
8727
|
+
);
|
|
8728
|
+
return { trackInfo, exists };
|
|
8729
|
+
} catch (e) {
|
|
8730
|
+
return { trackInfo, exists: false };
|
|
8731
|
+
}
|
|
8732
|
+
}
|
|
8733
|
+
}
|
|
8734
|
+
return { trackInfo, exists: false };
|
|
8735
|
+
});
|
|
8736
|
+
const validationResults = await Promise.all(validationPromises);
|
|
8737
|
+
const tracksToSwap = validationResults.filter((result) => result.exists);
|
|
8738
|
+
if (tracksToSwap.length > 0) {
|
|
8739
|
+
const trackModes = /* @__PURE__ */ new Map();
|
|
8740
|
+
tracksToSwap.forEach(({ trackInfo }) => {
|
|
8741
|
+
const textTrack = trackInfo.trackElement.track;
|
|
8742
|
+
if (textTrack) {
|
|
8743
|
+
trackModes.set(trackInfo, {
|
|
8744
|
+
wasShowing: textTrack.mode === "showing",
|
|
8745
|
+
wasHidden: textTrack.mode === "hidden"
|
|
8746
|
+
});
|
|
8747
|
+
} else {
|
|
8748
|
+
trackModes.set(trackInfo, { wasShowing: false, wasHidden: false });
|
|
8483
8749
|
}
|
|
8484
8750
|
});
|
|
8485
|
-
const
|
|
8486
|
-
|
|
8487
|
-
|
|
8751
|
+
const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {
|
|
8752
|
+
const attributes = {};
|
|
8753
|
+
Array.from(trackInfo.trackElement.attributes).forEach((attr) => {
|
|
8754
|
+
attributes[attr.name] = attr.value;
|
|
8755
|
+
});
|
|
8756
|
+
const result = {
|
|
8757
|
+
trackInfo,
|
|
8758
|
+
oldSrc: trackInfo.trackElement.getAttribute("src"),
|
|
8759
|
+
parent: trackInfo.trackElement.parentNode,
|
|
8760
|
+
nextSibling: trackInfo.trackElement.nextSibling,
|
|
8761
|
+
attributes
|
|
8762
|
+
};
|
|
8763
|
+
trackInfo.trackElement.remove();
|
|
8764
|
+
return result;
|
|
8488
8765
|
});
|
|
8489
|
-
this.element.
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8766
|
+
this.player.element.load();
|
|
8767
|
+
await new Promise((resolve) => {
|
|
8768
|
+
setTimeout(() => {
|
|
8769
|
+
tracksToReadd.forEach(({ trackInfo, parent, nextSibling, attributes }) => {
|
|
8770
|
+
swappedTracks.push(trackInfo);
|
|
8771
|
+
const newTrackElement = document.createElement("track");
|
|
8772
|
+
const newSrc = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;
|
|
8773
|
+
newTrackElement.setAttribute("src", newSrc);
|
|
8774
|
+
Object.keys(attributes).forEach((attrName) => {
|
|
8775
|
+
if (attrName !== "src" && attrName !== "data-desc-src") {
|
|
8776
|
+
newTrackElement.setAttribute(attrName, attributes[attrName]);
|
|
8777
|
+
}
|
|
8778
|
+
});
|
|
8779
|
+
const targetParent = parent || this.player.element;
|
|
8780
|
+
if (nextSibling && nextSibling.parentNode) {
|
|
8781
|
+
targetParent.insertBefore(newTrackElement, nextSibling);
|
|
8782
|
+
} else {
|
|
8783
|
+
targetParent.appendChild(newTrackElement);
|
|
8784
|
+
}
|
|
8785
|
+
trackInfo.trackElement = newTrackElement;
|
|
8786
|
+
});
|
|
8787
|
+
this.player.invalidateTrackCache();
|
|
8788
|
+
const setupNewTracks = () => {
|
|
8789
|
+
this.player.setManagedTimeout(() => {
|
|
8790
|
+
swappedTracks.forEach((trackInfo) => {
|
|
8791
|
+
const newTextTrack = trackInfo.trackElement.track;
|
|
8792
|
+
if (newTextTrack) {
|
|
8793
|
+
const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };
|
|
8794
|
+
newTextTrack.mode = "hidden";
|
|
8795
|
+
const restoreMode = () => {
|
|
8796
|
+
if (modeInfo.wasShowing || modeInfo.wasHidden) {
|
|
8797
|
+
newTextTrack.mode = "hidden";
|
|
8798
|
+
} else {
|
|
8799
|
+
newTextTrack.mode = "disabled";
|
|
8800
|
+
}
|
|
8801
|
+
};
|
|
8802
|
+
if (newTextTrack.readyState >= 2) {
|
|
8803
|
+
restoreMode();
|
|
8804
|
+
} else {
|
|
8805
|
+
newTextTrack.addEventListener("load", restoreMode, { once: true });
|
|
8806
|
+
newTextTrack.addEventListener("error", restoreMode, { once: true });
|
|
8807
|
+
}
|
|
8808
|
+
}
|
|
8809
|
+
});
|
|
8810
|
+
}, 300);
|
|
8811
|
+
};
|
|
8812
|
+
if (this.player.element.readyState >= 1) {
|
|
8813
|
+
setTimeout(setupNewTracks, 200);
|
|
8814
|
+
} else {
|
|
8815
|
+
this.player.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
|
|
8816
|
+
setTimeout(setupNewTracks, 2e3);
|
|
8817
|
+
}
|
|
8818
|
+
resolve();
|
|
8819
|
+
}, 100);
|
|
8820
|
+
});
|
|
8821
|
+
}
|
|
8822
|
+
return swappedTracks;
|
|
8823
|
+
}
|
|
8824
|
+
/**
|
|
8825
|
+
* Update source elements to described versions
|
|
8826
|
+
*/
|
|
8827
|
+
_updateSourceElements(toDescribed = true) {
|
|
8828
|
+
const sourceElements = this.player.sourceElements;
|
|
8829
|
+
const sourcesToUpdate = [];
|
|
8830
|
+
sourceElements.forEach((sourceEl) => {
|
|
8831
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
8832
|
+
const currentSrc = sourceEl.getAttribute("src");
|
|
8833
|
+
if (descSrcAttr) {
|
|
8834
|
+
const type = sourceEl.getAttribute("type");
|
|
8835
|
+
let origSrc = sourceEl.getAttribute("data-orig-src") || currentSrc;
|
|
8836
|
+
sourcesToUpdate.push({
|
|
8837
|
+
src: toDescribed ? descSrcAttr : origSrc,
|
|
8838
|
+
type,
|
|
8839
|
+
origSrc,
|
|
8840
|
+
descSrc: descSrcAttr
|
|
8841
|
+
});
|
|
8842
|
+
} else {
|
|
8843
|
+
sourcesToUpdate.push({
|
|
8844
|
+
src: sourceEl.getAttribute("src"),
|
|
8845
|
+
type: sourceEl.getAttribute("type"),
|
|
8846
|
+
origSrc: null,
|
|
8847
|
+
descSrc: null
|
|
8848
|
+
});
|
|
8849
|
+
}
|
|
8850
|
+
});
|
|
8851
|
+
if (this.player.element.hasAttribute("src")) {
|
|
8852
|
+
this.player.element.removeAttribute("src");
|
|
8853
|
+
}
|
|
8854
|
+
sourceElements.forEach((sourceEl) => sourceEl.remove());
|
|
8855
|
+
sourcesToUpdate.forEach((sourceInfo) => {
|
|
8856
|
+
const newSource = document.createElement("source");
|
|
8857
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
8858
|
+
if (sourceInfo.type) {
|
|
8859
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
8860
|
+
}
|
|
8861
|
+
if (sourceInfo.origSrc) {
|
|
8862
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
8863
|
+
}
|
|
8864
|
+
if (sourceInfo.descSrc) {
|
|
8865
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
8866
|
+
}
|
|
8867
|
+
const firstTrack = this.player.element.querySelector("track");
|
|
8868
|
+
if (firstTrack) {
|
|
8869
|
+
this.player.element.insertBefore(newSource, firstTrack);
|
|
8870
|
+
} else {
|
|
8871
|
+
this.player.element.appendChild(newSource);
|
|
8872
|
+
}
|
|
8873
|
+
});
|
|
8874
|
+
this.player._sourceElementsDirty = true;
|
|
8875
|
+
this.player._sourceElementsCache = null;
|
|
8876
|
+
}
|
|
8877
|
+
/**
|
|
8878
|
+
* Wait for media to be ready
|
|
8879
|
+
*/
|
|
8880
|
+
async _waitForMediaReady(needSeek = false) {
|
|
8881
|
+
await new Promise((resolve) => {
|
|
8882
|
+
if (this.player.element.readyState >= 1) {
|
|
8883
|
+
resolve();
|
|
8884
|
+
} else {
|
|
8885
|
+
const onLoad = () => {
|
|
8886
|
+
this.player.element.removeEventListener("loadedmetadata", onLoad);
|
|
8887
|
+
resolve();
|
|
8888
|
+
};
|
|
8889
|
+
this.player.element.addEventListener("loadedmetadata", onLoad);
|
|
8890
|
+
}
|
|
8891
|
+
});
|
|
8892
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
8893
|
+
if (needSeek) {
|
|
8894
|
+
await new Promise((resolve) => {
|
|
8895
|
+
if (this.player.element.readyState >= 3) {
|
|
8896
|
+
resolve();
|
|
8897
|
+
} else {
|
|
8898
|
+
const onCanPlay = () => {
|
|
8899
|
+
this.player.element.removeEventListener("canplay", onCanPlay);
|
|
8900
|
+
this.player.element.removeEventListener("canplaythrough", onCanPlay);
|
|
8901
|
+
resolve();
|
|
8902
|
+
};
|
|
8903
|
+
this.player.element.addEventListener("canplay", onCanPlay, { once: true });
|
|
8904
|
+
this.player.element.addEventListener("canplaythrough", onCanPlay, { once: true });
|
|
8905
|
+
setTimeout(() => {
|
|
8906
|
+
this.player.element.removeEventListener("canplay", onCanPlay);
|
|
8907
|
+
this.player.element.removeEventListener("canplaythrough", onCanPlay);
|
|
8908
|
+
resolve();
|
|
8909
|
+
}, 3e3);
|
|
8910
|
+
}
|
|
8911
|
+
});
|
|
8912
|
+
}
|
|
8913
|
+
}
|
|
8914
|
+
/**
|
|
8915
|
+
* Restore playback state after source change
|
|
8916
|
+
*/
|
|
8917
|
+
async _restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText) {
|
|
8918
|
+
let syncTime = currentTime;
|
|
8919
|
+
if (currentCaptionText && this.player.captionManager && this.player.captionManager.tracks.length > 0) {
|
|
8920
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
8921
|
+
const matchingTime = this.player.findMatchingCaptionTime(
|
|
8922
|
+
currentCaptionText,
|
|
8923
|
+
this.player.captionManager.tracks
|
|
8924
|
+
);
|
|
8925
|
+
if (matchingTime !== null) {
|
|
8926
|
+
syncTime = matchingTime;
|
|
8927
|
+
if (this.player.options.debug) {
|
|
8928
|
+
this.player.log("[VidPly] Syncing via caption: ".concat(currentTime, "s -> ").concat(syncTime, "s"));
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
}
|
|
8932
|
+
if (syncTime > 0) {
|
|
8933
|
+
this.player.seek(syncTime);
|
|
8934
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
8935
|
+
}
|
|
8936
|
+
if (wasPlaying) {
|
|
8937
|
+
await this.player.play();
|
|
8938
|
+
this.player.setManagedTimeout(() => {
|
|
8939
|
+
this.player.hidePosterOverlay();
|
|
8940
|
+
}, 100);
|
|
8941
|
+
} else {
|
|
8942
|
+
this.player.pause();
|
|
8943
|
+
if (!shouldKeepPoster) {
|
|
8944
|
+
this.player.hidePosterOverlay();
|
|
8945
|
+
}
|
|
8946
|
+
}
|
|
8947
|
+
}
|
|
8948
|
+
/**
|
|
8949
|
+
* Enable with source element method
|
|
8950
|
+
*/
|
|
8951
|
+
async _enableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText) {
|
|
8952
|
+
await this._swapCaptionTracks(true);
|
|
8953
|
+
this._updateSourceElements(true);
|
|
8954
|
+
if (posterValue && this.player.element.tagName === "VIDEO") {
|
|
8955
|
+
this.player.element.poster = posterValue;
|
|
8956
|
+
}
|
|
8957
|
+
this.player.element.load();
|
|
8958
|
+
await this._waitForMediaReady(currentTime > 0 || wasPlaying);
|
|
8959
|
+
await this._restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText);
|
|
8960
|
+
if (!this.desiredState) return;
|
|
8961
|
+
this.enabled = true;
|
|
8962
|
+
this.player.state.audioDescriptionEnabled = true;
|
|
8963
|
+
this.player.emit("audiodescriptionenabled");
|
|
8964
|
+
this._reloadTranscript();
|
|
8965
|
+
}
|
|
8966
|
+
/**
|
|
8967
|
+
* Enable with direct src method
|
|
8968
|
+
*/
|
|
8969
|
+
async _enableWithDirectSrc(currentTime, wasPlaying, posterValue, shouldKeepPoster) {
|
|
8970
|
+
await this._swapCaptionTracks(true);
|
|
8971
|
+
if (posterValue && this.player.element.tagName === "VIDEO") {
|
|
8972
|
+
this.player.element.poster = posterValue;
|
|
8973
|
+
}
|
|
8974
|
+
this.player.element.src = this.src;
|
|
8975
|
+
await this._waitForMediaReady(currentTime > 0 || wasPlaying);
|
|
8976
|
+
if (currentTime > 0) {
|
|
8977
|
+
this.player.seek(currentTime);
|
|
8978
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
8979
|
+
}
|
|
8980
|
+
if (wasPlaying) {
|
|
8981
|
+
await this.player.play();
|
|
8982
|
+
} else {
|
|
8983
|
+
this.player.pause();
|
|
8984
|
+
if (!shouldKeepPoster) {
|
|
8985
|
+
this.player.hidePosterOverlay();
|
|
8986
|
+
}
|
|
8987
|
+
}
|
|
8988
|
+
if (!this.desiredState) return;
|
|
8989
|
+
this.enabled = true;
|
|
8990
|
+
this.player.state.audioDescriptionEnabled = true;
|
|
8991
|
+
this.player.emit("audiodescriptionenabled");
|
|
8992
|
+
this._reloadTranscript();
|
|
8993
|
+
}
|
|
8994
|
+
/**
|
|
8995
|
+
* Disable with source element method
|
|
8996
|
+
*/
|
|
8997
|
+
async _disableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText) {
|
|
8998
|
+
await this._swapCaptionTracks(false);
|
|
8999
|
+
this._updateSourceElements(false);
|
|
9000
|
+
if (posterValue && this.player.element.tagName === "VIDEO") {
|
|
9001
|
+
this.player.element.poster = posterValue;
|
|
9002
|
+
}
|
|
9003
|
+
this.player.element.load();
|
|
9004
|
+
this.player.invalidateTrackCache();
|
|
9005
|
+
await this._waitForMediaReady(currentTime > 0 || wasPlaying);
|
|
9006
|
+
await this._restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText);
|
|
9007
|
+
if (this.player.captionManager) {
|
|
9008
|
+
this.player.captionManager.destroy();
|
|
9009
|
+
this.player.captionManager = new CaptionManager(this.player);
|
|
9010
|
+
}
|
|
9011
|
+
if (this.desiredState) return;
|
|
9012
|
+
this.enabled = false;
|
|
9013
|
+
this.player.state.audioDescriptionEnabled = false;
|
|
9014
|
+
this.player.emit("audiodescriptiondisabled");
|
|
9015
|
+
this._reloadTranscript();
|
|
9016
|
+
}
|
|
9017
|
+
/**
|
|
9018
|
+
* Disable with direct src method
|
|
9019
|
+
*/
|
|
9020
|
+
async _disableWithDirectSrc(currentTime, wasPlaying, posterValue) {
|
|
9021
|
+
await this._swapCaptionTracks(false);
|
|
9022
|
+
if (posterValue && this.player.element.tagName === "VIDEO") {
|
|
9023
|
+
this.player.element.poster = posterValue;
|
|
9024
|
+
}
|
|
9025
|
+
const originalSrcToUse = this.originalSource || this.player.originalSrc;
|
|
9026
|
+
this.player.element.src = originalSrcToUse;
|
|
9027
|
+
this.player.element.load();
|
|
9028
|
+
await this._waitForMediaReady(currentTime > 0 || wasPlaying);
|
|
9029
|
+
if (currentTime > 0) {
|
|
9030
|
+
this.player.seek(currentTime);
|
|
9031
|
+
}
|
|
9032
|
+
if (wasPlaying) {
|
|
9033
|
+
await this.player.play();
|
|
9034
|
+
}
|
|
9035
|
+
if (this.desiredState) return;
|
|
9036
|
+
this.enabled = false;
|
|
9037
|
+
this.player.state.audioDescriptionEnabled = false;
|
|
9038
|
+
this.player.emit("audiodescriptiondisabled");
|
|
9039
|
+
this._reloadTranscript();
|
|
9040
|
+
}
|
|
9041
|
+
/**
|
|
9042
|
+
* Reload transcript after audio description state change
|
|
9043
|
+
*/
|
|
9044
|
+
_reloadTranscript() {
|
|
9045
|
+
if (this.player.transcriptManager && this.player.transcriptManager.isVisible) {
|
|
9046
|
+
this.player.setManagedTimeout(() => {
|
|
9047
|
+
if (this.player.transcriptManager && this.player.transcriptManager.loadTranscriptData) {
|
|
9048
|
+
this.player.transcriptManager.loadTranscriptData();
|
|
9049
|
+
}
|
|
9050
|
+
}, 800);
|
|
9051
|
+
}
|
|
9052
|
+
}
|
|
9053
|
+
/**
|
|
9054
|
+
* Update sources (called when playlist changes)
|
|
9055
|
+
*/
|
|
9056
|
+
updateSources(audioDescriptionSrc) {
|
|
9057
|
+
this.src = audioDescriptionSrc || null;
|
|
9058
|
+
this.enabled = false;
|
|
9059
|
+
this.desiredState = false;
|
|
9060
|
+
this.sourceElement = null;
|
|
9061
|
+
this.originalSource = null;
|
|
9062
|
+
this.captionTracks = [];
|
|
9063
|
+
}
|
|
9064
|
+
/**
|
|
9065
|
+
* Reinitialize from current player elements (called after playlist loads new tracks)
|
|
9066
|
+
*/
|
|
9067
|
+
reinitialize() {
|
|
9068
|
+
this.player.invalidateTrackCache();
|
|
9069
|
+
this.initFromSourceElements(this.player.sourceElements, this.player.trackElements);
|
|
9070
|
+
}
|
|
9071
|
+
/**
|
|
9072
|
+
* Cleanup
|
|
9073
|
+
*/
|
|
9074
|
+
destroy() {
|
|
9075
|
+
this.enabled = false;
|
|
9076
|
+
this.desiredState = false;
|
|
9077
|
+
this.captionTracks = [];
|
|
9078
|
+
this.sourceElement = null;
|
|
9079
|
+
this.originalSource = null;
|
|
9080
|
+
}
|
|
9081
|
+
};
|
|
9082
|
+
|
|
9083
|
+
// src/core/SignLanguageManager.js
|
|
9084
|
+
init_DOMUtils();
|
|
9085
|
+
init_Icons();
|
|
9086
|
+
init_i18n();
|
|
9087
|
+
init_DraggableResizable();
|
|
9088
|
+
init_MenuUtils();
|
|
9089
|
+
init_FormUtils();
|
|
9090
|
+
var SignLanguageManager = class {
|
|
9091
|
+
constructor(player) {
|
|
9092
|
+
this.player = player;
|
|
9093
|
+
this.src = player.options.signLanguageSrc;
|
|
9094
|
+
this.sources = player.options.signLanguageSources || {};
|
|
9095
|
+
this.currentLanguage = null;
|
|
9096
|
+
this.desiredPosition = player.options.signLanguagePosition || "bottom-right";
|
|
9097
|
+
this.wrapper = null;
|
|
9098
|
+
this.header = null;
|
|
9099
|
+
this.video = null;
|
|
9100
|
+
this.selector = null;
|
|
9101
|
+
this.settingsButton = null;
|
|
9102
|
+
this.settingsMenu = null;
|
|
9103
|
+
this.resizeHandles = [];
|
|
9104
|
+
this.enabled = false;
|
|
9105
|
+
this.settingsMenuVisible = false;
|
|
9106
|
+
this.settingsMenuJustOpened = false;
|
|
9107
|
+
this.documentClickHandlerAdded = false;
|
|
9108
|
+
this.handlers = null;
|
|
9109
|
+
this.settingsHandlers = null;
|
|
9110
|
+
this.interactionHandlers = null;
|
|
9111
|
+
this.draggable = null;
|
|
9112
|
+
this.documentClickHandler = null;
|
|
9113
|
+
this.settingsMenuKeyHandler = null;
|
|
9114
|
+
this.customKeyHandler = null;
|
|
9115
|
+
this.dragOptionButton = null;
|
|
9116
|
+
this.dragOptionText = null;
|
|
9117
|
+
this.resizeOptionButton = null;
|
|
9118
|
+
this.resizeOptionText = null;
|
|
9119
|
+
}
|
|
9120
|
+
/**
|
|
9121
|
+
* Check if sign language is available
|
|
9122
|
+
*/
|
|
9123
|
+
isAvailable() {
|
|
9124
|
+
return Object.keys(this.sources).length > 0 || !!this.src;
|
|
9125
|
+
}
|
|
9126
|
+
/**
|
|
9127
|
+
* Enable sign language video
|
|
9128
|
+
*/
|
|
9129
|
+
enable() {
|
|
9130
|
+
const hasMultipleSources = Object.keys(this.sources).length > 0;
|
|
9131
|
+
const hasSingleSource = !!this.src;
|
|
9132
|
+
if (!hasMultipleSources && !hasSingleSource) {
|
|
9133
|
+
console.warn("No sign language video source provided");
|
|
9134
|
+
return;
|
|
9135
|
+
}
|
|
9136
|
+
if (this.wrapper) {
|
|
9137
|
+
this.wrapper.style.display = "block";
|
|
9138
|
+
this.enabled = true;
|
|
9139
|
+
this.player.state.signLanguageEnabled = true;
|
|
9140
|
+
this.player.emit("signlanguageenabled");
|
|
9141
|
+
this.player.setManagedTimeout(() => {
|
|
9142
|
+
if (this.settingsButton && document.contains(this.settingsButton)) {
|
|
9143
|
+
this.settingsButton.focus({ preventScroll: true });
|
|
9144
|
+
}
|
|
9145
|
+
}, 150);
|
|
9146
|
+
return;
|
|
9147
|
+
}
|
|
9148
|
+
let initialLang = null;
|
|
9149
|
+
let initialSrc = null;
|
|
9150
|
+
if (hasMultipleSources) {
|
|
9151
|
+
initialLang = this._determineInitialLanguage();
|
|
9152
|
+
initialSrc = this.sources[initialLang];
|
|
9153
|
+
this.currentLanguage = initialLang;
|
|
9154
|
+
} else {
|
|
9155
|
+
initialSrc = this.src;
|
|
9156
|
+
}
|
|
9157
|
+
this._createWrapper();
|
|
9158
|
+
this._createHeader(hasMultipleSources, initialLang);
|
|
9159
|
+
this._createVideo(initialSrc);
|
|
9160
|
+
this._createResizeHandles();
|
|
9161
|
+
this.wrapper.appendChild(this.header);
|
|
9162
|
+
this.wrapper.appendChild(this.video);
|
|
9163
|
+
this.resizeHandles.forEach((handle) => this.wrapper.appendChild(handle));
|
|
9164
|
+
this._applyInitialSize();
|
|
9165
|
+
this.player.container.appendChild(this.wrapper);
|
|
9166
|
+
requestAnimationFrame(() => {
|
|
9167
|
+
this.constrainPosition();
|
|
9168
|
+
});
|
|
9169
|
+
this.video.currentTime = this.player.state.currentTime;
|
|
9170
|
+
if (!this.player.state.paused) {
|
|
9171
|
+
this.video.play();
|
|
9172
|
+
}
|
|
9173
|
+
this._setupInteraction();
|
|
9174
|
+
this._setupEventHandlers(hasMultipleSources);
|
|
9175
|
+
this.enabled = true;
|
|
9176
|
+
this.player.state.signLanguageEnabled = true;
|
|
9177
|
+
this.player.emit("signlanguageenabled");
|
|
9178
|
+
this.player.setManagedTimeout(() => {
|
|
9179
|
+
if (this.settingsButton && document.contains(this.settingsButton)) {
|
|
9180
|
+
this.settingsButton.focus({ preventScroll: true });
|
|
9181
|
+
}
|
|
9182
|
+
}, 150);
|
|
9183
|
+
}
|
|
9184
|
+
/**
|
|
9185
|
+
* Disable sign language video
|
|
9186
|
+
*/
|
|
9187
|
+
disable() {
|
|
9188
|
+
if (this.settingsMenuVisible) {
|
|
9189
|
+
this.hideSettingsMenu({ focusButton: false });
|
|
9190
|
+
}
|
|
9191
|
+
if (this.wrapper) {
|
|
9192
|
+
this.wrapper.style.display = "none";
|
|
9193
|
+
}
|
|
9194
|
+
this.enabled = false;
|
|
9195
|
+
this.player.state.signLanguageEnabled = false;
|
|
9196
|
+
this.player.emit("signlanguagedisabled");
|
|
9197
|
+
}
|
|
9198
|
+
/**
|
|
9199
|
+
* Toggle sign language video
|
|
9200
|
+
*/
|
|
9201
|
+
toggle() {
|
|
9202
|
+
if (this.enabled) {
|
|
9203
|
+
this.disable();
|
|
9204
|
+
} else {
|
|
9205
|
+
this.enable();
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
/**
|
|
9209
|
+
* Switch to a different sign language
|
|
9210
|
+
*/
|
|
9211
|
+
switchLanguage(langCode) {
|
|
9212
|
+
if (!this.sources[langCode] || !this.video) {
|
|
9213
|
+
return;
|
|
9214
|
+
}
|
|
9215
|
+
const currentTime = this.video.currentTime;
|
|
9216
|
+
const wasPlaying = !this.video.paused;
|
|
9217
|
+
this.video.src = this.sources[langCode];
|
|
9218
|
+
this.currentLanguage = langCode;
|
|
9219
|
+
this.video.currentTime = currentTime;
|
|
9220
|
+
if (wasPlaying) {
|
|
9221
|
+
this.video.play().catch(() => {
|
|
9222
|
+
});
|
|
9223
|
+
}
|
|
9224
|
+
this.player.emit("signlanguagelanguagechanged", langCode);
|
|
9225
|
+
}
|
|
9226
|
+
/**
|
|
9227
|
+
* Get language label
|
|
9228
|
+
*/
|
|
9229
|
+
getLanguageLabel(langCode) {
|
|
9230
|
+
const langNames = {
|
|
9231
|
+
"en": "English",
|
|
9232
|
+
"de": "Deutsch",
|
|
9233
|
+
"es": "Español",
|
|
9234
|
+
"fr": "Français",
|
|
9235
|
+
"it": "Italiano",
|
|
9236
|
+
"ja": "日本語",
|
|
9237
|
+
"pt": "Português",
|
|
9238
|
+
"ar": "العربية",
|
|
9239
|
+
"hi": "हिन्दी"
|
|
9240
|
+
};
|
|
9241
|
+
return langNames[langCode] || langCode.toUpperCase();
|
|
9242
|
+
}
|
|
9243
|
+
/**
|
|
9244
|
+
* Determine initial sign language
|
|
9245
|
+
*/
|
|
9246
|
+
_determineInitialLanguage() {
|
|
9247
|
+
var _a;
|
|
9248
|
+
if (this.player.captionManager && this.player.captionManager.currentTrack) {
|
|
9249
|
+
const captionLang = (_a = this.player.captionManager.currentTrack.language) == null ? void 0 : _a.toLowerCase().split("-")[0];
|
|
9250
|
+
if (captionLang && this.sources[captionLang]) {
|
|
9251
|
+
return captionLang;
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
9254
|
+
if (this.player.options.language) {
|
|
9255
|
+
const playerLang = this.player.options.language.toLowerCase().split("-")[0];
|
|
9256
|
+
if (this.sources[playerLang]) {
|
|
9257
|
+
return playerLang;
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
return Object.keys(this.sources)[0];
|
|
9261
|
+
}
|
|
9262
|
+
/**
|
|
9263
|
+
* Create wrapper element
|
|
9264
|
+
*/
|
|
9265
|
+
_createWrapper() {
|
|
9266
|
+
this.wrapper = document.createElement("div");
|
|
9267
|
+
this.wrapper.className = "vidply-sign-language-wrapper";
|
|
9268
|
+
this.wrapper.setAttribute("tabindex", "0");
|
|
9269
|
+
this.wrapper.setAttribute("aria-label", i18n.t("player.signLanguageDragResize"));
|
|
9270
|
+
}
|
|
9271
|
+
/**
|
|
9272
|
+
* Create header element
|
|
9273
|
+
*/
|
|
9274
|
+
_createHeader(hasMultipleSources, initialLang) {
|
|
9275
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9276
|
+
this.header = DOMUtils.createElement("div", {
|
|
9277
|
+
className: "".concat(classPrefix, "-sign-language-header"),
|
|
9278
|
+
attributes: { "tabindex": "0" }
|
|
9279
|
+
});
|
|
9280
|
+
const headerLeft = DOMUtils.createElement("div", {
|
|
9281
|
+
className: "".concat(classPrefix, "-sign-language-header-left")
|
|
9282
|
+
});
|
|
9283
|
+
const title = DOMUtils.createElement("h3", {
|
|
9284
|
+
textContent: i18n.t("player.signLanguageVideo")
|
|
9285
|
+
});
|
|
9286
|
+
this._createSettingsButton(headerLeft);
|
|
9287
|
+
if (hasMultipleSources) {
|
|
9288
|
+
this._createLanguageSelector(headerLeft, initialLang);
|
|
9289
|
+
}
|
|
9290
|
+
headerLeft.appendChild(title);
|
|
9291
|
+
const closeButton = this._createCloseButton();
|
|
9292
|
+
this.header.appendChild(headerLeft);
|
|
9293
|
+
this.header.appendChild(closeButton);
|
|
9294
|
+
this.settingsMenuVisible = false;
|
|
9295
|
+
this.settingsMenu = null;
|
|
9296
|
+
this.settingsMenuJustOpened = false;
|
|
9297
|
+
}
|
|
9298
|
+
/**
|
|
9299
|
+
* Create settings button
|
|
9300
|
+
*/
|
|
9301
|
+
_createSettingsButton(container) {
|
|
9302
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9303
|
+
const ariaLabel = i18n.t("player.signLanguageSettings");
|
|
9304
|
+
this.settingsButton = DOMUtils.createElement("button", {
|
|
9305
|
+
className: "".concat(classPrefix, "-sign-language-settings"),
|
|
9306
|
+
attributes: {
|
|
9307
|
+
"type": "button",
|
|
9308
|
+
"aria-label": ariaLabel,
|
|
9309
|
+
"aria-expanded": "false"
|
|
9310
|
+
}
|
|
9311
|
+
});
|
|
9312
|
+
this.settingsButton.appendChild(createIconElement("settings"));
|
|
9313
|
+
DOMUtils.attachTooltip(this.settingsButton, ariaLabel, classPrefix);
|
|
9314
|
+
this.settingsHandlers = {
|
|
9315
|
+
click: (e) => {
|
|
9316
|
+
e.preventDefault();
|
|
9317
|
+
e.stopPropagation();
|
|
9318
|
+
if (this.documentClickHandler) {
|
|
9319
|
+
this.settingsMenuJustOpened = true;
|
|
9320
|
+
setTimeout(() => {
|
|
9321
|
+
this.settingsMenuJustOpened = false;
|
|
9322
|
+
}, 100);
|
|
9323
|
+
}
|
|
9324
|
+
if (this.settingsMenuVisible) {
|
|
9325
|
+
this.hideSettingsMenu();
|
|
9326
|
+
} else {
|
|
9327
|
+
this.showSettingsMenu();
|
|
9328
|
+
}
|
|
9329
|
+
},
|
|
9330
|
+
keydown: (e) => {
|
|
9331
|
+
if (e.key === "d" || e.key === "D") {
|
|
9332
|
+
e.preventDefault();
|
|
9333
|
+
e.stopPropagation();
|
|
9334
|
+
this.toggleKeyboardDragMode();
|
|
9335
|
+
} else if (e.key === "r" || e.key === "R") {
|
|
9336
|
+
e.preventDefault();
|
|
9337
|
+
e.stopPropagation();
|
|
9338
|
+
this.toggleResizeMode();
|
|
9339
|
+
} else if (e.key === "Escape" && this.settingsMenuVisible) {
|
|
9340
|
+
e.preventDefault();
|
|
9341
|
+
e.stopPropagation();
|
|
9342
|
+
this.hideSettingsMenu();
|
|
9343
|
+
}
|
|
9344
|
+
}
|
|
9345
|
+
};
|
|
9346
|
+
this.settingsButton.addEventListener("click", this.settingsHandlers.click);
|
|
9347
|
+
this.settingsButton.addEventListener("keydown", this.settingsHandlers.keydown);
|
|
9348
|
+
container.appendChild(this.settingsButton);
|
|
9349
|
+
}
|
|
9350
|
+
/**
|
|
9351
|
+
* Create language selector
|
|
9352
|
+
*/
|
|
9353
|
+
_createLanguageSelector(container, initialLang) {
|
|
9354
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9355
|
+
const selectId = "".concat(classPrefix, "-sign-language-select-").concat(Date.now());
|
|
9356
|
+
const options = Object.keys(this.sources).map((langCode) => ({
|
|
9357
|
+
value: langCode,
|
|
9358
|
+
text: this.getLanguageLabel(langCode),
|
|
9359
|
+
selected: langCode === initialLang
|
|
9360
|
+
}));
|
|
9361
|
+
const { label, select } = createLabeledSelect({
|
|
9362
|
+
classPrefix,
|
|
9363
|
+
labelClass: "".concat(classPrefix, "-sign-language-label"),
|
|
9364
|
+
selectClass: "".concat(classPrefix, "-sign-language-select"),
|
|
9365
|
+
labelText: "settings.language",
|
|
9366
|
+
selectId,
|
|
9367
|
+
options,
|
|
9368
|
+
onChange: (e) => {
|
|
9369
|
+
e.stopPropagation();
|
|
9370
|
+
this.switchLanguage(e.target.value);
|
|
9371
|
+
}
|
|
9372
|
+
});
|
|
9373
|
+
this.selector = select;
|
|
9374
|
+
const selectorWrapper = DOMUtils.createElement("div", {
|
|
9375
|
+
className: "".concat(classPrefix, "-sign-language-selector-wrapper")
|
|
9376
|
+
});
|
|
9377
|
+
selectorWrapper.appendChild(label);
|
|
9378
|
+
selectorWrapper.appendChild(this.selector);
|
|
9379
|
+
preventDragOnElement(selectorWrapper);
|
|
9380
|
+
container.appendChild(selectorWrapper);
|
|
9381
|
+
}
|
|
9382
|
+
/**
|
|
9383
|
+
* Create close button
|
|
9384
|
+
*/
|
|
9385
|
+
_createCloseButton() {
|
|
9386
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9387
|
+
const ariaLabel = i18n.t("player.closeSignLanguage");
|
|
9388
|
+
const closeButton = DOMUtils.createElement("button", {
|
|
9389
|
+
className: "".concat(classPrefix, "-sign-language-close"),
|
|
9390
|
+
attributes: {
|
|
9391
|
+
"type": "button",
|
|
9392
|
+
"aria-label": ariaLabel
|
|
9393
|
+
}
|
|
9394
|
+
});
|
|
9395
|
+
closeButton.appendChild(createIconElement("close"));
|
|
9396
|
+
DOMUtils.attachTooltip(closeButton, ariaLabel, classPrefix);
|
|
9397
|
+
closeButton.addEventListener("click", () => {
|
|
9398
|
+
var _a, _b;
|
|
9399
|
+
this.disable();
|
|
9400
|
+
if ((_b = (_a = this.player.controlBar) == null ? void 0 : _a.controls) == null ? void 0 : _b.signLanguage) {
|
|
9401
|
+
setTimeout(() => {
|
|
9402
|
+
this.player.controlBar.controls.signLanguage.focus({ preventScroll: true });
|
|
9403
|
+
}, 0);
|
|
9404
|
+
}
|
|
9405
|
+
});
|
|
9406
|
+
return closeButton;
|
|
9407
|
+
}
|
|
9408
|
+
/**
|
|
9409
|
+
* Create video element
|
|
9410
|
+
*/
|
|
9411
|
+
_createVideo(src) {
|
|
9412
|
+
this.video = document.createElement("video");
|
|
9413
|
+
this.video.className = "vidply-sign-language-video";
|
|
9414
|
+
this.video.src = src;
|
|
9415
|
+
this.video.setAttribute("aria-label", i18n.t("player.signLanguage"));
|
|
9416
|
+
this.video.muted = true;
|
|
9417
|
+
this.video.setAttribute("playsinline", "");
|
|
9418
|
+
}
|
|
9419
|
+
/**
|
|
9420
|
+
* Create resize handles
|
|
9421
|
+
*/
|
|
9422
|
+
_createResizeHandles() {
|
|
9423
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9424
|
+
this.resizeHandles = ["n", "s", "e", "w", "ne", "nw", "se", "sw"].map((dir) => {
|
|
9425
|
+
const handle = DOMUtils.createElement("div", {
|
|
9426
|
+
className: "".concat(classPrefix, "-sign-resize-handle ").concat(classPrefix, "-sign-resize-").concat(dir),
|
|
9427
|
+
attributes: {
|
|
9428
|
+
"data-direction": dir,
|
|
9429
|
+
"data-vidply-managed-resize": "true",
|
|
9430
|
+
"aria-hidden": "true"
|
|
9431
|
+
}
|
|
9432
|
+
});
|
|
9433
|
+
handle.style.display = "none";
|
|
9434
|
+
return handle;
|
|
9435
|
+
});
|
|
9436
|
+
}
|
|
9437
|
+
/**
|
|
9438
|
+
* Apply initial size
|
|
9439
|
+
*/
|
|
9440
|
+
_applyInitialSize() {
|
|
9441
|
+
var _a;
|
|
9442
|
+
const saved = this.player.storage.getSignLanguagePreferences();
|
|
9443
|
+
if ((_a = saved == null ? void 0 : saved.size) == null ? void 0 : _a.width) {
|
|
9444
|
+
this.wrapper.style.width = saved.size.width;
|
|
9445
|
+
} else {
|
|
9446
|
+
this.wrapper.style.width = "280px";
|
|
9447
|
+
}
|
|
9448
|
+
this.wrapper.style.height = "auto";
|
|
9449
|
+
}
|
|
9450
|
+
/**
|
|
9451
|
+
* Setup interaction (drag and resize)
|
|
9452
|
+
*/
|
|
9453
|
+
_setupInteraction() {
|
|
9454
|
+
const isMobile2 = window.innerWidth < 768;
|
|
9455
|
+
const isFullscreen = this.player.state.fullscreen;
|
|
9456
|
+
if (isMobile2 && !isFullscreen) {
|
|
9457
|
+
if (this.draggable) {
|
|
9458
|
+
this.draggable.destroy();
|
|
9459
|
+
this.draggable = null;
|
|
9460
|
+
}
|
|
9461
|
+
return;
|
|
9462
|
+
}
|
|
9463
|
+
if (this.draggable) return;
|
|
9464
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9465
|
+
this.draggable = new DraggableResizable(this.wrapper, {
|
|
9466
|
+
dragHandle: this.header,
|
|
9467
|
+
resizeHandles: this.resizeHandles,
|
|
9468
|
+
constrainToViewport: true,
|
|
9469
|
+
maintainAspectRatio: true,
|
|
9470
|
+
minWidth: 150,
|
|
9471
|
+
minHeight: 100,
|
|
9472
|
+
classPrefix: "".concat(classPrefix, "-sign"),
|
|
9473
|
+
keyboardDragKey: "d",
|
|
9474
|
+
keyboardResizeKey: "r",
|
|
9475
|
+
keyboardStep: 10,
|
|
9476
|
+
keyboardStepLarge: 50,
|
|
9477
|
+
pointerResizeIndicatorText: i18n.t("player.signLanguageResizeActive"),
|
|
9478
|
+
onPointerResizeToggle: (enabled) => {
|
|
9479
|
+
this.resizeHandles.forEach((handle) => {
|
|
9480
|
+
handle.style.display = enabled ? "block" : "none";
|
|
9481
|
+
});
|
|
9482
|
+
},
|
|
9483
|
+
onDragStart: (e) => {
|
|
9484
|
+
if (e.target.closest(".".concat(classPrefix, "-sign-language-close")) || e.target.closest(".".concat(classPrefix, "-sign-language-settings")) || e.target.closest(".".concat(classPrefix, "-sign-language-select")) || e.target.closest(".".concat(classPrefix, "-sign-language-label")) || e.target.closest(".".concat(classPrefix, "-sign-language-settings-menu"))) {
|
|
9485
|
+
return false;
|
|
9486
|
+
}
|
|
9487
|
+
return true;
|
|
9488
|
+
}
|
|
9489
|
+
});
|
|
9490
|
+
this._setupCustomKeyHandler();
|
|
9491
|
+
this.interactionHandlers = {
|
|
9492
|
+
draggable: this.draggable,
|
|
9493
|
+
customKeyHandler: this.customKeyHandler
|
|
9494
|
+
};
|
|
9495
|
+
}
|
|
9496
|
+
/**
|
|
9497
|
+
* Setup custom keyboard handler
|
|
9498
|
+
*/
|
|
9499
|
+
_setupCustomKeyHandler() {
|
|
9500
|
+
this.customKeyHandler = (e) => {
|
|
9501
|
+
var _a, _b, _c, _d;
|
|
9502
|
+
const key = e.key.toLowerCase();
|
|
9503
|
+
if (this.settingsMenuVisible) return;
|
|
9504
|
+
if (key === "home") {
|
|
9505
|
+
e.preventDefault();
|
|
9506
|
+
e.stopPropagation();
|
|
9507
|
+
if (this.draggable) {
|
|
9508
|
+
if (this.draggable.pointerResizeMode) {
|
|
9509
|
+
this.draggable.disablePointerResizeMode();
|
|
9510
|
+
}
|
|
9511
|
+
this.draggable.manuallyPositioned = false;
|
|
9512
|
+
this.constrainPosition();
|
|
9513
|
+
}
|
|
9514
|
+
return;
|
|
9515
|
+
}
|
|
9516
|
+
if (key === "r") {
|
|
9517
|
+
e.preventDefault();
|
|
9518
|
+
e.stopPropagation();
|
|
9519
|
+
if (this.toggleResizeMode()) {
|
|
9520
|
+
this.wrapper.focus({ preventScroll: true });
|
|
9521
|
+
}
|
|
9522
|
+
return;
|
|
9523
|
+
}
|
|
9524
|
+
if (key === "escape") {
|
|
9525
|
+
e.preventDefault();
|
|
9526
|
+
e.stopPropagation();
|
|
9527
|
+
if ((_a = this.draggable) == null ? void 0 : _a.pointerResizeMode) {
|
|
9528
|
+
this.draggable.disablePointerResizeMode();
|
|
9529
|
+
return;
|
|
9530
|
+
}
|
|
9531
|
+
if ((_b = this.draggable) == null ? void 0 : _b.keyboardDragMode) {
|
|
9532
|
+
this.draggable.disableKeyboardDragMode();
|
|
9533
|
+
return;
|
|
9534
|
+
}
|
|
9535
|
+
this.disable();
|
|
9536
|
+
if ((_d = (_c = this.player.controlBar) == null ? void 0 : _c.controls) == null ? void 0 : _d.signLanguage) {
|
|
9537
|
+
setTimeout(() => {
|
|
9538
|
+
this.player.controlBar.controls.signLanguage.focus({ preventScroll: true });
|
|
9539
|
+
}, 0);
|
|
9540
|
+
}
|
|
9541
|
+
}
|
|
9542
|
+
};
|
|
9543
|
+
this.wrapper.addEventListener("keydown", this.customKeyHandler);
|
|
9544
|
+
}
|
|
9545
|
+
/**
|
|
9546
|
+
* Setup event handlers
|
|
9547
|
+
*/
|
|
9548
|
+
_setupEventHandlers(hasMultipleSources) {
|
|
9549
|
+
this.handlers = {
|
|
9550
|
+
play: () => {
|
|
9551
|
+
if (this.video) this.video.play();
|
|
9552
|
+
},
|
|
9553
|
+
pause: () => {
|
|
9554
|
+
if (this.video) this.video.pause();
|
|
9555
|
+
},
|
|
9556
|
+
timeupdate: () => {
|
|
9557
|
+
if (this.video && Math.abs(this.video.currentTime - this.player.state.currentTime) > 0.5) {
|
|
9558
|
+
this.video.currentTime = this.player.state.currentTime;
|
|
9559
|
+
}
|
|
9560
|
+
},
|
|
9561
|
+
ratechange: () => {
|
|
9562
|
+
if (this.video) this.video.playbackRate = this.player.state.playbackSpeed;
|
|
9563
|
+
}
|
|
9564
|
+
};
|
|
9565
|
+
this.player.on("play", this.handlers.play);
|
|
9566
|
+
this.player.on("pause", this.handlers.pause);
|
|
9567
|
+
this.player.on("timeupdate", this.handlers.timeupdate);
|
|
9568
|
+
this.player.on("ratechange", this.handlers.ratechange);
|
|
9569
|
+
if (hasMultipleSources) {
|
|
9570
|
+
this.handlers.captionChange = () => {
|
|
9571
|
+
var _a, _b;
|
|
9572
|
+
if (((_a = this.player.captionManager) == null ? void 0 : _a.currentTrack) && this.selector) {
|
|
9573
|
+
const captionLang = (_b = this.player.captionManager.currentTrack.language) == null ? void 0 : _b.toLowerCase().split("-")[0];
|
|
9574
|
+
if (captionLang && this.sources[captionLang] && this.currentLanguage !== captionLang) {
|
|
9575
|
+
this.switchLanguage(captionLang);
|
|
9576
|
+
this.selector.value = captionLang;
|
|
9577
|
+
}
|
|
9578
|
+
}
|
|
9579
|
+
};
|
|
9580
|
+
this.player.on("captionsenabled", this.handlers.captionChange);
|
|
9581
|
+
}
|
|
9582
|
+
}
|
|
9583
|
+
/**
|
|
9584
|
+
* Constrain position within video wrapper
|
|
9585
|
+
*/
|
|
9586
|
+
constrainPosition() {
|
|
9587
|
+
var _a;
|
|
9588
|
+
if (!this.wrapper || !this.player.videoWrapper) return;
|
|
9589
|
+
if ((_a = this.draggable) == null ? void 0 : _a.manuallyPositioned) return;
|
|
9590
|
+
if (!this.wrapper.style.width) {
|
|
9591
|
+
this.wrapper.style.width = "280px";
|
|
9592
|
+
}
|
|
9593
|
+
const videoWrapperRect = this.player.videoWrapper.getBoundingClientRect();
|
|
9594
|
+
const containerRect = this.player.container.getBoundingClientRect();
|
|
9595
|
+
const wrapperRect = this.wrapper.getBoundingClientRect();
|
|
9596
|
+
const videoWrapperLeft = videoWrapperRect.left - containerRect.left;
|
|
9597
|
+
const videoWrapperTop = videoWrapperRect.top - containerRect.top;
|
|
9598
|
+
const videoWrapperWidth = videoWrapperRect.width;
|
|
9599
|
+
const videoWrapperHeight = videoWrapperRect.height;
|
|
9600
|
+
let wrapperWidth = wrapperRect.width || 280;
|
|
9601
|
+
let wrapperHeight = wrapperRect.height || 280 * 9 / 16;
|
|
9602
|
+
let left, top;
|
|
9603
|
+
const margin = 16;
|
|
9604
|
+
const controlsHeight = 95;
|
|
9605
|
+
const position = this.desiredPosition || "bottom-right";
|
|
9606
|
+
switch (position) {
|
|
9607
|
+
case "bottom-right":
|
|
9608
|
+
left = videoWrapperLeft + videoWrapperWidth - wrapperWidth - margin;
|
|
9609
|
+
top = videoWrapperTop + videoWrapperHeight - wrapperHeight - controlsHeight;
|
|
9610
|
+
break;
|
|
9611
|
+
case "bottom-left":
|
|
9612
|
+
left = videoWrapperLeft + margin;
|
|
9613
|
+
top = videoWrapperTop + videoWrapperHeight - wrapperHeight - controlsHeight;
|
|
9614
|
+
break;
|
|
9615
|
+
case "top-right":
|
|
9616
|
+
left = videoWrapperLeft + videoWrapperWidth - wrapperWidth - margin;
|
|
9617
|
+
top = videoWrapperTop + margin;
|
|
9618
|
+
break;
|
|
9619
|
+
case "top-left":
|
|
9620
|
+
left = videoWrapperLeft + margin;
|
|
9621
|
+
top = videoWrapperTop + margin;
|
|
9622
|
+
break;
|
|
9623
|
+
default:
|
|
9624
|
+
left = videoWrapperLeft + videoWrapperWidth - wrapperWidth - margin;
|
|
9625
|
+
top = videoWrapperTop + videoWrapperHeight - wrapperHeight - controlsHeight;
|
|
9626
|
+
}
|
|
9627
|
+
left = Math.max(videoWrapperLeft, Math.min(left, videoWrapperLeft + videoWrapperWidth - wrapperWidth));
|
|
9628
|
+
top = Math.max(videoWrapperTop, Math.min(top, videoWrapperTop + videoWrapperHeight - wrapperHeight - controlsHeight));
|
|
9629
|
+
this.wrapper.style.left = "".concat(left, "px");
|
|
9630
|
+
this.wrapper.style.top = "".concat(top, "px");
|
|
9631
|
+
this.wrapper.style.right = "auto";
|
|
9632
|
+
this.wrapper.style.bottom = "auto";
|
|
9633
|
+
}
|
|
9634
|
+
/**
|
|
9635
|
+
* Show settings menu
|
|
9636
|
+
*/
|
|
9637
|
+
showSettingsMenu() {
|
|
9638
|
+
var _a;
|
|
9639
|
+
this.settingsMenuJustOpened = true;
|
|
9640
|
+
setTimeout(() => {
|
|
9641
|
+
this.settingsMenuJustOpened = false;
|
|
9642
|
+
}, 350);
|
|
9643
|
+
this._addDocumentClickHandler();
|
|
9644
|
+
if (this.settingsMenu) {
|
|
9645
|
+
this.settingsMenu.style.display = "block";
|
|
9646
|
+
this.settingsMenuVisible = true;
|
|
9647
|
+
(_a = this.settingsButton) == null ? void 0 : _a.setAttribute("aria-expanded", "true");
|
|
9648
|
+
this._attachMenuKeyboardNavigation();
|
|
9649
|
+
this._positionSettingsMenu();
|
|
9650
|
+
this._updateDragOptionState();
|
|
9651
|
+
this._updateResizeOptionState();
|
|
9652
|
+
focusFirstMenuItem(this.settingsMenu, ".".concat(this.player.options.classPrefix, "-sign-language-settings-item"));
|
|
9653
|
+
return;
|
|
9654
|
+
}
|
|
9655
|
+
this._createSettingsMenu();
|
|
9656
|
+
}
|
|
9657
|
+
/**
|
|
9658
|
+
* Hide settings menu
|
|
9659
|
+
*/
|
|
9660
|
+
hideSettingsMenu({ focusButton = true } = {}) {
|
|
9661
|
+
if (this.settingsMenu) {
|
|
9662
|
+
this.settingsMenu.style.display = "none";
|
|
9663
|
+
this.settingsMenuVisible = false;
|
|
9664
|
+
this.settingsMenuJustOpened = false;
|
|
9665
|
+
if (this.settingsMenuKeyHandler) {
|
|
9666
|
+
this.settingsMenu.removeEventListener("keydown", this.settingsMenuKeyHandler);
|
|
9667
|
+
this.settingsMenuKeyHandler = null;
|
|
9668
|
+
}
|
|
9669
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9670
|
+
const menuItems = Array.from(this.settingsMenu.querySelectorAll(".".concat(classPrefix, "-sign-language-settings-item")));
|
|
9671
|
+
menuItems.forEach((item) => item.setAttribute("tabindex", "-1"));
|
|
9672
|
+
if (this.settingsButton) {
|
|
9673
|
+
this.settingsButton.setAttribute("aria-expanded", "false");
|
|
9674
|
+
if (focusButton) {
|
|
9675
|
+
this.settingsButton.focus({ preventScroll: true });
|
|
9676
|
+
}
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9679
|
+
}
|
|
9680
|
+
/**
|
|
9681
|
+
* Add document click handler
|
|
9682
|
+
*/
|
|
9683
|
+
_addDocumentClickHandler() {
|
|
9684
|
+
if (this.documentClickHandlerAdded) return;
|
|
9685
|
+
this.documentClickHandler = (e) => {
|
|
9686
|
+
if (this.settingsMenuJustOpened) return;
|
|
9687
|
+
if (this.settingsButton && (this.settingsButton === e.target || this.settingsButton.contains(e.target))) {
|
|
9688
|
+
return;
|
|
9689
|
+
}
|
|
9690
|
+
if (this.settingsMenu && this.settingsMenu.contains(e.target)) {
|
|
9691
|
+
return;
|
|
9692
|
+
}
|
|
9693
|
+
if (this.settingsMenuVisible) {
|
|
9694
|
+
this.hideSettingsMenu();
|
|
9695
|
+
}
|
|
9696
|
+
};
|
|
9697
|
+
setTimeout(() => {
|
|
9698
|
+
document.addEventListener("mousedown", this.documentClickHandler, true);
|
|
9699
|
+
this.documentClickHandlerAdded = true;
|
|
9700
|
+
}, 300);
|
|
9701
|
+
}
|
|
9702
|
+
/**
|
|
9703
|
+
* Create settings menu
|
|
9704
|
+
*/
|
|
9705
|
+
_createSettingsMenu() {
|
|
9706
|
+
var _a, _b;
|
|
9707
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9708
|
+
this.settingsMenu = DOMUtils.createElement("div", {
|
|
9709
|
+
className: "".concat(classPrefix, "-sign-language-settings-menu"),
|
|
9710
|
+
attributes: { "role": "menu" }
|
|
9711
|
+
});
|
|
9712
|
+
const dragOption = createMenuItem({
|
|
9713
|
+
classPrefix,
|
|
9714
|
+
itemClass: "".concat(classPrefix, "-sign-language-settings-item"),
|
|
9715
|
+
icon: "move",
|
|
9716
|
+
label: "player.enableSignDragMode",
|
|
9717
|
+
hasTextClass: true,
|
|
9718
|
+
onClick: () => {
|
|
9719
|
+
this.toggleKeyboardDragMode();
|
|
9720
|
+
this.hideSettingsMenu();
|
|
9721
|
+
}
|
|
9722
|
+
});
|
|
9723
|
+
dragOption.setAttribute("role", "switch");
|
|
9724
|
+
dragOption.setAttribute("aria-checked", "false");
|
|
9725
|
+
this._removeTooltipFromMenuItem(dragOption);
|
|
9726
|
+
this.dragOptionButton = dragOption;
|
|
9727
|
+
this.dragOptionText = dragOption.querySelector(".".concat(classPrefix, "-settings-text"));
|
|
9728
|
+
this._updateDragOptionState();
|
|
9729
|
+
const resizeOption = createMenuItem({
|
|
9730
|
+
classPrefix,
|
|
9731
|
+
itemClass: "".concat(classPrefix, "-sign-language-settings-item"),
|
|
9732
|
+
icon: "resize",
|
|
9733
|
+
label: "player.enableSignResizeMode",
|
|
9734
|
+
hasTextClass: true,
|
|
9735
|
+
onClick: (event) => {
|
|
9736
|
+
event.preventDefault();
|
|
9737
|
+
event.stopPropagation();
|
|
9738
|
+
const enabled = this.toggleResizeMode({ focus: false });
|
|
9739
|
+
if (enabled) {
|
|
9740
|
+
this.hideSettingsMenu({ focusButton: false });
|
|
9741
|
+
setTimeout(() => {
|
|
9742
|
+
if (this.wrapper) this.wrapper.focus({ preventScroll: true });
|
|
9743
|
+
}, 20);
|
|
9744
|
+
} else {
|
|
9745
|
+
this.hideSettingsMenu({ focusButton: true });
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9748
|
+
});
|
|
9749
|
+
resizeOption.setAttribute("role", "switch");
|
|
9750
|
+
resizeOption.setAttribute("aria-checked", "false");
|
|
9751
|
+
this._removeTooltipFromMenuItem(resizeOption);
|
|
9752
|
+
this.resizeOptionButton = resizeOption;
|
|
9753
|
+
this.resizeOptionText = resizeOption.querySelector(".".concat(classPrefix, "-settings-text"));
|
|
9754
|
+
this._updateResizeOptionState();
|
|
9755
|
+
const closeOption = createMenuItem({
|
|
9756
|
+
classPrefix,
|
|
9757
|
+
itemClass: "".concat(classPrefix, "-sign-language-settings-item"),
|
|
9758
|
+
icon: "close",
|
|
9759
|
+
label: "transcript.closeMenu",
|
|
9760
|
+
onClick: () => this.hideSettingsMenu()
|
|
9761
|
+
});
|
|
9762
|
+
this._removeTooltipFromMenuItem(closeOption);
|
|
9763
|
+
this.settingsMenu.appendChild(dragOption);
|
|
9764
|
+
this.settingsMenu.appendChild(resizeOption);
|
|
9765
|
+
this.settingsMenu.appendChild(closeOption);
|
|
9766
|
+
this.settingsMenu.style.visibility = "hidden";
|
|
9767
|
+
this.settingsMenu.style.display = "block";
|
|
9768
|
+
if ((_a = this.settingsButton) == null ? void 0 : _a.parentNode) {
|
|
9769
|
+
this.settingsButton.insertAdjacentElement("afterend", this.settingsMenu);
|
|
9770
|
+
} else if (this.wrapper) {
|
|
9771
|
+
this.wrapper.appendChild(this.settingsMenu);
|
|
9772
|
+
}
|
|
9773
|
+
this._positionSettingsMenuImmediate();
|
|
9774
|
+
requestAnimationFrame(() => {
|
|
9775
|
+
if (this.settingsMenu) {
|
|
9776
|
+
this.settingsMenu.style.visibility = "visible";
|
|
9777
|
+
}
|
|
9778
|
+
});
|
|
9779
|
+
this._attachMenuKeyboardNavigation();
|
|
9780
|
+
this.settingsMenuVisible = true;
|
|
9781
|
+
(_b = this.settingsButton) == null ? void 0 : _b.setAttribute("aria-expanded", "true");
|
|
9782
|
+
this._updateDragOptionState();
|
|
9783
|
+
this._updateResizeOptionState();
|
|
9784
|
+
focusFirstMenuItem(this.settingsMenu, ".".concat(classPrefix, "-sign-language-settings-item"));
|
|
9785
|
+
}
|
|
9786
|
+
/**
|
|
9787
|
+
* Remove tooltip from menu item
|
|
9788
|
+
*/
|
|
9789
|
+
_removeTooltipFromMenuItem(item) {
|
|
9790
|
+
const classPrefix = this.player.options.classPrefix;
|
|
9791
|
+
const tooltip = item.querySelector(".".concat(classPrefix, "-tooltip"));
|
|
9792
|
+
if (tooltip) tooltip.remove();
|
|
9793
|
+
const buttonText = item.querySelector(".".concat(classPrefix, "-button-text"));
|
|
9794
|
+
if (buttonText) buttonText.remove();
|
|
9795
|
+
}
|
|
9796
|
+
/**
|
|
9797
|
+
* Attach menu keyboard navigation
|
|
9798
|
+
*/
|
|
9799
|
+
_attachMenuKeyboardNavigation() {
|
|
9800
|
+
if (this.settingsMenuKeyHandler) {
|
|
9801
|
+
this.settingsMenu.removeEventListener("keydown", this.settingsMenuKeyHandler);
|
|
9802
|
+
}
|
|
9803
|
+
this.settingsMenuKeyHandler = attachMenuKeyboardNavigation(
|
|
9804
|
+
this.settingsMenu,
|
|
9805
|
+
this.settingsButton,
|
|
9806
|
+
".".concat(this.player.options.classPrefix, "-sign-language-settings-item"),
|
|
9807
|
+
() => this.hideSettingsMenu({ focusButton: true })
|
|
9808
|
+
);
|
|
9809
|
+
}
|
|
9810
|
+
/**
|
|
9811
|
+
* Position settings menu immediately
|
|
9812
|
+
*/
|
|
9813
|
+
_positionSettingsMenuImmediate() {
|
|
9814
|
+
if (!this.settingsMenu || !this.settingsButton) return;
|
|
9815
|
+
const buttonRect = this.settingsButton.getBoundingClientRect();
|
|
9816
|
+
const menuRect = this.settingsMenu.getBoundingClientRect();
|
|
9817
|
+
const viewportWidth = window.innerWidth;
|
|
9818
|
+
const viewportHeight = window.innerHeight;
|
|
9819
|
+
const parentContainer = this.settingsButton.parentElement;
|
|
9820
|
+
if (!parentContainer) return;
|
|
9821
|
+
const parentRect = parentContainer.getBoundingClientRect();
|
|
9822
|
+
const buttonCenterX = buttonRect.left + buttonRect.width / 2 - parentRect.left;
|
|
9823
|
+
const buttonBottom = buttonRect.bottom - parentRect.top;
|
|
9824
|
+
const buttonTop = buttonRect.top - parentRect.top;
|
|
9825
|
+
const spaceAbove = buttonRect.top;
|
|
9826
|
+
const spaceBelow = viewportHeight - buttonRect.bottom;
|
|
9827
|
+
let menuTop = buttonBottom + 8;
|
|
9828
|
+
let menuBottom = null;
|
|
9829
|
+
if (spaceBelow < menuRect.height + 20 && spaceAbove > spaceBelow) {
|
|
9830
|
+
menuTop = null;
|
|
9831
|
+
const parentHeight = parentRect.bottom - parentRect.top;
|
|
9832
|
+
menuBottom = parentHeight - buttonTop + 8;
|
|
9833
|
+
this.settingsMenu.classList.add("vidply-menu-above");
|
|
9834
|
+
} else {
|
|
9835
|
+
this.settingsMenu.classList.remove("vidply-menu-above");
|
|
9836
|
+
}
|
|
9837
|
+
let menuLeft = buttonCenterX - menuRect.width / 2;
|
|
9838
|
+
let menuRight = "auto";
|
|
9839
|
+
let transformX = "translateX(0)";
|
|
9840
|
+
const menuLeftAbsolute = buttonRect.left + buttonRect.width / 2 - menuRect.width / 2;
|
|
9841
|
+
if (menuLeftAbsolute < 10) {
|
|
9842
|
+
menuLeft = 0;
|
|
9843
|
+
} else if (menuLeftAbsolute + menuRect.width > viewportWidth - 10) {
|
|
9844
|
+
menuLeft = "auto";
|
|
9845
|
+
menuRight = 0;
|
|
9846
|
+
} else {
|
|
9847
|
+
menuLeft = buttonCenterX;
|
|
9848
|
+
transformX = "translateX(-50%)";
|
|
9849
|
+
}
|
|
9850
|
+
if (menuTop !== null) {
|
|
9851
|
+
this.settingsMenu.style.top = "".concat(menuTop, "px");
|
|
9852
|
+
this.settingsMenu.style.bottom = "auto";
|
|
9853
|
+
} else if (menuBottom !== null) {
|
|
9854
|
+
this.settingsMenu.style.top = "auto";
|
|
9855
|
+
this.settingsMenu.style.bottom = "".concat(menuBottom, "px");
|
|
9856
|
+
}
|
|
9857
|
+
if (menuLeft !== "auto") {
|
|
9858
|
+
this.settingsMenu.style.left = "".concat(menuLeft, "px");
|
|
9859
|
+
this.settingsMenu.style.right = "auto";
|
|
9860
|
+
} else {
|
|
9861
|
+
this.settingsMenu.style.left = "auto";
|
|
9862
|
+
this.settingsMenu.style.right = "".concat(menuRight, "px");
|
|
9863
|
+
}
|
|
9864
|
+
this.settingsMenu.style.transform = transformX;
|
|
9865
|
+
}
|
|
9866
|
+
/**
|
|
9867
|
+
* Position settings menu with RAF
|
|
9868
|
+
*/
|
|
9869
|
+
_positionSettingsMenu() {
|
|
9870
|
+
requestAnimationFrame(() => {
|
|
9871
|
+
setTimeout(() => {
|
|
9872
|
+
this._positionSettingsMenuImmediate();
|
|
9873
|
+
}, 10);
|
|
9874
|
+
});
|
|
9875
|
+
}
|
|
9876
|
+
/**
|
|
9877
|
+
* Toggle keyboard drag mode
|
|
9878
|
+
*/
|
|
9879
|
+
toggleKeyboardDragMode() {
|
|
9880
|
+
if (this.draggable) {
|
|
9881
|
+
const wasEnabled = this.draggable.keyboardDragMode;
|
|
9882
|
+
this.draggable.toggleKeyboardDragMode();
|
|
9883
|
+
const isEnabled = this.draggable.keyboardDragMode;
|
|
9884
|
+
if (!wasEnabled && isEnabled) {
|
|
9885
|
+
this._enableMoveMode();
|
|
9886
|
+
}
|
|
9887
|
+
this._updateDragOptionState();
|
|
9888
|
+
}
|
|
9889
|
+
}
|
|
9890
|
+
/**
|
|
9891
|
+
* Enable move mode visual feedback
|
|
9892
|
+
*/
|
|
9893
|
+
_enableMoveMode() {
|
|
9894
|
+
this.wrapper.classList.add("".concat(this.player.options.classPrefix, "-sign-move-mode"));
|
|
9895
|
+
this._updateResizeOptionState();
|
|
9896
|
+
setTimeout(() => {
|
|
9897
|
+
this.wrapper.classList.remove("".concat(this.player.options.classPrefix, "-sign-move-mode"));
|
|
9898
|
+
}, 2e3);
|
|
9899
|
+
}
|
|
9900
|
+
/**
|
|
9901
|
+
* Toggle resize mode
|
|
9902
|
+
*/
|
|
9903
|
+
toggleResizeMode({ focus = true } = {}) {
|
|
9904
|
+
if (!this.draggable) return false;
|
|
9905
|
+
if (this.draggable.pointerResizeMode) {
|
|
9906
|
+
this.draggable.disablePointerResizeMode({ focus });
|
|
9907
|
+
this._updateResizeOptionState();
|
|
9908
|
+
return false;
|
|
9909
|
+
}
|
|
9910
|
+
this.draggable.enablePointerResizeMode({ focus });
|
|
9911
|
+
this._updateResizeOptionState();
|
|
9912
|
+
return true;
|
|
9913
|
+
}
|
|
9914
|
+
/**
|
|
9915
|
+
* Update drag option state
|
|
9916
|
+
*/
|
|
9917
|
+
_updateDragOptionState() {
|
|
9918
|
+
var _a;
|
|
9919
|
+
if (!this.dragOptionButton) return;
|
|
9920
|
+
const isEnabled = !!((_a = this.draggable) == null ? void 0 : _a.keyboardDragMode);
|
|
9921
|
+
const text = isEnabled ? i18n.t("player.disableSignDragMode") : i18n.t("player.enableSignDragMode");
|
|
9922
|
+
const ariaLabel = isEnabled ? i18n.t("player.disableSignDragModeAria") : i18n.t("player.enableSignDragModeAria");
|
|
9923
|
+
this.dragOptionButton.setAttribute("aria-checked", isEnabled ? "true" : "false");
|
|
9924
|
+
this.dragOptionButton.setAttribute("aria-label", ariaLabel);
|
|
9925
|
+
if (this.dragOptionText) {
|
|
9926
|
+
this.dragOptionText.textContent = text;
|
|
9927
|
+
}
|
|
9928
|
+
}
|
|
9929
|
+
/**
|
|
9930
|
+
* Update resize option state
|
|
9931
|
+
*/
|
|
9932
|
+
_updateResizeOptionState() {
|
|
9933
|
+
var _a;
|
|
9934
|
+
if (!this.resizeOptionButton) return;
|
|
9935
|
+
const isEnabled = !!((_a = this.draggable) == null ? void 0 : _a.pointerResizeMode);
|
|
9936
|
+
const text = isEnabled ? i18n.t("player.disableSignResizeMode") : i18n.t("player.enableSignResizeMode");
|
|
9937
|
+
const ariaLabel = isEnabled ? i18n.t("player.disableSignResizeModeAria") : i18n.t("player.enableSignResizeModeAria");
|
|
9938
|
+
this.resizeOptionButton.setAttribute("aria-checked", isEnabled ? "true" : "false");
|
|
9939
|
+
this.resizeOptionButton.setAttribute("aria-label", ariaLabel);
|
|
9940
|
+
if (this.resizeOptionText) {
|
|
9941
|
+
this.resizeOptionText.textContent = text;
|
|
9942
|
+
}
|
|
9943
|
+
}
|
|
9944
|
+
/**
|
|
9945
|
+
* Save preferences
|
|
9946
|
+
*/
|
|
9947
|
+
savePreferences() {
|
|
9948
|
+
if (!this.wrapper) return;
|
|
9949
|
+
this.player.storage.saveSignLanguagePreferences({
|
|
9950
|
+
size: { width: this.wrapper.style.width }
|
|
9951
|
+
});
|
|
9952
|
+
}
|
|
9953
|
+
/**
|
|
9954
|
+
* Update sources (called when playlist changes)
|
|
9955
|
+
*/
|
|
9956
|
+
updateSources(signLanguageSrc, signLanguageSources) {
|
|
9957
|
+
this.src = signLanguageSrc || null;
|
|
9958
|
+
this.sources = signLanguageSources || {};
|
|
9959
|
+
this.currentLanguage = null;
|
|
9960
|
+
}
|
|
9961
|
+
/**
|
|
9962
|
+
* Cleanup
|
|
9963
|
+
*/
|
|
9964
|
+
cleanup() {
|
|
9965
|
+
var _a;
|
|
9966
|
+
if (this.settingsMenuVisible) {
|
|
9967
|
+
this.hideSettingsMenu({ focusButton: false });
|
|
9968
|
+
}
|
|
9969
|
+
if (this.documentClickHandler && this.documentClickHandlerAdded) {
|
|
9970
|
+
document.removeEventListener("mousedown", this.documentClickHandler, true);
|
|
9971
|
+
this.documentClickHandlerAdded = false;
|
|
9972
|
+
this.documentClickHandler = null;
|
|
9973
|
+
}
|
|
9974
|
+
if (this.settingsHandlers && this.settingsButton) {
|
|
9975
|
+
this.settingsButton.removeEventListener("click", this.settingsHandlers.click);
|
|
9976
|
+
this.settingsButton.removeEventListener("keydown", this.settingsHandlers.keydown);
|
|
9977
|
+
}
|
|
9978
|
+
this.settingsHandlers = null;
|
|
9979
|
+
if (this.handlers) {
|
|
9980
|
+
this.player.off("play", this.handlers.play);
|
|
9981
|
+
this.player.off("pause", this.handlers.pause);
|
|
9982
|
+
this.player.off("timeupdate", this.handlers.timeupdate);
|
|
9983
|
+
this.player.off("ratechange", this.handlers.ratechange);
|
|
9984
|
+
if (this.handlers.captionChange) {
|
|
9985
|
+
this.player.off("captionsenabled", this.handlers.captionChange);
|
|
9986
|
+
}
|
|
9987
|
+
this.handlers = null;
|
|
9988
|
+
}
|
|
9989
|
+
if (this.wrapper && this.customKeyHandler) {
|
|
9990
|
+
this.wrapper.removeEventListener("keydown", this.customKeyHandler);
|
|
9991
|
+
}
|
|
9992
|
+
if (this.draggable) {
|
|
9993
|
+
if (this.draggable.pointerResizeMode) {
|
|
9994
|
+
this.draggable.disablePointerResizeMode();
|
|
9995
|
+
}
|
|
9996
|
+
this.draggable.destroy();
|
|
9997
|
+
this.draggable = null;
|
|
9998
|
+
}
|
|
9999
|
+
this.interactionHandlers = null;
|
|
10000
|
+
if ((_a = this.wrapper) == null ? void 0 : _a.parentNode) {
|
|
10001
|
+
if (this.video) {
|
|
10002
|
+
this.video.pause();
|
|
10003
|
+
this.video.src = "";
|
|
10004
|
+
}
|
|
10005
|
+
this.wrapper.parentNode.removeChild(this.wrapper);
|
|
10006
|
+
}
|
|
10007
|
+
this.wrapper = null;
|
|
10008
|
+
this.video = null;
|
|
10009
|
+
this.settingsButton = null;
|
|
10010
|
+
this.settingsMenu = null;
|
|
10011
|
+
}
|
|
10012
|
+
/**
|
|
10013
|
+
* Destroy
|
|
10014
|
+
*/
|
|
10015
|
+
destroy() {
|
|
10016
|
+
this.cleanup();
|
|
10017
|
+
this.enabled = false;
|
|
10018
|
+
}
|
|
10019
|
+
};
|
|
10020
|
+
|
|
10021
|
+
// src/core/Player.js
|
|
10022
|
+
var playerInstanceCounter = 0;
|
|
10023
|
+
var Player = class _Player extends EventEmitter {
|
|
10024
|
+
constructor(element, options = {}) {
|
|
10025
|
+
super();
|
|
10026
|
+
this.element = typeof element === "string" ? document.querySelector(element) : element;
|
|
10027
|
+
if (!this.element) {
|
|
10028
|
+
throw new Error("VidPly: Element not found");
|
|
10029
|
+
}
|
|
10030
|
+
playerInstanceCounter++;
|
|
10031
|
+
this.instanceId = playerInstanceCounter;
|
|
10032
|
+
if (this.element.tagName !== "VIDEO" && this.element.tagName !== "AUDIO") {
|
|
10033
|
+
const mediaType = options.mediaType || "video";
|
|
10034
|
+
const mediaElement = document.createElement(mediaType);
|
|
10035
|
+
Array.from(this.element.attributes).forEach((attr) => {
|
|
10036
|
+
if (attr.name !== "id" && attr.name !== "class" && !attr.name.startsWith("data-")) {
|
|
10037
|
+
mediaElement.setAttribute(attr.name, attr.value);
|
|
10038
|
+
}
|
|
10039
|
+
});
|
|
10040
|
+
const tracks = this.element.querySelectorAll("track");
|
|
10041
|
+
tracks.forEach((track) => {
|
|
10042
|
+
mediaElement.appendChild(track.cloneNode(true));
|
|
10043
|
+
});
|
|
10044
|
+
this.element.innerHTML = "";
|
|
10045
|
+
this.element.appendChild(mediaElement);
|
|
10046
|
+
this.element = mediaElement;
|
|
10047
|
+
}
|
|
10048
|
+
this._originalElement = this.element;
|
|
10049
|
+
this.options = __spreadValues({
|
|
10050
|
+
// Display
|
|
10051
|
+
width: null,
|
|
10052
|
+
height: null,
|
|
8498
10053
|
poster: null,
|
|
8499
10054
|
responsive: true,
|
|
8500
10055
|
fillContainer: false,
|
|
@@ -8649,6 +10204,58 @@
|
|
|
8649
10204
|
this.settingsDialog = null;
|
|
8650
10205
|
this.metadataCueChangeHandler = null;
|
|
8651
10206
|
this.metadataAlertHandlers = /* @__PURE__ */ new Map();
|
|
10207
|
+
this.audioDescriptionManager = new AudioDescriptionManager(this);
|
|
10208
|
+
this.signLanguageManager = new SignLanguageManager(this);
|
|
10209
|
+
Object.defineProperties(this, {
|
|
10210
|
+
signLanguageWrapper: {
|
|
10211
|
+
get: () => this.signLanguageManager.wrapper,
|
|
10212
|
+
set: (v) => {
|
|
10213
|
+
this.signLanguageManager.wrapper = v;
|
|
10214
|
+
}
|
|
10215
|
+
},
|
|
10216
|
+
signLanguageVideo: {
|
|
10217
|
+
get: () => this.signLanguageManager.video,
|
|
10218
|
+
set: (v) => {
|
|
10219
|
+
this.signLanguageManager.video = v;
|
|
10220
|
+
}
|
|
10221
|
+
},
|
|
10222
|
+
signLanguageHeader: {
|
|
10223
|
+
get: () => this.signLanguageManager.header,
|
|
10224
|
+
set: (v) => {
|
|
10225
|
+
this.signLanguageManager.header = v;
|
|
10226
|
+
}
|
|
10227
|
+
},
|
|
10228
|
+
signLanguageSettingsButton: {
|
|
10229
|
+
get: () => this.signLanguageManager.settingsButton,
|
|
10230
|
+
set: (v) => {
|
|
10231
|
+
this.signLanguageManager.settingsButton = v;
|
|
10232
|
+
}
|
|
10233
|
+
},
|
|
10234
|
+
signLanguageSettingsMenu: {
|
|
10235
|
+
get: () => this.signLanguageManager.settingsMenu,
|
|
10236
|
+
set: (v) => {
|
|
10237
|
+
this.signLanguageManager.settingsMenu = v;
|
|
10238
|
+
}
|
|
10239
|
+
},
|
|
10240
|
+
signLanguageSettingsMenuVisible: {
|
|
10241
|
+
get: () => this.signLanguageManager.settingsMenuVisible,
|
|
10242
|
+
set: (v) => {
|
|
10243
|
+
this.signLanguageManager.settingsMenuVisible = v;
|
|
10244
|
+
}
|
|
10245
|
+
},
|
|
10246
|
+
signLanguageDraggable: {
|
|
10247
|
+
get: () => this.signLanguageManager.draggable,
|
|
10248
|
+
set: (v) => {
|
|
10249
|
+
this.signLanguageManager.draggable = v;
|
|
10250
|
+
}
|
|
10251
|
+
},
|
|
10252
|
+
currentSignLanguage: {
|
|
10253
|
+
get: () => this.signLanguageManager.currentLanguage,
|
|
10254
|
+
set: (v) => {
|
|
10255
|
+
this.signLanguageManager.currentLanguage = v;
|
|
10256
|
+
}
|
|
10257
|
+
}
|
|
10258
|
+
});
|
|
8652
10259
|
this.init();
|
|
8653
10260
|
}
|
|
8654
10261
|
async init() {
|
|
@@ -8911,53 +10518,7 @@
|
|
|
8911
10518
|
}
|
|
8912
10519
|
this.currentSource = src;
|
|
8913
10520
|
this._pendingSource = null;
|
|
8914
|
-
|
|
8915
|
-
for (const sourceEl of sourceElements) {
|
|
8916
|
-
const descSrc = sourceEl.getAttribute("data-desc-src");
|
|
8917
|
-
const origSrc = sourceEl.getAttribute("data-orig-src");
|
|
8918
|
-
if (descSrc || origSrc) {
|
|
8919
|
-
if (!this.audioDescriptionSourceElement) {
|
|
8920
|
-
this.audioDescriptionSourceElement = sourceEl;
|
|
8921
|
-
}
|
|
8922
|
-
if (origSrc) {
|
|
8923
|
-
if (!this.originalAudioDescriptionSource) {
|
|
8924
|
-
this.originalAudioDescriptionSource = origSrc;
|
|
8925
|
-
}
|
|
8926
|
-
if (!this.originalSrc) {
|
|
8927
|
-
this.originalSrc = origSrc;
|
|
8928
|
-
}
|
|
8929
|
-
} else {
|
|
8930
|
-
const currentSrcAttr = sourceEl.getAttribute("src");
|
|
8931
|
-
if (!this.originalAudioDescriptionSource && currentSrcAttr) {
|
|
8932
|
-
this.originalAudioDescriptionSource = currentSrcAttr;
|
|
8933
|
-
}
|
|
8934
|
-
if (!this.originalSrc && currentSrcAttr) {
|
|
8935
|
-
this.originalSrc = currentSrcAttr;
|
|
8936
|
-
}
|
|
8937
|
-
}
|
|
8938
|
-
if (descSrc && !this.audioDescriptionSrc) {
|
|
8939
|
-
this.audioDescriptionSrc = descSrc;
|
|
8940
|
-
}
|
|
8941
|
-
}
|
|
8942
|
-
}
|
|
8943
|
-
const trackElements = this.trackElements;
|
|
8944
|
-
trackElements.forEach((trackEl) => {
|
|
8945
|
-
const trackKind = trackEl.getAttribute("kind");
|
|
8946
|
-
const trackDescSrc = trackEl.getAttribute("data-desc-src");
|
|
8947
|
-
if (trackKind === "captions" || trackKind === "subtitles" || trackKind === "chapters") {
|
|
8948
|
-
if (trackDescSrc) {
|
|
8949
|
-
this.audioDescriptionCaptionTracks.push({
|
|
8950
|
-
trackElement: trackEl,
|
|
8951
|
-
originalSrc: trackEl.getAttribute("src"),
|
|
8952
|
-
describedSrc: trackDescSrc,
|
|
8953
|
-
originalTrackSrc: trackEl.getAttribute("data-orig-src") || trackEl.getAttribute("src"),
|
|
8954
|
-
explicit: true
|
|
8955
|
-
// Explicitly defined, so we should validate it
|
|
8956
|
-
});
|
|
8957
|
-
this.log("Found explicit described ".concat(trackKind, " track: ").concat(trackEl.getAttribute("src"), " -> ").concat(trackDescSrc));
|
|
8958
|
-
}
|
|
8959
|
-
}
|
|
8960
|
-
});
|
|
10521
|
+
this.audioDescriptionManager.initFromSourceElements(this.sourceElements, this.trackElements);
|
|
8961
10522
|
if (!this.originalSrc) {
|
|
8962
10523
|
this.originalSrc = src;
|
|
8963
10524
|
}
|
|
@@ -9202,6 +10763,9 @@
|
|
|
9202
10763
|
if (trackConfig.default) {
|
|
9203
10764
|
track.default = true;
|
|
9204
10765
|
}
|
|
10766
|
+
if (trackConfig.describedSrc) {
|
|
10767
|
+
track.setAttribute("data-desc-src", trackConfig.describedSrc);
|
|
10768
|
+
}
|
|
9205
10769
|
const firstChild = this.element.firstChild;
|
|
9206
10770
|
if (firstChild && firstChild.nodeType === Node.ELEMENT_NODE && firstChild.tagName !== "TRACK") {
|
|
9207
10771
|
this.element.insertBefore(track, firstChild);
|
|
@@ -9216,6 +10780,13 @@
|
|
|
9216
10780
|
this.audioDescriptionSrc = config.audioDescriptionSrc || null;
|
|
9217
10781
|
this.signLanguageSrc = config.signLanguageSrc || null;
|
|
9218
10782
|
this.originalSrc = config.src;
|
|
10783
|
+
if (this.audioDescriptionManager) {
|
|
10784
|
+
this.audioDescriptionManager.updateSources(config.audioDescriptionSrc);
|
|
10785
|
+
this.audioDescriptionManager.reinitialize();
|
|
10786
|
+
}
|
|
10787
|
+
if (this.signLanguageManager) {
|
|
10788
|
+
this.signLanguageManager.updateSources(config.signLanguageSrc, config.signLanguageSources);
|
|
10789
|
+
}
|
|
9219
10790
|
if (wasAudioDescriptionEnabled) {
|
|
9220
10791
|
this.disableAudioDescription();
|
|
9221
10792
|
}
|
|
@@ -9624,8 +11195,12 @@
|
|
|
9624
11195
|
}
|
|
9625
11196
|
return null;
|
|
9626
11197
|
}
|
|
9627
|
-
// Audio Description
|
|
11198
|
+
// Audio Description (delegated to AudioDescriptionManager)
|
|
9628
11199
|
async enableAudioDescription() {
|
|
11200
|
+
return this.audioDescriptionManager.enable();
|
|
11201
|
+
}
|
|
11202
|
+
// Legacy method body preserved for reference - can be removed after testing
|
|
11203
|
+
async _legacyEnableAudioDescription() {
|
|
9629
11204
|
const hasSourceElementsWithDesc = this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
9630
11205
|
const hasTracksWithDesc = this.audioDescriptionCaptionTracks.length > 0;
|
|
9631
11206
|
if (!this.audioDescriptionSrc && !hasSourceElementsWithDesc && !hasTracksWithDesc) {
|
|
@@ -10358,6 +11933,10 @@
|
|
|
10358
11933
|
this.emit("audiodescriptionenabled");
|
|
10359
11934
|
}
|
|
10360
11935
|
async disableAudioDescription() {
|
|
11936
|
+
return this.audioDescriptionManager.disable();
|
|
11937
|
+
}
|
|
11938
|
+
// Legacy method body preserved for reference - can be removed after testing
|
|
11939
|
+
async _legacyDisableAudioDescription() {
|
|
10361
11940
|
if (!this.originalSrc) {
|
|
10362
11941
|
return;
|
|
10363
11942
|
}
|
|
@@ -10632,64 +12211,14 @@
|
|
|
10632
12211
|
this.emit("audiodescriptiondisabled");
|
|
10633
12212
|
}
|
|
10634
12213
|
async toggleAudioDescription() {
|
|
10635
|
-
|
|
10636
|
-
const hasAudioDescriptionSrc = this.audioDescriptionSrc || this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
10637
|
-
if (descriptionTrack && hasAudioDescriptionSrc) {
|
|
10638
|
-
if (this.state.audioDescriptionEnabled) {
|
|
10639
|
-
this._audioDescriptionDesiredState = false;
|
|
10640
|
-
descriptionTrack.mode = "hidden";
|
|
10641
|
-
await this.disableAudioDescription();
|
|
10642
|
-
} else {
|
|
10643
|
-
this._audioDescriptionDesiredState = true;
|
|
10644
|
-
await this.enableAudioDescription();
|
|
10645
|
-
const enableDescriptionTrack = () => {
|
|
10646
|
-
this.invalidateTrackCache();
|
|
10647
|
-
const descTrack = this.findTextTrack("descriptions");
|
|
10648
|
-
if (descTrack) {
|
|
10649
|
-
if (descTrack.mode === "disabled") {
|
|
10650
|
-
descTrack.mode = "hidden";
|
|
10651
|
-
this.setManagedTimeout(() => {
|
|
10652
|
-
descTrack.mode = "showing";
|
|
10653
|
-
}, 50);
|
|
10654
|
-
} else {
|
|
10655
|
-
descTrack.mode = "showing";
|
|
10656
|
-
}
|
|
10657
|
-
} else if (this.element.readyState < 2) {
|
|
10658
|
-
this.setManagedTimeout(enableDescriptionTrack, 100);
|
|
10659
|
-
}
|
|
10660
|
-
};
|
|
10661
|
-
if (this.element.readyState >= 1) {
|
|
10662
|
-
this.setManagedTimeout(enableDescriptionTrack, 200);
|
|
10663
|
-
} else {
|
|
10664
|
-
this.element.addEventListener("loadedmetadata", () => {
|
|
10665
|
-
this.setManagedTimeout(enableDescriptionTrack, 200);
|
|
10666
|
-
}, { once: true });
|
|
10667
|
-
}
|
|
10668
|
-
}
|
|
10669
|
-
} else if (descriptionTrack) {
|
|
10670
|
-
if (descriptionTrack.mode === "showing") {
|
|
10671
|
-
this._audioDescriptionDesiredState = false;
|
|
10672
|
-
descriptionTrack.mode = "hidden";
|
|
10673
|
-
this.state.audioDescriptionEnabled = false;
|
|
10674
|
-
this.emit("audiodescriptiondisabled");
|
|
10675
|
-
} else {
|
|
10676
|
-
this._audioDescriptionDesiredState = true;
|
|
10677
|
-
descriptionTrack.mode = "showing";
|
|
10678
|
-
this.state.audioDescriptionEnabled = true;
|
|
10679
|
-
this.emit("audiodescriptionenabled");
|
|
10680
|
-
}
|
|
10681
|
-
} else if (hasAudioDescriptionSrc) {
|
|
10682
|
-
if (this.state.audioDescriptionEnabled) {
|
|
10683
|
-
this._audioDescriptionDesiredState = false;
|
|
10684
|
-
await this.disableAudioDescription();
|
|
10685
|
-
} else {
|
|
10686
|
-
this._audioDescriptionDesiredState = true;
|
|
10687
|
-
await this.enableAudioDescription();
|
|
10688
|
-
}
|
|
10689
|
-
}
|
|
12214
|
+
return this.audioDescriptionManager.toggle();
|
|
10690
12215
|
}
|
|
10691
|
-
// Sign Language
|
|
12216
|
+
// Sign Language (delegated to SignLanguageManager)
|
|
10692
12217
|
enableSignLanguage() {
|
|
12218
|
+
return this.signLanguageManager.enable();
|
|
12219
|
+
}
|
|
12220
|
+
// Legacy method body preserved for reference - can be removed after testing
|
|
12221
|
+
_legacyEnableSignLanguage() {
|
|
10693
12222
|
var _a;
|
|
10694
12223
|
const hasMultipleSources = Object.keys(this.signLanguageSources).length > 0;
|
|
10695
12224
|
const hasSingleSource = !!this.signLanguageSrc;
|
|
@@ -10942,23 +12471,16 @@
|
|
|
10942
12471
|
}, 150);
|
|
10943
12472
|
}
|
|
10944
12473
|
disableSignLanguage() {
|
|
10945
|
-
|
|
10946
|
-
this.hideSignLanguageSettingsMenu({ focusButton: false });
|
|
10947
|
-
}
|
|
10948
|
-
if (this.signLanguageWrapper) {
|
|
10949
|
-
this.signLanguageWrapper.style.display = "none";
|
|
10950
|
-
}
|
|
10951
|
-
this.state.signLanguageEnabled = false;
|
|
10952
|
-
this.emit("signlanguagedisabled");
|
|
12474
|
+
return this.signLanguageManager.disable();
|
|
10953
12475
|
}
|
|
10954
12476
|
toggleSignLanguage() {
|
|
10955
|
-
|
|
10956
|
-
this.disableSignLanguage();
|
|
10957
|
-
} else {
|
|
10958
|
-
this.enableSignLanguage();
|
|
10959
|
-
}
|
|
12477
|
+
return this.signLanguageManager.toggle();
|
|
10960
12478
|
}
|
|
10961
12479
|
setupSignLanguageInteraction() {
|
|
12480
|
+
return this.signLanguageManager._setupInteraction();
|
|
12481
|
+
}
|
|
12482
|
+
// Legacy method preserved for reference
|
|
12483
|
+
_legacySetupSignLanguageInteraction() {
|
|
10962
12484
|
if (!this.signLanguageWrapper) return;
|
|
10963
12485
|
const isMobile2 = window.innerWidth < 768;
|
|
10964
12486
|
const isFullscreen = this.state.fullscreen;
|
|
@@ -11096,6 +12618,10 @@
|
|
|
11096
12618
|
return langNames[langCode] || langCode.toUpperCase();
|
|
11097
12619
|
}
|
|
11098
12620
|
switchSignLanguage(langCode) {
|
|
12621
|
+
return this.signLanguageManager.switchLanguage(langCode);
|
|
12622
|
+
}
|
|
12623
|
+
// Legacy method preserved for reference
|
|
12624
|
+
_legacySwitchSignLanguage(langCode) {
|
|
11099
12625
|
if (!this.signLanguageSources[langCode] || !this.signLanguageVideo) {
|
|
11100
12626
|
return;
|
|
11101
12627
|
}
|
|
@@ -11111,6 +12637,10 @@
|
|
|
11111
12637
|
this.emit("signlanguagelanguagechanged", langCode);
|
|
11112
12638
|
}
|
|
11113
12639
|
showSignLanguageSettingsMenu() {
|
|
12640
|
+
return this.signLanguageManager.showSettingsMenu();
|
|
12641
|
+
}
|
|
12642
|
+
// Legacy method preserved for reference
|
|
12643
|
+
_legacyShowSignLanguageSettingsMenu() {
|
|
11114
12644
|
this.signLanguageSettingsMenuJustOpened = true;
|
|
11115
12645
|
setTimeout(() => {
|
|
11116
12646
|
this.signLanguageSettingsMenuJustOpened = false;
|
|
@@ -11254,25 +12784,7 @@
|
|
|
11254
12784
|
focusFirstMenuItem(this.signLanguageSettingsMenu, ".".concat(this.options.classPrefix, "-sign-language-settings-item"));
|
|
11255
12785
|
}
|
|
11256
12786
|
hideSignLanguageSettingsMenu({ focusButton = true } = {}) {
|
|
11257
|
-
|
|
11258
|
-
this.signLanguageSettingsMenu.style.display = "none";
|
|
11259
|
-
this.signLanguageSettingsMenuVisible = false;
|
|
11260
|
-
this.signLanguageSettingsMenuJustOpened = false;
|
|
11261
|
-
if (this.signLanguageSettingsMenuKeyHandler) {
|
|
11262
|
-
this.signLanguageSettingsMenu.removeEventListener("keydown", this.signLanguageSettingsMenuKeyHandler);
|
|
11263
|
-
this.signLanguageSettingsMenuKeyHandler = null;
|
|
11264
|
-
}
|
|
11265
|
-
const menuItems = Array.from(this.signLanguageSettingsMenu.querySelectorAll(".".concat(this.options.classPrefix, "-sign-language-settings-item")));
|
|
11266
|
-
menuItems.forEach((item) => {
|
|
11267
|
-
item.setAttribute("tabindex", "-1");
|
|
11268
|
-
});
|
|
11269
|
-
if (this.signLanguageSettingsButton) {
|
|
11270
|
-
this.signLanguageSettingsButton.setAttribute("aria-expanded", "false");
|
|
11271
|
-
if (focusButton) {
|
|
11272
|
-
this.signLanguageSettingsButton.focus({ preventScroll: true });
|
|
11273
|
-
}
|
|
11274
|
-
}
|
|
11275
|
-
}
|
|
12787
|
+
return this.signLanguageManager.hideSettingsMenu({ focusButton });
|
|
11276
12788
|
}
|
|
11277
12789
|
positionSignLanguageSettingsMenuImmediate() {
|
|
11278
12790
|
if (!this.signLanguageSettingsMenu || !this.signLanguageSettingsButton) return;
|
|
@@ -11376,6 +12888,13 @@
|
|
|
11376
12888
|
}
|
|
11377
12889
|
}
|
|
11378
12890
|
constrainSignLanguagePosition() {
|
|
12891
|
+
return this.signLanguageManager.constrainPosition();
|
|
12892
|
+
}
|
|
12893
|
+
saveSignLanguagePreferences() {
|
|
12894
|
+
return this.signLanguageManager.savePreferences();
|
|
12895
|
+
}
|
|
12896
|
+
// Legacy methods preserved for reference - can be removed after testing
|
|
12897
|
+
_legacyConstrainSignLanguagePosition() {
|
|
11379
12898
|
if (!this.signLanguageWrapper || !this.videoWrapper) return;
|
|
11380
12899
|
if (this.signLanguageDraggable && this.signLanguageDraggable.manuallyPositioned) {
|
|
11381
12900
|
return;
|
|
@@ -11425,7 +12944,7 @@
|
|
|
11425
12944
|
this.signLanguageWrapper.style.bottom = "auto";
|
|
11426
12945
|
this.signLanguageWrapper.classList.remove(...Array.from(this.signLanguageWrapper.classList).filter((c) => c.startsWith("vidply-sign-position-")));
|
|
11427
12946
|
}
|
|
11428
|
-
|
|
12947
|
+
_legacySaveSignLanguagePreferences() {
|
|
11429
12948
|
if (!this.signLanguageWrapper) return;
|
|
11430
12949
|
this.storage.saveSignLanguagePreferences({
|
|
11431
12950
|
size: {
|
|
@@ -11435,58 +12954,7 @@
|
|
|
11435
12954
|
});
|
|
11436
12955
|
}
|
|
11437
12956
|
cleanupSignLanguage() {
|
|
11438
|
-
|
|
11439
|
-
this.hideSignLanguageSettingsMenu({ focusButton: false });
|
|
11440
|
-
}
|
|
11441
|
-
if (this.signLanguageDocumentClickHandler && this.signLanguageDocumentClickHandlerAdded) {
|
|
11442
|
-
document.removeEventListener("mousedown", this.signLanguageDocumentClickHandler, true);
|
|
11443
|
-
this.signLanguageDocumentClickHandlerAdded = false;
|
|
11444
|
-
this.signLanguageDocumentClickHandler = null;
|
|
11445
|
-
}
|
|
11446
|
-
if (this.signLanguageSettingsHandlers) {
|
|
11447
|
-
if (this.signLanguageSettingsButton) {
|
|
11448
|
-
this.signLanguageSettingsButton.removeEventListener("click", this.signLanguageSettingsHandlers.settingsClick);
|
|
11449
|
-
this.signLanguageSettingsButton.removeEventListener("keydown", this.signLanguageSettingsHandlers.settingsKeydown);
|
|
11450
|
-
}
|
|
11451
|
-
this.signLanguageSettingsHandlers = null;
|
|
11452
|
-
}
|
|
11453
|
-
if (this.signLanguageHandlers) {
|
|
11454
|
-
this.off("play", this.signLanguageHandlers.play);
|
|
11455
|
-
this.off("pause", this.signLanguageHandlers.pause);
|
|
11456
|
-
this.off("timeupdate", this.signLanguageHandlers.timeupdate);
|
|
11457
|
-
this.off("ratechange", this.signLanguageHandlers.ratechange);
|
|
11458
|
-
if (this.signLanguageHandlers.captionChange) {
|
|
11459
|
-
this.off("captionsenabled", this.signLanguageHandlers.captionChange);
|
|
11460
|
-
}
|
|
11461
|
-
this.signLanguageHandlers = null;
|
|
11462
|
-
}
|
|
11463
|
-
if (this.signLanguageInteractionHandlers) {
|
|
11464
|
-
if (this.signLanguageHeader && this.signLanguageInteractionHandlers.headerKeyHandler) {
|
|
11465
|
-
this.signLanguageHeader.removeEventListener("keydown", this.signLanguageInteractionHandlers.headerKeyHandler);
|
|
11466
|
-
}
|
|
11467
|
-
if (this.signLanguageWrapper && this.signLanguageInteractionHandlers.customKeyHandler) {
|
|
11468
|
-
this.signLanguageWrapper.removeEventListener("keydown", this.signLanguageInteractionHandlers.customKeyHandler);
|
|
11469
|
-
}
|
|
11470
|
-
}
|
|
11471
|
-
if (this.signLanguageDraggable) {
|
|
11472
|
-
if (this.signLanguageDraggable.pointerResizeMode) {
|
|
11473
|
-
this.signLanguageDraggable.disablePointerResizeMode();
|
|
11474
|
-
}
|
|
11475
|
-
this.signLanguageDraggable.destroy();
|
|
11476
|
-
this.signLanguageDraggable = null;
|
|
11477
|
-
}
|
|
11478
|
-
this.signLanguageInteractionHandlers = null;
|
|
11479
|
-
if (this.signLanguageWrapper && this.signLanguageWrapper.parentNode) {
|
|
11480
|
-
if (this.signLanguageVideo) {
|
|
11481
|
-
this.signLanguageVideo.pause();
|
|
11482
|
-
this.signLanguageVideo.src = "";
|
|
11483
|
-
}
|
|
11484
|
-
this.signLanguageWrapper.parentNode.removeChild(this.signLanguageWrapper);
|
|
11485
|
-
}
|
|
11486
|
-
this.signLanguageWrapper = null;
|
|
11487
|
-
this.signLanguageVideo = null;
|
|
11488
|
-
this.signLanguageSettingsButton = null;
|
|
11489
|
-
this.signLanguageSettingsMenu = null;
|
|
12957
|
+
return this.signLanguageManager.cleanup();
|
|
11490
12958
|
}
|
|
11491
12959
|
// Settings
|
|
11492
12960
|
// Settings dialog removed - using individual control buttons instead
|
|
@@ -12721,7 +14189,7 @@
|
|
|
12721
14189
|
id: "".concat(this.uniqueId, "-keyboard-instructions")
|
|
12722
14190
|
}
|
|
12723
14191
|
});
|
|
12724
|
-
instructions.textContent = "
|
|
14192
|
+
instructions.textContent = i18n.t("playlist.keyboardInstructions");
|
|
12725
14193
|
this.playlistPanel.appendChild(instructions);
|
|
12726
14194
|
const list = DOMUtils.createElement("ul", {
|
|
12727
14195
|
className: "vidply-playlist-list",
|
|
@@ -12877,7 +14345,7 @@
|
|
|
12877
14345
|
if (index < buttons.length - 1) {
|
|
12878
14346
|
newIndex = index + 1;
|
|
12879
14347
|
} else {
|
|
12880
|
-
announcement = "
|
|
14348
|
+
announcement = i18n.t("playlist.endOfPlaylist", { current: buttons.length, total: buttons.length });
|
|
12881
14349
|
}
|
|
12882
14350
|
break;
|
|
12883
14351
|
case "ArrowUp":
|
|
@@ -12886,7 +14354,7 @@
|
|
|
12886
14354
|
if (index > 0) {
|
|
12887
14355
|
newIndex = index - 1;
|
|
12888
14356
|
} else {
|
|
12889
|
-
announcement = "
|
|
14357
|
+
announcement = i18n.t("playlist.beginningOfPlaylist", { total: buttons.length });
|
|
12890
14358
|
}
|
|
12891
14359
|
break;
|
|
12892
14360
|
case "PageDown":
|
|
@@ -12894,7 +14362,7 @@
|
|
|
12894
14362
|
e.stopPropagation();
|
|
12895
14363
|
newIndex = Math.min(index + 5, buttons.length - 1);
|
|
12896
14364
|
if (newIndex === buttons.length - 1 && index !== newIndex) {
|
|
12897
|
-
announcement = "
|
|
14365
|
+
announcement = i18n.t("playlist.jumpedToLastTrack", { current: newIndex + 1, total: buttons.length });
|
|
12898
14366
|
}
|
|
12899
14367
|
break;
|
|
12900
14368
|
case "PageUp":
|
|
@@ -12902,7 +14370,7 @@
|
|
|
12902
14370
|
e.stopPropagation();
|
|
12903
14371
|
newIndex = Math.max(index - 5, 0);
|
|
12904
14372
|
if (newIndex === 0 && index !== newIndex) {
|
|
12905
|
-
announcement = "
|
|
14373
|
+
announcement = i18n.t("playlist.jumpedToFirstTrack", { total: buttons.length });
|
|
12906
14374
|
}
|
|
12907
14375
|
break;
|
|
12908
14376
|
case "Home":
|
|
@@ -12910,7 +14378,7 @@
|
|
|
12910
14378
|
e.stopPropagation();
|
|
12911
14379
|
newIndex = 0;
|
|
12912
14380
|
if (index !== 0) {
|
|
12913
|
-
announcement = "
|
|
14381
|
+
announcement = i18n.t("playlist.firstTrack", { total: buttons.length });
|
|
12914
14382
|
}
|
|
12915
14383
|
break;
|
|
12916
14384
|
case "End":
|
|
@@ -12918,7 +14386,7 @@
|
|
|
12918
14386
|
e.stopPropagation();
|
|
12919
14387
|
newIndex = buttons.length - 1;
|
|
12920
14388
|
if (index !== buttons.length - 1) {
|
|
12921
|
-
announcement = "
|
|
14389
|
+
announcement = i18n.t("playlist.lastTrack", { current: buttons.length, total: buttons.length });
|
|
12922
14390
|
}
|
|
12923
14391
|
break;
|
|
12924
14392
|
}
|