fcad-core-dragon 2.0.0-beta.0 → 2.0.0-beta.2

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.
Files changed (84) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +13 -18
  2. package/README.md +1 -1
  3. package/bk.scss +117 -0
  4. package/package.json +22 -40
  5. package/src/$locales/en.json +57 -19
  6. package/src/$locales/fr.json +66 -28
  7. package/src/components/AppBase.vue +790 -376
  8. package/src/components/AppBaseButton.vue +33 -5
  9. package/src/components/AppBaseErrorDisplay.vue +62 -25
  10. package/src/components/AppBaseModule.vue +831 -754
  11. package/src/components/AppBasePage.vue +60 -74
  12. package/src/components/AppCompAudio.vue +266 -0
  13. package/src/components/AppCompBranchButtons.vue +79 -89
  14. package/src/components/AppCompButtonProgress.vue +35 -61
  15. package/src/components/AppCompCarousel.vue +160 -249
  16. package/src/components/AppCompInputCheckBox.vue +9 -3
  17. package/src/components/AppCompInputDropdown.vue +2 -4
  18. package/src/components/AppCompInputRadio.vue +8 -15
  19. package/src/components/AppCompInputTextTable.vue +15 -12
  20. package/src/components/AppCompInputTextToFillDropdown.vue +16 -14
  21. package/src/components/AppCompInputTextToFillText.vue +2 -2
  22. package/src/components/AppCompJauge.vue +14 -3
  23. package/src/components/AppCompMenu.vue +284 -85
  24. package/src/components/AppCompMenuItem.vue +67 -92
  25. package/src/components/AppCompNavigation.vue +945 -0
  26. package/src/components/AppCompNoteCall.vue +141 -0
  27. package/src/components/AppCompNoteCredit.vue +267 -0
  28. package/src/components/AppCompPlayBar.vue +1122 -1391
  29. package/src/components/AppCompPlayBarProgress.vue +73 -0
  30. package/src/components/AppCompPopUp.vue +195 -135
  31. package/src/components/AppCompPopover.vue +27 -0
  32. package/src/components/AppCompQuiz.vue +90 -113
  33. package/src/components/AppCompQuizRecall.vue +277 -0
  34. package/src/components/AppCompSVG.vue +335 -0
  35. package/src/components/AppCompSettingsMenu.vue +7 -8
  36. package/src/components/AppCompTableOfContent.vue +264 -88
  37. package/src/components/AppCompTranscript.vue +19 -0
  38. package/src/components/AppCompVideoPlayer.vue +380 -0
  39. package/src/components/BaseModule.vue +37 -114
  40. package/src/main.js +130 -85
  41. package/src/mixins/$mediaMixins.js +827 -0
  42. package/src/mixins/$pageMixins.js +149 -115
  43. package/src/mixins/$quizMixins.js +12 -26
  44. package/src/mixins/timerMixin.js +39 -16
  45. package/src/module/store.js +218 -78
  46. package/src/module/xapi/ADL.js +90 -53
  47. package/src/module/xapi/Crypto/Hasher.js +8 -8
  48. package/src/module/xapi/Crypto/WordArray.js +6 -6
  49. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
  50. package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
  51. package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
  52. package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
  53. package/src/module/xapi/Crypto/encoders/Base.js +7 -7
  54. package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
  55. package/src/module/xapi/Crypto/encoders/Hex.js +4 -3
  56. package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
  57. package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
  58. package/src/module/xapi/Statement/index.js +1 -1
  59. package/src/module/xapi/launch.js +10 -10
  60. package/src/module/xapi/utils.js +17 -17
  61. package/src/module/xapi/wrapper.js +127 -54
  62. package/src/module/xapi/xapiStatement.js +29 -29
  63. package/src/plugins/gsap.js +4 -1
  64. package/src/plugins/helper.js +58 -24
  65. package/src/plugins/i18n.js +23 -10
  66. package/src/plugins/idb.js +1 -0
  67. package/src/plugins/scorm.js +14 -14
  68. package/src/public/index.html +1 -1
  69. package/src/router/index.js +40 -0
  70. package/src/router/routes.js +317 -0
  71. package/src/shared/generalfuncs.js +91 -9
  72. package/src/shared/validators.js +959 -0
  73. package/.prettierrc.js +0 -5
  74. package/babel.config.js +0 -3
  75. package/src/components/AppBaseDragChoice.vue +0 -91
  76. package/src/components/AppBaseDropZone.vue +0 -112
  77. package/src/components/AppCompDragAndDrop.vue +0 -339
  78. package/src/components/AppCompInputAssociation.vue +0 -332
  79. package/src/components/AppCompMediaPlayer.vue +0 -365
  80. package/src/components/AppCompNavigationFull.vue +0 -1791
  81. package/src/components/AppCompToolTip.vue +0 -94
  82. package/src/plugins/timeManager.js +0 -77
  83. package/src/routes.js +0 -734
  84. package/vue.config.js +0 -83
@@ -1,1540 +1,1271 @@
1
- <!-- Add Bref Description of what component does and need
2
- -->
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-->
3
4
  <template>
4
5
  <div
5
- id="playbar-wrapper"
6
- :class="{ minimized: barMinimized }"
7
- @mouseover="playbarOver"
6
+ ref="$pb-container"
7
+ class="pb-container"
8
+ :class="{ video: mediaToPlay.mType === 'video' }"
8
9
  >
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>
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
51
 
52
- <div class="range-slider__progress"></div>
53
- </div>
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
+ savedIsPlaying = isPlaying
99
+ progressThumbDown = true
100
+ "
101
+ @mouseover="progressThumbHover = true"
102
+ @mouseleave="progressThumbHover = false"
103
+ ></div>
54
104
  </div>
55
105
  </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"
