fcad-core-dragon 2.0.0-beta.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/.editorconfig +33 -0
- package/.eslintignore +29 -0
- package/.eslintrc.js +86 -0
- package/.prettierrc.js +5 -0
- package/CHANGELOG +364 -0
- package/README.md +72 -0
- package/babel.config.js +3 -0
- package/package.json +65 -0
- package/src/$locales/en.json +155 -0
- package/src/$locales/fr.json +156 -0
- package/src/assets/data/onboardingMessages.json +47 -0
- package/src/assets/img/BeanEater-1s-200px.svg +25 -0
- package/src/components/AppBase.vue +635 -0
- package/src/components/AppBaseButton.vue +63 -0
- package/src/components/AppBaseDragChoice.vue +91 -0
- package/src/components/AppBaseDropZone.vue +112 -0
- package/src/components/AppBaseErrorDisplay.vue +391 -0
- package/src/components/AppBaseFlipCard.vue +83 -0
- package/src/components/AppBaseModule.vue +1589 -0
- package/src/components/AppBasePage.vue +338 -0
- package/src/components/AppCompBranchButtons.vue +581 -0
- package/src/components/AppCompButtonProgress.vue +158 -0
- package/src/components/AppCompCarousel.vue +285 -0
- package/src/components/AppCompDragAndDrop.vue +339 -0
- package/src/components/AppCompInputAssociation.vue +332 -0
- package/src/components/AppCompInputCheckBox.vue +227 -0
- package/src/components/AppCompInputDropdown.vue +184 -0
- package/src/components/AppCompInputRadio.vue +169 -0
- package/src/components/AppCompInputTextBox.vue +91 -0
- package/src/components/AppCompInputTextTable.vue +155 -0
- package/src/components/AppCompInputTextToFillDropdown.vue +255 -0
- package/src/components/AppCompInputTextToFillText.vue +164 -0
- package/src/components/AppCompJauge.vue +56 -0
- package/src/components/AppCompMediaPlayer.vue +365 -0
- package/src/components/AppCompMenu.vue +203 -0
- package/src/components/AppCompMenuItem.vue +216 -0
- package/src/components/AppCompNavigationFull.vue +1791 -0
- package/src/components/AppCompPlayBar.vue +1540 -0
- package/src/components/AppCompPopUp.vue +523 -0
- package/src/components/AppCompQuiz.vue +2998 -0
- package/src/components/AppCompSettingsMenu.vue +170 -0
- package/src/components/AppCompTableOfContent.vue +209 -0
- package/src/components/AppCompToolTip.vue +94 -0
- package/src/components/AppCompViewDisplay.vue +6 -0
- package/src/components/BaseModule.vue +148 -0
- package/src/main.js +218 -0
- package/src/mixins/$pageMixins.js +381 -0
- package/src/mixins/$quizMixins.js +456 -0
- package/src/mixins/timerMixin.js +132 -0
- package/src/module/store.js +874 -0
- package/src/module/xapi/ADL.js +339 -0
- package/src/module/xapi/Crypto/Hasher.js +241 -0
- package/src/module/xapi/Crypto/WordArray.js +278 -0
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -0
- package/src/module/xapi/Crypto/algorithms/C_algo.js +319 -0
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -0
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -0
- package/src/module/xapi/Crypto/encoders/Base.js +105 -0
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -0
- package/src/module/xapi/Crypto/encoders/Hex.js +60 -0
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -0
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -0
- package/src/module/xapi/Crypto/index.js +53 -0
- package/src/module/xapi/Statement/activity.js +47 -0
- package/src/module/xapi/Statement/agent.js +55 -0
- package/src/module/xapi/Statement/group.js +26 -0
- package/src/module/xapi/Statement/index.js +259 -0
- package/src/module/xapi/Statement/statement.js +253 -0
- package/src/module/xapi/Statement/statementRef.js +23 -0
- package/src/module/xapi/Statement/substatement.js +22 -0
- package/src/module/xapi/Statement/verb.js +36 -0
- package/src/module/xapi/activitytypes.js +17 -0
- package/src/module/xapi/launch.js +157 -0
- package/src/module/xapi/utils.js +167 -0
- package/src/module/xapi/verbs.js +294 -0
- package/src/module/xapi/wrapper.js +1890 -0
- package/src/module/xapi/xapiStatement.js +444 -0
- package/src/plugins/bus.js +3 -0
- package/src/plugins/gsap.js +14 -0
- package/src/plugins/helper.js +260 -0
- package/src/plugins/i18n.js +31 -0
- package/src/plugins/idb.js +211 -0
- package/src/plugins/save.js +37 -0
- package/src/plugins/scorm.js +287 -0
- package/src/plugins/timeManager.js +77 -0
- package/src/plugins/xapi.js +11 -0
- package/src/public/index.html +21 -0
- package/src/routes.js +734 -0
- package/src/shared/generalfuncs.js +113 -0
- package/vue.config.js +83 -0
|
@@ -0,0 +1,1540 @@
|
|
|
1
|
+
<!-- Add Bref Description of what component does and need
|
|
2
|
+
-->
|
|
3
|
+
<template>
|
|
4
|
+
<div
|
|
5
|
+
id="playbar-wrapper"
|
|
6
|
+
:class="{ minimized: barMinimized }"
|
|
7
|
+
@mouseover="playbarOver"
|
|
8
|
+
>
|
|
9
|
+
<div id="playbar-btns">
|
|
10
|
+
<div id="playbar-flex-wrap">
|
|
11
|
+
<div id="playbar-current-time">
|
|
12
|
+
<span aria-hidden="true">
|
|
13
|
+
{{ timecode }}
|
|
14
|
+
</span>
|
|
15
|
+
<span class="sr-only">{{ mediaA11Y.timeCode }}</span>
|
|
16
|
+
</div>
|
|
17
|
+
<div
|
|
18
|
+
id="playbar-volume"
|
|
19
|
+
ref="volumeButton"
|
|
20
|
+
class="playbar-btn btn"
|
|
21
|
+
role="button"
|
|
22
|
+
tabindex="0"
|
|
23
|
+
:aria-label="volumeLabel"
|
|
24
|
+
@mouseover="volumeOver"
|
|
25
|
+
@mouseleave="volumeLeave"
|
|
26
|
+
@click="volumeClick"
|
|
27
|
+
@keyup.space="volumeClick"
|
|
28
|
+
@keyup.enter="volumeClick"
|
|
29
|
+
@focus="playbarFocus"
|
|
30
|
+
>
|
|
31
|
+
<div v-show="volumeSliderOpen" id="playbar-volume-slider-zone">
|
|
32
|
+
<div id="playbar-volume-slider">
|
|
33
|
+
<div class="range-slider" :style="volumeStyle" tabindex="0">
|
|
34
|
+
<input
|
|
35
|
+
ref="volume-range"
|
|
36
|
+
type="range"
|
|
37
|
+
min="0"
|
|
38
|
+
max="1"
|
|
39
|
+
step="0.05"
|
|
40
|
+
aria-label="Volume"
|
|
41
|
+
:aria-valuenow="displayVol"
|
|
42
|
+
aria-valuemin="0"
|
|
43
|
+
aria-valuemax="100"
|
|
44
|
+
:aria-valuetext="`${displayVol}% volume`"
|
|
45
|
+
tabindex="0"
|
|
46
|
+
:value="volDefault"
|
|
47
|
+
:name="$t('button.volume')"
|
|
48
|
+
@input="volumeInputChange"
|
|
49
|
+
/>
|
|
50
|
+
<output>{{ displayVol }} %</output>
|
|
51
|
+
|
|
52
|
+
<div class="range-slider__progress"></div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<svg
|
|
57
|
+
v-if="volDefault > 0"
|
|
58
|
+
id="volume_up_black_24dp"
|
|
59
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
60
|
+
width="24"
|
|
61
|
+
height="24"
|
|
62
|
+
viewBox="0 0 24 24"
|
|
63
|
+
>
|
|
64
|
+
<path
|
|
65
|
+
id="Path_3896"
|
|
66
|
+
data-name="Path 3896"
|
|
67
|
+
d="M0,0H24V24H0Z"
|
|
68
|
+
fill="none"
|
|
69
|
+
/>
|
|
70
|
+
<path
|
|
71
|
+
id="Path_3897"
|
|
72
|
+
data-name="Path 3897"
|
|
73
|
+
d="M3,9v6H7l5,5V4L7,9Zm13.5,3A4.5,4.5,0,0,0,14,7.97v8.05A4.474,4.474,0,0,0,16.5,12ZM14,3.23V5.29a7,7,0,0,1,0,13.42v2.06A8.994,8.994,0,0,0,14,3.23Z"
|
|
74
|
+
/>
|
|
75
|
+
</svg>
|
|
76
|
+
<svg
|
|
77
|
+
v-if="volDefault <= 0"
|
|
78
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
79
|
+
width="18"
|
|
80
|
+
height="18"
|
|
81
|
+
viewBox="0 0 18 18"
|
|
82
|
+
>
|
|
83
|
+
<path
|
|
84
|
+
id="Path_3893"
|
|
85
|
+
data-name="Path 3893"
|
|
86
|
+
d="M16.5,12A4.5,4.5,0,0,0,14,7.97v2.21l2.45,2.45A4.232,4.232,0,0,0,16.5,12ZM19,12a6.843,6.843,0,0,1-.54,2.64l1.51,1.51A8.8,8.8,0,0,0,21,12a9,9,0,0,0-7-8.77V5.29A7.005,7.005,0,0,1,19,12ZM4.27,3,3,4.27,7.73,9H3v6H7l5,5V13.27l4.25,4.25A6.924,6.924,0,0,1,14,18.7v2.06a8.99,8.99,0,0,0,3.69-1.81L19.73,21,21,19.73l-9-9ZM12,4,9.91,6.09,12,8.18Z"
|
|
87
|
+
transform="translate(-3 -3)"
|
|
88
|
+
/>
|
|
89
|
+
</svg>
|
|
90
|
+
</div>
|
|
91
|
+
<app-base-button
|
|
92
|
+
id="playbar-play"
|
|
93
|
+
ref="playButton"
|
|
94
|
+
class="playbar-btn"
|
|
95
|
+
:aria-label="label"
|
|
96
|
+
:title="label"
|
|
97
|
+
@click="playClick"
|
|
98
|
+
@focus="playbarFocus"
|
|
99
|
+
>
|
|
100
|
+
<!--------------------------------Pause SVG------------------------------------>
|
|
101
|
+
<svg
|
|
102
|
+
v-if="isPlaying"
|
|
103
|
+
id="pause-icon"
|
|
104
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
105
|
+
width="12"
|
|
106
|
+
height="14"
|
|
107
|
+
viewBox="0 0 12 14"
|
|
108
|
+
>
|
|
109
|
+
<path
|
|
110
|
+
id="Path_3887"
|
|
111
|
+
data-name="Path 3887"
|
|
112
|
+
d="M6,19h4V5H6ZM14,5V19h4V5Z"
|
|
113
|
+
transform="translate(-6 -5)"
|
|
114
|
+
/>
|
|
115
|
+
</svg>
|
|
116
|
+
<!--------------------------------Replay SVG------------------------------------>
|
|
117
|
+
<svg
|
|
118
|
+
v-if="canReplay && !isPlaying"
|
|
119
|
+
id="replay-icon"
|
|
120
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
121
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
122
|
+
width="24"
|
|
123
|
+
height="24"
|
|
124
|
+
viewBox="0 0 16 20"
|
|
125
|
+
xml:space="preserve"
|
|
126
|
+
>
|
|
127
|
+
<path
|
|
128
|
+
class="st0"
|
|
129
|
+
d="M8,4V0L3,5l5,5V6c3.3,0,6,2.7,6,6s-2.7,6-6,6s-6-2.7-6-6H0c0,4.4,3.6,8,8,8s8-3.6,8-8 S12.4,4,8,4z"
|
|
130
|
+
/>
|
|
131
|
+
</svg>
|
|
132
|
+
<!--------------------------------- Play SVG ----------------------------------->
|
|
133
|
+
<svg
|
|
134
|
+
v-if="!isPlaying && !canReplay"
|
|
135
|
+
id="play-icon"
|
|
136
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
137
|
+
width="15.278"
|
|
138
|
+
height="19.444"
|
|
139
|
+
viewBox="0 0 15.278 19.444"
|
|
140
|
+
>
|
|
141
|
+
<path
|
|
142
|
+
id="Path_3858"
|
|
143
|
+
data-name="Path 3858"
|
|
144
|
+
d="M8,5V24.444l15.278-9.722Z"
|
|
145
|
+
transform="translate(-8 -5)"
|
|
146
|
+
/>
|
|
147
|
+
</svg>
|
|
148
|
+
</app-base-button>
|
|
149
|
+
<app-base-button
|
|
150
|
+
id="acessMenu"
|
|
151
|
+
ref="subButton"
|
|
152
|
+
class="playbar-btn"
|
|
153
|
+
:aria-label="$t('button.accessibility')"
|
|
154
|
+
:title="$t('button.accessibility')"
|
|
155
|
+
@click="toggleSubtitleMenu"
|
|
156
|
+
@focus="playbarFocus"
|
|
157
|
+
>
|
|
158
|
+
<svg
|
|
159
|
+
v-if="!subtitleMenuOpen"
|
|
160
|
+
id="Component_86_3"
|
|
161
|
+
data-name="Component 86 – 3"
|
|
162
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
163
|
+
width="21.642"
|
|
164
|
+
height="16"
|
|
165
|
+
viewBox="0 0 21.642 16"
|
|
166
|
+
>
|
|
167
|
+
<path
|
|
168
|
+
id="Path_3989"
|
|
169
|
+
data-name="Path 3989"
|
|
170
|
+
d="M690,385a4.991,4.991,0,0,1-4.578-7H673a2.006,2.006,0,0,0-2,2v12a2.006,2.006,0,0,0,2,2h16a2.006,2.006,0,0,0,2-2v-7.1A4.987,4.987,0,0,1,690,385Zm-17,1h4v2h-4Zm10,6H673v-2h10Zm6,0h-4v-2h4Zm0-4H679v-2h10Z"
|
|
171
|
+
transform="translate(-671 -378)"
|
|
172
|
+
/>
|
|
173
|
+
<path
|
|
174
|
+
id="Path_3902"
|
|
175
|
+
data-name="Path 3902"
|
|
176
|
+
d="M688.061,379l-.609.61,2.595,2.6,2.6-2.6-.61-.61-1.985,1.981Z"
|
|
177
|
+
transform="translate(-671 -378)"
|
|
178
|
+
/>
|
|
179
|
+
</svg>
|
|
180
|
+
|
|
181
|
+
<svg
|
|
182
|
+
v-if="subtitleMenuOpen"
|
|
183
|
+
id="Component_86_5"
|
|
184
|
+
data-name="Component 86 – 5"
|
|
185
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
186
|
+
width="21.642"
|
|
187
|
+
height="16"
|
|
188
|
+
viewBox="0 0 21.642 16"
|
|
189
|
+
>
|
|
190
|
+
<path
|
|
191
|
+
id="Path_3989"
|
|
192
|
+
data-name="Path 3989"
|
|
193
|
+
d="M690,385a4.991,4.991,0,0,1-4.578-7H673a2.006,2.006,0,0,0-2,2v12a2.006,2.006,0,0,0,2,2h16a2.006,2.006,0,0,0,2-2v-7.1A4.987,4.987,0,0,1,690,385Zm-17,1h4v2h-4Zm10,6H673v-2h10Zm6,0h-4v-2h4Zm0-4H679v-2h10Z"
|
|
194
|
+
transform="translate(-671 -378)"
|
|
195
|
+
/>
|
|
196
|
+
<path
|
|
197
|
+
id="Path_3902"
|
|
198
|
+
data-name="Path 3902"
|
|
199
|
+
d="M688.061,379l-.609.61,2.595,2.6,2.6-2.6-.61-.61-1.985,1.981Z"
|
|
200
|
+
transform="translate(709.094 383.205) rotate(180)"
|
|
201
|
+
/>
|
|
202
|
+
</svg>
|
|
203
|
+
</app-base-button>
|
|
204
|
+
<div id="playbar-cc" :class="{ submenu_active: subtitleMenuOpen }">
|
|
205
|
+
<div v-show="subtitleMenuOpen" id="subtitleMenuWrapper">
|
|
206
|
+
<app-base-button
|
|
207
|
+
id="btn-transcript"
|
|
208
|
+
class="subtitleBtns"
|
|
209
|
+
:aria-label="$t('button.download_transcript')"
|
|
210
|
+
:title="$t('button.download_transcript')"
|
|
211
|
+
:disabled="!hasTranscript"
|
|
212
|
+
:class="{ md_disabled: !hasTranscript }"
|
|
213
|
+
@click="downloadTranscript"
|
|
214
|
+
@focus="playbarFocus"
|
|
215
|
+
>
|
|
216
|
+
<svg
|
|
217
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
218
|
+
width="18"
|
|
219
|
+
height="18"
|
|
220
|
+
viewBox="0 0 18 18"
|
|
221
|
+
>
|
|
222
|
+
<path
|
|
223
|
+
id="Path_3846"
|
|
224
|
+
data-name="Path 3846"
|
|
225
|
+
d="M19,12v7H5V12H3v7a2.006,2.006,0,0,0,2,2H19a2.006,2.006,0,0,0,2-2V12Zm-6,.67,2.59-2.58L17,11.5l-5,5-5-5,1.41-1.41L11,12.67V3h2Z"
|
|
226
|
+
transform="translate(-3 -3)"
|
|
227
|
+
/>
|
|
228
|
+
</svg>
|
|
229
|
+
<span>{{ $t('button.download_transcript') }}</span>
|
|
230
|
+
</app-base-button>
|
|
231
|
+
<app-base-button
|
|
232
|
+
v-if="!subtitlesEnabled"
|
|
233
|
+
id="btn-subtitles"
|
|
234
|
+
class="btn subtitleBtns"
|
|
235
|
+
:aria-label="$t('button.show_subtitle')"
|
|
236
|
+
:title="$t('button.show_subtitle')"
|
|
237
|
+
:disabled="!hasSubtitle"
|
|
238
|
+
:class="{ md_disabled: !hasSubtitle }"
|
|
239
|
+
@click="showSubtitles"
|
|
240
|
+
@focus="playbarFocus"
|
|
241
|
+
>
|
|
242
|
+
<svg
|
|
243
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
244
|
+
width="20"
|
|
245
|
+
height="16"
|
|
246
|
+
viewBox="0 0 20 16"
|
|
247
|
+
>
|
|
248
|
+
<path
|
|
249
|
+
id="Path_3854"
|
|
250
|
+
data-name="Path 3854"
|
|
251
|
+
d="M20,4H4A2.006,2.006,0,0,0,2,6V18a2.006,2.006,0,0,0,2,2H20a2.006,2.006,0,0,0,2-2V6A2.006,2.006,0,0,0,20,4ZM4,12H8v2H4Zm10,6H4V16H14Zm6,0H16V16h4Zm0-4H10V12H20Z"
|
|
252
|
+
transform="translate(-2 -4)"
|
|
253
|
+
/>
|
|
254
|
+
</svg>
|
|
255
|
+
<span>{{ $t('button.show_subtitle') }}</span>
|
|
256
|
+
</app-base-button>
|
|
257
|
+
<app-base-button
|
|
258
|
+
v-if="subtitlesEnabled"
|
|
259
|
+
id="btn-hide-subtitles"
|
|
260
|
+
class="subtitleBtns"
|
|
261
|
+
:aria-label="$t('button.hide_subtitle')"
|
|
262
|
+
:title="$t('button.hide_subtitle')"
|
|
263
|
+
@click="hideSubtitles"
|
|
264
|
+
>
|
|
265
|
+
<svg
|
|
266
|
+
id="sub-off-icon"
|
|
267
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
268
|
+
width="20.96"
|
|
269
|
+
height="20.51"
|
|
270
|
+
viewBox="0 0 20.96 20.51"
|
|
271
|
+
>
|
|
272
|
+
<g
|
|
273
|
+
id="Group_6950"
|
|
274
|
+
data-name="Group 6950"
|
|
275
|
+
transform="translate(-1.04 -2.45)"
|
|
276
|
+
>
|
|
277
|
+
<g id="Group_6949" data-name="Group 6949">
|
|
278
|
+
<path
|
|
279
|
+
id="Path_3855"
|
|
280
|
+
data-name="Path 3855"
|
|
281
|
+
d="M20,4H6.83l8,8H20v2H16.83l4.93,4.93A1.949,1.949,0,0,0,22,18V6A2.006,2.006,0,0,0,20,4Z"
|
|
282
|
+
/>
|
|
283
|
+
<path
|
|
284
|
+
id="Path_3856"
|
|
285
|
+
data-name="Path 3856"
|
|
286
|
+
d="M1.04,3.87l1.2,1.2A1.949,1.949,0,0,0,2,6V18a2.006,2.006,0,0,0,2,2H17.17l2.96,2.96,1.41-1.41L2.45,2.45ZM8,12v2H4V12Zm6,4.83V18H4V16h9.17Z"
|
|
287
|
+
/>
|
|
288
|
+
</g>
|
|
289
|
+
</g>
|
|
290
|
+
</svg>
|
|
291
|
+
<span>{{ $t('button.hide_subtitle') }}</span>
|
|
292
|
+
</app-base-button>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
<div id="playbar-total-time">
|
|
296
|
+
<!-- <time datetime="PT2H30M"> -->
|
|
297
|
+
<span aria-hidden="true">
|
|
298
|
+
{{ duration }}
|
|
299
|
+
</span>
|
|
300
|
+
<span class="sr-only">{{ mediaA11Y.duration }}</span>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
<div
|
|
305
|
+
id="playbar-progress"
|
|
306
|
+
ref="playbar-progress"
|
|
307
|
+
role="progressbar"
|
|
308
|
+
aria-valuemin="0"
|
|
309
|
+
:aria-label="mediaA11Y.label"
|
|
310
|
+
:aria-valuenow="mediaA11Y.valNow"
|
|
311
|
+
:aria-valuemax="mediaA11Y.valMax"
|
|
312
|
+
:aria-valuetext="mediaA11Y.valueText"
|
|
313
|
+
@mouseover="progressOver"
|
|
314
|
+
@mouseleave="progressLeave"
|
|
315
|
+
@mousemove="progressMove"
|
|
316
|
+
@click="progressClick"
|
|
317
|
+
>
|
|
318
|
+
<div id="playbar-buffer" ref="playbar-buffer"></div>
|
|
319
|
+
<div id="playbar-playback" ref="playbar-playback">
|
|
320
|
+
<div
|
|
321
|
+
id="playbar-cursor"
|
|
322
|
+
tabindex="0"
|
|
323
|
+
@mousedown="cursorDown"
|
|
324
|
+
@focus="playbarFocus"
|
|
325
|
+
></div>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</template>
|
|
330
|
+
|
|
331
|
+
<script>
|
|
332
|
+
import { mapGetters } from 'vuex'
|
|
333
|
+
|
|
334
|
+
export default {
|
|
335
|
+
props: {
|
|
336
|
+
appStatus: { type: Boolean, required: true },
|
|
337
|
+
mediaToShow: { type: [Object, Boolean], default: false }
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
data() {
|
|
341
|
+
return {
|
|
342
|
+
previousBtn: null,
|
|
343
|
+
menuBtn: null,
|
|
344
|
+
nextBtn: null,
|
|
345
|
+
mediaIs: null,
|
|
346
|
+
playBtn: null,
|
|
347
|
+
stopBtn: null,
|
|
348
|
+
muteBtn: null,
|
|
349
|
+
progressBar: null,
|
|
350
|
+
progressShaddow: null,
|
|
351
|
+
progress: null,
|
|
352
|
+
progressTime: null,
|
|
353
|
+
mediaDuration: '--:--',
|
|
354
|
+
navigation: {},
|
|
355
|
+
currentPath: null,
|
|
356
|
+
isBranching: false,
|
|
357
|
+
//bCounter: null,
|
|
358
|
+
index: 0,
|
|
359
|
+
navigationRoutes: null,
|
|
360
|
+
hasMedia: false,
|
|
361
|
+
hasSound: false,
|
|
362
|
+
media: null,
|
|
363
|
+
nextActivity: null,
|
|
364
|
+
previousActivity: null,
|
|
365
|
+
cursorDragged: false,
|
|
366
|
+
bufferDomElem: '',
|
|
367
|
+
playbackDomElem: '',
|
|
368
|
+
isPlaying: false,
|
|
369
|
+
mediaHandlersSet: false,
|
|
370
|
+
duration: '',
|
|
371
|
+
timecode: '',
|
|
372
|
+
subtitlesEnabled: false,
|
|
373
|
+
volumeSliderOpen: false,
|
|
374
|
+
volumeBtn: null,
|
|
375
|
+
volDefault: 1,
|
|
376
|
+
hover: false,
|
|
377
|
+
lastVolume: '',
|
|
378
|
+
muted: false,
|
|
379
|
+
isAnimation: false,
|
|
380
|
+
subtitleMenuOpen: false,
|
|
381
|
+
hasTranscript: false,
|
|
382
|
+
hasSubtitle: false,
|
|
383
|
+
canReplay: false,
|
|
384
|
+
focusTimeout: null,
|
|
385
|
+
delayUntilHide: 3000,
|
|
386
|
+
playbarLrs: [],
|
|
387
|
+
mediaA11Y: {
|
|
388
|
+
label: 'progress',
|
|
389
|
+
valMax: null,
|
|
390
|
+
valNow: null,
|
|
391
|
+
valueText: null,
|
|
392
|
+
timeCode: 0,
|
|
393
|
+
duration: 0
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
computed: {
|
|
398
|
+
...mapGetters([
|
|
399
|
+
'getCurrentPage',
|
|
400
|
+
'getModuleChildren',
|
|
401
|
+
'hasMediaElOrTimeline',
|
|
402
|
+
'getCurrentMediaDuration',
|
|
403
|
+
'getIntroStatus',
|
|
404
|
+
'getCurrentBrowser',
|
|
405
|
+
'getAutoplayEnabled',
|
|
406
|
+
'getMediaVolume',
|
|
407
|
+
'getModuleInfo',
|
|
408
|
+
'getMediaSubtitles',
|
|
409
|
+
'getUserInteraction'
|
|
410
|
+
]),
|
|
411
|
+
isViewed() {
|
|
412
|
+
let userData = this.getUserInteraction
|
|
413
|
+
let activityId = this.getCurrentPage.activityRef
|
|
414
|
+
let pageId = this.getCurrentPage.id
|
|
415
|
+
let viewed = false
|
|
416
|
+
if (
|
|
417
|
+
userData &&
|
|
418
|
+
userData[activityId] &&
|
|
419
|
+
userData[activityId][pageId] &&
|
|
420
|
+
userData[activityId][pageId].userInteraction &&
|
|
421
|
+
userData[activityId][pageId].userInteraction.mediaIsViewed
|
|
422
|
+
) {
|
|
423
|
+
viewed = true
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return viewed
|
|
427
|
+
},
|
|
428
|
+
barMinimized() {
|
|
429
|
+
//si la vidéo est en pause: ne pas minimiser la playbar
|
|
430
|
+
if (!this.isPlaying) {
|
|
431
|
+
return false
|
|
432
|
+
} else {
|
|
433
|
+
//si la vidéo joue, minimiser seulement si la souris ne la survole pas.
|
|
434
|
+
return !this.hover
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
volumeStyle() {
|
|
438
|
+
return (
|
|
439
|
+
'--min:0; --max:1; --step:0.05; --value:' +
|
|
440
|
+
this.volDefault +
|
|
441
|
+
'; --text-value:"' +
|
|
442
|
+
this.volDefault +
|
|
443
|
+
'";'
|
|
444
|
+
)
|
|
445
|
+
},
|
|
446
|
+
label() {
|
|
447
|
+
let label = null
|
|
448
|
+
if (this.isPlaying) {
|
|
449
|
+
label = `${this.$t('button.pause')}`
|
|
450
|
+
} else {
|
|
451
|
+
label = `${this.$t('button.play')}`
|
|
452
|
+
}
|
|
453
|
+
return label
|
|
454
|
+
},
|
|
455
|
+
volumeLabel() {
|
|
456
|
+
let label = null
|
|
457
|
+
if (this.muted) {
|
|
458
|
+
label = `${this.$t('button.unmute')}`
|
|
459
|
+
} else {
|
|
460
|
+
label = `${this.$t('button.mute')}`
|
|
461
|
+
}
|
|
462
|
+
return label
|
|
463
|
+
},
|
|
464
|
+
displayVol() {
|
|
465
|
+
let test = Math.floor(this.volDefault * 100)
|
|
466
|
+
return test
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
watch: {
|
|
471
|
+
/* animation duration is 0:00 on load, we watch the media object for a change and update duration display accordingly
|
|
472
|
+
*/
|
|
473
|
+
media() {
|
|
474
|
+
this.updateDurationDisplay()
|
|
475
|
+
},
|
|
476
|
+
/*
|
|
477
|
+
* Why this watcher:
|
|
478
|
+
* When the component is created : the HMTLMediaElement (mElement in the mediaToshow)doesn't exist
|
|
479
|
+
* This compenent need to ensure that the data is available in the store before it will try o access and attach the event listeners (click, seek, stop etc.)
|
|
480
|
+
*/
|
|
481
|
+
mediaToShow: {
|
|
482
|
+
handler() {
|
|
483
|
+
if (this.mediaToShow) {
|
|
484
|
+
const { type, mElement } = this.mediaToShow
|
|
485
|
+
|
|
486
|
+
switch (type) {
|
|
487
|
+
case 'pg_animation':
|
|
488
|
+
this.isAnimation = true
|
|
489
|
+
this.setMedia()
|
|
490
|
+
if (this.mediaHandlersSet === false && this.hasMedia) {
|
|
491
|
+
if (this.getAutoplayEnabled) {
|
|
492
|
+
this.playClick()
|
|
493
|
+
}
|
|
494
|
+
//animation was viewed previously
|
|
495
|
+
if (this.isViewed) {
|
|
496
|
+
//set animation timeline at the end
|
|
497
|
+
this.setMediaTime(this.mediaToShow.timeline.totalDuration())
|
|
498
|
+
this.canReplay = true
|
|
499
|
+
}
|
|
500
|
+
this.setMediaHandlers()
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
this.updateTimecodeDisplay()
|
|
504
|
+
|
|
505
|
+
break
|
|
506
|
+
|
|
507
|
+
case 'pg_media':
|
|
508
|
+
if (mElement) {
|
|
509
|
+
this.setMedia()
|
|
510
|
+
this.updateDurationDisplay()
|
|
511
|
+
this.setMediaA11Y()
|
|
512
|
+
if (this.mediaHandlersSet === false && this.hasMedia) {
|
|
513
|
+
this.setVolume()
|
|
514
|
+
if (this.subtitlesEnabled) {
|
|
515
|
+
this.showSubtitles()
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
this.updateTimecodeDisplay()
|
|
519
|
+
|
|
520
|
+
if (this.getAutoplayEnabled) {
|
|
521
|
+
this.playClick()
|
|
522
|
+
}
|
|
523
|
+
//video was viewed previously
|
|
524
|
+
if (this.isViewed) {
|
|
525
|
+
//set video timecode at the end
|
|
526
|
+
this.setMediaTime(this.media.mElement.duration)
|
|
527
|
+
this.canReplay = true
|
|
528
|
+
}
|
|
529
|
+
this.setMediaHandlers()
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
break
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
immediate: true
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
$route() {
|
|
540
|
+
setTimeout(() => {
|
|
541
|
+
this.$scorm.getDataValues()
|
|
542
|
+
}, 200)
|
|
543
|
+
},
|
|
544
|
+
/*
|
|
545
|
+
* In some cases (when the page has media ) the media metadata is not ready and total duration is not available.
|
|
546
|
+
* Watching getCurrentMediaDuration allow to track changes in the value of the duration and call updateDurationDisplay()
|
|
547
|
+
*/
|
|
548
|
+
getCurrentMediaDuration() {
|
|
549
|
+
this.updateDurationDisplay()
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
beforeDestroy() {
|
|
553
|
+
this.saveToLrs()
|
|
554
|
+
if (this.isPlaying) {
|
|
555
|
+
this.playClick()
|
|
556
|
+
}
|
|
557
|
+
this.cancelTimeout()
|
|
558
|
+
if (this.mediaHandlersSet) {
|
|
559
|
+
this.removeMediaHandlers()
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
mounted() {
|
|
564
|
+
this.volumeBtn = this.$refs['volume-range']
|
|
565
|
+
this.bufferDomElem = this.$refs['playbar-buffer']
|
|
566
|
+
this.playbackDomElem = this.$refs['playbar-playback']
|
|
567
|
+
//get volume from the store
|
|
568
|
+
this.volDefault = this.getMediaVolume
|
|
569
|
+
this.subtitlesEnabled = this.getMediaSubtitles
|
|
570
|
+
|
|
571
|
+
//TODO: Trying to get the focus on the volume slider to them be able to inscrease/descrease volume with key up/key down
|
|
572
|
+
|
|
573
|
+
// if (!this.$refs || !this.$refs.volumeButton) return
|
|
574
|
+
|
|
575
|
+
// this.$refs.volumeButton.addEventListener('focus', () => {
|
|
576
|
+
// this.volumeSliderOpen = true
|
|
577
|
+
// // this.$refs.volume-range
|
|
578
|
+
// this.volumeBtn.addEventListener('keydown', (e) => {
|
|
579
|
+
// var key = e.key || e.keyCode
|
|
580
|
+
// if (key === 'ArrowUp' || key === 38) {
|
|
581
|
+
// // console.log('VolumeUP')
|
|
582
|
+
// }
|
|
583
|
+
|
|
584
|
+
// if (key === 'ArrowDown' || key === 40) {
|
|
585
|
+
// //console.log('VolumeDOWN')
|
|
586
|
+
// }
|
|
587
|
+
// })
|
|
588
|
+
// })
|
|
589
|
+
},
|
|
590
|
+
methods: {
|
|
591
|
+
setIsViewed() {
|
|
592
|
+
let userData = this.getUserInteraction
|
|
593
|
+
let activityId = this.getCurrentPage.activityRef
|
|
594
|
+
let pageId = this.getCurrentPage.id
|
|
595
|
+
|
|
596
|
+
this.$set(
|
|
597
|
+
userData[activityId][pageId].userInteraction,
|
|
598
|
+
'mediaIsViewed',
|
|
599
|
+
true
|
|
600
|
+
)
|
|
601
|
+
},
|
|
602
|
+
saveToLrs() {
|
|
603
|
+
let text
|
|
604
|
+
//Defining the text to display for stmt description and definition
|
|
605
|
+
switch (this.$i18n.locale) {
|
|
606
|
+
case 'fr':
|
|
607
|
+
if (this.getModuleInfo.courseID)
|
|
608
|
+
text = `Le ${this.getModuleInfo.id} de ${this.getModuleInfo.courseID}`
|
|
609
|
+
else text = `Le ${this.getModuleInfo.id}`
|
|
610
|
+
break
|
|
611
|
+
case 'en':
|
|
612
|
+
if (this.getModuleInfo.courseID)
|
|
613
|
+
text = `The ${this.getModuleInfo.id} of ${this.getModuleInfo.courseID}`
|
|
614
|
+
else text = `The ${this.getModuleInfo.id}`
|
|
615
|
+
break
|
|
616
|
+
}
|
|
617
|
+
//Creating custom statement
|
|
618
|
+
const stmt = {
|
|
619
|
+
id: (() => {
|
|
620
|
+
if (this.getModuleInfo.courseID) return this.getModuleInfo.courseID
|
|
621
|
+
else return null
|
|
622
|
+
})(),
|
|
623
|
+
verb: 'played',
|
|
624
|
+
definition: text,
|
|
625
|
+
description: text,
|
|
626
|
+
extension: [
|
|
627
|
+
{
|
|
628
|
+
id: 'playbar-values',
|
|
629
|
+
content: {
|
|
630
|
+
volume: this.volDefault,
|
|
631
|
+
subtitlesEnabled: this.subtitlesEnabled
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
]
|
|
635
|
+
}
|
|
636
|
+
setTimeout(() => {
|
|
637
|
+
this.$bus.$emit('send-xapi-statement', stmt) //send xapi statement
|
|
638
|
+
}, 1000)
|
|
639
|
+
},
|
|
640
|
+
|
|
641
|
+
/*TODO: Need to refactor the control key mecanique to take in account Arrow Keys UP/DOWN
|
|
642
|
+
* ISSUE: Not possible to controle volume with Arrow keys UP/DOWN
|
|
643
|
+
*/
|
|
644
|
+
windowKeydown(e) {
|
|
645
|
+
var key = e.key || e.keyCode
|
|
646
|
+
// Listen to keyEvent only on media Elements are on focus
|
|
647
|
+
if (
|
|
648
|
+
!this.media.mElement ||
|
|
649
|
+
this.media.mElement !== document.activeElement
|
|
650
|
+
)
|
|
651
|
+
return
|
|
652
|
+
|
|
653
|
+
//when focus is on the video element, spacebar = play/pause
|
|
654
|
+
if (key === ' ' || key === 'Esc' || key === 32) this.playClick()
|
|
655
|
+
|
|
656
|
+
//left and right arrows to seek video
|
|
657
|
+
if (key === 'ArrowLeft' || key === 37) {
|
|
658
|
+
this.cursorLeft()
|
|
659
|
+
}
|
|
660
|
+
if (key === 'ArrowRight' || key === 39) {
|
|
661
|
+
this.cursorRight()
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// if (key === 'ArrowUp' || key === 38) {
|
|
665
|
+
// // this.cursorRight()
|
|
666
|
+
// console.log('VolumeUP')
|
|
667
|
+
// }
|
|
668
|
+
|
|
669
|
+
// if (key === 'ArrowDown' || key === 40) {
|
|
670
|
+
// console.log('VolumeDOWN')
|
|
671
|
+
// //this.cursorRight()
|
|
672
|
+
// }
|
|
673
|
+
},
|
|
674
|
+
getCurrentTime() {
|
|
675
|
+
return this.isAnimation
|
|
676
|
+
? this.media.timeline.time()
|
|
677
|
+
: this.media.mElement.currentTime
|
|
678
|
+
},
|
|
679
|
+
cursorLeft() {
|
|
680
|
+
this.playbarFocus()
|
|
681
|
+
this.setMediaTime(this.getCurrentTime() - 10)
|
|
682
|
+
},
|
|
683
|
+
cursorRight() {
|
|
684
|
+
this.playbarFocus()
|
|
685
|
+
this.setMediaTime(this.getCurrentTime() + 10)
|
|
686
|
+
},
|
|
687
|
+
playbarFocus() {
|
|
688
|
+
this.playbarOver()
|
|
689
|
+
},
|
|
690
|
+
startTimeout() {
|
|
691
|
+
this.focusTimeout = setTimeout(this.playbarLeave, this.delayUntilHide)
|
|
692
|
+
},
|
|
693
|
+
cancelTimeout() {
|
|
694
|
+
if (this.focusTimeout) {
|
|
695
|
+
clearTimeout(this.focusTimeout)
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
playbarOver() {
|
|
699
|
+
this.hover = true
|
|
700
|
+
this.cancelTimeout()
|
|
701
|
+
this.startTimeout()
|
|
702
|
+
},
|
|
703
|
+
playbarLeave() {
|
|
704
|
+
this.hover = this.cursorDragged ? true : false
|
|
705
|
+
this.volumeLeave()
|
|
706
|
+
if (this.subtitleMenuOpen) {
|
|
707
|
+
this.toggleSubtitleMenu()
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
volumeOver() {
|
|
711
|
+
this.volumeSliderOpen = true
|
|
712
|
+
},
|
|
713
|
+
volumeLeave() {
|
|
714
|
+
this.volumeSliderOpen = false
|
|
715
|
+
},
|
|
716
|
+
volumeClick(ev) {
|
|
717
|
+
if (ev.target.id !== 'playbar-volume') {
|
|
718
|
+
return false
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (this.muted) {
|
|
722
|
+
this.volumeBtn.value = this.lastVolume
|
|
723
|
+
if (this.media.mElement) this.media.mElement.muted = false
|
|
724
|
+
this.muted = false
|
|
725
|
+
} else {
|
|
726
|
+
this.lastVolume = this.volumeBtn.value
|
|
727
|
+
this.volumeBtn.value = 0
|
|
728
|
+
this.muted = true
|
|
729
|
+
}
|
|
730
|
+
this.playbarFocus()
|
|
731
|
+
this.setVolume()
|
|
732
|
+
},
|
|
733
|
+
volumeInputChange() {
|
|
734
|
+
if (this.muted) {
|
|
735
|
+
this.muted = false
|
|
736
|
+
}
|
|
737
|
+
this.setVolume()
|
|
738
|
+
},
|
|
739
|
+
updateDurationDisplay() {
|
|
740
|
+
if (this.isAnimation) {
|
|
741
|
+
this.duration = this.$helper.formatTime(
|
|
742
|
+
this.mediaToShow.timeline.totalDuration()
|
|
743
|
+
)
|
|
744
|
+
} else {
|
|
745
|
+
this.duration = Number.isNaN(this.media.mElement.duration)
|
|
746
|
+
? '--:--'
|
|
747
|
+
: this.$helper.formatTime(this.media.mElement.duration)
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
updateTimecodeDisplay() {
|
|
751
|
+
this.timecode = this.isAnimation
|
|
752
|
+
? this.$helper.formatTime(this.media.timeline.time())
|
|
753
|
+
: this.$helper.formatTime(this.media.mElement.currentTime)
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
updateCursorPosition() {
|
|
757
|
+
const currentTime = this.isAnimation
|
|
758
|
+
? this.media.timeline.time()
|
|
759
|
+
: this.media.mElement.currentTime
|
|
760
|
+
const duration = this.isAnimation
|
|
761
|
+
? this.mediaToShow.timeline.totalDuration()
|
|
762
|
+
: this.media.mElement.duration
|
|
763
|
+
const cursorPos = this.getCursorPosFromTime(currentTime, duration)
|
|
764
|
+
this.setCursorPosition(cursorPos)
|
|
765
|
+
},
|
|
766
|
+
setMediaHandlers() {
|
|
767
|
+
if (this.media && (this.media.mElement || this.media.timeline)) {
|
|
768
|
+
//window handlers for playbar progress
|
|
769
|
+
window.addEventListener('mousemove', this.windowMove)
|
|
770
|
+
window.addEventListener('mouseup', this.windowUp)
|
|
771
|
+
window.addEventListener('resize', this.updateCursorPosition)
|
|
772
|
+
window.addEventListener('keydown', this.windowKeydown)
|
|
773
|
+
if (this.isAnimation) {
|
|
774
|
+
this.media.timeline.eventCallback('onUpdate', this.mediaTimeupdate)
|
|
775
|
+
this.media.timeline.eventCallback('onComplete', this.mediaEnded)
|
|
776
|
+
} else {
|
|
777
|
+
this.media.mElement.addEventListener(
|
|
778
|
+
'timeupdate',
|
|
779
|
+
this.mediaTimeupdate
|
|
780
|
+
)
|
|
781
|
+
this.media.mElement.addEventListener('ended', this.mediaEnded)
|
|
782
|
+
/// hide the this.playbar()
|
|
783
|
+
|
|
784
|
+
this.media.mElement.addEventListener('click', () => {
|
|
785
|
+
if (this.focusTimeout) {
|
|
786
|
+
this.playbarLeave()
|
|
787
|
+
this.cancelTimeout()
|
|
788
|
+
}
|
|
789
|
+
})
|
|
790
|
+
this.media.mElement.focus()
|
|
791
|
+
//There is a timeline animation link to media set the callback event for timeline
|
|
792
|
+
if (this.media.timeline) {
|
|
793
|
+
this.media.timeline.eventCallback('onUpdate', this.mediaTimeupdate)
|
|
794
|
+
this.media.timeline.eventCallback('onComplete', this.mediaEnded)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
this.mediaHandlersSet = true
|
|
799
|
+
},
|
|
800
|
+
removeMediaHandlers() {
|
|
801
|
+
if (this.isAnimation || this.media.timeline) {
|
|
802
|
+
this.media.timeline.eventCallback('onUpdate', null)
|
|
803
|
+
this.media.timeline.eventCallback('onComplete', null)
|
|
804
|
+
this.media.timeline.kill()
|
|
805
|
+
}
|
|
806
|
+
if (this.media.mElement) {
|
|
807
|
+
this.media.mElement.removeEventListener(
|
|
808
|
+
'timeupdate',
|
|
809
|
+
this.mediaTimeupdate
|
|
810
|
+
)
|
|
811
|
+
this.media.mElement.removeEventListener('ended', this.mediaEnded)
|
|
812
|
+
}
|
|
813
|
+
//window handlers for playbar progress
|
|
814
|
+
window.removeEventListener('mousemove', this.windowMove)
|
|
815
|
+
window.removeEventListener('mouseup', this.windowUp)
|
|
816
|
+
window.removeEventListener('resize', this.updateCursorPosition)
|
|
817
|
+
window.removeEventListener('keydown', this.windowKeydown)
|
|
818
|
+
|
|
819
|
+
this.mediaHandlersSet = false
|
|
820
|
+
},
|
|
821
|
+
playClick() {
|
|
822
|
+
if (this.canReplay) {
|
|
823
|
+
this.setMediaTime(0)
|
|
824
|
+
}
|
|
825
|
+
if (this.isAnimation) {
|
|
826
|
+
this.playAnimation()
|
|
827
|
+
} else {
|
|
828
|
+
this.playVideo()
|
|
829
|
+
// play also the animation if there is one link to the media
|
|
830
|
+
if (this.media.timeline) this.playAnimation()
|
|
831
|
+
}
|
|
832
|
+
this.isPlaying = !this.isPlaying
|
|
833
|
+
this.canReplay = false
|
|
834
|
+
this.playbarFocus()
|
|
835
|
+
},
|
|
836
|
+
|
|
837
|
+
playVideo() {
|
|
838
|
+
if (this.isPlaying) {
|
|
839
|
+
this.media.mElement.pause()
|
|
840
|
+
} else {
|
|
841
|
+
this.media.mElement.play()
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
|
|
845
|
+
playAnimation() {
|
|
846
|
+
if (this.isPlaying) {
|
|
847
|
+
this.media.timeline.pause()
|
|
848
|
+
this.media.timeline.paused(true)
|
|
849
|
+
} else {
|
|
850
|
+
this.media.timeline.paused(false)
|
|
851
|
+
this.media.timeline.play()
|
|
852
|
+
}
|
|
853
|
+
},
|
|
854
|
+
setCursorPosition(xPos) {
|
|
855
|
+
this.playbackDomElem.style.width = xPos + 'px'
|
|
856
|
+
},
|
|
857
|
+
setMediaTime(time) {
|
|
858
|
+
if (this.isAnimation) {
|
|
859
|
+
this.media.timeline.time(time)
|
|
860
|
+
} else {
|
|
861
|
+
this.media.mElement.currentTime = time
|
|
862
|
+
if (this.media.timeline) this.media.timeline.time(time)
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
mediaTimeupdate() {
|
|
866
|
+
if (!this.cursorDragged) {
|
|
867
|
+
this.updateCursorPosition()
|
|
868
|
+
}
|
|
869
|
+
this.updateTimecodeDisplay()
|
|
870
|
+
this.setMediaA11Y()
|
|
871
|
+
},
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* @fires mediaPlaybackEnded to AppCompNavigationFull.vue
|
|
875
|
+
*/
|
|
876
|
+
mediaEnded() {
|
|
877
|
+
if ((this.media.timeline || this.isAnimation) && this.isPlaying) {
|
|
878
|
+
this.media.timeline.pause()
|
|
879
|
+
this.media.timeline.paused(true)
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
this.isPlaying = false
|
|
883
|
+
this.canReplay = true
|
|
884
|
+
this.$bus.$emit('mediaPlaybackEnded')
|
|
885
|
+
//save the viewed status in userInteraction
|
|
886
|
+
if (this.isViewed === false) {
|
|
887
|
+
this.setIsViewed()
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
windowMove(ev) {
|
|
891
|
+
if (this.cursorDragged) {
|
|
892
|
+
this.setCursorPosition(ev.clientX)
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
windowUp(ev) {
|
|
896
|
+
if (this.cursorDragged) {
|
|
897
|
+
this.setCursorDragging(false)
|
|
898
|
+
const timeToSet = this.getTimeFromClickPos(ev)
|
|
899
|
+
this.setMediaTime(timeToSet)
|
|
900
|
+
}
|
|
901
|
+
},
|
|
902
|
+
setCursorDragging(bool) {
|
|
903
|
+
this.cursorDragged = bool
|
|
904
|
+
},
|
|
905
|
+
cursorDown() {
|
|
906
|
+
this.setCursorDragging(true)
|
|
907
|
+
},
|
|
908
|
+
progressOver() {
|
|
909
|
+
this.bufferDomElem.style.opacity = '1'
|
|
910
|
+
},
|
|
911
|
+
progressLeave() {
|
|
912
|
+
this.bufferDomElem.style.opacity = '0'
|
|
913
|
+
},
|
|
914
|
+
progressMove(ev) {
|
|
915
|
+
this.bufferDomElem.style.width = ev.clientX + 'px'
|
|
916
|
+
},
|
|
917
|
+
progressClick(ev) {
|
|
918
|
+
if (!this.cursorDragged) {
|
|
919
|
+
this.bufferDomElem.style.width = ev.clientX + 'px'
|
|
920
|
+
const timeToSet = this.getTimeFromClickPos(ev)
|
|
921
|
+
this.setMediaTime(timeToSet)
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
getCursorPosFromTime(currentTime, totalTime) {
|
|
925
|
+
let cursorPosition = 0
|
|
926
|
+
const domElem = this.$refs['playbar-progress']
|
|
927
|
+
const width = domElem.offsetWidth
|
|
928
|
+
const percentageTime = currentTime / totalTime
|
|
929
|
+
cursorPosition = width * percentageTime
|
|
930
|
+
return cursorPosition
|
|
931
|
+
},
|
|
932
|
+
getTimeFromClickPos(ev) {
|
|
933
|
+
let timeToSet = 0
|
|
934
|
+
const domElem = this.$refs['playbar-progress']
|
|
935
|
+
const clickPos = ev.clientX
|
|
936
|
+
const width = domElem.offsetWidth
|
|
937
|
+
const percentage = (clickPos / width) * 100
|
|
938
|
+
if (this.media && (this.media.timeline || this.media.mElement))
|
|
939
|
+
timeToSet = this.isAnimation
|
|
940
|
+
? (this.media.timeline.totalDuration() / 100) * percentage
|
|
941
|
+
: (this.media.mElement.duration / 100) * percentage
|
|
942
|
+
else console.warn('media undefined')
|
|
943
|
+
return Math.floor(timeToSet)
|
|
944
|
+
},
|
|
945
|
+
|
|
946
|
+
/* Controle the Volume of the Media */
|
|
947
|
+
setVolume() {
|
|
948
|
+
if (this.media.mElement && this.volumeBtn) {
|
|
949
|
+
this.media.mElement.volume = this.volDefault = this.volumeBtn.value
|
|
950
|
+
this.$store.dispatch('setMediaVolume', this.volumeBtn.value)
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
|
|
954
|
+
toggleSubtitleMenu() {
|
|
955
|
+
this.subtitleMenuOpen = !this.subtitleMenuOpen
|
|
956
|
+
},
|
|
957
|
+
/**
|
|
958
|
+
* @description method to download transcription.
|
|
959
|
+
* @summary Create a tag with downloadable link and download the element to user PC. remove a element after download
|
|
960
|
+
*/
|
|
961
|
+
async downloadTranscript() {
|
|
962
|
+
try {
|
|
963
|
+
const tFile = this.media.mTranscript
|
|
964
|
+
const fileUrl = `./${tFile}`
|
|
965
|
+
let atype = tFile.split('.')[1]
|
|
966
|
+
|
|
967
|
+
const res = await this.axios.get(fileUrl, { responseType: 'blob' })
|
|
968
|
+
const blob = new Blob([res.data], {
|
|
969
|
+
type: this.$helper.mimeTypeFor(atype)
|
|
970
|
+
})
|
|
971
|
+
const alink = document.createElement('a') //create a element
|
|
972
|
+
|
|
973
|
+
alink.href = URL.createObjectURL(blob) //create url reference for the object
|
|
974
|
+
alink.download = `${tFile}` // set download name
|
|
975
|
+
alink.click() //symulate click event to download the object
|
|
976
|
+
URL.revokeObjectURL(alink.href) //release the url referrence of the object
|
|
977
|
+
} catch (err) {
|
|
978
|
+
console.warn(this.$t('err_download_transcript'))
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
showSubtitles() {
|
|
982
|
+
this.media.mElement.textTracks[0].mode = 'showing'
|
|
983
|
+
this.subtitlesEnabled = true
|
|
984
|
+
this.$store.dispatch('setMediaSubtitles', true)
|
|
985
|
+
if (this.getCurrentBrowser == 'Firefox') {
|
|
986
|
+
let video = document.getElementsByTagName('video')[0]
|
|
987
|
+
let tracks = video.textTracks
|
|
988
|
+
let track = tracks[0]
|
|
989
|
+
|
|
990
|
+
track.addEventListener('cuechange', function fireFoxMoveCC() {
|
|
991
|
+
if (track.activeCues[0]) {
|
|
992
|
+
track.activeCues[0].line = 12
|
|
993
|
+
}
|
|
994
|
+
})
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
hideSubtitles() {
|
|
998
|
+
this.media.mElement.textTracks[0].mode = 'hidden' // value can be 'disabled' also.
|
|
999
|
+
this.subtitlesEnabled = false
|
|
1000
|
+
this.$store.dispatch('setMediaSubtitles', false)
|
|
1001
|
+
},
|
|
1002
|
+
|
|
1003
|
+
/** ******************* Rendering Methods****************************** */
|
|
1004
|
+
|
|
1005
|
+
// TODO: Integrate customization of the subtitle (size, position ect...)
|
|
1006
|
+
setSubtitle() {},
|
|
1007
|
+
setMedia() {
|
|
1008
|
+
this.media = this.mediaToShow
|
|
1009
|
+
|
|
1010
|
+
// Setting Video / audio listeners
|
|
1011
|
+
if (this.media && this.media.type === 'pg_media') {
|
|
1012
|
+
this.hasMedia = true
|
|
1013
|
+
this.hasSound = true
|
|
1014
|
+
|
|
1015
|
+
if (this.media.mSubtitle) this.hasSubtitle = true
|
|
1016
|
+
|
|
1017
|
+
if (this.media.mTranscript) this.hasTranscript = true
|
|
1018
|
+
} else if (this.media && this.media.type === 'pg_animation') {
|
|
1019
|
+
this.hasMedia = true
|
|
1020
|
+
this.hasSound = false
|
|
1021
|
+
}
|
|
1022
|
+
if (this.media && Object.keys(this.media).length < 1) {
|
|
1023
|
+
this.hasMedia = false
|
|
1024
|
+
this.hasSound = false
|
|
1025
|
+
this.hasTranscript = false
|
|
1026
|
+
this.hasSubtitle = false
|
|
1027
|
+
}
|
|
1028
|
+
}, // END setMedia
|
|
1029
|
+
|
|
1030
|
+
/** @description - methode to set A11Y support for screen reader on media element (video/audio/animation) */
|
|
1031
|
+
setMediaA11Y() {
|
|
1032
|
+
if (!this.hasMedia) return
|
|
1033
|
+
|
|
1034
|
+
const w = this.$i18n.locale === 'en' ? 'at' : 'à'
|
|
1035
|
+
const dm =
|
|
1036
|
+
this.$i18n.locale === 'en'
|
|
1037
|
+
? `${this.media.mType} duration`
|
|
1038
|
+
: `durée ${this.media.mType}`
|
|
1039
|
+
|
|
1040
|
+
this.mediaA11Y.valMax = this.isAnimation
|
|
1041
|
+
? Math.round(this.media.timeline.totalDuration())
|
|
1042
|
+
: Math.round(this.media.mElement.duration) // media duration in floating-point
|
|
1043
|
+
|
|
1044
|
+
//get media Current time
|
|
1045
|
+
this.mediaA11Y.valNow = this.isAnimation
|
|
1046
|
+
? Math.round(this.media.timeline.time())
|
|
1047
|
+
: Math.round(this.media.mElement.currentTime)
|
|
1048
|
+
|
|
1049
|
+
//format text output for screen reader
|
|
1050
|
+
let currentTime = this.timecode.split(':')
|
|
1051
|
+
let fullTime = this.duration.split(':')
|
|
1052
|
+
let hrsTxt, mnTxt, ssTxt, HRSTxt, MNTxt, SSTxt, fTimeTxt, cTimeTxt
|
|
1053
|
+
|
|
1054
|
+
const mediaTitle = this.media.mTitle || this.media.mType
|
|
1055
|
+
|
|
1056
|
+
this.mediaA11Y.label = this.$t('a11y_sr.seek_slider')
|
|
1057
|
+
|
|
1058
|
+
//format string to remove leading 0 digit for time
|
|
1059
|
+
const formatNum = (n) => {
|
|
1060
|
+
return n[0] == '0' ? n.substring(1).trim() : n.trim()
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const formatMsg = (x, y) => {
|
|
1064
|
+
let formatStr = this.$t('a11y_sr.range_expression')
|
|
1065
|
+
.replace('{x}', x)
|
|
1066
|
+
.replace('{y}', y)
|
|
1067
|
+
|
|
1068
|
+
return formatStr
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
switch (fullTime.length) {
|
|
1072
|
+
case 3:
|
|
1073
|
+
if (!currentTime.length) return
|
|
1074
|
+
|
|
1075
|
+
//format current hours text text with plurialization apply
|
|
1076
|
+
hrsTxt =
|
|
1077
|
+
currentTime.length == 3 && parseInt(currentTime[0]) > 0
|
|
1078
|
+
? `${formatNum(currentTime[0])} ${this.$tc(
|
|
1079
|
+
'a11y_sr.time.hours',
|
|
1080
|
+
formatNum(currentTime[0])
|
|
1081
|
+
)}`
|
|
1082
|
+
: ''
|
|
1083
|
+
|
|
1084
|
+
//format minutes text text with plurialization apply
|
|
1085
|
+
mnTxt =
|
|
1086
|
+
parseInt(currentTime[1]) > 0
|
|
1087
|
+
? `${formatNum(currentTime[1])} ${this.$tc(
|
|
1088
|
+
'a11y_sr.time.minute',
|
|
1089
|
+
formatNum(currentTime[1])
|
|
1090
|
+
)}`
|
|
1091
|
+
: ''
|
|
1092
|
+
|
|
1093
|
+
//format seconds text with plurialization apply
|
|
1094
|
+
ssTxt =
|
|
1095
|
+
parseInt(currentTime[2]) > 0
|
|
1096
|
+
? `${formatNum(currentTime[2])} ${this.$tc(
|
|
1097
|
+
'a11y_sr.time.second',
|
|
1098
|
+
formatNum(currentTime[2])
|
|
1099
|
+
)}`
|
|
1100
|
+
: `0 ${this.$tc('a11y_sr.time.second')}`
|
|
1101
|
+
|
|
1102
|
+
HRSTxt =
|
|
1103
|
+
parseInt(fullTime[0]) > 0
|
|
1104
|
+
? `${formatNum(fullTime[0])} ${this.$tc(
|
|
1105
|
+
'a11y_sr.time.hours',
|
|
1106
|
+
formatNum(fullTime[0])
|
|
1107
|
+
)}`
|
|
1108
|
+
: ''
|
|
1109
|
+
|
|
1110
|
+
MNTxt =
|
|
1111
|
+
parseInt(fullTime[1]) > 0
|
|
1112
|
+
? `${formatNum(fullTime[1])} ${this.$tc(
|
|
1113
|
+
'a11y_sr.time.minute',
|
|
1114
|
+
formatNum(fullTime[1])
|
|
1115
|
+
)}`
|
|
1116
|
+
: ''
|
|
1117
|
+
|
|
1118
|
+
SSTxt =
|
|
1119
|
+
parseInt(fullTime[2]) > 0
|
|
1120
|
+
? `${formatNum(fullTime[2])} ${this.$tc(
|
|
1121
|
+
'a11y_sr.time.second',
|
|
1122
|
+
formatNum(fullTime[2])
|
|
1123
|
+
)}`
|
|
1124
|
+
: ''
|
|
1125
|
+
|
|
1126
|
+
cTimeTxt = `${hrsTxt} ${mnTxt} ${ssTxt}`
|
|
1127
|
+
fTimeTxt = `${HRSTxt} ${MNTxt} ${SSTxt}`
|
|
1128
|
+
|
|
1129
|
+
//Format to show 0 when no value is present
|
|
1130
|
+
cTimeTxt = cTimeTxt.trim().length ? cTimeTxt.trim() : 0
|
|
1131
|
+
|
|
1132
|
+
// Set sr text for timeCode && duration
|
|
1133
|
+
this.mediaA11Y.timeCode = `${this.media.mType} ${w} ${cTimeTxt}`
|
|
1134
|
+
this.mediaA11Y.duration = `${dm} ${fTimeTxt}`
|
|
1135
|
+
this.mediaA11Y.valueText = `${mediaTitle} ${formatMsg(
|
|
1136
|
+
cTimeTxt,
|
|
1137
|
+
fTimeTxt
|
|
1138
|
+
)}`
|
|
1139
|
+
|
|
1140
|
+
break
|
|
1141
|
+
case 2:
|
|
1142
|
+
if (!currentTime.length) return
|
|
1143
|
+
|
|
1144
|
+
//format current time text
|
|
1145
|
+
mnTxt =
|
|
1146
|
+
parseInt(currentTime[0]) > 0
|
|
1147
|
+
? `${formatNum(currentTime[0])} ${this.$tc(
|
|
1148
|
+
'a11y_sr.time.minute',
|
|
1149
|
+
formatNum(currentTime[0])
|
|
1150
|
+
)}`
|
|
1151
|
+
: ''
|
|
1152
|
+
ssTxt =
|
|
1153
|
+
parseInt(currentTime[1]) > 0
|
|
1154
|
+
? `${formatNum(currentTime[1])} ${this.$tc(
|
|
1155
|
+
'a11y_sr.time.second',
|
|
1156
|
+
formatNum(currentTime[1])
|
|
1157
|
+
)}`
|
|
1158
|
+
: `0 ${this.$tc('a11y_sr.time.second')}`
|
|
1159
|
+
|
|
1160
|
+
// format full time text
|
|
1161
|
+
MNTxt =
|
|
1162
|
+
parseInt(fullTime[0]) > 0
|
|
1163
|
+
? `${formatNum(fullTime[0])} ${this.$tc(
|
|
1164
|
+
'a11y_sr.time.minute',
|
|
1165
|
+
formatNum(fullTime[0])
|
|
1166
|
+
)}`
|
|
1167
|
+
: ''
|
|
1168
|
+
SSTxt =
|
|
1169
|
+
parseInt(fullTime[1]) > 0
|
|
1170
|
+
? `${formatNum(fullTime[1])} ${this.$tc(
|
|
1171
|
+
'a11y_sr.time.second',
|
|
1172
|
+
formatNum(fullTime[1])
|
|
1173
|
+
)}`
|
|
1174
|
+
: ''
|
|
1175
|
+
|
|
1176
|
+
cTimeTxt = `${mnTxt} ${ssTxt}`
|
|
1177
|
+
fTimeTxt = `${MNTxt} ${SSTxt}`
|
|
1178
|
+
|
|
1179
|
+
//Format to show 0 when no value is present
|
|
1180
|
+
cTimeTxt = cTimeTxt.trim().length ? cTimeTxt.trim() : 0
|
|
1181
|
+
|
|
1182
|
+
// Set sr text for timeCode && duration
|
|
1183
|
+
this.mediaA11Y.timeCode = `${this.media.mType} ${w} ${cTimeTxt}`
|
|
1184
|
+
this.mediaA11Y.duration = `${dm} ${fTimeTxt}`
|
|
1185
|
+
|
|
1186
|
+
this.mediaA11Y.valueText = `${mediaTitle} ${formatMsg(
|
|
1187
|
+
cTimeTxt,
|
|
1188
|
+
fTimeTxt
|
|
1189
|
+
)}`
|
|
1190
|
+
|
|
1191
|
+
break
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
</script>
|
|
1197
|
+
<style lang="scss">
|
|
1198
|
+
/*
|
|
1199
|
+
Playbar hover/leave transition
|
|
1200
|
+
*/
|
|
1201
|
+
#playbar-wrapper {
|
|
1202
|
+
width: 100%;
|
|
1203
|
+
position: absolute;
|
|
1204
|
+
left: 0;
|
|
1205
|
+
position: fixed;
|
|
1206
|
+
//z-index: 99999;
|
|
1207
|
+
z-index: 10;
|
|
1208
|
+
bottom: 0;
|
|
1209
|
+
|
|
1210
|
+
* {
|
|
1211
|
+
-webkit-user-select: none; /* Safari */
|
|
1212
|
+
-ms-user-select: none; /* IE 10 and IE 11 */
|
|
1213
|
+
user-select: none; /* Standard syntax */
|
|
1214
|
+
}
|
|
1215
|
+
&.minimized {
|
|
1216
|
+
bottom: -7px;
|
|
1217
|
+
#playbar-btns {
|
|
1218
|
+
#playbar-flex-wrap {
|
|
1219
|
+
.playbar-btn {
|
|
1220
|
+
background-color: transparent;
|
|
1221
|
+
border: none;
|
|
1222
|
+
padding: 0px;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
#playbar-btns {
|
|
1229
|
+
width: 55%;
|
|
1230
|
+
max-width: 485px;
|
|
1231
|
+
min-width: 290px;
|
|
1232
|
+
height: 70px;
|
|
1233
|
+
margin: 0 auto;
|
|
1234
|
+
|
|
1235
|
+
#playbar-flex-wrap {
|
|
1236
|
+
display: flex;
|
|
1237
|
+
flex-direction: row;
|
|
1238
|
+
justify-content: space-around;
|
|
1239
|
+
align-items: center;
|
|
1240
|
+
height: 100%;
|
|
1241
|
+
position: relative;
|
|
1242
|
+
#playbar-current-time {
|
|
1243
|
+
flex: 0 0 45px;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
.playbar-btn {
|
|
1247
|
+
cursor: pointer;
|
|
1248
|
+
display: flex;
|
|
1249
|
+
align-items: center;
|
|
1250
|
+
padding: 0px;
|
|
1251
|
+
|
|
1252
|
+
svg {
|
|
1253
|
+
pointer-events: none;
|
|
1254
|
+
margin: 0 auto;
|
|
1255
|
+
display: block;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
&#playbar-volume {
|
|
1259
|
+
position: relative;
|
|
1260
|
+
#playbar-volume-slider-zone {
|
|
1261
|
+
background-color: transparent;
|
|
1262
|
+
width: 90px;
|
|
1263
|
+
height: 120px;
|
|
1264
|
+
position: absolute;
|
|
1265
|
+
top: -120px;
|
|
1266
|
+
left: -27px;
|
|
1267
|
+
|
|
1268
|
+
#playbar-volume-slider {
|
|
1269
|
+
width: 30px;
|
|
1270
|
+
height: 100px;
|
|
1271
|
+
margin: 6px auto;
|
|
1272
|
+
padding-bottom: 100px;
|
|
1273
|
+
position: relative;
|
|
1274
|
+
|
|
1275
|
+
.range-slider {
|
|
1276
|
+
--progress-radius: 20px;
|
|
1277
|
+
--track-height: calc(var(--thumb-size) / 2);
|
|
1278
|
+
--thumb-size: 20px;
|
|
1279
|
+
|
|
1280
|
+
--value-a: Clamp(
|
|
1281
|
+
var(--min),
|
|
1282
|
+
var(--value, 0),
|
|
1283
|
+
var(--max)
|
|
1284
|
+
); // default value ("--value" is used in single-range markup)
|
|
1285
|
+
--value-b: var(--value, 0); // default value
|
|
1286
|
+
|
|
1287
|
+
--completed-a: calc(
|
|
1288
|
+
(var(--value-a) - var(--min)) / (var(--max) - var(--min)) *
|
|
1289
|
+
100
|
|
1290
|
+
);
|
|
1291
|
+
--completed-b: calc(
|
|
1292
|
+
(var(--value-b) - var(--min)) / (var(--max) - var(--min)) *
|
|
1293
|
+
100
|
|
1294
|
+
);
|
|
1295
|
+
--ca: Min(var(--completed-a), var(--completed-b));
|
|
1296
|
+
--cb: Max(var(--completed-a), var(--completed-b));
|
|
1297
|
+
|
|
1298
|
+
// breakdown of the below super-complex brain-breaking CSS math:
|
|
1299
|
+
// "clamp" is used to ensure either "-1" or "1"
|
|
1300
|
+
// "calc" is used to inflat the outcome into a huge number, to get rid of any value between -1 & 1
|
|
1301
|
+
// if absolute diff of both completed % is above "5" (%)
|
|
1302
|
+
// ".001" bumps the value just a bit, to avoid a scenario where calc resulted in "0" (then clamp will also be "0")
|
|
1303
|
+
--thumbs-too-close: Clamp(
|
|
1304
|
+
-1,
|
|
1305
|
+
1000 * (Min(1, Max(var(--cb) - var(--ca) - 5, -1)) + 0.001),
|
|
1306
|
+
1
|
|
1307
|
+
);
|
|
1308
|
+
--thumb-close-to-min: Min(
|
|
1309
|
+
1,
|
|
1310
|
+
Max(var(--ca) - 2, 0)
|
|
1311
|
+
); // 2% threshold
|
|
1312
|
+
--thumb-close-to-max: Min(
|
|
1313
|
+
1,
|
|
1314
|
+
Max(98 - var(--cb), 0)
|
|
1315
|
+
); // 2% threshold
|
|
1316
|
+
|
|
1317
|
+
@mixin thumb {
|
|
1318
|
+
appearance: none;
|
|
1319
|
+
height: var(--thumb-size);
|
|
1320
|
+
width: var(--thumb-size);
|
|
1321
|
+
pointer-events: auto;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
display: inline-block;
|
|
1325
|
+
height: Max(var(--track-height), var(--thumb-size));
|
|
1326
|
+
position: absolute;
|
|
1327
|
+
z-index: 1;
|
|
1328
|
+
top: 40px;
|
|
1329
|
+
left: -32px;
|
|
1330
|
+
z-index: 1;
|
|
1331
|
+
width: 95px;
|
|
1332
|
+
transform: rotate(270deg);
|
|
1333
|
+
|
|
1334
|
+
&__progress {
|
|
1335
|
+
--start-end: calc(var(--thumb-size) / 2);
|
|
1336
|
+
--clip-end: calc(100% - (var(--cb)) * 1%);
|
|
1337
|
+
--clip-start: calc(var(--ca) * 1%);
|
|
1338
|
+
--clip: inset(-20px var(--clip-end) -20px var(--clip-start));
|
|
1339
|
+
position: absolute;
|
|
1340
|
+
left: var(--start-end);
|
|
1341
|
+
right: var(--start-end);
|
|
1342
|
+
top: calc(var(--thumb-size) / 2 - var(--track-height) / 2);
|
|
1343
|
+
height: calc(var(--track-height));
|
|
1344
|
+
background: var(--progress-background, #eee);
|
|
1345
|
+
border: 2px solid;
|
|
1346
|
+
pointer-events: none;
|
|
1347
|
+
z-index: -1;
|
|
1348
|
+
border-radius: var(--progress-radius);
|
|
1349
|
+
|
|
1350
|
+
// fill area
|
|
1351
|
+
&::before {
|
|
1352
|
+
content: '';
|
|
1353
|
+
position: absolute;
|
|
1354
|
+
left: 0;
|
|
1355
|
+
right: 0;
|
|
1356
|
+
clip-path: var(--clip);
|
|
1357
|
+
top: 0;
|
|
1358
|
+
bottom: 0;
|
|
1359
|
+
z-index: 1;
|
|
1360
|
+
border-radius: inherit;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// shadow-effect
|
|
1364
|
+
&::after {
|
|
1365
|
+
content: '';
|
|
1366
|
+
position: absolute;
|
|
1367
|
+
top: 0;
|
|
1368
|
+
right: 0;
|
|
1369
|
+
bottom: 0;
|
|
1370
|
+
left: 0;
|
|
1371
|
+
pointer-events: none;
|
|
1372
|
+
border-radius: inherit;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
& > input {
|
|
1377
|
+
-webkit-appearance: none;
|
|
1378
|
+
width: 100%;
|
|
1379
|
+
height: var(--thumb-size);
|
|
1380
|
+
margin: 0;
|
|
1381
|
+
position: absolute;
|
|
1382
|
+
left: 0;
|
|
1383
|
+
cursor: -webkit-grab;
|
|
1384
|
+
cursor: grab;
|
|
1385
|
+
outline: none;
|
|
1386
|
+
background: none;
|
|
1387
|
+
|
|
1388
|
+
&:not(:only-of-type) {
|
|
1389
|
+
pointer-events: none;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
&::-webkit-slider-thumb {
|
|
1393
|
+
@include thumb;
|
|
1394
|
+
}
|
|
1395
|
+
&::-moz-range-thumb {
|
|
1396
|
+
@include thumb;
|
|
1397
|
+
}
|
|
1398
|
+
&::-ms-thumb {
|
|
1399
|
+
@include thumb;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
&:active {
|
|
1403
|
+
cursor: grabbing;
|
|
1404
|
+
z-index: 2; // when sliding left thumb over the right or vice-versa, make sure the moved thumb is on top
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
&:nth-of-type(1) {
|
|
1408
|
+
--is-left-most: Clamp(
|
|
1409
|
+
0,
|
|
1410
|
+
(var(--value-a) - var(--value-b)) * 99999,
|
|
1411
|
+
1
|
|
1412
|
+
);
|
|
1413
|
+
& + output {
|
|
1414
|
+
&:not(:only-of-type) {
|
|
1415
|
+
--flip: calc(var(--thumbs-too-close) * -1);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
&:nth-of-type(2) {
|
|
1421
|
+
--is-left-most: Clamp(
|
|
1422
|
+
0,
|
|
1423
|
+
(var(--value-b) - var(--value-a)) * 99999,
|
|
1424
|
+
1
|
|
1425
|
+
);
|
|
1426
|
+
& + output {
|
|
1427
|
+
--value: var(--value-b);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// non-multiple range should not clip start of progress bar
|
|
1432
|
+
&:only-of-type {
|
|
1433
|
+
~ .range-slider__progress {
|
|
1434
|
+
--clip-start: 0;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
& + output {
|
|
1439
|
+
--flip: -1;
|
|
1440
|
+
--x-offset: calc(var(--completed-b) * -1%);
|
|
1441
|
+
--pos: calc(
|
|
1442
|
+
((var(--value) - var(--min)) / (var(--max) - var(--min))) *
|
|
1443
|
+
68%
|
|
1444
|
+
);
|
|
1445
|
+
|
|
1446
|
+
opacity: 0;
|
|
1447
|
+
pointer-events: none;
|
|
1448
|
+
position: absolute;
|
|
1449
|
+
z-index: 5;
|
|
1450
|
+
left: var(--pos);
|
|
1451
|
+
transform: translate(
|
|
1452
|
+
var(--x-offset),
|
|
1453
|
+
calc(
|
|
1454
|
+
150% * var(--flip) -
|
|
1455
|
+
(var(--y-offset, 0px) + var(--value-offset-y)) *
|
|
1456
|
+
var(--flip)
|
|
1457
|
+
)
|
|
1458
|
+
);
|
|
1459
|
+
margin-left: -5px;
|
|
1460
|
+
margin-top: 45px;
|
|
1461
|
+
line-height: 0;
|
|
1462
|
+
transform: rotate(90deg);
|
|
1463
|
+
transition: all 0.12s ease-out, left 0s;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
#playbar-cc {
|
|
1473
|
+
position: absolute;
|
|
1474
|
+
top: -83px;
|
|
1475
|
+
right: 170px;
|
|
1476
|
+
|
|
1477
|
+
&.submenu_active {
|
|
1478
|
+
box-sizing: border-box;
|
|
1479
|
+
padding: 4px;
|
|
1480
|
+
}
|
|
1481
|
+
#subtitleMenuWrapper {
|
|
1482
|
+
position: absolute;
|
|
1483
|
+
width: 294px;
|
|
1484
|
+
|
|
1485
|
+
.subtitleBtns {
|
|
1486
|
+
width: 100%;
|
|
1487
|
+
height: 40px;
|
|
1488
|
+
padding: 0 24px;
|
|
1489
|
+
line-height: 40px;
|
|
1490
|
+
border: none;
|
|
1491
|
+
text-align: left;
|
|
1492
|
+
|
|
1493
|
+
svg {
|
|
1494
|
+
display: inline-block;
|
|
1495
|
+
margin-right: 20px;
|
|
1496
|
+
transform: translateY(-3px);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
#playbar-progress {
|
|
1505
|
+
-webkit-user-select: none; /* Safari */
|
|
1506
|
+
-ms-user-select: none; /* IE 10 and IE 11 */
|
|
1507
|
+
user-select: none; /* Standard syntax */
|
|
1508
|
+
width: 100%;
|
|
1509
|
+
height: 12px;
|
|
1510
|
+
position: relative;
|
|
1511
|
+
cursor: pointer;
|
|
1512
|
+
& > div {
|
|
1513
|
+
height: 100%;
|
|
1514
|
+
position: absolute;
|
|
1515
|
+
}
|
|
1516
|
+
#playbar-playback {
|
|
1517
|
+
min-width: 8px;
|
|
1518
|
+
#playbar-cursor {
|
|
1519
|
+
width: 16px;
|
|
1520
|
+
height: 16px;
|
|
1521
|
+
position: absolute;
|
|
1522
|
+
right: -8px;
|
|
1523
|
+
top: -2px;
|
|
1524
|
+
&:focus {
|
|
1525
|
+
transform: scale(1.1);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
#acessMenu {
|
|
1533
|
+
&:hover {
|
|
1534
|
+
svg {
|
|
1535
|
+
width: 21.642px !important;
|
|
1536
|
+
height: 16px !important;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
</style>
|