@meuuhdia/video-player 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +126 -0
- package/icons.js +14 -0
- package/locale/fr.js +47 -0
- package/meuuhdia-css.js +1 -0
- package/meuuhdia.js +34 -0
- package/modules/caption.js +429 -0
- package/modules/controls.js +328 -0
- package/modules/disclosure.js +99 -0
- package/modules/icon.js +32 -0
- package/modules/player.js +95 -0
- package/modules/time.js +64 -0
- package/modules/transcription.js +142 -0
- package/package.json +41 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { MeuuhdiaCaption } from "./caption.js";
|
|
2
|
+
import { meuuhdiaDisclosure } from "./disclosure.js";
|
|
3
|
+
import { MeuuhdiaIcon } from "./icon.js";
|
|
4
|
+
import { MeuuhdiaTime } from "./time.js";
|
|
5
|
+
|
|
6
|
+
class MeuuhdiaControls {
|
|
7
|
+
/**
|
|
8
|
+
* @type {Object}
|
|
9
|
+
*/
|
|
10
|
+
locale;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @type {Node}
|
|
14
|
+
*/
|
|
15
|
+
controls;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @type {Node}
|
|
19
|
+
*/
|
|
20
|
+
wrapper;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @type {Node}
|
|
24
|
+
*/
|
|
25
|
+
video;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @type {int}
|
|
29
|
+
*/
|
|
30
|
+
controlsTimeout;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* ? MeuuhdiaControls constructor
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} locale
|
|
36
|
+
* @param {Node} wrapper
|
|
37
|
+
* @param {Node} video
|
|
38
|
+
*/
|
|
39
|
+
constructor(locale, wrapper, video) {
|
|
40
|
+
this.locale = locale;
|
|
41
|
+
this.wrapper = wrapper;
|
|
42
|
+
this.video = video;
|
|
43
|
+
|
|
44
|
+
this.#initControls();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#initControls() {
|
|
48
|
+
this.controls = document.createElement("ul");
|
|
49
|
+
this.controls.classList.add("meuuhdia_controls", "list-unstyled");
|
|
50
|
+
|
|
51
|
+
let play = this.#createButton("meuuhdia_playpause", MeuuhdiaIcon.createIcon("play"), this.locale.controls.play);
|
|
52
|
+
let progress = this.#createProgressDuration();
|
|
53
|
+
let volume = this.#createProgressVolume();
|
|
54
|
+
let fullscreen = this.#createButton("meuuhdia_fs", MeuuhdiaIcon.createIcon("expand"), this.locale.controls.fullscreen);
|
|
55
|
+
fullscreen.setAttribute("aria-pressed", "false");
|
|
56
|
+
|
|
57
|
+
this.controls.appendChild(play);
|
|
58
|
+
this.controls.appendChild(progress);
|
|
59
|
+
this.controls.appendChild(volume);
|
|
60
|
+
|
|
61
|
+
if(0 < this.video.textTracks.length) {
|
|
62
|
+
let Caption = new MeuuhdiaCaption(this.locale, this.wrapper, this.video, this.controls);
|
|
63
|
+
this.wrapper.appendChild(Caption.createCaptionContainer());
|
|
64
|
+
this.controls.appendChild(Caption.createCaption(this.video.textTracks));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.controls.appendChild(fullscreen);
|
|
68
|
+
this.#actionAddEventListeners();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#actionAddEventListeners() {
|
|
72
|
+
let playPause = this.controls.querySelector(".meuuhdia_playpause");
|
|
73
|
+
let mute = this.controls.querySelector(".meuuhdia_mute");
|
|
74
|
+
let volume = this.controls.querySelector(".meuuhdia_volume_bar");
|
|
75
|
+
let fullscreen = this.controls.querySelector(".meuuhdia_fs");
|
|
76
|
+
|
|
77
|
+
playPause.addEventListener("click", this.#actionPlayPause.bind(this));
|
|
78
|
+
this.video.addEventListener("click", this.#actionPlayPause.bind(this));
|
|
79
|
+
this.video.addEventListener("keyup", this.#checkKey.bind(this));
|
|
80
|
+
mute.addEventListener("click", this.#actionMute.bind(this));
|
|
81
|
+
volume.addEventListener("change", (evt) => {
|
|
82
|
+
this.video.volume = evt.currentTarget.value;
|
|
83
|
+
});
|
|
84
|
+
this.#timeProgress();
|
|
85
|
+
|
|
86
|
+
if (!document?.fullscreenEnabled) fullscreen.style.display = "none";
|
|
87
|
+
else {
|
|
88
|
+
fullscreen.addEventListener("click", this.#fullscreen.bind(this));
|
|
89
|
+
|
|
90
|
+
document.addEventListener("fullscreenchange", (e) => {
|
|
91
|
+
this.#setFullscreenData(!!document.fullscreenElement);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.video.addEventListener("volumechange", this.#setVolumeIcon.bind(this));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#checkKey(evt) {
|
|
99
|
+
if(evt.originalTarget == evt.currentTarget && "Space" == evt.code) {
|
|
100
|
+
this.#actionPlayPause();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#actionPlayPause() {
|
|
105
|
+
let play = this.controls.querySelector(".meuuhdia_playpause");
|
|
106
|
+
let icon = play.querySelector("svg");
|
|
107
|
+
let label = play.querySelector(".sr-only");
|
|
108
|
+
|
|
109
|
+
if (this.video.paused || this.video.ended) {
|
|
110
|
+
this.video.play();
|
|
111
|
+
play.replaceChild(MeuuhdiaIcon.createIcon("pause"), icon);
|
|
112
|
+
label.textContent = "pause";
|
|
113
|
+
this.#toggleControls();
|
|
114
|
+
} else {
|
|
115
|
+
clearTimeout(this.controlsTimeout);
|
|
116
|
+
this.video.pause();
|
|
117
|
+
play.replaceChild(MeuuhdiaIcon.createIcon("play"), icon);
|
|
118
|
+
label.textContent = "lecture";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#toggleControls() {
|
|
123
|
+
clearTimeout(this.controlsTimeout);
|
|
124
|
+
this.wrapper.classList.remove("meuuhdia_hide_controls");
|
|
125
|
+
this.wrapper.removeEventListener("keydown", this.#toggleControls.bind(this), {once: true});
|
|
126
|
+
this.wrapper.removeEventListener("mousemove", this.#toggleControls.bind(this), {once: true});
|
|
127
|
+
|
|
128
|
+
if(this.video.paused) return;
|
|
129
|
+
|
|
130
|
+
this.wrapper.addEventListener("keydown", this.#toggleControls.bind(this), {once: true});
|
|
131
|
+
this.wrapper.addEventListener("mousemove", this.#toggleControls.bind(this), {once: true});
|
|
132
|
+
|
|
133
|
+
this.controlsTimeout = setTimeout(() => this.wrapper.classList.add("meuuhdia_hide_controls"), 5000);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* ? Create button wrapped in list item
|
|
138
|
+
*
|
|
139
|
+
* @param {string} btnClass
|
|
140
|
+
* @param {Node} btnIcon
|
|
141
|
+
* @param {string} btnLabel
|
|
142
|
+
* @returns {Node} <li> element
|
|
143
|
+
*/
|
|
144
|
+
#createButton(btnClass, btnIcon, btnLabel) {
|
|
145
|
+
let item = document.createElement("li");
|
|
146
|
+
|
|
147
|
+
let btn = document.createElement("button");
|
|
148
|
+
btn.classList.add(btnClass);
|
|
149
|
+
btn.setAttribute("type", "button");
|
|
150
|
+
|
|
151
|
+
let label = document.createElement("span");
|
|
152
|
+
label.textContent = btnLabel;
|
|
153
|
+
label.classList.add("sr-only");
|
|
154
|
+
|
|
155
|
+
btn.appendChild(label);
|
|
156
|
+
btn.appendChild(btnIcon);
|
|
157
|
+
item.appendChild(btn);
|
|
158
|
+
|
|
159
|
+
return item;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#createProgressDuration() {
|
|
163
|
+
/**
|
|
164
|
+
* ? Progress label
|
|
165
|
+
*/
|
|
166
|
+
let progress_index = document.querySelectorAll(".meuuhdia_progress").length + 1;
|
|
167
|
+
let progress_label = document.createElement("label");
|
|
168
|
+
progress_label.setAttribute("for", "meuuhdia_progress_bar_" + progress_index);
|
|
169
|
+
|
|
170
|
+
let progress_duration = document.createElement("span");
|
|
171
|
+
progress_duration.classList.add("sr-only");
|
|
172
|
+
progress_duration.textContent = this.locale.controls.progress + ": ";
|
|
173
|
+
|
|
174
|
+
let progress_timenow = document.createElement("span");
|
|
175
|
+
progress_timenow.classList.add("meuuhdia_timenow");
|
|
176
|
+
MeuuhdiaTime.populateTimeNode(this.locale, progress_timenow, 0);
|
|
177
|
+
|
|
178
|
+
let progress_time_separator = document.createElement("span");
|
|
179
|
+
progress_time_separator.textContent = " / ";
|
|
180
|
+
|
|
181
|
+
let progress_timetotal = document.createElement("span");
|
|
182
|
+
progress_timetotal.classList.add("meuuhdia_timetotal");
|
|
183
|
+
|
|
184
|
+
progress_label.appendChild(progress_duration);
|
|
185
|
+
progress_label.appendChild(progress_timenow);
|
|
186
|
+
progress_label.appendChild(progress_time_separator);
|
|
187
|
+
progress_label.appendChild(progress_timetotal);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* ? Progress bar
|
|
191
|
+
*/
|
|
192
|
+
let progress_bar = document.createElement("input");
|
|
193
|
+
progress_bar.setAttribute("id", "meuuhdia_progress_bar_" + progress_index);
|
|
194
|
+
progress_bar.setAttribute("type", "range");
|
|
195
|
+
progress_bar.classList.add("meuuhdia_progress_bar", "meuuhdia_range_horizontal");
|
|
196
|
+
progress_bar.setAttribute("value", 0);
|
|
197
|
+
progress_bar.setAttribute("min", 0);
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* ? Progress item list
|
|
201
|
+
*/
|
|
202
|
+
let item = document.createElement("li");
|
|
203
|
+
item.classList.add("meuuhdia_progress");
|
|
204
|
+
item.appendChild(progress_label);
|
|
205
|
+
item.appendChild(progress_bar);
|
|
206
|
+
|
|
207
|
+
return item;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
#createProgressVolume() {
|
|
211
|
+
/**
|
|
212
|
+
* ? Volume off
|
|
213
|
+
*/
|
|
214
|
+
let volContentOff = document.createElement("button");
|
|
215
|
+
volContentOff.setAttribute("type", "button");
|
|
216
|
+
volContentOff.setAttribute("aria-pressed", "false");
|
|
217
|
+
volContentOff.classList.add("meuuhdia_mute");
|
|
218
|
+
|
|
219
|
+
let volContentOffLabel = document.createElement("span");
|
|
220
|
+
volContentOffLabel.classList.add("sr-only");
|
|
221
|
+
volContentOffLabel.textContent = this.locale.controls.mute;
|
|
222
|
+
|
|
223
|
+
let volContentOffIcon = MeuuhdiaIcon.createIcon("volume_off");
|
|
224
|
+
|
|
225
|
+
volContentOff.appendChild(volContentOffLabel);
|
|
226
|
+
volContentOff.appendChild(volContentOffIcon);
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* ? Volume progress
|
|
230
|
+
*/
|
|
231
|
+
let volProgress = document.createElement("input");
|
|
232
|
+
volProgress.setAttribute("type", "range");
|
|
233
|
+
volProgress.setAttribute("value", 1);
|
|
234
|
+
volProgress.setAttribute("min", 0);
|
|
235
|
+
volProgress.setAttribute("max", 1);
|
|
236
|
+
volProgress.setAttribute("step", 0.1);
|
|
237
|
+
volProgress.classList.add("meuuhdia_volume_bar", "meuuhdia_range_vertical");
|
|
238
|
+
|
|
239
|
+
let volume = meuuhdiaDisclosure.createDisclosure("volume", "volume_high", this.locale.controls.volume, [volContentOff, volProgress], "100%");
|
|
240
|
+
let progressLabel = volume.querySelector(".meuuhdia_disclosure_action").getAttribute("id");
|
|
241
|
+
volume.querySelector(".meuuhdia_volume_bar").setAttribute("aria-labelledby", progressLabel);
|
|
242
|
+
|
|
243
|
+
return volume;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#actionMute() {
|
|
247
|
+
let mute_btn = this.controls.querySelector(".meuuhdia_mute");
|
|
248
|
+
|
|
249
|
+
this.video.muted = !this.video.muted;
|
|
250
|
+
mute_btn.setAttribute("aria-pressed", this.video.muted);
|
|
251
|
+
|
|
252
|
+
this.#setVolumeIcon();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#setVolumeIcon() {
|
|
256
|
+
let volume = this.controls.querySelector(".meuuhdia_volume_action")
|
|
257
|
+
let volume_icon = volume.querySelector("svg");
|
|
258
|
+
let newIcon = null;
|
|
259
|
+
|
|
260
|
+
if(this.video.muted) {
|
|
261
|
+
volume.replaceChild(MeuuhdiaIcon.createIcon("volume_off", "0%"), volume_icon);
|
|
262
|
+
} else {
|
|
263
|
+
switch(this.video.volume) {
|
|
264
|
+
case 0:
|
|
265
|
+
newIcon = "volume_off";
|
|
266
|
+
break;
|
|
267
|
+
case 0.1:
|
|
268
|
+
case 0.2:
|
|
269
|
+
case 0.3:
|
|
270
|
+
newIcon = "volume_low";
|
|
271
|
+
break;
|
|
272
|
+
case 0.4:
|
|
273
|
+
case 0.5:
|
|
274
|
+
case 0.6:
|
|
275
|
+
newIcon = "volume_mid";
|
|
276
|
+
break;
|
|
277
|
+
default:
|
|
278
|
+
newIcon = "volume_high";
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
volume.replaceChild(MeuuhdiaIcon.createIcon(newIcon, this.video.volume * 100 + "%"), volume_icon);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#timeProgress() {
|
|
286
|
+
let progress_container = this.controls.querySelector(".meuuhdia_progress");
|
|
287
|
+
let progress = progress_container.querySelector(".meuuhdia_progress_bar");
|
|
288
|
+
let timetotal_el = progress_container.querySelector(".meuuhdia_timetotal");
|
|
289
|
+
MeuuhdiaTime.populateTimeNode(this.locale, timetotal_el, this.video.duration);
|
|
290
|
+
|
|
291
|
+
this.video.addEventListener("loadedmetadata", () => progress.setAttribute("max", this.video.duration));
|
|
292
|
+
|
|
293
|
+
this.video.addEventListener("timeupdate", () => {
|
|
294
|
+
if (!progress.hasAttribute("max")) progress.setAttribute("max", this.video.duration);
|
|
295
|
+
|
|
296
|
+
let duration_now = MeuuhdiaTime.convertSecondsToDuration(this.locale, this.video.currentTime);
|
|
297
|
+
|
|
298
|
+
progress.value = this.video.currentTime;
|
|
299
|
+
progress_container.querySelector(".meuuhdia_timenow span[aria-hidden]").textContent = duration_now.text;
|
|
300
|
+
progress_container.querySelector(".meuuhdia_timenow .sr-only").textContent = duration_now.label;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
progress.addEventListener("change", (evt) => this.video.currentTime = evt.currentTarget.value);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
#fullscreen() {
|
|
307
|
+
if (document.fullscreenElement !== null) {
|
|
308
|
+
document.exitFullscreen();
|
|
309
|
+
} else {
|
|
310
|
+
this.wrapper.closest(".meuuhdia").requestFullscreen();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#setFullscreenData(state) {
|
|
315
|
+
let fs_btn = this.controls.querySelector(".meuuhdia_fs");
|
|
316
|
+
let fs_icon = fs_btn.querySelector("svg");
|
|
317
|
+
let icons = {
|
|
318
|
+
false: "expand",
|
|
319
|
+
true: "compress"
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
this.wrapper.closest(".meuuhdia").setAttribute("data-fullscreen", !!state);
|
|
323
|
+
fs_btn.setAttribute("aria-pressed", !!state);
|
|
324
|
+
fs_btn.replaceChild(MeuuhdiaIcon.createIcon(icons[state]), fs_icon);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export { MeuuhdiaControls };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { MeuuhdiaIcon } from "./icon.js";
|
|
2
|
+
|
|
3
|
+
export let meuuhdiaDisclosure = {
|
|
4
|
+
init: function() {
|
|
5
|
+
let disclosureActions = document.querySelectorAll(".meuuhdia_disclosure_action");
|
|
6
|
+
|
|
7
|
+
disclosureActions.forEach(btn => {
|
|
8
|
+
btn.addEventListener("click", meuuhdiaDisclosure.toggleDisclosure);
|
|
9
|
+
btn.closest(".meuuhdia_disclosure").addEventListener("focusout", meuuhdiaDisclosure.closeDisclosure);
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* ? Create disclosure component
|
|
15
|
+
*
|
|
16
|
+
* @param {string} actionName
|
|
17
|
+
* @param {string} actionIconName
|
|
18
|
+
* @param {string} actionLabel
|
|
19
|
+
* @param {Array<Node>} contentNodes
|
|
20
|
+
* @param {?string} iconLabel
|
|
21
|
+
* @returns {Node} <li> element
|
|
22
|
+
*/
|
|
23
|
+
createDisclosure: function(actionName, actionIconName, actionLabel, contentNodes, iconLabel = null) {
|
|
24
|
+
let index = document.querySelectorAll("meuuhdia_disclosure").length + 1;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* ? Action
|
|
28
|
+
*/
|
|
29
|
+
let action = document.createElement("button");
|
|
30
|
+
action.setAttribute("id", "meuuhdia_disclosure_action-" + actionName + "-" + index);
|
|
31
|
+
action.setAttribute("type", "button");
|
|
32
|
+
action.setAttribute("aria-expanded", "false");
|
|
33
|
+
action.setAttribute("aria-controls", "meuuhdia_disclosure_content-" + actionName + "-" + index);
|
|
34
|
+
action.classList.add(`meuuhdia_${actionName}_action`, "meuuhdia_disclosure_action");
|
|
35
|
+
|
|
36
|
+
let actionLabelContainer = document.createElement("span");
|
|
37
|
+
actionLabelContainer.classList.add("sr-only");
|
|
38
|
+
actionLabelContainer.textContent = actionLabel;
|
|
39
|
+
|
|
40
|
+
let actionIcon = MeuuhdiaIcon.createIcon(actionIconName, iconLabel);
|
|
41
|
+
|
|
42
|
+
action.appendChild(actionLabelContainer);
|
|
43
|
+
action.appendChild(actionIcon);
|
|
44
|
+
action.addEventListener("click", meuuhdiaDisclosure.toggleDisclosure);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* ? Content
|
|
48
|
+
*/
|
|
49
|
+
let content = document.createElement("div");
|
|
50
|
+
content.setAttribute("id", "meuuhdia_disclosure_content-" + actionName + "-" + index);
|
|
51
|
+
content.classList.add("meuuhdia_disclosure_content", "meuuhdia_" + actionName + "_content");
|
|
52
|
+
|
|
53
|
+
contentNodes.forEach(el => content.appendChild(el));
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ? Caption item
|
|
57
|
+
*/
|
|
58
|
+
let item = document.createElement("li");
|
|
59
|
+
item.classList.add(`meuuhdia_${actionName}`, "meuuhdia_disclosure");
|
|
60
|
+
|
|
61
|
+
item.appendChild(action);
|
|
62
|
+
item.appendChild(content);
|
|
63
|
+
item.addEventListener("focusout", meuuhdiaDisclosure.closeDisclosure);
|
|
64
|
+
|
|
65
|
+
return item;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* ? Toggle disclosure state
|
|
70
|
+
* @function toggleDisclosure
|
|
71
|
+
* @param {Event} evt
|
|
72
|
+
* @returns {void}
|
|
73
|
+
*/
|
|
74
|
+
toggleDisclosure: function(evt) {
|
|
75
|
+
let state = "false" == evt.currentTarget.getAttribute("aria-expanded");
|
|
76
|
+
|
|
77
|
+
evt.currentTarget.setAttribute("aria-expanded", state);
|
|
78
|
+
document.getElementById(evt.currentTarget.getAttribute("aria-controls")).style.display = state ? "inline-flex" : "none";
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* ? Close disclosure
|
|
83
|
+
* @function closeDisclosure
|
|
84
|
+
* @param {Event} evt
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
87
|
+
closeDisclosure: function(evt) {
|
|
88
|
+
if(evt.relatedTarget) {
|
|
89
|
+
let inDisclosure = evt.relatedTarget.closest(".meuuhdia_disclosure") === evt.currentTarget.closest(".meuuhdia_disclosure");
|
|
90
|
+
|
|
91
|
+
if(!inDisclosure) {
|
|
92
|
+
let disclosure = evt.target.closest(".meuuhdia_disclosure");
|
|
93
|
+
|
|
94
|
+
disclosure.querySelector(".meuuhdia_disclosure_action").setAttribute("aria-expanded", "false");
|
|
95
|
+
disclosure.querySelector(".meuuhdia_disclosure_content").style.display = "none";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
package/modules/icon.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { icons } from "../icons.js";
|
|
2
|
+
|
|
3
|
+
class MeuuhdiaIcon {
|
|
4
|
+
/**
|
|
5
|
+
* ? Create SVG icon
|
|
6
|
+
*
|
|
7
|
+
* @param {string} name
|
|
8
|
+
* @returns {Node} <svg> element
|
|
9
|
+
*/
|
|
10
|
+
static createIcon(name, label = null) {
|
|
11
|
+
const xmlns = "http://www.w3.org/2000/svg";
|
|
12
|
+
let svg = document.createElementNS(xmlns, 'svg');
|
|
13
|
+
svg.setAttribute("xmlns", xmlns);
|
|
14
|
+
svg.setAttribute("viewBox", "0 0 400 400");
|
|
15
|
+
|
|
16
|
+
if(label) {
|
|
17
|
+
svg.setAttribute("aria-hidden", "false");
|
|
18
|
+
svg.setAttribute("aria-label", label);
|
|
19
|
+
svg.setAttribute("role", "img");
|
|
20
|
+
} else svg.setAttribute("aria-hidden", "true");
|
|
21
|
+
|
|
22
|
+
let path = document.createElementNS(xmlns, "path");
|
|
23
|
+
path.setAttribute("d", icons[name]);
|
|
24
|
+
path.setAttribute("class", "meuuhdia_icon-path");
|
|
25
|
+
|
|
26
|
+
svg.appendChild(path);
|
|
27
|
+
|
|
28
|
+
return svg;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { MeuuhdiaIcon };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { MeuuhdiaControls } from "./controls.js";
|
|
2
|
+
import { locale } from "../locale/fr.js";
|
|
3
|
+
|
|
4
|
+
class MeuuhdiaPlayer {
|
|
5
|
+
/**
|
|
6
|
+
* @type {Boolean}
|
|
7
|
+
*/
|
|
8
|
+
static #supportsVideo = !!document.createElement("video").canPlayType;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @type {Object}
|
|
12
|
+
*/
|
|
13
|
+
locale;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @type {Boolean}
|
|
17
|
+
*/
|
|
18
|
+
meuuhdiaIsInit = false;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @type {Node}
|
|
22
|
+
*/
|
|
23
|
+
meuuhdia;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @type {Node}
|
|
27
|
+
*/
|
|
28
|
+
wrapper;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @type {Node}
|
|
32
|
+
*/
|
|
33
|
+
video;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @type {Node}
|
|
37
|
+
*/
|
|
38
|
+
controls;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* ? MeuuhdiaPlayer constructor
|
|
42
|
+
*
|
|
43
|
+
* @param {Node} meuuhdia
|
|
44
|
+
*/
|
|
45
|
+
constructor(meuuhdia) {
|
|
46
|
+
this.meuuhdia = meuuhdia;
|
|
47
|
+
this.locale = locale;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* ? Initialise player
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
initPlayer() {
|
|
55
|
+
let video_origin = this.meuuhdia ? this.meuuhdia.querySelector("video") : null;
|
|
56
|
+
|
|
57
|
+
if(MeuuhdiaPlayer.#supportsVideo && video_origin && 0 < video_origin.querySelectorAll("source").length) {
|
|
58
|
+
if(video_origin.readyState) {
|
|
59
|
+
if(!this.meuuhdiaIsInit) this.#createMeuuhdiaWrapper();
|
|
60
|
+
else this.#displayPlayer();
|
|
61
|
+
} else video_origin.addEventListener("loadeddata", this.initPlayer.bind(this));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* ? Create player wrapper
|
|
67
|
+
* @private
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
70
|
+
#createMeuuhdiaWrapper() {
|
|
71
|
+
this.wrapper = this.meuuhdia.querySelector(".meuuhdia_wrapper");
|
|
72
|
+
this.wrapper.setAttribute("lang", this.locale.code);
|
|
73
|
+
|
|
74
|
+
this.video = this.meuuhdia.querySelector("video");
|
|
75
|
+
this.video.querySelectorAll("track").forEach(track => track.id = track.getAttribute("kind") + "_" + track.getAttribute("srclang"));
|
|
76
|
+
|
|
77
|
+
let Controls = new MeuuhdiaControls(this.locale, this.wrapper, this.video);
|
|
78
|
+
this.controls = Controls.controls;
|
|
79
|
+
|
|
80
|
+
this.wrapper.appendChild(this.controls);
|
|
81
|
+
|
|
82
|
+
this.meuuhdiaIsInit = true;
|
|
83
|
+
this.#displayPlayer();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#displayPlayer() {
|
|
87
|
+
if(this.video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
|
|
88
|
+
this.video.controls = false;
|
|
89
|
+
this.wrapper.style.maxWidth = this.video.videoWidth + "px";
|
|
90
|
+
this.video.classList.add("meuuhdia_video");
|
|
91
|
+
} else this.video.addEventListener("loadeddata", this.#displayPlayer.bind(this));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { MeuuhdiaPlayer };
|
package/modules/time.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
class MeuuhdiaTime {
|
|
2
|
+
static populateTimeNode(locale, timeNode, seconds = null, duration_text = null) {
|
|
3
|
+
let duration = null !== seconds ? MeuuhdiaTime.convertSecondsToDuration(locale, seconds) : null;
|
|
4
|
+
let time_numeric = document.createElement("span");
|
|
5
|
+
time_numeric.setAttribute("aria-hidden", true);
|
|
6
|
+
time_numeric.textContent = duration ? duration.text : duration_text;
|
|
7
|
+
timeNode.appendChild(time_numeric);
|
|
8
|
+
|
|
9
|
+
let time_accname = document.createElement("span");
|
|
10
|
+
time_accname.classList.add("sr-only");
|
|
11
|
+
time_accname.textContent = duration ? duration.label : MeuuhdiaTime.getDurationLabel(locale, duration_text.split(":"));
|
|
12
|
+
timeNode.appendChild(time_accname);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static convertSecondsToDuration(locale, seconds) {
|
|
16
|
+
let slice = 3600 <= seconds ? 11 : 14;
|
|
17
|
+
let duration_ISO = new Date(Math.round(seconds) * 1000).toISOString();
|
|
18
|
+
let duration_text = duration_ISO.slice(slice, 19);
|
|
19
|
+
let duration_datetime = "PT" + duration_ISO.slice(11, 19);
|
|
20
|
+
let duration_label = MeuuhdiaTime.getDurationLabel(locale, duration_text.split(":"));
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
text: duration_text,
|
|
24
|
+
label: duration_label,
|
|
25
|
+
datetime: duration_datetime
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static getDurationLabel(locale, duration_obj) {
|
|
30
|
+
let response = "";
|
|
31
|
+
|
|
32
|
+
if(3 === duration_obj.length) {
|
|
33
|
+
let round = Math.round(duration_obj[0]);
|
|
34
|
+
|
|
35
|
+
if(0 < round) {
|
|
36
|
+
response += round + " " + (1 < round ? locale.datetime.hour.plur : locale.datetime.hour.sing) + ", ";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
duration_obj.forEach((el, index) => {
|
|
41
|
+
let key = 0 === index ? "minute" : "second";
|
|
42
|
+
let round = Math.round(el);
|
|
43
|
+
|
|
44
|
+
response += round + " " + (1 < round ? locale.datetime[key].plur : locale.datetime[key].sing);
|
|
45
|
+
|
|
46
|
+
if(0 === index) response += ", ";
|
|
47
|
+
}, this);
|
|
48
|
+
|
|
49
|
+
return response;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static convertTextToSeconds(duration_text) {
|
|
53
|
+
let timeObj = duration_text.split(":");
|
|
54
|
+
|
|
55
|
+
while(1 < timeObj.length) {
|
|
56
|
+
let add = parseInt(timeObj.shift()) * 60;
|
|
57
|
+
timeObj[0] = add + parseInt(timeObj[0]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return timeObj[0];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { MeuuhdiaTime };
|