106
+ <!--------------------------------- PLAY-BAR ToolTip (video only) ----------------------------------->
107
+ <div
108
+ v-if="mediaToPlay.mType === 'video'"
109
+ ref="$seek-tooltip"
110
+ aria-hidden="true"
111
+ class="seek-tooltip"
63
112
  >
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>
113
+ {{ tooTipTimeCode }}
114
+ </div>
90
115
  </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"
116
+ </div>
117
+ <div class="pb-controls">
118
+ <div class="pb-areas left">
119
+ <!--------------------------------- PLAY-BAR Play ------------------------------------>
120
+ <app-base-button
121
+ ref="$btn-play"
122
+ class="playbar-btn playbar-play"
123
+ :class="{ displayLabel: btnsLabelDisplay['playbar-play'] === true }"
124
+ :aria-label="
125
+ playLabel + (mediaToPlay.mType === 'video' ? ' secondaire' : '')
126
+ "
127
+ :data-title="`${playLabel} (k)`"
128
+ @click="
129
+ () => {
130
+ mediaToPlay.mType === 'video' ? handlePlayVideo() : togglePlay()
131
+ }
132
+ "
133
+ @mouseenter="activateLabel($event)"
134
+ @mouseleave="deactivateLabel($event)"
135
+ @focus="activateLabel($event)"
136
+ @blur="deactivateLabel($event)"
189
137
  >
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"
138
+ <svg
139
+ v-show="!canReplay && !isPlaying"
140
+ class="app-icons-svg play-icon"
215
141
  >
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"
142
+ <use href="#play-icon" />
143
+ </svg>
144
+ <svg
145
+ v-show="isPlaying && !canReplay"
146
+ class="app-icons-svg pause-icon"
241
147
  >
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"
148
+ <use href="#pause-icon" />
149
+ </svg>
150
+ <svg v-show="canReplay" class="app-icons-svg replay-icon">
151
+ <use href="#replay-icon" />
152
+ </svg>
153
+ </app-base-button>
154
+ <!--------------------------------- PLAY-BAR Timer ------------------------------------>
155
+ <div class="pb-timer">
156
+ <span
157
+ aria-hidden="true"
158
+ style="-webkit-user-select: none"
159
+ draggable="false"
264
160
  >
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"
161
+ {{ timecode }} / {{ mediaDurationTime }}
162
+ </span>
163
+ </div>
164
+ <!--------------------------------- PLAY-BAR Progress (audio only) -------------------------------------->
165
+ <div
166
+ v-if="mediaToPlay.mType === 'audio'"
167
+ ref="$playbar-timeline"
168
+ class="pb-timeline"
169
+ >
170
+ <div ref="$progress-area" class="progress-area">
171
+ <div
172
+ id="progress-bar"
173
+ ref="$progress-bar"
174
+ draggable="false"
175
+ tabindex="0"
176
+ class="pb-progress-bar"
177
+ role="slider"
178
+ aria-valuemin="0"
179
+ :aria-label="mediaA11Y.label"
180
+ :aria-valuenow="mediaA11Y.valNow"
181
+ :aria-valuemax="mediaA11Y.valMax"
182
+ :aria-valuetext="mediaA11Y.valueText"
183
+ @focus="changeFocusState('progressBar', true)"
184
+ @blur="changeFocusState('progressBar', false)"
271
185
  >
272
- <g
273
- id="Group_6950"
274
- data-name="Group 6950"
275
- transform="translate(-1.04 -2.45)"
186
+ <!--Class progress-animation is apply when we are not using the thumb to change the progression-->
187
+ <div
188
+ id="progress-indicator"
189
+ ref="$progress-indicator"
190
+ draggable="false"
191
+ class="progress-indicator"
192
+ :class="{ 'progress-animation': !progressThumbDown }"
193
+ :style="{ width: progressBarPercentage + '%' }"
276
194
  >
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>
195
+ <!--Mousedown validate if the thumb is cliqued (if yes, the mouse is follow to modify the progress)
196
+ MouseOver/MouseOut deactivate click event on progressBar if the mouse hovered the thumb-->
197
+ <div
198
+ ref="$progress-thumb"
199
+ draggable="false"
200
+ class="progress-thumb"
201
+ style="z-index: 9"
202
+ @mousedown="
203
+ savedIsPlaying = isPlaying
204
+ progressThumbDown = true
205
+ "
206
+ @mouseover="progressThumbHover = true"
207
+ @mouseleave="progressThumbHover = false"
208
+ ></div>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <!--------------------------------- PLAY-BAR Volume -------------------------------------->
214
+ <app-base-button
215
+ id="playbar-volume"
216
+ ref="$btn-volume"
217
+ class="volume-btn"
218
+ :class="{
219
+ displayLabel: btnsLabelDisplay['playbar-volume'] === true
220
+ }"
221
+ tabindex="0"
222
+ :aria-label="volumeLabel"
223
+ :data-title="`${volumeLabel} (m)`"
224
+ @click="toggleMute"
225
+ @mouseenter="activateLabel($event)"
226
+ @mouseleave="deactivateLabel($event)"
227
+ @focus="activateLabel($event)"
228
+ @blur="deactivateLabel($event)"
229
+ >
230
+ <svg v-show="volumeState == 'muted'" class="app-icons-svg">
231
+ <use href="#volume-mute-icon" />
232
+ </svg>
233
+ <svg v-show="volumeState == 'high'" class="app-icons-svg">
234
+ <use href="#volume-high-icon" />
235
+ </svg>
236
+ <svg
237
+ v-show="volumeState == 'low'"
238
+ class="app-icons-svg volume-low-icon"
239
+ >
240
+ <use href="#volume-low-icon" />
241
+ </svg>
242
+ </app-base-button>
243
+ <div ref="zone-volume" class="volume-controls">
244
+ <input
245
+ ref="$volume-slider"
246
+ class="volume-slider"
247
+ :class="{
248
+ displayLabel: btnsLabelDisplay['volume-slider'] === true
249
+ }"
250
+ :value="currentVolume"
251
+ :aria-valuetext="volumeLevelA11Y"
252
+ type="range"
253
+ max="1"
254
+ min="0"
255
+ step="0.01"
256
+ tabindex="0"
257
+ :aria-label="`${$t('button.volume')}`"
258
+ @click="updateVolumeLevel"
259
+ @mouseenter="activateLabel($event.currentTarget)"
260
+ @mouseleave="deactivateLabel($event.currentTarget)"
261
+ @focus="
262
+ activateLabel($event.currentTarget),
263
+ changeFocusState('volumeSlider', true)
264
+ "
265
+ @blur="
266
+ deactivateLabel($event.currentTarget),
267
+ changeFocusState('volumeSlider', false)
268
+ "
269
+ />
270
+ <span class="volume-label" aria-hidden="true">
271
+ {{ $t('button.volume') }}
272
+ </span>
273
+ <!--using span because ::before on input is not supported by firefox-->
274
+ <span
275
+ ref="$volume-progress"
276
+ class="volume-progress"
277
+ :style="{
278
+ '--background-size-vs': `calc(${volumeSliderBackground} - 2px)`
279
+ }"
280
+ ></span>
293
281
  </div>
