fcad-core-dragon 2.0.0-beta.4 → 2.0.0-beta.6

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