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

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