294
282
  </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>
283
+ <!--------------------------------- PLAY-BAR CC (video only) ------------------------------------------>
284
+ <div v-if="mediaToPlay.mType === 'video'" class="pb-areas right">
285
+ <app-base-button
286
+ id="btn-subtitles"
287
+ ref="$btn-subtitle"
288
+ class="btn subtitleBtns"
289
+ :aria-label="ccLabel"
290
+ :data-title="`${ccLabel} (c)`"
291
+ :disabled="!hasSubtitle"
292
+ :class="{
293
+ md_disabled: !hasSubtitle,
294
+ displayLabel: btnsLabelDisplay['btn-subtitles'] === true
295
+ }"
296
+ @click="toggleViewSubtitle()"
297
+ @mouseenter="activateLabel($event)"
298
+ @mouseleave="deactivateLabel($event)"
299
+ @focus="activateLabel($event)"
300
+ @blur="deactivateLabel($event)"
301
+ >
302
+ <svg
303
+ v-show="(hasSubtitle && !subtitlesEnabled) || !hasSubtitle"
304
+ class="app-icons-svg"
305
+ >
306
+ <use href="#subtitle-off-icon" />
307
+ </svg>
308
+ <svg v-show="hasSubtitle && subtitlesEnabled" class="app-icons-svg">
309
+ <use href="#subtitle-on-icon" />
310
+ </svg>
311
+ </app-base-button>
312
+ <!--------------------------------- PLAY-BAR Transcript ----------------------------------->
313
+ <app-base-button
314
+ id="btn-transcript"
315
+ ref="$btn-transcript"
316
+ class="btn-transcript"
317
+ :aria-label="transcriptLabel"
318
+ :data-title="`${transcriptLabel} (t)`"
319
+ :disabled="!hasTranscript || fullscreenOn"
320
+ :class="{
321
+ md_disabled: !hasTranscript || fullscreenOn,
322
+ displayLabel: btnsLabelDisplay['btn-transcript'] === true
323
+ }"
324
+ @click="toggleViewTranscript()"
325
+ @mouseenter="activateLabel($event)"
326
+ @mouseleave="deactivateLabel($event)"
327
+ @focus="activateLabel($event)"
328
+ @blur="deactivateLabel($event)"
329
+ >
330
+ <svg
331
+ v-show="(hasTranscript && !transcriptEnabled) || !hasTranscript"
332
+ class="app-icons-svg"
333
+ >
334
+ <use href="#transcript-off-icon" />
335
+ </svg>
336
+ <svg
337
+ v-show="hasTranscript && transcriptEnabled"
338
+ class="app-icons-svg"
339
+ >
340
+ <use href="#transcript-on-icon" />
341
+ </svg>
342
+ </app-base-button>
343
+ <!--------------------------------- PLAY-BAR FullScreen ----------------------------------->
344
+ <app-base-button
345
+ id="btn-fullscreen"
346
+ ref="$btn-fullscreen"
347
+ class="fullscreenBtns"
348
+ :aria-label="fullscreenLabel"
349
+ :disabled="transcriptEnabled"
350
+ :class="{
351
+ md_disabled: transcriptEnabled,
352
+ displayLabel: btnsLabelDisplay['btn-fullscreen'] === true
353
+ }"
354
+ :data-title="`${fullscreenLabel} (f)`"
355
+ @click="toggleFullScreen()"
356
+ @mouseenter="activateLabel($event)"
357
+ @mouseleave="deactivateLabel($event)"
358
+ @focus="activateLabel($event)"
359
+ @blur="deactivateLabel($event)"
360
+ >
361
+ <svg v-show="!fullscreenOn" class="app-icons-svg">
362
+ <use href="#fullscreen-icon" />
363
+ </svg>
364
+ <svg v-show="fullscreenOn" class="app-icons-svg">
365
+ <use href="#fullscreen-exit-icon" />
366
+ </svg>
367
+ </app-base-button>
301
368
  </div>
302
369
  </div>
303
370
  </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
371
  </div>
329
372
  </template>
330
373
 
331
374
  <script>
375
+ import $extendsMedia from '../mixins/$mediaMixins'
332
376
  import { mapGetters } from 'vuex'
333
377
 
