fcad-core-dragon 2.1.1 → 2.1.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 (160) hide show
  1. package/.editorconfig +7 -7
  2. package/.gitlab-ci.yml +124 -0
  3. package/.prettierrc +11 -11
  4. package/.vscode/extensions.json +8 -8
  5. package/.vscode/settings.json +46 -16
  6. package/CHANGELOG +520 -520
  7. package/README.md +57 -57
  8. package/documentation/.vitepress/config.js +114 -114
  9. package/documentation/api-examples.md +49 -49
  10. package/documentation/composants/app-base-button.md +58 -58
  11. package/documentation/composants/app-base-error-display.md +59 -59
  12. package/documentation/composants/app-base-popover.md +68 -68
  13. package/documentation/composants/app-comp-audio.md +75 -75
  14. package/documentation/composants/app-comp-branch-buttons.md +111 -111
  15. package/documentation/composants/app-comp-button-progress.md +53 -53
  16. package/documentation/composants/app-comp-carousel.md +53 -53
  17. package/documentation/composants/app-comp-container.md +53 -53
  18. package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
  19. package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
  20. package/documentation/composants/app-comp-input-radio-next.md +39 -39
  21. package/documentation/composants/app-comp-input-text-next.md +35 -35
  22. package/documentation/composants/app-comp-input-text-table-next.md +34 -34
  23. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
  24. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
  25. package/documentation/composants/app-comp-jauge.md +31 -31
  26. package/documentation/composants/app-comp-menu-item.md +55 -55
  27. package/documentation/composants/app-comp-menu.md +29 -29
  28. package/documentation/composants/app-comp-navigation.md +41 -41
  29. package/documentation/composants/app-comp-note-call.md +53 -53
  30. package/documentation/composants/app-comp-note-credit.md +53 -53
  31. package/documentation/composants/app-comp-play-bar-next.md +53 -53
  32. package/documentation/composants/app-comp-pop-up-next.md +93 -93
  33. package/documentation/composants/app-comp-quiz-next.md +235 -235
  34. package/documentation/composants/app-comp-quiz-recall.md +53 -53
  35. package/documentation/composants/app-comp-svg-next.md +53 -53
  36. package/documentation/composants/app-comp-table-of-content.md +50 -50
  37. package/documentation/composants/app-comp-video-player.md +82 -82
  38. package/documentation/composants.md +46 -46
  39. package/documentation/composants_critiques/ModelPageComposant.md +53 -53
  40. package/documentation/composants_critiques/app-base-module.md +43 -43
  41. package/documentation/composants_critiques/app-base-page.md +48 -48
  42. package/documentation/composants_critiques/app-base.md +311 -311
  43. package/documentation/composants_critiques/main.md +15 -15
  44. package/documentation/demarrage.md +50 -50
  45. package/documentation/deploiement.md +57 -57
  46. package/documentation/index.md +33 -33
  47. package/documentation/markdown-examples.md +85 -85
  48. package/documentation/public/vite.svg +14 -14
  49. package/documentation/public/vuejs.svg +1 -1
  50. package/documentation/public/vuetify.svg +5 -5
  51. package/eslint.config.js +60 -60
  52. package/junit-report.xml +182 -0
  53. package/package.json +66 -59
  54. package/playwright/index.html +12 -0
  55. package/playwright/index.js +21 -0
  56. package/playwright-ct.config.js +95 -0
  57. package/src/$locales/en.json +157 -157
  58. package/src/$locales/fr.json +120 -120
  59. package/src/assets/data/onboardingMessages.json +47 -47
  60. package/src/components/AppBase.vue +1171 -1169
  61. package/src/components/AppBaseButton.vue +90 -95
  62. package/src/components/AppBaseErrorDisplay.vue +438 -438
  63. package/src/components/AppBaseFlipCard.vue +84 -84
  64. package/src/components/AppBaseModule.vue +1639 -1634
  65. package/src/components/AppBasePage.vue +867 -866
  66. package/src/components/AppBasePopover.vue +41 -41
  67. package/src/components/AppBaseSkeleton.vue +66 -66
  68. package/src/components/AppCompAudio.vue +261 -256
  69. package/src/components/AppCompBranchButtons.vue +508 -508
  70. package/src/components/AppCompButtonProgress.vue +137 -132
  71. package/src/components/AppCompCarousel.vue +342 -336
  72. package/src/components/AppCompContainer.vue +29 -29
  73. package/src/components/AppCompInputCheckBoxNx.vue +325 -323
  74. package/src/components/AppCompInputDropdownNx.vue +302 -299
  75. package/src/components/AppCompInputRadioNx.vue +287 -284
  76. package/src/components/AppCompInputTextNx.vue +156 -153
  77. package/src/components/AppCompInputTextTableNx.vue +205 -202
  78. package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
  79. package/src/components/AppCompInputTextToFillNx.vue +316 -313
  80. package/src/components/AppCompJauge.vue +81 -81
  81. package/src/components/AppCompMenu.vue +6 -1
  82. package/src/components/AppCompMenuItem.vue +246 -240
  83. package/src/components/AppCompNavigation.vue +977 -972
  84. package/src/components/AppCompNoteCall.vue +167 -161
  85. package/src/components/AppCompNoteCredit.vue +496 -491
  86. package/src/components/AppCompPlayBarNext.vue +2290 -2288
  87. package/src/components/AppCompPopUpNext.vue +508 -504
  88. package/src/components/AppCompQuizNext.vue +515 -510
  89. package/src/components/AppCompQuizRecall.vue +355 -350
  90. package/src/components/AppCompSVGNext.vue +346 -346
  91. package/src/components/AppCompSettingsMenu.vue +177 -172
  92. package/src/components/AppCompTableOfContent.vue +433 -427
  93. package/src/components/AppCompVideoPlayer.vue +377 -377
  94. package/src/components/AppCompViewDisplay.vue +6 -6
  95. package/src/components/BaseModule.vue +55 -55
  96. package/src/composables/useIdleDetector.js +56 -56
  97. package/src/composables/useQuiz.js +89 -89
  98. package/src/composables/useTimer.js +172 -172
  99. package/src/directives/nvdaFix.js +53 -53
  100. package/src/externalComps/ModuleView.vue +22 -22
  101. package/src/externalComps/SummaryView.vue +91 -91
  102. package/src/main.js +493 -476
  103. package/src/module/stores/appStore.js +960 -947
  104. package/src/module/xapi/ADL.js +520 -520
  105. package/src/module/xapi/Crypto/Hasher.js +241 -241
  106. package/src/module/xapi/Crypto/WordArray.js +278 -278
  107. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  108. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  109. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  110. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  111. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  112. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  113. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  114. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  115. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  116. package/src/module/xapi/Crypto/index.js +53 -53
  117. package/src/module/xapi/Statement/activity.js +47 -47
  118. package/src/module/xapi/Statement/agent.js +55 -55
  119. package/src/module/xapi/Statement/group.js +26 -26
  120. package/src/module/xapi/Statement/index.js +259 -259
  121. package/src/module/xapi/Statement/statement.js +253 -253
  122. package/src/module/xapi/Statement/statementRef.js +23 -23
  123. package/src/module/xapi/Statement/substatement.js +22 -22
  124. package/src/module/xapi/Statement/verb.js +36 -36
  125. package/src/module/xapi/activitytypes.js +17 -17
  126. package/src/module/xapi/launch.js +157 -157
  127. package/src/module/xapi/utils.js +167 -167
  128. package/src/module/xapi/verbs.js +294 -294
  129. package/src/module/xapi/wrapper.js +1895 -1895
  130. package/src/module/xapi/xapiStatement.js +444 -444
  131. package/src/plugins/analytics.js +34 -34
  132. package/src/plugins/bus.js +12 -8
  133. package/src/plugins/gsap.js +17 -17
  134. package/src/plugins/helper.js +355 -358
  135. package/src/plugins/i18n.js +27 -26
  136. package/src/plugins/idb.js +227 -227
  137. package/src/plugins/save.js +37 -37
  138. package/src/plugins/scorm.js +287 -287
  139. package/src/plugins/xapi.js +11 -11
  140. package/src/public/index.html +33 -33
  141. package/src/router/index.js +57 -57
  142. package/src/router/routes.js +312 -312
  143. package/src/shared/generalfuncs.js +344 -344
  144. package/src/shared/validators.js +1018 -1018
  145. package/tests/component/AppBaseButton.spec.js +53 -0
  146. package/tests/component/pinia.spec.js +24 -0
  147. package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
  148. package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
  149. package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
  150. package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
  151. package/tests/unit/AppCompInputTextNx.spec.js +44 -0
  152. package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
  153. package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
  154. package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
  155. package/tests/unit/AppCompQuizNext.spec.js +114 -0
  156. package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
  157. package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
  158. package/vitest.config.js +28 -19
  159. package/vitest.setup.js +28 -0
  160. package/src/components/AppBaseButton.test.js +0 -21
