fcad-core-dragon 2.0.0-beta.5 → 2.0.0-beta.6
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/.editorconfig +33 -33
- package/.eslintignore +29 -29
- package/.eslintrc.cjs +81 -81
- package/CHANGELOG +392 -377
- package/README.md +71 -71
- package/bk.scss +117 -117
- package/package.json +61 -61
- package/src/$locales/en.json +23 -25
- package/src/$locales/fr.json +22 -23
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +166 -99
- package/src/components/AppBaseButton.vue +2 -0
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +164 -106
- package/src/components/AppBasePage.vue +14 -4
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppCompAudio.vue +20 -48
- package/src/components/AppCompBranchButtons.vue +7 -53
- package/src/components/{AppCompTranscript.vue → AppCompContainer.vue} +8 -1
- package/src/components/AppCompInputRadioNext.vue +152 -152
- package/src/components/AppCompInputTextToFillNext.vue +171 -171
- package/src/components/AppCompJauge.vue +74 -74
- package/src/components/AppCompMenu.vue +429 -428
- package/src/components/AppCompMenuItem.vue +228 -228
- package/src/components/AppCompNavigation.vue +2 -2
- package/src/components/AppCompPlayBarNext.vue +5 -0
- package/src/components/AppCompPlayBarProgress.vue +82 -82
- package/src/components/AppCompSVGNext.vue +2 -3
- package/src/components/AppCompSettingsMenu.vue +172 -172
- package/src/components/AppCompVideoPlayer.vue +17 -15
- package/src/composables/useQuiz.js +206 -206
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +34 -29
- package/src/mixins/$mediaMixins.js +819 -819
- package/src/mixins/timerMixin.js +155 -155
- package/src/module/stores/appStore.js +1 -1
- package/src/module/xapi/ADL.js +144 -4
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper copy.js +1963 -0
- package/src/module/xapi/wrapper.js +121 -188
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/bus.js +8 -8
- package/src/plugins/gsap.js +14 -14
- package/src/plugins/helper.js +0 -1
- package/src/plugins/i18n.js +44 -44
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +2 -1
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +210 -210
- package/src/shared/validators.js +2 -0
- package/src/components/AppCompPlayBar.vue +0 -1217
|
@@ -1,1217 +0,0 @@
|
|
|
1
|
-
<!--@ Description: This component is used to display the playbar associate with the videoPlayer
|
|
2
|
-
@ What it does: The component is used to interacted with the videoPlayer or audioPlayer via buttons.
|
|
3
|
-
mediaMixins is used for all the methods/data shared between audio and video. In the appCompPlayBar component, there is also all the methods/data specific to video-->
|
|
4
|
-
<template>
|
|
5
|
-
<div
|
|
6
|
-
ref="$pb-container"
|
|
7
|
-
class="pb-container"
|
|
8
|
-
:class="{ video: mediaToPlay.mType === 'video' }"
|
|
9
|
-
>
|
|
10
|
-
<!--------------------------------- Btn Play Principal (video only) -------------------------------------->
|
|
11
|
-
<app-base-button
|
|
12
|
-
v-if="mediaToPlay.mType === 'video'"
|
|
13
|
-
ref="$btn-play-playback"
|
|
14
|
-
class="playback-button"
|
|
15
|
-
:class="{
|
|
16
|
-
initial: !playClicked,
|
|
17
|
-
'playback-animation': playBackAnim && playClicked
|
|
18
|
-
}"
|
|
19
|
-
:aria-label="playLabel + ' principal'"
|
|
20
|
-
:data-title="`${playLabel}`"
|
|
21
|
-
@click="
|
|
22
|
-
() => {
|
|
23
|
-
mediaToPlay.mType === 'video'
|
|
24
|
-
? handlePlayVideo('playBackAnim')
|
|
25
|
-
: togglePlay()
|
|
26
|
-
}
|
|
27
|
-
"
|
|
28
|
-
>
|
|
29
|
-
<div
|
|
30
|
-
v-show="(!playClicked && !canReplay) || (!canReplay && isPlaying)"
|
|
31
|
-
class="playback-wrapper"
|
|
32
|
-
>
|
|
33
|
-
<svg class="app-icons-svg play-icon">
|
|
34
|
-
<use href="#play-icon" />
|
|
35
|
-
</svg>
|
|
36
|
-
</div>
|
|
37
|
-
<div
|
|
38
|
-
v-show="playClicked && !isPlaying && !canReplay"
|
|
39
|
-
class="playback-wrapper"
|
|
40
|
-
>
|
|
41
|
-
<svg class="app-icons-svg pause-icon">
|
|
42
|
-
<use href="#pause-icon" />
|
|
43
|
-
</svg>
|
|
44
|
-
</div>
|
|
45
|
-
<div v-show="!playClicked && canReplay" class="playback-wrapper">
|
|
46
|
-
<svg class="app-icons-svg replay-icon">
|
|
47
|
-
<use href="#replay-icon" />
|
|
48
|
-
</svg>
|
|
49
|
-
</div>
|
|
50
|
-
</app-base-button>
|
|
51
|
-
|
|
52
|
-
<div
|
|
53
|
-
v-if="mediaToPlay"
|
|
54
|
-
class="pb-wrapper"
|
|
55
|
-
:class="{
|
|
56
|
-
'show-controls': showControlsValue || mediaToPlay.mType === 'audio',
|
|
57
|
-
audio: mediaToPlay.mType === 'audio',
|
|
58
|
-
video: mediaToPlay.mType === 'video'
|
|
59
|
-
}"
|
|
60
|
-
>
|
|
61
|
-
<!--------------------------------- PLAY-BAR Progress (video only) -------------------------------------->
|
|
62
|
-
<div
|
|
63
|
-
v-if="mediaToPlay.mType === 'video'"
|
|
64
|
-
ref="$playbar-timeline"
|
|
65
|
-
class="pb-timeline"
|
|
66
|
-
>
|
|
67
|
-
<div ref="$progress-area" class="progress-area">
|
|
68
|
-
<div
|
|
69
|
-
ref="$progress-bar"
|
|
70
|
-
draggable="false"
|
|
71
|
-
tabindex="0"
|
|
72
|
-
class="pb-progress-bar"
|
|
73
|
-
role="slider"
|
|
74
|
-
aria-valuemin="0"
|
|
75
|
-
:aria-label="mediaA11Y.label"
|
|
76
|
-
:aria-valuenow="mediaA11Y.valNow"
|
|
77
|
-
:aria-valuemax="mediaA11Y.valMax"
|
|
78
|
-
:aria-valuetext="mediaA11Y.valueText"
|
|
79
|
-
@focus="changeFocusState('progressBar', true)"
|
|
80
|
-
@blur="changeFocusState('progressBar', false)"
|
|
81
|
-
>
|
|
82
|
-
<!--Class progress-animation is apply when we are not using the thumb to change the progression-->
|
|
83
|
-
<div
|
|
84
|
-
ref="$progress-indicator"
|
|
85
|
-
draggable="false"
|
|
86
|
-
class="progress-indicator"
|
|
87
|
-
:class="{ 'progress-animation': !progressThumbDown }"
|
|
88
|
-
:style="{ width: progressBarPercentage + '%' }"
|
|
89
|
-
>
|
|
90
|
-
<!--Mousedown validate if the thumb is cliqued (if yes, the mouse is follow to modify the progress)
|
|
91
|
-
MouseOver/MouseOut deactivate click event on progressBar if the mouse hovered the thumb-->
|
|
92
|
-
<div
|
|
93
|
-
ref="$progress-thumb"
|
|
94
|
-
draggable="false"
|
|
95
|
-
class="progress-thumb"
|
|
96
|
-
style="z-index: 9"
|
|
97
|
-
@mousedown="
|
|
98
|
-
function () {
|
|
99
|
-
savedIsPlaying = isPlaying
|
|
100
|
-
progressThumbDown = true
|
|
101
|
-
}
|
|
102
|
-
"
|
|
103
|
-
@mouseover="progressThumbHover = true"
|
|
104
|
-
@mouseleave="progressThumbHover = false"
|
|
105
|
-
></div>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
<!--------------------------------- PLAY-BAR ToolTip (video only) ----------------------------------->
|
|
109
|
-
<div
|
|
110
|
-
v-if="mediaToPlay.mType === 'video'"
|
|
111
|
-
ref="$seek-tooltip"
|
|
112
|
-
aria-hidden="true"
|
|
113
|
-
class="seek-tooltip"
|
|
114
|
-
>
|
|
115
|
-
{{ tooTipTimeCode }}
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
<div class="pb-controls">
|
|
120
|
-
<div class="pb-areas left">
|
|
121
|
-
<!--------------------------------- PLAY-BAR Play ------------------------------------>
|
|
122
|
-
<app-base-button
|
|
123
|
-
ref="$btn-play"
|
|
124
|
-
class="playbar-btn playbar-play"
|
|
125
|
-
:class="{ displayLabel: btnsLabelDisplay['playbar-play'] === true }"
|
|
126
|
-
:aria-label="
|
|
127
|
-
playLabel + (mediaToPlay.mType === 'video' ? ' secondaire' : '')
|
|
128
|
-
"
|
|
129
|
-
:data-title="`${playLabel} (k)`"
|
|
130
|
-
@click="
|
|
131
|
-
() => {
|
|
132
|
-
mediaToPlay.mType === 'video' ? handlePlayVideo() : togglePlay()
|
|
133
|
-
}
|
|
134
|
-
"
|
|
135
|
-
@mouseenter="activateLabel($event)"
|
|
136
|
-
@mouseleave="deactivateLabel($event)"
|
|
137
|
-
@focus="activateLabel($event)"
|
|
138
|
-
@blur="deactivateLabel($event)"
|
|
139
|
-
>
|
|
140
|
-
<svg
|
|
141
|
-
v-show="!canReplay && !isPlaying"
|
|
142
|
-
class="app-icons-svg play-icon"
|
|
143
|
-
>
|
|
144
|
-
<use href="#play-icon" />
|
|
145
|
-
</svg>
|
|
146
|
-
<svg
|
|
147
|
-
v-show="isPlaying && !canReplay"
|
|
148
|
-
class="app-icons-svg pause-icon"
|
|
149
|
-
>
|
|
150
|
-
<use href="#pause-icon" />
|
|
151
|
-
</svg>
|
|
152
|
-
<svg v-show="canReplay" class="app-icons-svg replay-icon">
|
|
153
|
-
<use href="#replay-icon" />
|
|
154
|
-
</svg>
|
|
155
|
-
</app-base-button>
|
|
156
|
-
<!--------------------------------- PLAY-BAR Timer ------------------------------------>
|
|
157
|
-
<div class="pb-timer">
|
|
158
|
-
<span
|
|
159
|
-
aria-hidden="true"
|
|
160
|
-
style="-webkit-user-select: none"
|
|
161
|
-
draggable="false"
|
|
162
|
-
>
|
|
163
|
-
{{ timecode }} / {{ mediaDurationTime }}
|
|
164
|
-
</span>
|
|
165
|
-
</div>
|
|
166
|
-
<!--------------------------------- PLAY-BAR Progress (audio only) -------------------------------------->
|
|
167
|
-
<div
|
|
168
|
-
v-if="mediaToPlay.mType === 'audio'"
|
|
169
|
-
ref="$playbar-timeline"
|
|
170
|
-
class="pb-timeline"
|
|
171
|
-
>
|
|
172
|
-
<div ref="$progress-area" class="progress-area">
|
|
173
|
-
<div
|
|
174
|
-
id="progress-bar"
|
|
175
|
-
ref="$progress-bar"
|
|
176
|
-
draggable="false"
|
|
177
|
-
tabindex="0"
|
|
178
|
-
class="pb-progress-bar"
|
|
179
|
-
role="slider"
|
|
180
|
-
aria-valuemin="0"
|
|
181
|
-
:aria-label="mediaA11Y.label"
|
|
182
|
-
:aria-valuenow="mediaA11Y.valNow"
|
|
183
|
-
:aria-valuemax="mediaA11Y.valMax"
|
|
184
|
-
:aria-valuetext="mediaA11Y.valueText"
|
|
185
|
-
@focus="changeFocusState('progressBar', true)"
|
|
186
|
-
@blur="changeFocusState('progressBar', false)"
|
|
187
|
-
>
|
|
188
|
-
<!--Class progress-animation is apply when we are not using the thumb to change the progression-->
|
|
189
|
-
<div
|
|
190
|
-
id="progress-indicator"
|
|
191
|
-
ref="$progress-indicator"
|
|
192
|
-
draggable="false"
|
|
193
|
-
class="progress-indicator"
|
|
194
|
-
:class="{ 'progress-animation': !progressThumbDown }"
|
|
195
|
-
:style="{ width: progressBarPercentage + '%' }"
|
|
196
|
-
>
|
|
197
|
-
<!--Mousedown validate if the thumb is cliqued (if yes, the mouse is follow to modify the progress)
|
|
198
|
-
MouseOver/MouseOut deactivate click event on progressBar if the mouse hovered the thumb-->
|
|
199
|
-
<div
|
|
200
|
-
ref="$progress-thumb"
|
|
201
|
-
draggable="false"
|
|
202
|
-
class="progress-thumb"
|
|
203
|
-
style="z-index: 9"
|
|
204
|
-
@mousedown="
|
|
205
|
-
function () {
|
|
206
|
-
savedIsPlaying = isPlaying
|
|
207
|
-
progressThumbDown = true
|
|
208
|
-
}
|
|
209
|
-
"
|
|
210
|
-
@mouseover="progressThumbHover = true"
|
|
211
|
-
@mouseleave="progressThumbHover = false"
|
|
212
|
-
></div>
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
215
|
-
</div>
|
|
216
|
-
</div>
|
|
217
|
-
<!--------------------------------- PLAY-BAR Volume -------------------------------------->
|
|
218
|
-
<app-base-button
|
|
219
|
-
id="playbar-volume"
|
|
220
|
-
ref="$btn-volume"
|
|
221
|
-
class="volume-btn"
|
|
222
|
-
:class="{
|
|
223
|
-
displayLabel: btnsLabelDisplay['playbar-volume'] === true
|
|
224
|
-
}"
|
|
225
|
-
tabindex="0"
|
|
226
|
-
:aria-label="volumeLabel"
|
|
227
|
-
:data-title="`${volumeLabel} (m)`"
|
|
228
|
-
@click="toggleMute"
|
|
229
|
-
@mouseenter="activateLabel($event)"
|
|
230
|
-
@mouseleave="deactivateLabel($event)"
|
|
231
|
-
@focus="activateLabel($event)"
|
|
232
|
-
@blur="deactivateLabel($event)"
|
|
233
|
-
>
|
|
234
|
-
<svg v-show="volumeState == 'muted'" class="app-icons-svg">
|
|
235
|
-
<use href="#volume-mute-icon" />
|
|
236
|
-
</svg>
|
|
237
|
-
<svg v-show="volumeState == 'high'" class="app-icons-svg">
|
|
238
|
-
<use href="#volume-high-icon" />
|
|
239
|
-
</svg>
|
|
240
|
-
<svg
|
|
241
|
-
v-show="volumeState == 'low'"
|
|
242
|
-
class="app-icons-svg volume-low-icon"
|
|
243
|
-
>
|
|
244
|
-
<use href="#volume-low-icon" />
|
|
245
|
-
</svg>
|
|
246
|
-
</app-base-button>
|
|
247
|
-
<div ref="zone-volume" class="volume-controls">
|
|
248
|
-
<input
|
|
249
|
-
ref="$volume-slider"
|
|
250
|
-
class="volume-slider"
|
|
251
|
-
:class="{
|
|
252
|
-
displayLabel: btnsLabelDisplay['volume-slider'] === true
|
|
253
|
-
}"
|
|
254
|
-
:value="currentVolume"
|
|
255
|
-
:aria-valuetext="volumeLevelA11Y"
|
|
256
|
-
type="range"
|
|
257
|
-
max="1"
|
|
258
|
-
min="0"
|
|
259
|
-
step="0.01"
|
|
260
|
-
tabindex="0"
|
|
261
|
-
:aria-label="`${$t('button.volume')}`"
|
|
262
|
-
@click="updateVolumeLevel"
|
|
263
|
-
@mouseenter="activateLabel($event.currentTarget)"
|
|
264
|
-
@mouseleave="deactivateLabel($event.currentTarget)"
|
|
265
|
-
@focus="
|
|
266
|
-
activateLabel($event.currentTarget),
|
|
267
|
-
changeFocusState('volumeSlider', true)
|
|
268
|
-
"
|
|
269
|
-
@blur="
|
|
270
|
-
deactivateLabel($event.currentTarget),
|
|
271
|
-
changeFocusState('volumeSlider', false)
|
|
272
|
-
"
|
|
273
|
-
/>
|
|
274
|
-
<span class="volume-label" aria-hidden="true">
|
|
275
|
-
{{ $t('button.volume') }}
|
|
276
|
-
</span>
|
|
277
|
-
<!--using span because ::before on input is not supported by firefox-->
|
|
278
|
-
<span
|
|
279
|
-
ref="$volume-progress"
|
|
280
|
-
class="volume-progress"
|
|
281
|
-
:style="{
|
|
282
|
-
'--background-size-vs': `calc(${volumeSliderBackground} - 2px)`
|
|
283
|
-
}"
|
|
284
|
-
></span>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
<!--------------------------------- PLAY-BAR CC (video only) ------------------------------------------>
|
|
288
|
-
<div v-if="mediaToPlay.mType === 'video'" class="pb-areas right">
|
|
289
|
-
<app-base-button
|
|
290
|
-
id="btn-subtitles"
|
|
291
|
-
ref="$btn-subtitle"
|
|
292
|
-
class="btn subtitleBtns"
|
|
293
|
-
:aria-label="ccLabel"
|
|
294
|
-
:data-title="`${ccLabel} (c)`"
|
|
295
|
-
:aria-disabled="!hasSubtitle"
|
|
296
|
-
:class="{
|
|
297
|
-
md_disabled: !hasSubtitle,
|
|
298
|
-
displayLabel: btnsLabelDisplay['btn-subtitles'] === true
|
|
299
|
-
}"
|
|
300
|
-
@click="toggleViewSubtitle()"
|
|
301
|
-
@mouseenter="activateLabel($event)"
|
|
302
|
-
@mouseleave="deactivateLabel($event)"
|
|
303
|
-
@focus="activateLabel($event)"
|
|
304
|
-
@blur="deactivateLabel($event)"
|
|
305
|
-
>
|
|
306
|
-
<svg
|
|
307
|
-
v-show="(hasSubtitle && !subtitlesEnabled) || !hasSubtitle"
|
|
308
|
-
class="app-icons-svg"
|
|
309
|
-
>
|
|
310
|
-
<use href="#subtitle-off-icon" />
|
|
311
|
-
</svg>
|
|
312
|
-
<svg v-show="hasSubtitle && subtitlesEnabled" class="app-icons-svg">
|
|
313
|
-
<use href="#subtitle-on-icon" />
|
|
314
|
-
</svg>
|
|
315
|
-
</app-base-button>
|
|
316
|
-
<!--------------------------------- PLAY-BAR Transcript ----------------------------------->
|
|
317
|
-
<app-base-button
|
|
318
|
-
id="btn-transcript"
|
|
319
|
-
ref="$btn-transcript"
|
|
320
|
-
class="btn-transcript"
|
|
321
|
-
:aria-label="transcriptLabel"
|
|
322
|
-
:data-title="`${transcriptLabel} (t)`"
|
|
323
|
-
:aria-disabled="!hasTranscript || fullscreenOn"
|
|
324
|
-
:class="{
|
|
325
|
-
md_disabled: !hasTranscript || fullscreenOn,
|
|
326
|
-
displayLabel: btnsLabelDisplay['btn-transcript'] === true
|
|
327
|
-
}"
|
|
328
|
-
@click="toggleViewTranscript()"
|
|
329
|
-
@mouseenter="activateLabel($event)"
|
|
330
|
-
@mouseleave="deactivateLabel($event)"
|
|
331
|
-
@focus="activateLabel($event)"
|
|
332
|
-
@blur="deactivateLabel($event)"
|
|
333
|
-
>
|
|
334
|
-
<svg
|
|
335
|
-
v-show="(hasTranscript && !transcriptEnabled) || !hasTranscript"
|
|
336
|
-
class="app-icons-svg"
|
|
337
|
-
>
|
|
338
|
-
<use href="#transcript-off-icon" />
|
|
339
|
-
</svg>
|
|
340
|
-
<svg
|
|
341
|
-
v-show="hasTranscript && transcriptEnabled"
|
|
342
|
-
class="app-icons-svg"
|
|
343
|
-
>
|
|
344
|
-
<use href="#transcript-on-icon" />
|
|
345
|
-
</svg>
|
|
346
|
-
</app-base-button>
|
|
347
|
-
<!--------------------------------- PLAY-BAR FullScreen ----------------------------------->
|
|
348
|
-
<app-base-button
|
|
349
|
-
id="btn-fullscreen"
|
|
350
|
-
ref="$btn-fullscreen"
|
|
351
|
-
class="fullscreenBtns"
|
|
352
|
-
:aria-label="fullscreenLabel"
|
|
353
|
-
:aria-disabled="transcriptEnabled"
|
|
354
|
-
:class="{
|
|
355
|
-
md_disabled: transcriptEnabled,
|
|
356
|
-
displayLabel: btnsLabelDisplay['btn-fullscreen'] === true
|
|
357
|
-
}"
|
|
358
|
-
:data-title="`${fullscreenLabel} (f)`"
|
|
359
|
-
@click="toggleFullScreen()"
|
|
360
|
-
@mouseenter="activateLabel($event)"
|
|
361
|
-
@mouseleave="deactivateLabel($event)"
|
|
362
|
-
@focus="activateLabel($event)"
|
|
363
|
-
@blur="deactivateLabel($event)"
|
|
364
|
-
>
|
|
365
|
-
<svg v-show="!fullscreenOn" class="app-icons-svg">
|
|
366
|
-
<use href="#fullscreen-icon" />
|
|
367
|
-
</svg>
|
|
368
|
-
<svg v-show="fullscreenOn" class="app-icons-svg">
|
|
369
|
-
<use href="#fullscreen-exit-icon" />
|
|
370
|
-
</svg>
|
|
371
|
-
</app-base-button>
|
|
372
|
-
</div>
|
|
373
|
-
</div>
|
|
374
|
-
</div>
|
|
375
|
-
</div>
|
|
376
|
-
</template>
|
|
377
|
-
|
|
378
|
-
<script>
|
|
379
|
-
import $extendsMedia from '../mixins/$mediaMixins'
|
|
380
|
-
import { mapState, mapActions } from 'pinia'
|
|
381
|
-
import { useAppStore } from '../module/stores/appStore'
|
|
382
|
-
import axios from 'axios'
|
|
383
|
-
|
|
384
|
-
export default {
|
|
385
|
-
mixins: [$extendsMedia],
|
|
386
|
-
props: {
|
|
387
|
-
mediaToPlay: { type: [Object, Boolean], default: false }
|
|
388
|
-
},
|
|
389
|
-
emits: ['resize-video'],
|
|
390
|
-
|
|
391
|
-
data() {
|
|
392
|
-
return {
|
|
393
|
-
id: `plyr_${this.mediaToPlay.id}`,
|
|
394
|
-
//Playback animation
|
|
395
|
-
playClicked: false,
|
|
396
|
-
playBackAnim: true,
|
|
397
|
-
//ProgressSeek
|
|
398
|
-
progressSeek: null,
|
|
399
|
-
//Tooltip
|
|
400
|
-
seekTooltip: null,
|
|
401
|
-
tooTipTimeCode: '00:00',
|
|
402
|
-
//Hiding playbar animation
|
|
403
|
-
focusTimeout: null,
|
|
404
|
-
delayUntilHide: 5000,
|
|
405
|
-
hideTimer: null,
|
|
406
|
-
//Transcript
|
|
407
|
-
transcriptEnabled: false,
|
|
408
|
-
transcriptToShow: null,
|
|
409
|
-
//Fullscreen
|
|
410
|
-
fullscreenOn: false
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
computed: {
|
|
414
|
-
...mapState(useAppStore, ['getCurrentBrowser', 'getMediaSubtitles']),
|
|
415
|
-
//Transcript
|
|
416
|
-
hasTranscript() {
|
|
417
|
-
if (!this.mediaToPlay) return
|
|
418
|
-
return this.mediaToPlay.mTranscript || false
|
|
419
|
-
},
|
|
420
|
-
//Subtitle
|
|
421
|
-
hasSubtitle() {
|
|
422
|
-
if (!this.mediaToPlay) return
|
|
423
|
-
return this.mediaToPlay.mSubtitles || false
|
|
424
|
-
},
|
|
425
|
-
subtitlesEnabled() {
|
|
426
|
-
if (!this.hasSubtitle) return false
|
|
427
|
-
else return this.getMediaSubtitles
|
|
428
|
-
},
|
|
429
|
-
//mediaContainer (only used for video)
|
|
430
|
-
mediaContainer() {
|
|
431
|
-
if (!this.mediaToPlay) return
|
|
432
|
-
return this.mediaToPlay.mMediaContainer
|
|
433
|
-
},
|
|
434
|
-
|
|
435
|
-
//MediaDuration :Number (used for tooltip)
|
|
436
|
-
mediaDuration() {
|
|
437
|
-
return this.mediaToPlay.mElement.duration
|
|
438
|
-
? this.mediaToPlay.mElement.duration
|
|
439
|
-
: 0
|
|
440
|
-
},
|
|
441
|
-
//Labels
|
|
442
|
-
fullscreenLabel() {
|
|
443
|
-
let label = null
|
|
444
|
-
if (this.fullscreenOn) label = `${this.$t('button.full_screen_off')}`
|
|
445
|
-
else label = `${this.$t('button.full_screen_on')}`
|
|
446
|
-
return label
|
|
447
|
-
},
|
|
448
|
-
transcriptLabel() {
|
|
449
|
-
let label = null
|
|
450
|
-
if (this.transcriptEnabled) label = `${this.$t('button.transcript_off')}`
|
|
451
|
-
else label = `${this.$t('button.transcript_on')}`
|
|
452
|
-
return label
|
|
453
|
-
},
|
|
454
|
-
ccLabel() {
|
|
455
|
-
let label = null
|
|
456
|
-
if (this.subtitlesEnabled) label = `${this.$t('button.subtitle_off')}`
|
|
457
|
-
else label = `${this.$t('button.subtitle_on')}`
|
|
458
|
-
return label
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
mounted() {
|
|
462
|
-
//initialize media $refs
|
|
463
|
-
this.initializeMediaElm()
|
|
464
|
-
//initialize video $refs
|
|
465
|
-
if (this.mediaToPlay.mType === 'video') this.initializeVideoElm()
|
|
466
|
-
|
|
467
|
-
//Set eventlistener for media
|
|
468
|
-
this.setMediaHandlers()
|
|
469
|
-
//Set eventlistener for video
|
|
470
|
-
if (this.mediaToPlay.mType === 'video') this.setVideoHandlers()
|
|
471
|
-
|
|
472
|
-
//Set label for ProgressBar
|
|
473
|
-
this.setProgressBarA11Y()
|
|
474
|
-
//Set initial volume and duration
|
|
475
|
-
this.updateVolumeLevel()
|
|
476
|
-
|
|
477
|
-
//update the the information of the media element linked to this play bar. playbar instance will be added to the media info
|
|
478
|
-
this.updateCurrentMediaElements({
|
|
479
|
-
id: this.mediaToPlay.id,
|
|
480
|
-
...this.$parent.$refs
|
|
481
|
-
})
|
|
482
|
-
},
|
|
483
|
-
beforeUnmount() {
|
|
484
|
-
if (this.isPlaying) {
|
|
485
|
-
this.togglePlay()
|
|
486
|
-
}
|
|
487
|
-
this.removeMediaHandlers()
|
|
488
|
-
|
|
489
|
-
if (this.mediaToPlay.mType === 'video') this.removeVideoHandlers()
|
|
490
|
-
|
|
491
|
-
if (this.firefoxTrack) {
|
|
492
|
-
this.firefoxTrack.removeEventListener('cuechange', this.fireFoxMoveCC)
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
this.$bus.$off('transcript-hidden', this.resetTranscript)
|
|
496
|
-
},
|
|
497
|
-
methods: {
|
|
498
|
-
...mapActions(useAppStore, [
|
|
499
|
-
'setMediaSubtitles',
|
|
500
|
-
'updateCurrentMediaElements'
|
|
501
|
-
]),
|
|
502
|
-
/**
|
|
503
|
-
* @description - Set the DOM elements specific for video
|
|
504
|
-
*/
|
|
505
|
-
initializeVideoElm() {
|
|
506
|
-
//SeekTooltip
|
|
507
|
-
this.seekTooltip = this.$refs['$seek-tooltip']
|
|
508
|
-
//Transcript
|
|
509
|
-
this.$bus.$on('transcript-hidden', this.resetTranscript)
|
|
510
|
-
this.setTranscript()
|
|
511
|
-
},
|
|
512
|
-
|
|
513
|
-
/** "@description -update the information of the seek tooltip*/
|
|
514
|
-
updateSeekTooltip(evt) {
|
|
515
|
-
//Elm progress Bar
|
|
516
|
-
const progressBar = this.progressBar
|
|
517
|
-
//progressBar position on the page
|
|
518
|
-
const left = progressBar.getBoundingClientRect().left
|
|
519
|
-
//Mouse position versus progressBar
|
|
520
|
-
const hoverPos = evt.clientX - left
|
|
521
|
-
//Get progressBar offsetWidth
|
|
522
|
-
const width = progressBar.offsetWidth
|
|
523
|
-
|
|
524
|
-
//Define the value of the skip tooltip position
|
|
525
|
-
let offsetX = null
|
|
526
|
-
|
|
527
|
-
switch (true) {
|
|
528
|
-
case hoverPos < 15:
|
|
529
|
-
offsetX = 20
|
|
530
|
-
break
|
|
531
|
-
case hoverPos > width - 55:
|
|
532
|
-
offsetX = width - 55
|
|
533
|
-
break
|
|
534
|
-
default:
|
|
535
|
-
offsetX = hoverPos
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
this.seekTooltip.style.left = `${offsetX}px` // update visual position of tooltip
|
|
539
|
-
|
|
540
|
-
//Get % of the cursor hover position/progressBar width
|
|
541
|
-
let percentage = 0
|
|
542
|
-
if (hoverPos > width) percentage = 100
|
|
543
|
-
if (hoverPos < 0) percentage = 0
|
|
544
|
-
else {
|
|
545
|
-
percentage = hoverPos / width
|
|
546
|
-
}
|
|
547
|
-
let skipTo = percentage * this.mediaDuration
|
|
548
|
-
//Update the Tooltip time
|
|
549
|
-
this.tooTipTimeCode = this.$helper.formatTime(skipTo)
|
|
550
|
-
},
|
|
551
|
-
/**
|
|
552
|
-
* @description - handle all the listeners for videos
|
|
553
|
-
*/
|
|
554
|
-
setVideoHandlers() {
|
|
555
|
-
//Tooltip
|
|
556
|
-
this.progressArea.addEventListener('mousemove', this.updateSeekTooltip)
|
|
557
|
-
//Fullscreen
|
|
558
|
-
this.mediaContainer.addEventListener(
|
|
559
|
-
'fullscreenchange',
|
|
560
|
-
this.updateFullScreenState
|
|
561
|
-
)
|
|
562
|
-
//Show controls
|
|
563
|
-
this.mediaContainer.addEventListener('mousemove', this.showControls)
|
|
564
|
-
},
|
|
565
|
-
/**
|
|
566
|
-
* @description - Unset all the listeners (video)
|
|
567
|
-
*/
|
|
568
|
-
removeVideoHandlers() {
|
|
569
|
-
//Tooltip
|
|
570
|
-
this.progressArea.removeEventListener('mousemove', this.updateSeekTooltip)
|
|
571
|
-
//Fullscreen
|
|
572
|
-
this.mediaContainer.removeEventListener(
|
|
573
|
-
'fullscreenchange',
|
|
574
|
-
this.updateFullScreenState
|
|
575
|
-
)
|
|
576
|
-
//Show controls
|
|
577
|
-
this.mediaContainer.removeEventListener('mousemove', this.showControls)
|
|
578
|
-
},
|
|
579
|
-
/**
|
|
580
|
-
/* @description - handle the play /pause of the media and the playback video animation
|
|
581
|
-
*/
|
|
582
|
-
handlePlayVideo(type = null) {
|
|
583
|
-
//Play/pause
|
|
584
|
-
this.togglePlay()
|
|
585
|
-
//Playbar animation (show/hide)
|
|
586
|
-
this.isPlaying ? this.hideControls() : this.showControls()
|
|
587
|
-
|
|
588
|
-
//Playback animation (only the first play click)
|
|
589
|
-
if (this.playClicked && !type) {
|
|
590
|
-
this.playBackAnim = false
|
|
591
|
-
return
|
|
592
|
-
}
|
|
593
|
-
//this.runPlaybackAnimation()
|
|
594
|
-
this.playBackAnim = true
|
|
595
|
-
this.playClicked = true
|
|
596
|
-
// Should indicate with media is playing and set it in the store
|
|
597
|
-
},
|
|
598
|
-
/**
|
|
599
|
-
* @description - Toggle the state of the screen
|
|
600
|
-
* Set or unset the media in full screen
|
|
601
|
-
*/
|
|
602
|
-
toggleFullScreen() {
|
|
603
|
-
const fullscreenElement = this.mediaContainer
|
|
604
|
-
if (document.fullscreenElement) {
|
|
605
|
-
// exitFullscreen is only available on the Document object.
|
|
606
|
-
document.exitFullscreen()
|
|
607
|
-
} else {
|
|
608
|
-
//fullscreen is available on Element.
|
|
609
|
-
fullscreenElement.requestFullscreen()
|
|
610
|
-
}
|
|
611
|
-
},
|
|
612
|
-
/**
|
|
613
|
-
* @description update the value of fulls screen state
|
|
614
|
-
*/
|
|
615
|
-
updateFullScreenState() {
|
|
616
|
-
this.fullscreenOn = !this.fullscreenOn
|
|
617
|
-
},
|
|
618
|
-
|
|
619
|
-
fireFoxMoveCC() {
|
|
620
|
-
if (this.firefoxTrack.activeCues[0]) {
|
|
621
|
-
this.firefoxTrack.activeCues[0].line = 12
|
|
622
|
-
}
|
|
623
|
-
},
|
|
624
|
-
/**
|
|
625
|
-
* @description show the subtitle
|
|
626
|
-
*/
|
|
627
|
-
showSubtitles() {
|
|
628
|
-
this.mediaElement.textTracks[0].mode = 'showing'
|
|
629
|
-
this.setMediaSubtitles(true)
|
|
630
|
-
if (this.getCurrentBrowser == 'Firefox') {
|
|
631
|
-
let video = document.getElementsByTagName('video')[0]
|
|
632
|
-
let tracks = video.textTracks
|
|
633
|
-
this.firefoxTrack = tracks[0]
|
|
634
|
-
|
|
635
|
-
this.firefoxTrack.addEventListener('cuechange', this.fireFoxMoveCC)
|
|
636
|
-
}
|
|
637
|
-
},
|
|
638
|
-
|
|
639
|
-
hideSubtitles() {
|
|
640
|
-
this.mediaElement.textTracks[0].mode = 'hidden' // value can be 'disabled' also.
|
|
641
|
-
this.setMediaSubtitles(false)
|
|
642
|
-
},
|
|
643
|
-
toggleViewSubtitle() {
|
|
644
|
-
if (this.subtitlesEnabled) return this.hideSubtitles()
|
|
645
|
-
if (!this.subtitlesEnabled) return this.showSubtitles()
|
|
646
|
-
},
|
|
647
|
-
/**
|
|
648
|
-
* @description method to show or hide the transcipt.
|
|
649
|
-
* @summary get toggle the value of transcriptEnabled and get content from the transcript file
|
|
650
|
-
* and transfer it to Module for display.
|
|
651
|
-
* @fires 'open-sidebar' to AppBaseModule
|
|
652
|
-
* @fires 'close-sidebar' to AppBaseModule
|
|
653
|
-
*/
|
|
654
|
-
toggleViewTranscript() {
|
|
655
|
-
this.transcriptEnabled = !this.transcriptEnabled
|
|
656
|
-
|
|
657
|
-
if (this.transcriptEnabled && this.transcriptToShow) {
|
|
658
|
-
//this.$bus.$emit('resize-media', 'sm')
|
|
659
|
-
//Resize video container
|
|
660
|
-
this.$emit('resize-video', 'sm')
|
|
661
|
-
//Open sidebar with the transcript
|
|
662
|
-
return this.$bus.$emit('open-sidebar', {
|
|
663
|
-
ctx: 'ctxTranscript',
|
|
664
|
-
e: this.transcriptToShow,
|
|
665
|
-
w: this.pbContainer
|
|
666
|
-
})
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
// Send close signal for the side bar when transcipt state is not enabled
|
|
670
|
-
if (!this.transcriptEnabled) {
|
|
671
|
-
//this.$bus.$emit('resize-media', 'lg')
|
|
672
|
-
//Resize video container
|
|
673
|
-
this.$emit('resize-video', 'lg')
|
|
674
|
-
//Open sidebar with the transcript
|
|
675
|
-
return this.$bus.$emit('close-sidebar', 'ctxTranscript')
|
|
676
|
-
}
|
|
677
|
-
},
|
|
678
|
-
/**
|
|
679
|
-
* @description Fetching method for transcript
|
|
680
|
-
* @summary Fetch a transcript from HTML file to display. File URL is defined in the media data
|
|
681
|
-
* and return its content for display
|
|
682
|
-
* @return {String} HTML string
|
|
683
|
-
*/
|
|
684
|
-
async fetchTranscript() {
|
|
685
|
-
try {
|
|
686
|
-
// const tFile = 'exemple_transcript2.html'
|
|
687
|
-
const { mTranscript } = this.mediaToPlay
|
|
688
|
-
const tFile = mTranscript
|
|
689
|
-
|
|
690
|
-
if (!tFile) throw new Error('Missing transcript File!')
|
|
691
|
-
|
|
692
|
-
// validate file types. Only .html are allowed
|
|
693
|
-
if (!tFile.endsWith('.html'))
|
|
694
|
-
throw new Error(
|
|
695
|
-
'Invalid valid transcript file. \n Expecting .html file'
|
|
696
|
-
)
|
|
697
|
-
|
|
698
|
-
const fileUrl = `./${tFile}`
|
|
699
|
-
const res = await axios.get(fileUrl, { responseType: 'blob' })
|
|
700
|
-
const content = await res.data.text()
|
|
701
|
-
|
|
702
|
-
return content
|
|
703
|
-
} catch (err) {
|
|
704
|
-
console.warn("YOU'VE GOT AN ERROR!\n", err)
|
|
705
|
-
}
|
|
706
|
-
},
|
|
707
|
-
/** ******************* Rendering Methods****************************** */
|
|
708
|
-
|
|
709
|
-
/**
|
|
710
|
-
* @Description Method to set the transcription that need to be displayed
|
|
711
|
-
*/
|
|
712
|
-
async setTranscript() {
|
|
713
|
-
if (!this.hasTranscript) return (this.transcriptToShow = null)
|
|
714
|
-
|
|
715
|
-
this.transcriptToShow = await this.fetchTranscript()
|
|
716
|
-
},
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* @description Method to reset the transcript state
|
|
720
|
-
*/
|
|
721
|
-
resetTranscript(content) {
|
|
722
|
-
this.transcriptEnabled = false
|
|
723
|
-
},
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* @description Show the media controler */
|
|
727
|
-
showControls() {
|
|
728
|
-
this.showControlsValue = true
|
|
729
|
-
if (this.hideTimer) clearTimeout(this.hideTimer) //cancel existing timer
|
|
730
|
-
|
|
731
|
-
this.hideControls()
|
|
732
|
-
},
|
|
733
|
-
/**
|
|
734
|
-
* @description Hide the media after the video start playing
|
|
735
|
-
*/
|
|
736
|
-
|
|
737
|
-
hideControls() {
|
|
738
|
-
if (this.mediaElement.paused) return
|
|
739
|
-
|
|
740
|
-
this.hideTimer = setTimeout(() => {
|
|
741
|
-
this.showControlsValue = false
|
|
742
|
-
}, this.delayUntilHide)
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
</script>
|
|
747
|
-
<style lang="scss" scoped>
|
|
748
|
-
.pb-container.video {
|
|
749
|
-
position: absolute;
|
|
750
|
-
height: 100%;
|
|
751
|
-
width: 100%;
|
|
752
|
-
justify-content: center;
|
|
753
|
-
display: flex;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
.playback-button {
|
|
757
|
-
position: absolute;
|
|
758
|
-
top: 0;
|
|
759
|
-
left: 0;
|
|
760
|
-
width: 100%;
|
|
761
|
-
height: 100%;
|
|
762
|
-
display: flex;
|
|
763
|
-
flex-flow: column wrap;
|
|
764
|
-
justify-content: center;
|
|
765
|
-
align-items: center;
|
|
766
|
-
background-color: transparent;
|
|
767
|
-
.playback-wrapper {
|
|
768
|
-
height: 64px;
|
|
769
|
-
width: 64px;
|
|
770
|
-
display: flex;
|
|
771
|
-
justify-content: center;
|
|
772
|
-
align-items: center;
|
|
773
|
-
border-radius: 50%;
|
|
774
|
-
|
|
775
|
-
svg {
|
|
776
|
-
height: 32px;
|
|
777
|
-
width: 26.29px;
|
|
778
|
-
&.play-icon {
|
|
779
|
-
margin-left: 11%;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
//Wrapper
|
|
785
|
-
.pb-wrapper {
|
|
786
|
-
width: 100%;
|
|
787
|
-
display: flex;
|
|
788
|
-
flex-direction: column;
|
|
789
|
-
position: absolute;
|
|
790
|
-
bottom: 0;
|
|
791
|
-
opacity: 0;
|
|
792
|
-
z-index: 2;
|
|
793
|
-
&.show-controls {
|
|
794
|
-
opacity: 1;
|
|
795
|
-
transition: opacity 0.35s ease-in-out;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
//Wrapper timeline
|
|
799
|
-
.pb-timeline {
|
|
800
|
-
--progress-bar-height: 6px;
|
|
801
|
-
position: relative;
|
|
802
|
-
width: 100%;
|
|
803
|
-
height: var(--progress-bar-height);
|
|
804
|
-
.progress-area {
|
|
805
|
-
width: 100%;
|
|
806
|
-
height: 400%; //Add contact surface of click on progressBar
|
|
807
|
-
position: relative;
|
|
808
|
-
top: -150%;
|
|
809
|
-
display: flex;
|
|
810
|
-
cursor: pointer;
|
|
811
|
-
align-items: center;
|
|
812
|
-
z-index: 8; //z-index on top of the video elm
|
|
813
|
-
&:hover {
|
|
814
|
-
.seek-tooltip {
|
|
815
|
-
opacity: 1;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
//Progress bar
|
|
820
|
-
.pb-progress-bar {
|
|
821
|
-
--progress-bar-border-height: 1px;
|
|
822
|
-
cursor: pointer;
|
|
823
|
-
width: 100%;
|
|
824
|
-
height: var(--progress-bar-height);
|
|
825
|
-
//background-color: transparent;
|
|
826
|
-
overflow: visible;
|
|
827
|
-
&:focus {
|
|
828
|
-
.progress-thumb {
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
.progress-indicator {
|
|
834
|
-
width: 0;
|
|
835
|
-
height: 100%;
|
|
836
|
-
position: relative;
|
|
837
|
-
user-select: none;
|
|
838
|
-
|
|
839
|
-
&.progress-animation {
|
|
840
|
-
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
841
|
-
transition-duration: 0.01s;
|
|
842
|
-
transition-property: all;
|
|
843
|
-
transition: width 0.1s linear;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
//Custom thumb
|
|
847
|
-
.progress-thumb {
|
|
848
|
-
//Cliquable thumb
|
|
849
|
-
--progress-thumb-size: 30px;
|
|
850
|
-
width: var(--progress-thumb-size);
|
|
851
|
-
height: var(--progress-thumb-size);
|
|
852
|
-
position: absolute;
|
|
853
|
-
|
|
854
|
-
right: calc(var(--progress-thumb-size) / -2);
|
|
855
|
-
top: calc(
|
|
856
|
-
-1 * (
|
|
857
|
-
var(--progress-thumb-size) / 2 - var(--progress-bar-height) / 2
|
|
858
|
-
) - var(--progress-bar-border-height) / 2
|
|
859
|
-
);
|
|
860
|
-
display: flex;
|
|
861
|
-
align-items: center;
|
|
862
|
-
justify-content: center;
|
|
863
|
-
&::after {
|
|
864
|
-
content: '';
|
|
865
|
-
//Visible thumb
|
|
866
|
-
height: 16px;
|
|
867
|
-
width: 16px;
|
|
868
|
-
//Colors and styling
|
|
869
|
-
border-radius: 50%;
|
|
870
|
-
//background-color: var(--primary700);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
&:hover {
|
|
875
|
-
.seek-tooltip {
|
|
876
|
-
//tooltip visible only when hovering progress-area
|
|
877
|
-
opacity: 1;
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
.seek-tooltip {
|
|
882
|
-
position: absolute;
|
|
883
|
-
top: -30px;
|
|
884
|
-
opacity: 0;
|
|
885
|
-
user-select: none;
|
|
886
|
-
border-radius: 1px;
|
|
887
|
-
padding: 2px;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
//Controls
|
|
893
|
-
.pb-controls {
|
|
894
|
-
padding: 12px 32px;
|
|
895
|
-
@media screen and (max-width: 800px) {
|
|
896
|
-
padding: 12px 12px;
|
|
897
|
-
}
|
|
898
|
-
width: 100%;
|
|
899
|
-
//height: 76px;
|
|
900
|
-
height: auto;
|
|
901
|
-
display: flex;
|
|
902
|
-
flex-flow: row wrap;
|
|
903
|
-
justify-content: space-between;
|
|
904
|
-
-webkit-justify-content: space-between;
|
|
905
|
-
align-items: center;
|
|
906
|
-
|
|
907
|
-
.pb-areas {
|
|
908
|
-
display: flex;
|
|
909
|
-
flex-flow: row nowrap;
|
|
910
|
-
justify-content: center;
|
|
911
|
-
align-items: center;
|
|
912
|
-
|
|
913
|
-
&.right {
|
|
914
|
-
.btn {
|
|
915
|
-
margin-right: 24px;
|
|
916
|
-
|
|
917
|
-
&:last-child {
|
|
918
|
-
margin-right: 0;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
.btn {
|
|
924
|
-
position: relative;
|
|
925
|
-
|
|
926
|
-
&::before {
|
|
927
|
-
content: attr(data-title);
|
|
928
|
-
position: absolute;
|
|
929
|
-
top: -50px;
|
|
930
|
-
right: 0;
|
|
931
|
-
margin-left: -35px;
|
|
932
|
-
word-break: keep-all;
|
|
933
|
-
white-space: pre;
|
|
934
|
-
display: none;
|
|
935
|
-
opacity: 0;
|
|
936
|
-
width: fit-content;
|
|
937
|
-
border-radius: 1px;
|
|
938
|
-
padding: 2px;
|
|
939
|
-
}
|
|
940
|
-
//Icons btn specific
|
|
941
|
-
&.playbar-btn {
|
|
942
|
-
&::before {
|
|
943
|
-
left: 0 !important;
|
|
944
|
-
margin-left: 0 !important;
|
|
945
|
-
}
|
|
946
|
-
margin-right: 16px;
|
|
947
|
-
//Style
|
|
948
|
-
border-radius: 50%;
|
|
949
|
-
//Style
|
|
950
|
-
&:hover {
|
|
951
|
-
&::before {
|
|
952
|
-
display: block;
|
|
953
|
-
opacity: 1;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
@media screen and (max-width: 800px) {
|
|
957
|
-
height: 32px;
|
|
958
|
-
width: 32px;
|
|
959
|
-
}
|
|
960
|
-
svg.play-icon {
|
|
961
|
-
margin-left: 11.875%; // 11.8% off center on the design
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
&.volume-btn {
|
|
965
|
-
margin: 0 20px;
|
|
966
|
-
svg.volume-low-icon {
|
|
967
|
-
height: 17.75px;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
//Manage displayLabel (dynamic class)
|
|
972
|
-
button.displayLabel {
|
|
973
|
-
&:hover,
|
|
974
|
-
&:focus {
|
|
975
|
-
&::before {
|
|
976
|
-
display: block;
|
|
977
|
-
opacity: 1;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
.pb-timer {
|
|
983
|
-
width: max-content;
|
|
984
|
-
}
|
|
985
|
-
//Volume
|
|
986
|
-
.volume-controls {
|
|
987
|
-
--volumecontrols-height: 22px;
|
|
988
|
-
--volume-thumb-size: 16px;
|
|
989
|
-
display: flex;
|
|
990
|
-
flex-flow: row wrap;
|
|
991
|
-
justify-content: center;
|
|
992
|
-
position: relative;
|
|
993
|
-
height: var(--volumecontrols-height);
|
|
994
|
-
&:hover {
|
|
995
|
-
//button title //DisplayLabel is a dynamic class
|
|
996
|
-
.volume-slider.displayLabel + span {
|
|
997
|
-
display: block;
|
|
998
|
-
opacity: 1;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
.volume-slider {
|
|
1002
|
-
-webkit-appearance: none;
|
|
1003
|
-
width: 100px;
|
|
1004
|
-
//Responsive
|
|
1005
|
-
@media screen and (max-width: 800px) {
|
|
1006
|
-
width: 80px;
|
|
1007
|
-
}
|
|
1008
|
-
border-radius: 24px;
|
|
1009
|
-
cursor: pointer;
|
|
1010
|
-
position: relative;
|
|
1011
|
-
z-index: 2;
|
|
1012
|
-
&::-webkit-slider-runnable-track {
|
|
1013
|
-
background: transparent;
|
|
1014
|
-
}
|
|
1015
|
-
//Input slider thumb
|
|
1016
|
-
@mixin volume-thumb {
|
|
1017
|
-
-webkit-appearance: none;
|
|
1018
|
-
width: var(--volume-thumb-size);
|
|
1019
|
-
height: var(--volume-thumb-size);
|
|
1020
|
-
margin-top: 12px;
|
|
1021
|
-
border-radius: 50%;
|
|
1022
|
-
position: relative;
|
|
1023
|
-
cursor: pointer;
|
|
1024
|
-
}
|
|
1025
|
-
&::-webkit-slider-thumb {
|
|
1026
|
-
@include volume-thumb;
|
|
1027
|
-
}
|
|
1028
|
-
//button title
|
|
1029
|
-
& + span {
|
|
1030
|
-
position: absolute;
|
|
1031
|
-
top: -62px;
|
|
1032
|
-
right: 0;
|
|
1033
|
-
margin-left: -35px;
|
|
1034
|
-
word-break: keep-all;
|
|
1035
|
-
white-space: pre;
|
|
1036
|
-
display: none;
|
|
1037
|
-
opacity: 0;
|
|
1038
|
-
user-select: none;
|
|
1039
|
-
//Colors
|
|
1040
|
-
//background-color: var(--primary100);
|
|
1041
|
-
//color: var(--primary700);
|
|
1042
|
-
border-radius: 1px;
|
|
1043
|
-
padding: 2px;
|
|
1044
|
-
}
|
|
1045
|
-
//DisplayLabel is a dynamic class
|
|
1046
|
-
&.displayLabel:focus {
|
|
1047
|
-
& + span {
|
|
1048
|
-
display: block;
|
|
1049
|
-
opacity: 1;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
.volume-progress {
|
|
1055
|
-
--volumeIndicator-height: 9px;
|
|
1056
|
-
height: var(--volumeIndicator-height);
|
|
1057
|
-
top: calc(
|
|
1058
|
-
var(--volumecontrols-height) / 2 - var(--volumeIndicator-height) / 2
|
|
1059
|
-
); //centrer en hauteur
|
|
1060
|
-
width: calc(
|
|
1061
|
-
100% - var(--volume-thumb-size) / 2
|
|
1062
|
-
); //Width smaller than the range width to be sure the progress width don't exceed the range width
|
|
1063
|
-
position: absolute;
|
|
1064
|
-
border-radius: 20px;
|
|
1065
|
-
background-repeat: no-repeat;
|
|
1066
|
-
background-size: var(--background-size-vs, 0%) 100%; //Js dynamic variable
|
|
1067
|
-
//colors
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
svg {
|
|
1074
|
-
//width: 19px;
|
|
1075
|
-
//height: 19px;
|
|
1076
|
-
//colors
|
|
1077
|
-
//stroke: var(--primary700);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
button {
|
|
1081
|
-
height: 48px;
|
|
1082
|
-
width: 48px;
|
|
1083
|
-
background-color: transparent;
|
|
1084
|
-
padding: 0;
|
|
1085
|
-
display: flex;
|
|
1086
|
-
justify-content: center;
|
|
1087
|
-
align-items: center;
|
|
1088
|
-
svg {
|
|
1089
|
-
height: 24px;
|
|
1090
|
-
@media screen and (max-width: 800px) {
|
|
1091
|
-
height: 18px;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
//CSS for audio only
|
|
1098
|
-
.audio.pb-wrapper {
|
|
1099
|
-
border-radius: 4px;
|
|
1100
|
-
position: initial;
|
|
1101
|
-
.pb-controls {
|
|
1102
|
-
padding: 8px 16px;
|
|
1103
|
-
.volume-controls {
|
|
1104
|
-
margin-left: 8px;
|
|
1105
|
-
width: 100%;
|
|
1106
|
-
--volume-thumb-size: 12px;
|
|
1107
|
-
.volume-slider {
|
|
1108
|
-
width: 50px;
|
|
1109
|
-
}
|
|
1110
|
-
.volume-progress {
|
|
1111
|
-
--volumeIndicator-height: 6px;
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
.pb-timer {
|
|
1116
|
-
margin-right: 20px;
|
|
1117
|
-
}
|
|
1118
|
-
.pb-timeline {
|
|
1119
|
-
.progress-area {
|
|
1120
|
-
justify-content: center;
|
|
1121
|
-
}
|
|
1122
|
-
.pb-progress-bar {
|
|
1123
|
-
border-radius: 4px;
|
|
1124
|
-
//max-width: 90%;
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
.volume-btn {
|
|
1128
|
-
margin-left: 16px;
|
|
1129
|
-
}
|
|
1130
|
-
.pb-areas {
|
|
1131
|
-
width: 100%;
|
|
1132
|
-
display: grid;
|
|
1133
|
-
grid-template-columns:
|
|
1134
|
-
min-content
|
|
1135
|
-
min-content
|
|
1136
|
-
1fr
|
|
1137
|
-
min-content
|
|
1138
|
-
min-content;
|
|
1139
|
-
align-items: center;
|
|
1140
|
-
justify-content: center;
|
|
1141
|
-
button {
|
|
1142
|
-
height: 24px;
|
|
1143
|
-
width: 24px;
|
|
1144
|
-
&.playbar-btn {
|
|
1145
|
-
margin-right: 8px;
|
|
1146
|
-
}
|
|
1147
|
-
svg {
|
|
1148
|
-
height: 12.25px;
|
|
1149
|
-
@media screen and (max-width: 800px) {
|
|
1150
|
-
height: 18px;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
&.volume-btn {
|
|
1154
|
-
svg {
|
|
1155
|
-
height: 16px;
|
|
1156
|
-
}
|
|
1157
|
-
svg.volume-low-icon {
|
|
1158
|
-
height: 14px;
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
//Playback button video (to finish)
|
|
1165
|
-
.playback-button {
|
|
1166
|
-
.playback-wrapper {
|
|
1167
|
-
opacity: 0;
|
|
1168
|
-
}
|
|
1169
|
-
&.initial {
|
|
1170
|
-
.playback-wrapper {
|
|
1171
|
-
animation-name: initial;
|
|
1172
|
-
opacity: 1;
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
&.playback-animation {
|
|
1176
|
-
.playback-wrapper {
|
|
1177
|
-
opacity: 0;
|
|
1178
|
-
animation-name: handlePlayBack;
|
|
1179
|
-
animation-duration: 0.8s;
|
|
1180
|
-
animation-timing-function: ease-in-out;
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
.audio-media-player {
|
|
1186
|
-
.pb-areas {
|
|
1187
|
-
.btn {
|
|
1188
|
-
&::before {
|
|
1189
|
-
top: inherit !important;
|
|
1190
|
-
bottom: -50px;
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
@keyframes handlePlayBack {
|
|
1197
|
-
0% {
|
|
1198
|
-
opacity: 0;
|
|
1199
|
-
}
|
|
1200
|
-
50% {
|
|
1201
|
-
opacity: 1;
|
|
1202
|
-
transform: scale(0.95);
|
|
1203
|
-
}
|
|
1204
|
-
100% {
|
|
1205
|
-
opacity: 0;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
@keyframes disappear {
|
|
1210
|
-
0% {
|
|
1211
|
-
opacity: 1;
|
|
1212
|
-
}
|
|
1213
|
-
100% {
|
|
1214
|
-
opacity: 0;
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
</style>
|