334
378
  export default {
379
+ mixins: [$extendsMedia],
335
380
  props: {
336
- appStatus: { type: Boolean, required: true },
337
- mediaToShow: { type: [Object, Boolean], default: false }
381
+ mediaToPlay: { type: [Object, Boolean], default: false }
338
382
  },
339
383
 
340
384
  data() {
341
385
  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,
386
+ id: `plyr_${this.mediaToPlay.id}`,
387
+ //Playback animation
388
+ playClicked: false,
389
+ playBackAnim: true,
390
+ //ProgressSeek
391
+ progressSeek: null,
392
+ //Tooltip
393
+ seekTooltip: null,
394
+ tooTipTimeCode: '00:00',
395
+ //Hiding playbar animation
384
396
  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
- }
397
+ delayUntilHide: 5000,
398
+ hideTimer: null,
399
+ //Transcript
400
+ transcriptEnabled: false,
401
+ transcriptToShow: null,
402
+ //Fullscreen
403
+ fullscreenOn: false
395
404
  }
396
405
  },
397
406
  computed: {
398
407
  ...mapGetters([
399
- 'getCurrentPage',
400
- 'getModuleChildren',
401
- 'hasMediaElOrTimeline',
402
- 'getCurrentMediaDuration',
403
- 'getIntroStatus',
404
- 'getCurrentBrowser',
405
- 'getAutoplayEnabled',
406
- 'getMediaVolume',
407
- 'getModuleInfo',
408
- 'getMediaSubtitles',
409
- 'getUserInteraction'
408
+ // 'getCurrentPage',
409
+ // 'getModuleChildren',
410
+ //'hasMediaElOrTimeline',
411
+ //'getCurrentMediaDuration',
412
+ //'getCurrentBrowser',
413
+ // 'getAutoplayEnabled',
414
+ //'getModuleInfo',
415
+ //'getMediaSubtitles',
416
+ //'getUserInteraction'
410
417
  ]),
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
418
+ //Transcript
419
+ hasTranscript() {
420
+ if (!this.mediaToPlay) return
421
+ return this.mediaToPlay.mTranscript || false
427
422
  },
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
- }
423
+ //Subtitle
424
+ hasSubtitle() {
425
+ if (!this.mediaToPlay) return
426
+ return this.mediaToPlay.mSubtitles || false
436
427
  },
437
- volumeStyle() {
438
- return (
439
- '--min:0; --max:1; --step:0.05; --value:' +
440
- this.volDefault +
441
- '; --text-value:"' +
442
- this.volDefault +
443
- '";'
444
- )
428
+ subtitlesEnabled() {
429
+ if (!this.hasSubtitle) return false
430
+ else return this.getMediaSubtitles
445
431
  },
446
- label() {
432
+ //mediaContainer (only used for video)
433
+ mediaContainer() {
434
+ if (!this.mediaToPlay) return
435
+ return this.mediaToPlay.mMediaContainer
436
+ },
437
+
438
+ //MediaDuration :Number (used for tooltip)
439
+ mediaDuration() {
440
+ return this.mediaToPlay.mElement.duration
441
+ ? this.mediaToPlay.mElement.duration
442
+ : 0
443
+ },
444
+ //Labels
445
+ fullscreenLabel() {
447
446
  let label = null
448
- if (this.isPlaying) {
449
- label = `${this.$t('button.pause')}`
450
- } else {
451
- label = `${this.$t('button.play')}`
452
- }
447
+ if (this.fullscreenOn) label = `${this.$t('button.full_screen_off')}`
448
+ else label = `${this.$t('button.full_screen_on')}`
453
449
  return label
454
450
  },
455
- volumeLabel() {
451
+ transcriptLabel() {
456
452
  let label = null
457
- if (this.muted) {
458
- label = `${this.$t('button.unmute')}`
459
- } else {
460
- label = `${this.$t('button.mute')}`
461
- }
453
+ if (this.transcriptEnabled) label = `${this.$t('button.transcript_off')}`
454
+ else label = `${this.$t('button.transcript_on')}`
462
455
  return label
463
456
  },
464
- displayVol() {
465
- let test = Math.floor(this.volDefault * 100)
466
- return test
457
+ ccLabel() {
458
+ let label = null
459
+ if (this.subtitlesEnabled) label = `${this.$t('button.subtitle_off')}`
460
+ else label = `${this.$t('button.subtitle_on')}`
461
+ return label
467
462
  }
468
463
  },
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
- }
464
+ mounted() {
465
+ //initialize media $refs
466
+ this.initializeMediaElm()
467
+ //initialize video $refs
468
+ if (this.mediaToPlay.mType === 'video') this.initializeVideoElm()
469
+
470
+ //Set eventlistener for media
471
+ this.setMediaHandlers()
472
+ //Set eventlistener for video
473
+ if (this.mediaToPlay.mType === 'video') this.setVideoHandlers()
474
+
475
+ //Set label for ProgressBar
476
+ this.setProgressBarA11Y()
477
+ //Set initial volume and duration
478
+ this.updateVolumeLevel()
479
+
480
+ //update the the information of the media element linked to this play bar. playbar instance will be added to the media info
481
+ this.$store.dispatch('updateCurrentMediaElements', {
482
+ id: this.mediaToPlay.id,
483
+ ...this.$parent.$refs
484
+ })
551
485
  },
552
- beforeDestroy() {
553
- this.saveToLrs()
486
+ beforeUnmount() {
554
487
  if (this.isPlaying) {
555
- this.playClick()
556
- }
557
- this.cancelTimeout()
558
- if (this.mediaHandlersSet) {
559
- this.removeMediaHandlers()
488
+ this.togglePlay()
560
489
  }
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
490
+ this.removeMediaHandlers()
570
491
 
571
- //TODO: Trying to get the focus on the volume slider to them be able to inscrease/descrease volume with key up/key down
492
+ if (this.mediaToPlay.mType === 'video') this.removeVideoHandlers()
572
493
 
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
- // })
494
+ if (this.firefoxTrack) {
495
+ this.firefoxTrack.removeEventListener('cuechange', this.fireFoxMoveCC)
496
+ }
497
+ //this.$bus.$off('play-media', this.handleMediaControls)
498
+ this.$bus.$off('transcript-hidden', this.resetTranscript)
499
+ return this.$bus.$off('close-sidebar', 'ctxTranscript')
589
500
  },
590
501
  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
502
+ /**
503
+ * @description - Set the DOM elements specific for video
643
504
  */
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
505
+ initializeVideoElm() {
506
+ //SeekTooltip
507
+ this.seekTooltip = this.$refs['$seek-tooltip']
508
+ //Transcript
509
+ this.$bus.$on('transcript-hidden', this.resetTranscript)
510
+ this.setTranscript()
712
511
  },
713
- volumeLeave() {
714
- this.volumeSliderOpen = false
715
- },
716
- volumeClick(ev) {
717
- if (ev.target.id !== 'playbar-volume') {
718
- return false
719
- }
720
512
 
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)
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
748
536
  }
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
537
 
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()
538
+ this.seekTooltip.style.left = `${offsetX}px` // update visual position of tooltip
783
539
 
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)
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
812
546
  }
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
547
+ let skipTo = percentage * this.mediaDuration
548
+ //Update the Tooltip time
549
+ this.tooTipTimeCode = this.$helper.formatTime(skipTo)
820
550
  },
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()
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)
835
564
  },