@@ -1,377 +1,377 @@
1
- <!--
2
- @ Description: This component is used to display video element video
3
- @ What it does: The component create the HTMLMediaElement video tag from data provided by user.
4
- Once the HTMLMediaElement tag is created, the component save the HTMLMediaElement to store and
5
- make it available to all other component that will need id
6
- -->
7
- <template>
8
- <section
9
- v-if="vidData"
10
- :id="'video_' + id"
11
- ref="$media-container"
12
- class="v-media __media-container app-video-player"
13
- :class="[{ FS: fullScreen }, CCBrowser]"
14
- aria-label="Video"
15
- >
16
- <app-base-error-display
17
- v-if="hasErr.length"
18
- :error-group="'component'"
19
- :error-title="'ERREUR: COMPOSANT DE VIDEO'"
20
- :errors-list="hasErr"
21
- />
22
-
23
- <!------------------video section --------------------------->
24
-
25
- <template v-else>
26
- <div class="vid-wrapper">
27
- <!--Vid-skeleton-->
28
- <div
29
- v-if="isLoading || hasSourceLoadingError"
30
- class="skeleton"
31
- :class="{ isloading: isLoading, error: hasSourceLoadingError }"
32
- >
33
- <p v-if="hasSourceLoadingError">
34
- Une erreur s'est produite lors du chargement de la vidéo, veuillez
35
- réessayer
36
- </p>
37
- </div>
38
- <!--End vid-sleleton-->
39
- <video
40
- :id="id"
41
- ref="m-video"
42
- :poster="vidPoster"
43
- @loadedmetadata="updateMediaDataVideo($event.target)"
44
- >
45
- <source
46
- v-for="(aSource, index) in vidSources"
47
- :key="index"
48
- :src="`${aSource.src}`"
49
- :type="`video/${aSource.type}`"
50
- @error="errorHandling($event)"
51
- />
52
- <track
53
- v-for="(subtitle, index) in vidSubtitles"
54
- :key="`subtitle_${index + 1}`"
55
- :src="subtitle.src"
56
- :srclang="subtitle.srclang"
57
- :label="subtitle.label"
58
- />
59
- </video>
60
- </div>
61
- <app-comp-play-bar-next
62
- v-if="$vidElement && !isLoading && !hasSourceLoadingError"
63
- :ref="`plyr_${id}`"
64
- :media-to-play="$vidElement"
65
- @resize-video="resizeVideo"
66
- />
67
- </template>
68
- </section>
69
- </template>
70
-
71
- <script>
72
- import { mapState, mapActions } from 'pinia'
73
- import { useAppStore } from '../module/stores/appStore'
74
- import { validateVideoData } from '../shared/validators'
75
- import AppCompPlayBarNext from './AppCompPlayBarNext.vue'
76
-
77
- export default {
78
- components: { AppCompPlayBarNext },
79
- props: {
80
- vidData: {
81
- type: Object,
82
- required: true,
83
- validator: (value) => {
84
- return validateVideoData(value).length === 0
85
- }
86
- },
87
- fullScreen: {
88
- type: Boolean,
89
- default: true
90
- }
91
- },
92
-
93
- data() {
94
- return {
95
- id: this.vidData.id,
96
- vidSources: [],
97
- vidSubtitles: [],
98
- vidPoster: null,
99
- vidTranscript: null,
100
- hasSourceLoadingError: false,
101
- hasErr: validateVideoData(this.vidData),
102
- isSet: false,
103
- playClicked: false,
104
- vidFocus: false,
105
- vidElm: null,
106
- vidElmFocus: false,
107
- mediaContainer: null
108
- }
109
- },
110
- computed: {
111
- ...mapState(useAppStore, ['getCurrentBrowser', 'getCurrentPage']),
112
- //Return true if the video is loading (to show the loading display)
113
- isLoading() {
114
- if (!this.isSet) return true
115
- else return false
116
- },
117
-
118
- setTimeout() {
119
- return window.setTimeout
120
- },
121
- //Return current browser
122
- CCBrowser() {
123
- let browser = this.getCurrentBrowser
124
-
125
- if (browser === 'Safari') {
126
- return 'safari'
127
- } else {
128
- return 'chrome'
129
- }
130
- },
131
- //Return the videoElement (used in the playbar)
132
- $vidElement() {
133
- if (!this.isSet) return null
134
- const { id, mTranscript, mSubtitles } = this.vidData
135
- const mElement = this.$refs['m-video']
136
- return {
137
- id,
138
- mTranscript,
139
- mType: 'video',
140
- mSubtitles,
141
- mElement,
142
- mMediaContainer: this.$refs['$media-container']
143
- }
144
- }
145
- },
146
-
147
- mounted() {
148
- this.initVideo()
149
- },
150
-
151
- beforeUnmount() {
152
- this.$bus.$off('hide-playback', this.hideAnimation)
153
- this.$bus.$off('resize-media', this.resizeVideo)
154
- },
155
-
156
- methods: {
157
- ...mapActions(useAppStore, ['updateCurrentMediaElements']),
158
- /**
159
- * @description search the DOM for medias with duplicated ID declaration in the page
160
- * @return return Array of all DOM MediaElement with same id
161
- */
162
- checkDuplicatedID() {
163
- const mediaList = document.querySelectorAll('.__media-container')
164
- const duplicate = Array.from(mediaList).filter((media) =>
165
- media.id.includes(this.id)
166
- )
167
- return duplicate
168
- },
169
- errorHandling(e) {
170
- this.hasSourceLoadingError = true
171
- },
172
- initVideo() {
173
- const { mSources, mPoster, mSubtitles, mTranscript, id } = this.vidData
174
- if (id) this.id = id
175
- if (mSources) this.vidSources = mSources
176
- if (mPoster) this.vidPoster = mPoster
177
- if (mSubtitles) this.vidSubtitles = mSubtitles
178
- if (mTranscript) this.vidTranscript = mTranscript
179
-
180
- this.$bus.$on('resize-media', this.resizeVideo)
181
- this.$bus.$on('hide-playback', this.hideAnimation)
182
- },
183
-
184
- /**
185
- * @description update the information for the mediaElement in the store
186
- * @param {htmlElement} e
187
- * @fires update-page to AppBaseModule.vue
188
- */
189
- updateMediaDataVideo(e) {
190
- this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'loading')
191
- this.$bus.$emit('update-media-duration')
192
-
193
- if (this.checkDuplicatedID().length > 1) {
194
- const errmsg = `Cet élément a le même ID (<b>${this.id}</b>) q'un autre media. Vous ne devez pas avoir de médias avec le même ID dans une page.`
195
-
196
- console.warn(
197
- `%c WARNING!>>> You cannot use the same ID (${this.id}) in your media elements for your page.`,
198
- 'background: orange; color: white; display: block; border-radius:5px; margin:5px;'
199
- )
200
-
201
- this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
202
- return this.hasErr.push(errmsg)
203
- }
204
-
205
- this.updateCurrentMediaElements(e).then(() => {
206
- this.isSet = true
207
- this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
208
- })
209
- },
210
- /**
211
- *@description - small animation over the playback button
212
- * Play a little animation on the svg and then send a play event
213
- *@emits play-media - To AppComPLayBar
214
- */
215
- runPlaybackAnimation() {
216
- //this.isPlaying = !this.$vidElement.mElement.paused
217
- // let animation = this.$gsap.fromTo(
218
- // '.playback-animation',
219
- // { opacity: 1, scale: 0.8 },
220
- // {
221
- // opacity: 0,
222
- // scale: 1.3,
223
- // duration: 0.8,
224
- // ease: 'power2.out'
225
- // }
226
- // )
227
- // animation.play()
228
- },
229
-
230
- /**
231
- * @description initiate play back of media when click on the video Element with animation
232
- * @emits play-media to AppComplayBar
233
- *
234
- */
235
- /*handlePlayBack(evtType) {
236
- this.runPlaybackAnimation()
237
- this.$bus.$emit('play-media', 'KeyK')
238
- },*/
239
- /**
240
- * @description hide the animation when the media is played with click on playbar play button
241
- * this ensures that animation does not run every time user controls the media via the playbar controls
242
- */
243
- hideAnimation(type = null) {
244
- if (this.playClicked && !type) return
245
- this.runPlaybackAnimation()
246
- this.playClicked = true
247
- },
248
-
249
- /**
250
- * @description- Skip directly to the specify containt/region*
251
- * main content is Node with defined ID
252
- * @param {String} targetID- Css selector for the target element
253
- */
254
-
255
- skipTo(targetID) {
256
- let skipTo = document.querySelector(`#wrapper-content`) //default definition of main element
257
-
258
- if (targetID) {
259
- let targetEl = document.querySelector(`#${targetID}`) // search for node element specified as main
260
-
261
- if (targetEl) skipTo = targetEl
262
- }
263
- let targetTop = skipTo.offsetTop
264
-
265
- let scrollOpt = {
266
- top: targetTop - 100,
267
- left: 0,
268
- behavior: 'auto' //auto
269
- }
270
-
271
- // Allowing accessibility control with keyboard
272
- skipTo.setAttribute('tabIndex', -1)
273
- window.scrollTo(scrollOpt)
274
- skipTo.focus()
275
- },
276
-
277
- /**
278
- * @description -set the size of the video element when transcript is open or closed
279
- */
280
-
281
- resizeVideo(size, container) {
282
- let defaultSize = 100
283
- if (size == 'sm') defaultSize = 68
284
- //const videoElement = document.querySelector('.__media-container')
285
- const videoElement = this.$vidElement.mMediaContainer
286
- setTimeout(() => {
287
- videoElement.style.width = `${defaultSize}%`
288
- }, 100)
289
- },
290
-
291
- spaceKeyPreventDefault(evt) {
292
- let { code } = evt
293
- if (code == 'Space') {
294
- evt.preventDefault()
295
- this.handlePlayBack('spacebar')
296
- }
297
- }
298
- }
299
- }
300
- </script>
301
- <style lang="scss">
302
- $widthVideo: 100%;
303
-
304
- .app-video-player {
305
- display: flex;
306
- flex-direction: row;
307
- flex-wrap: wrap;
308
- position: relative;
309
- align-items: center;
310
-
311
- .vid-wrapper {
312
- width: 100%;
313
- position: relative;
314
- overflow: hidden;
315
-
316
- .playback-button {
317
- position: absolute;
318
- top: 0;
319
- left: 0;
320
- width: 100%;
321
- height: 100%;
322
-
323
- display: flex;
324
- flex-flow: column wrap;
325
- justify-content: center;
326
- align-items: center;
327
- background-color: transparent;
328
- }
329
-
330
- video {
331
- width: 100%;
332
- height: auto;
333
- display: block;
334
- user-select: none;
335
- }
336
- }
337
- }
338
-
339
- //Skeleton animation style
340
- .skeleton {
341
- height: 100%;
342
- width: 100%;
343
- position: absolute;
344
- z-index: 3;
345
- &.isloading {
346
- animation: skeleton-loading 1s linear infinite alternate;
347
- }
348
- &.error {
349
- display: flex;
350
- justify-content: center;
351
- align-items: center;
352
- //colors
353
- background-color: hsl(200, 20%, 80%);
354
- }
355
- &.audio {
356
- height: 277px;
357
- max-width: 600px;
358
- }
359
- }
360
-
361
- @keyframes skeleton-loading {
362
- //colors
363
- 0% {
364
- background-color: hsl(200, 20%, 80%);
365
- }
366
- 100% {
367
- background-color: hsl(200, 20%, 95%);
368
- }
369
- }
370
-
371
- .debug{
372
-
373
- &:focus{
374
- border: 5px solid rgba(#fff, 0.05);
375
- }
376
- }
377
- </style>
1
+ <!--
2
+ @ Description: This component is used to display video element video
3
+ @ What it does: The component create the HTMLMediaElement video tag from data provided by user.
4
+ Once the HTMLMediaElement tag is created, the component save the HTMLMediaElement to store and
5
+ make it available to all other component that will need id
6
+ -->
7
+ <template>
8
+ <section
9
+ v-if="vidData"
10
+ :id="'video_' + id"
11
+ ref="$media-container"
12
+ class="v-media __media-container app-video-player"
13
+ :class="[{ FS: fullScreen }, CCBrowser]"
14
+ aria-label="Video"
15
+ >
16
+ <app-base-error-display
17
+ v-if="hasErr.length"
18
+ :error-group="'component'"
19
+ :error-title="'ERREUR: COMPOSANT DE VIDEO'"
20
+ :errors-list="hasErr"
21
+ />
22
+
23
+ <!------------------video section --------------------------->
24
+
25
+ <template v-else>
26
+ <div class="vid-wrapper">
27
+ <!--Vid-skeleton-->
28
+ <div
29
+ v-if="isLoading || hasSourceLoadingError"
30
+ class="skeleton"
31
+ :class="{ isloading: isLoading, error: hasSourceLoadingError }"
32
+ >
33
+ <p v-if="hasSourceLoadingError">
34
+ Une erreur s'est produite lors du chargement de la vidéo, veuillez
35
+ réessayer
36
+ </p>
37
+ </div>
38
+ <!--End vid-sleleton-->
39
+ <video
40
+ :id="id"
41
+ ref="m-video"
42
+ :poster="vidPoster"
43
+ @loadedmetadata="updateMediaDataVideo($event.target)"
44
+ >
45
+ <source
46
+ v-for="(aSource, index) in vidSources"
47
+ :key="index"
48
+ :src="`${aSource.src}`"
49
+ :type="`video/${aSource.type}`"
50
+ @error="errorHandling($event)"
51
+ />
52
+ <track
53
+ v-for="(subtitle, index) in vidSubtitles"
54
+ :key="`subtitle_${index + 1}`"
55
+ :src="subtitle.src"
56
+ :srclang="subtitle.srclang"
57
+ :label="subtitle.label"
58
+ />
59
+ </video>
60
+ </div>
61
+ <app-comp-play-bar-next
62
+ v-if="$vidElement && !isLoading && !hasSourceLoadingError"
63
+ :ref="`plyr_${id}`"
64
+ :media-to-play="$vidElement"
65
+ @resize-video="resizeVideo"
66
+ />
67
+ </template>
68
+ </section>
69
+ </template>
70
+
71
+ <script>
72
+ import { mapState, mapActions } from 'pinia'
73
+ import { useAppStore } from '../module/stores/appStore'
74
+ import { validateVideoData } from '../shared/validators'
75
+ import AppCompPlayBarNext from './AppCompPlayBarNext.vue'
76
+
77
+ export default {
78
+ components: { AppCompPlayBarNext },
79
+ props: {
80
+ vidData: {
81
+ type: Object,
82
+ required: true,
83
+ validator: (value) => {
84
+ return validateVideoData(value).length === 0
85
+ }
86
+ },
87
+ fullScreen: {
88
+ type: Boolean,
89
+ default: true
90
+ }
91
+ },
92
+
93
+ data() {
94
+ return {
95
+ id: this.vidData.id,
96
+ vidSources: [],
97
+ vidSubtitles: [],
98
+ vidPoster: null,
99
+ vidTranscript: null,
100
+ hasSourceLoadingError: false,
101
+ hasErr: validateVideoData(this.vidData),
102
+ isSet: false,
103
+ playClicked: false,
104
+ vidFocus: false,
105
+ vidElm: null,
106
+ vidElmFocus: false,
107
+ mediaContainer: null
108
+ }
109
+ },
110
+ computed: {
111
+ ...mapState(useAppStore, ['getCurrentBrowser', 'getCurrentPage']),
112
+
113
+ //Return true if the video is loading (to show the loading display)
114
+ isLoading() {
115
+ if (!this.isSet) return true
116
+ else return false
117
+ },
118
+
119
+ setTimeout() {
120
+ return window.setTimeout
121
+ },
122
+ //Return current browser
123
+ CCBrowser() {
124
+ let browser = this.getCurrentBrowser
125
+
126
+ if (browser === 'Safari') {
127
+ return 'safari'
128
+ } else {
129
+ return 'chrome'
130
+ }
131
+ },
132
+ //Return the videoElement (used in the playbar)
133
+ $vidElement() {
134
+ if (!this.isSet) return null
135
+ const { id, mTranscript, mSubtitles } = this.vidData
136
+ const mElement = this.$refs['m-video']
137
+ return {
138
+ id,
139
+ mTranscript,
140
+ mType: 'video',
141
+ mSubtitles,
142
+ mElement,
143
+ mMediaContainer: this.$refs['$media-container']
144
+ }
145
+ }
146
+ },
147
+
148
+ mounted() {
149
+ this.initVideo()
150
+ },
151
+
152
+ beforeUnmount() {
153
+ this.$bus.$off('hide-playback', this.hideAnimation)
154
+ this.$bus.$off('resize-media', this.resizeVideo)
155
+ },
156
+
157
+ methods: {
158
+ ...mapActions(useAppStore, ['updateCurrentMediaElements']),
159
+ /**
160
+ * @description search the DOM for medias with duplicated ID declaration in the page
161
+ * @return return Array of all DOM MediaElement with same id
162
+ */
163
+ checkDuplicatedID() {
164
+ const mediaList = document.querySelectorAll('.__media-container')
165
+ const duplicate = Array.from(mediaList).filter((media) =>
166
+ media.id.includes(this.id)
167
+ )
168
+ return duplicate
169
+ },
170
+ errorHandling(e) {
171
+ this.hasSourceLoadingError = true
172
+ },
173
+ initVideo() {
174
+ const { mSources, mPoster, mSubtitles, mTranscript, id } = this.vidData
175
+ if (id) this.id = id
176
+ if (mSources) this.vidSources = mSources
177
+ if (mPoster) this.vidPoster = mPoster
178
+ if (mSubtitles) this.vidSubtitles = mSubtitles
179
+ if (mTranscript) this.vidTranscript = mTranscript
180
+
181
+ this.$bus.$on('resize-media', this.resizeVideo)
182
+ this.$bus.$on('hide-playback', this.hideAnimation)
183
+ },
184
+
185
+ /**
186
+ * @description update the information for the mediaElement in the store
187
+ * @param {htmlElement} e
188
+ * @fires update-page to AppBaseModule.vue
189
+ */
190
+ updateMediaDataVideo(e) {
191
+ this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'loading')
192
+ this.$bus.$emit('update-media-duration')
193
+
194
+ if (this.checkDuplicatedID().length > 1) {
195
+ const errmsg = `Cet élément a le même ID (<b>${this.id}</b>) q'un autre media. Vous ne devez pas avoir de médias avec le même ID dans une page.`
196
+
197
+ console.warn(
198
+ `%c WARNING!>>> You cannot use the same ID (${this.id}) in your media elements for your page.`,
199
+ 'background: orange; color: white; display: block; border-radius:5px; margin:5px;'
200
+ )
201
+
202
+ this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
203
+ return this.hasErr.push(errmsg)
204
+ }
205
+
206
+ this.updateCurrentMediaElements(e).then(() => {
207
+ this.isSet = true
208
+ this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
209
+ })
210
+ },
211
+ /**
212
+ *@description - small animation over the playback button
213
+ * Play a little animation on the svg and then send a play event
214
+ *@emits play-media - To AppComPLayBar
215
+ */
216
+ runPlaybackAnimation() {
217
+ //this.isPlaying = !this.$vidElement.mElement.paused
218
+ // let animation = this.$gsap.fromTo(
219
+ // '.playback-animation',
220
+ // { opacity: 1, scale: 0.8 },
221
+ // {
222
+ // opacity: 0,
223
+ // scale: 1.3,
224
+ // duration: 0.8,
225
+ // ease: 'power2.out'
226
+ // }
227
+ // )
228
+ // animation.play()
229
+ },
230
+
231
+ /**
232
+ * @description initiate play back of media when click on the video Element with animation
233
+ * @emits play-media to AppComplayBar
234
+ *
235
+ */
236
+ /*handlePlayBack(evtType) {
237
+ this.runPlaybackAnimation()
238
+ this.$bus.$emit('play-media', 'KeyK')
239
+ },*/
240
+ /**
241
+ * @description hide the animation when the media is played with click on playbar play button
242
+ * this ensures that animation does not run every time user controls the media via the playbar controls
243
+ */
244
+ hideAnimation(type = null) {
245
+ if (this.playClicked && !type) return
246
+ this.runPlaybackAnimation()
247
+ this.playClicked = true
248
+ },
249
+
250
+ /**
251
+ * @description- Skip directly to the specify containt/region*
252
+ * main content is Node with defined ID
253
+ * @param {String} targetID- Css selector for the target element
254
+ */
255
+
256
+ skipTo(targetID) {
257
+ let skipTo = document.querySelector(`#wrapper-content`) //default definition of main element
258
+
259
+ if (targetID) {
260
+ let targetEl = document.querySelector(`#${targetID}`) // search for node element specified as main
261
+
262
+ if (targetEl) skipTo = targetEl
263
+ }
264
+ let targetTop = skipTo.offsetTop
265
+
266
+ let scrollOpt = {
267
+ top: targetTop - 100,
268
+ left: 0,
269
+ behavior: 'auto' //auto
270
+ }
271
+
272
+ // Allowing accessibility control with keyboard
273
+ skipTo.setAttribute('tabIndex', -1)
274
+ window.scrollTo(scrollOpt)
275
+ skipTo.focus()
276
+ },
277
+
278
+ /**
279
+ * @description -set the size of the video element when transcript is open or closed
280
+ */
281
+
282
+ resizeVideo(size, container) {
283
+ let defaultSize = 100
284
+ if (size == 'sm') defaultSize = 68
285
+ //const videoElement = document.querySelector('.__media-container')
286
+ const videoElement = this.$vidElement.mMediaContainer
287
+ setTimeout(() => {
288
+ videoElement.style.width = `${defaultSize}%`
289
+ }, 100)
290
+ },
291
+
292
+ spaceKeyPreventDefault(evt) {
293
+ let { code } = evt
294
+ if (code == 'Space') {
295
+ evt.preventDefault()
296
+ this.handlePlayBack('spacebar')
297
+ }
298
+ }
299
+ }
300
+ }
301
+ </script>
302
+ <style lang="scss">
303
+ $widthVideo: 100%;
304
+
305
+ .app-video-player {
306
+ display: flex;
307
+ flex-direction: row;
308
+ flex-wrap: wrap;
309
+ position: relative;
310
+ align-items: center;
311
+
312
+ .vid-wrapper {
313
+ width: 100%;
314
+ position: relative;
315
+ overflow: hidden;
316
+
317
+ .playback-button {
318
+ position: absolute;
319
+ top: 0;
320
+ left: 0;
321
+ width: 100%;
322
+ height: 100%;
323
+
324
+ display: flex;
325
+ flex-flow: column wrap;
326
+ justify-content: center;
327
+ align-items: center;
328
+ background-color: transparent;
329
+ }
330
+
331
+ video {
332
+ width: 100%;
333
+ height: auto;
334
+ display: block;
335
+ user-select: none;
336
+ }
337
+ }
338
+ }
339
+
340
+ //Skeleton animation style
341
+ .skeleton {
342
+ height: 100%;
343
+ width: 100%;
344
+ position: absolute;
345
+ z-index: 3;
346
+ &.isloading {
347
+ animation: skeleton-loading 1s linear infinite alternate;
348
+ }
349
+ &.error {
350
+ display: flex;
351
+ justify-content: center;
352
+ align-items: center;
353
+ //colors
354
+ background-color: hsl(200, 20%, 80%);
355
+ }
356
+ &.audio {
357
+ height: 277px;
358
+ max-width: 600px;
359
+ }
360
+ }
361
+
362
+ @keyframes skeleton-loading {
363
+ //colors
364
+ 0% {
365
+ background-color: hsl(200, 20%, 80%);
366
+ }
367
+ 100% {
368
+ background-color: hsl(200, 20%, 95%);
369
+ }
370
+ }
371
+
372
+ .debug {
373
+ &:focus {
374
+ border: 5px solid rgba(#fff, 0.05);
375
+ }
376
+ }
377
+ </style>