836
-
837
- playVideo() {
838
- if (this.isPlaying) {
839
- this.media.mElement.pause()
840
- } else {
841
- this.media.mElement.play()
842
- }
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)
843
578
  },
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()
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
852
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
853
597
  },
854
- setCursorPosition(xPos) {
855
- this.playbackDomElem.style.width = xPos + 'px'
856
- },
857
- setMediaTime(time) {
858
- if (this.isAnimation) {
859
- this.media.timeline.time(time)
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()
860
607
  } 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()
608
+ //fullscreen is available on Element.
609
+ fullscreenElement.requestFullscreen()
868
610
  }
869
- this.updateTimecodeDisplay()
870
- this.setMediaA11Y()
871
611
  },
872
-
873
612
  /**
874
- * @fires mediaPlaybackEnded to AppCompNavigationFull.vue
613
+ * @description update the value of fulls screen state
875
614
  */
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)
615
+ updateFullScreenState() {
616
+ this.fullscreenOn = !this.fullscreenOn
944
617
  },
945
618
 
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)
619
+ fireFoxMoveCC() {
620
+ if (this.firefoxTrack.activeCues[0]) {
621
+ this.firefoxTrack.activeCues[0].line = 12
951
622
  }
952
623
  },
953
-
954
- toggleSubtitleMenu() {
955
- this.subtitleMenuOpen = !this.subtitleMenuOpen
956
- },
957
624
  /**
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
625
+ * @description show the subtitle
960
626
  */
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
627
  showSubtitles() {
982
- this.media.mElement.textTracks[0].mode = 'showing'
983
- this.subtitlesEnabled = true
628
+ this.mediaElement.textTracks[0].mode = 'showing'
984
629
  this.$store.dispatch('setMediaSubtitles', true)
985
630
  if (this.getCurrentBrowser == 'Firefox') {
986
631
  let video = document.getElementsByTagName('video')[0]
987
632
  let tracks = video.textTracks
988
- let track = tracks[0]
633
+ this.firefoxTrack = tracks[0]
989
634
 
990
- track.addEventListener('cuechange', function fireFoxMoveCC() {
991
- if (track.activeCues[0]) {
992
- track.activeCues[0].line = 12
993
- }
994
- })
635
+ this.firefoxTrack.addEventListener('cuechange', this.fireFoxMoveCC)
995
636
  }
996
637
  },
638
+
997
639
  hideSubtitles() {
998
- this.media.mElement.textTracks[0].mode = 'hidden' // value can be 'disabled' also.
999
- this.subtitlesEnabled = false
640
+ this.mediaElement.textTracks[0].mode = 'hidden' // value can be 'disabled' also.
1000
641
  this.$store.dispatch('setMediaSubtitles', false)
1001
642
  },
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()
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
+ })
1061
667
  }
1062
668
 
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
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')
1069
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
1070
689
 
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}`
690
+ if (!tFile) throw new Error('Missing transcript File!')
1128
691
 
1129
- //Format to show 0 when no value is present
1130
- cTimeTxt = cTimeTxt.trim().length ? cTimeTxt.trim() : 0
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
+ )
1131
697
 
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
- )}`
698
+ const fileUrl = `./${tFile}`
699
+ const res = await this.axios.get(fileUrl, { responseType: 'blob' })
700
+ const content = await res.data.text()
1139
701
 
1140
- break
1141
- case 2:
1142
- if (!currentTime.length) return
702
+ return content
703
+ } catch (err) {
704
+ console.warn("YOU'VE GOT AN ERROR!\n", err)
705
+ }
706
+ },
707
+ /** ******************* Rendering Methods****************************** */
1143
708
 
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')}`
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)
1159
714
 
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
- : ''
715
+ this.transcriptToShow = await this.fetchTranscript()
716
+ },
1175
717
 
1176
- cTimeTxt = `${mnTxt} ${ssTxt}`
1177
- fTimeTxt = `${MNTxt} ${SSTxt}`
718
+ /**
719
+ * @description Method to reset the transcript state
720
+ */
721
+ resetTranscript(content) {
722
+ this.transcriptEnabled = false
723
+ },
1178
724
 
1179
- //Format to show 0 when no value is present
1180
- cTimeTxt = cTimeTxt.trim().length ? cTimeTxt.trim() : 0
725
+ /**
726
+ * @description Show the media controler */
727
+ showControls() {
728
+ this.showControlsValue = true
729
+ if (this.hideTimer) clearTimeout(this.hideTimer) //cancel existing timer
1181
730
 
1182
- // Set sr text for timeCode && duration
1183
- this.mediaA11Y.timeCode = `${this.media.mType} ${w} ${cTimeTxt}`
1184
- this.mediaA11Y.duration = `${dm} ${fTimeTxt}`
731
+ this.hideControls()
732
+ },
733
+ /**
734
+ * @description Hide the media after the video start playing
735
+ */
1185
736
 
1186
- this.mediaA11Y.valueText = `${mediaTitle} ${formatMsg(
1187
- cTimeTxt,
1188
- fTimeTxt
1189
- )}`
737
+ hideControls() {
738
+ if (this.mediaElement.paused) return
1190
739
 
1191
- break
1192
- }
740
+ this.hideTimer = setTimeout(() => {
741
+ this.showControlsValue = false
742
+ }, this.delayUntilHide)
1193
743
  }
1194
744
  }
1195
745
  }
1196
746
  </script>
1197
- <style lang="scss">
1198
- /*
1199
- Playbar hover/leave transition
1200
- */
1201
- #playbar-wrapper {
747
+ <style lang="scss" scoped>
748
+ .pb-container {
749
+ //colors
750
+ --primary100: #deeff8;
751
+ --primary600: #006fab;
752
+ --primary700: #005c8d;
753
+ --primary800: #004870;
754
+ --primary900: #003552;
755
+ }
756
+ .pb-container.video {
757
+ position: absolute;
758
+ height: 100%;
1202
759
  width: 100%;
760
+ justify-content: center;
761
+ display: flex;
762
+ }
763
+
764
+ .playback-button {
1203
765
  position: absolute;
766
+ top: 0;
1204
767
  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
- }
768
+ width: 100%;
769
+ height: 100%;
770
+ display: flex;
771
+ flex-flow: column wrap;
772
+ justify-content: center;
773
+ align-items: center;
774
+ background-color: transparent;
775
+ .playback-wrapper {
776
+ height: 64px;
777
+ width: 64px;
778
+ display: flex;
779
+ justify-content: center;
780
+ align-items: center;
781
+ background-color: var(--primary700);
782
+ border-radius: 50%;
783
+ &:hover {
784
+ background-color: var(--primary600);
785
+ }
786
+ svg {
787
+ fill: var(--primary100);
788
+ height: 32px;
789
+ width: 26.29px;
790
+ &.play-icon {
791
+ margin-left: 11%;
1224
792
  }
1225
793
  }
1226
794
  }
795
+ }
796
+ //Wrapper
797
+ .pb-wrapper {
798
+ width: 100%;
799
+ display: flex;
800
+ flex-direction: column;
801
+ position: absolute;
802
+ bottom: 0;
803
+ opacity: 0;
804
+ z-index: 2;
805
+ background-color: var(--primary100);
806
+ &.show-controls {
807
+ opacity: 1;
808
+ transition: opacity 0.35s ease-in-out;
809
+ }
1227
810
 
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 {
811
+ //Wrapper timeline
812
+ .pb-timeline {
813
+ --progress-bar-height: 6px;
814
+ position: relative;
815
+ width: 100%;
816
+ height: var(--progress-bar-height);
817
+ .progress-area {
818
+ width: 100%;
819
+ height: 400%; //Add contact surface of click on progressBar
820
+ position: relative;
821
+ top: -150%;
1236
822
  display: flex;
1237
- flex-direction: row;
1238
- justify-content: space-around;
823
+ cursor: pointer;
1239
824
  align-items: center;
1240
- height: 100%;
1241
- position: relative;
1242
- #playbar-current-time {
1243
- flex: 0 0 45px;
825
+ z-index: 8; //z-index on top of the video elm
826
+ &:hover {
827
+ .seek-tooltip {
828
+ opacity: 1;
829
+ }
1244
830
  }
1245
831
 
1246
- .playbar-btn {
832
+ //Progress bar
833
+ .pb-progress-bar {
834
+ --progress-bar-border-height: 1px;
1247
835
  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;
836
+ width: 100%;
837
+ height: var(--progress-bar-height);
838
+ background-color: transparent;
839
+ border: var(--progress-bar-border-height) var(--primary700) solid;
840
+ overflow: visible;
841
+ &:focus {
842
+ .progress-thumb {
843
+ }
1256
844
  }
845
+ }
1257
846
 
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
- }
847
+ .progress-indicator {
848
+ width: 0;
849
+ height: 100%;
850
+ position: relative;
851
+ user-select: none;
852
+ //colors
853
+ background-color: var(--primary700);
854
+ &.progress-animation {
855
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
856
+ transition-duration: 0.01s;
857
+ transition-property: all;
858
+ transition: width 0.1s linear;
859
+ //colors
860
+ background-color: var(--primary700);
861
+ }
862
+ }
863
+ //Custom thumb
864
+ .progress-thumb {
865
+ //Cliquable thumb
866
+ --progress-thumb-size: 30px;
867
+ width: var(--progress-thumb-size);
868
+ height: var(--progress-thumb-size);
869
+ position: absolute;
1401
870
 
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
- }
871
+ right: calc(var(--progress-thumb-size) / -2);
872
+ top: calc(
873
+ -1 * (
874
+ var(--progress-thumb-size) / 2 - var(--progress-bar-height) / 2
875
+ ) - var(--progress-bar-border-height) / 2
876
+ );
877
+ display: flex;
878
+ align-items: center;
879
+ justify-content: center;
880
+ &::after {
881
+ content: '';
882
+ //Visible thumb
883
+ height: 16px;
884
+ width: 16px;
885
+ //Colors and styling
886
+ border-radius: 50%;
887
+ background-color: var(--primary700);
888
+ }
889
+ }
1406
890
 
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
- }
891
+ &:hover {
892
+ .seek-tooltip {
893
+ //tooltip visible only when hovering progress-area
894
+ opacity: 1;
895
+ }
896
+ }
1419
897
 
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
- }
898
+ .seek-tooltip {
899
+ position: absolute;
900
+ top: -30px;
901
+ opacity: 0;
902
+ user-select: none;
903
+ //Colors
904
+ background-color: var(--primary100);
905
+ color: var(--primary700);
906
+ border-radius: 1px;
907
+ padding: 2px;
908
+ }
909
+ }
910
+ }
1430
911
 
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
- }
912
+ //Controls
913
+ .pb-controls {
914
+ padding: 12px 32px;
915
+ @media screen and (max-width: 800px) {
916
+ padding: 12px 12px;
917
+ }
918
+ width: 100%;
919
+ //height: 76px;
920
+ height: auto;
921
+ display: flex;
922
+ flex-flow: row wrap;
923
+ justify-content: space-between;
924
+ -webkit-justify-content: space-between;
925
+ align-items: center;
926
+
927
+ .pb-areas {
928
+ display: flex;
929
+ flex-flow: row nowrap;
930
+ justify-content: center;
931
+ align-items: center;
1437
932
 
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
- );
933
+ button {
934
+ position: relative;
1445
935
 
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
- }
936
+ &::before {
937
+ content: attr(data-title);
938
+ position: absolute;
939
+ top: -50px;
940
+ right: 0;
941
+ margin-left: -35px;
942
+ word-break: keep-all;
943
+ white-space: pre;
944
+ display: none;
945
+ opacity: 0;
946
+ width: fit-content;
947
+ //user-select:none;
948
+ //Colors and styling
949
+ background-color: var(--primary100);
950
+ color: var(--primary700);
951
+ border-radius: 1px;
952
+ padding: 2px;
953
+ }
954
+ //Icons btn specific
955
+ &.playbar-btn {
956
+ &::before {
957
+ left: 0 !important;
958
+ margin-left: 0 !important;
959
+ }
960
+ margin-right: 16px;
961
+ //Style
962
+ border-radius: 50%;
963
+ background-color: var(--primary700);
964
+ //Style
965
+ &:hover {
966
+ background-color: var(--primary600);
967
+ }
968
+ @media screen and (max-width: 800px) {
969
+ height: 32px;
970
+ width: 32px;
971
+ }
972
+ svg {
973
+ //Style
974
+ fill: var(--primary100);
975
+ }
976
+ svg.play-icon {
977
+ margin-left: 11.875%; // 11.8% off center on the design
978
+ }
979
+ }
980
+ &.volume-btn {
981
+ svg.volume-low-icon {
982
+ height: 17.75px;
1468
983
  }
1469
984
  }
1470
985
  }
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;
986
+ //Manage displayLabel (dynamic class)
987
+ button.displayLabel {
988
+ &:hover,
989
+ &:focus {
990
+ &::before {
991
+ display: block;
992
+ opacity: 1;
993
+ }
1480
994
  }
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;
995
+ }
1492
996
 
1493
- svg {
1494
- display: inline-block;
1495
- margin-right: 20px;
1496
- transform: translateY(-3px);
997
+ .pb-timer {
998
+ width: max-content;
999
+ span {
1000
+ //colors
1001
+ color: var(--primary700);
1002
+ }
1003
+ }
1004
+ //Volume
1005
+ .volume-controls {
1006
+ --volumecontrols-height: 22px;
1007
+ --volume-thumb-size: 16px;
1008
+ display: flex;
1009
+ flex-flow: row wrap;
1010
+ justify-content: center;
1011
+ position: relative;
1012
+ height: var(--volumecontrols-height);
1013
+ &:hover {
1014
+ //button title //DisplayLabel is a dynamic class
1015
+ .volume-slider.displayLabel + span {
1016
+ display: block;
1017
+ opacity: 1;
1018
+ }
1019
+ }
1020
+ .volume-slider {
1021
+ -webkit-appearance: none;
1022
+ width: 100px;
1023
+ //Responsive
1024
+ @media screen and (max-width: 800px) {
1025
+ width: 80px;
1026
+ }
1027
+ border-radius: 24px;
1028
+ cursor: pointer;
1029
+ position: relative;
1030
+ z-index: 2;
1031
+ &::-webkit-slider-runnable-track {
1032
+ background: transparent;
1033
+ }
1034
+ //Input slider thumb
1035
+ @mixin volume-thumb {
1036
+ -webkit-appearance: none;
1037
+ width: var(--volume-thumb-size);
1038
+ height: var(--volume-thumb-size);
1039
+ border-radius: 50%;
1040
+ position: relative;
1041
+ cursor: pointer;
1042
+ }
1043
+ &::-webkit-slider-thumb {
1044
+ @include volume-thumb;
1045
+ }
1046
+ //button title
1047
+ & + span {
1048
+ position: absolute;
1049
+ top: -62px;
1050
+ right: 0;
1051
+ margin-left: -35px;
1052
+ word-break: keep-all;
1053
+ white-space: pre;
1054
+ display: none;
1055
+ opacity: 0;
1056
+ user-select: none;
1057
+ //Colors
1058
+ background-color: var(--primary100);
1059
+ color: var(--primary700);
1060
+ border-radius: 1px;
1061
+ padding: 2px;
1062
+ }
1063
+ //DisplayLabel is a dynamic class
1064
+ &.displayLabel:focus {
1065
+ & + span {
1066
+ display: block;
1067
+ opacity: 1;
1497
1068
  }
1498
1069
  }
1499
1070
  }
1071
+
1072
+ .volume-progress {
1073
+ --volumeIndicator-height: 9px;
1074
+ height: var(--volumeIndicator-height);
1075
+ top: calc(
1076
+ var(--volumecontrols-height) / 2 - var(--volumeIndicator-height) / 2
1077
+ ); //centrer en hauteur
1078
+ width: calc(
1079
+ 100% - var(--volume-thumb-size) / 2
1080
+ ); //Width smaller than the range width to be sure the progress width don't exceed the range width
1081
+ position: absolute;
1082
+ border-radius: 20px;
1083
+ background-repeat: no-repeat;
1084
+ background-size: var(--background-size-vs, 0%) 100%; //Js dynamic variable
1085
+ //colors
1086
+ border: 2px solid var(--primary700);
1087
+ background-image: linear-gradient(
1088
+ var(--primary700),
1089
+ var(--primary700)
1090
+ );
1091
+ background-image: -moz-linear-gradient(
1092
+ var(--primary700),
1093
+ var(--primary700)
1094
+ );
1095
+ background-image: -webkit-linear-gradient(
1096
+ var(--primary700),
1097
+ var(--primary700)
1098
+ );
1099
+ }
1500
1100
  }
1501
1101
  }
1502
1102
  }
1503
1103
 
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;
1104
+ svg {
1105
+ //width: 19px;
1106
+ //height: 19px;
1107
+ //colors
1108
+ //stroke: var(--primary700);
1109
+ }
1110
+
1111
+ button {
1112
+ height: 48px;
1113
+ width: 48px;
1114
+ background-color: transparent;
1115
+ fill: var(--primary700);
1116
+ padding: 0;
1117
+ display: flex;
1118
+ justify-content: center;
1119
+ align-items: center;
1120
+ svg {
1121
+ height: 24px;
1122
+ @media screen and (max-width: 800px) {
1123
+ height: 18px;
1124
+ }
1515
1125
  }
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);
1126
+ &:hover,
1127
+ &:focus,
1128
+ &:target {
1129
+ fill: var(--primary100);
1130
+ }
1131
+ &:hover {
1132
+ background-color: var(--primary600);
1133
+ }
1134
+ &:focus {
1135
+ background-color: var(--primary600);
1136
+ outline: 1px solid var(--primary600);
1137
+ outline-offset: 2px;
1138
+ }
1139
+ &:target {
1140
+ background-color: var(--primary800);
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ //Colors for volume-slider (thumb and background)
1146
+
1147
+ .volume-slider {
1148
+ --primary100: #deeff8;
1149
+ --primary700: #005c8d;
1150
+ --primary900: #003552;
1151
+ background: transparent;
1152
+ &::-webkit-slider-thumb {
1153
+ border: solid var(--primary700) 1px;
1154
+ background: var(--primary700);
1155
+ }
1156
+
1157
+ &::-moz-range-thumb {
1158
+ border: solid var(--primary700) 1px;
1159
+ background: var(--primary700);
1160
+ }
1161
+ }
1162
+ //CSS for audio only
1163
+ .audio.pb-wrapper {
1164
+ border-radius: 4px;
1165
+ position: initial;
1166
+ .pb-controls {
1167
+ padding: 8px 16px;
1168
+ .volume-controls {
1169
+ margin-left: 8px;
1170
+ width: 100%;
1171
+ --volume-thumb-size: 12px;
1172
+ .volume-slider {
1173
+ width: 50px;
1174
+ }
1175
+ .volume-progress {
1176
+ --volumeIndicator-height: 6px;
1177
+ }
1178
+ }
1179
+ }
1180
+ .pb-timer {
1181
+ margin-right: 20px;
1182
+ }
1183
+ .pb-timeline {
1184
+ .progress-area {
1185
+ justify-content: center;
1186
+ }
1187
+ .pb-progress-bar {
1188
+ border-radius: 4px;
1189
+ //max-width: 90%;
1190
+ }
1191
+ }
1192
+ .volume-btn {
1193
+ margin-left: 16px;
1194
+ }
1195
+ .pb-areas {
1196
+ width: 100%;
1197
+ display: grid;
1198
+ grid-template-columns:
1199
+ min-content
1200
+ min-content
1201
+ 1fr
1202
+ min-content
1203
+ min-content;
1204
+ align-items: center;
1205
+ justify-content: center;
1206
+ button {
1207
+ height: 24px;
1208
+ width: 24px;
1209
+ &.playbar-btn {
1210
+ margin-right: 8px;
1211
+ }
1212
+ svg {
1213
+ height: 12.25px;
1214
+ @media screen and (max-width: 800px) {
1215
+ height: 18px;
1216
+ }
1217
+ }
1218
+ &.volume-btn {
1219
+ svg {
1220
+ height: 16px;
1221
+ }
1222
+ svg.volume-low-icon {
1223
+ height: 14px;
1526
1224
  }
1527
1225
  }
1528
1226
  }
1529
1227
  }
1530
1228
  }
1531
-
1532
- #acessMenu {
1533
- &:hover {
1534
- svg {
1535
- width: 21.642px !important;
1536
- height: 16px !important;
1229
+ //Playback button video (to finish)
1230
+ .playback-button {
1231
+ .playback-wrapper {
1232
+ opacity: 0;
1233
+ }
1234
+ &.initial {
1235
+ .playback-wrapper {
1236
+ animation-name: initial;
1237
+ opacity: 1;
1238
+ }
1239
+ }
1240
+ &.playback-animation {
1241
+ .playback-wrapper {
1242
+ opacity: 0;
1243
+ animation-name: handlePlayBack;
1244
+ animation-duration: 0.8s;
1245
+ animation-timing-function: ease-in-out;
1537
1246
  }
1538
1247
  }
1539
1248
  }
1249
+
1250
+ @keyframes handlePlayBack {
1251
+ 0% {
1252
+ opacity: 0;
1253
+ }
1254
+ 50% {
1255
+ opacity: 1;
1256
+ transform: scale(0.95);
1257
+ }
1258
+ 100% {
1259
+ opacity: 0;
1260
+ }
1261
+ }
1262
+
1263
+ @keyframes disappear {
1264
+ 0% {
1265
+ opacity: 1;
1266
+ }
1267
+ 100% {
1268
+ opacity: 0;
1269
+ }
1270
+ }
1540
1271
  </style>