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.
- package/.editorconfig +7 -7
- package/.gitlab-ci.yml +124 -0
- package/.prettierrc +11 -11
- package/.vscode/extensions.json +8 -8
- package/.vscode/settings.json +46 -16
- package/CHANGELOG +520 -520
- package/README.md +57 -57
- package/documentation/.vitepress/config.js +114 -114
- package/documentation/api-examples.md +49 -49
- package/documentation/composants/app-base-button.md +58 -58
- package/documentation/composants/app-base-error-display.md +59 -59
- package/documentation/composants/app-base-popover.md +68 -68
- package/documentation/composants/app-comp-audio.md +75 -75
- package/documentation/composants/app-comp-branch-buttons.md +111 -111
- package/documentation/composants/app-comp-button-progress.md +53 -53
- package/documentation/composants/app-comp-carousel.md +53 -53
- package/documentation/composants/app-comp-container.md +53 -53
- package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
- package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
- package/documentation/composants/app-comp-input-radio-next.md +39 -39
- package/documentation/composants/app-comp-input-text-next.md +35 -35
- package/documentation/composants/app-comp-input-text-table-next.md +34 -34
- package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
- package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
- package/documentation/composants/app-comp-jauge.md +31 -31
- package/documentation/composants/app-comp-menu-item.md +55 -55
- package/documentation/composants/app-comp-menu.md +29 -29
- package/documentation/composants/app-comp-navigation.md +41 -41
- package/documentation/composants/app-comp-note-call.md +53 -53
- package/documentation/composants/app-comp-note-credit.md +53 -53
- package/documentation/composants/app-comp-play-bar-next.md +53 -53
- package/documentation/composants/app-comp-pop-up-next.md +93 -93
- package/documentation/composants/app-comp-quiz-next.md +235 -235
- package/documentation/composants/app-comp-quiz-recall.md +53 -53
- package/documentation/composants/app-comp-svg-next.md +53 -53
- package/documentation/composants/app-comp-table-of-content.md +50 -50
- package/documentation/composants/app-comp-video-player.md +82 -82
- package/documentation/composants.md +46 -46
- package/documentation/composants_critiques/ModelPageComposant.md +53 -53
- package/documentation/composants_critiques/app-base-module.md +43 -43
- package/documentation/composants_critiques/app-base-page.md +48 -48
- package/documentation/composants_critiques/app-base.md +311 -311
- package/documentation/composants_critiques/main.md +15 -15
- package/documentation/demarrage.md +50 -50
- package/documentation/deploiement.md +57 -57
- package/documentation/index.md +33 -33
- package/documentation/markdown-examples.md +85 -85
- package/documentation/public/vite.svg +14 -14
- package/documentation/public/vuejs.svg +1 -1
- package/documentation/public/vuetify.svg +5 -5
- package/eslint.config.js +60 -60
- package/junit-report.xml +182 -0
- package/package.json +66 -59
- package/playwright/index.html +12 -0
- package/playwright/index.js +21 -0
- package/playwright-ct.config.js +95 -0
- package/src/$locales/en.json +157 -157
- package/src/$locales/fr.json +120 -120
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +1171 -1169
- package/src/components/AppBaseButton.vue +90 -95
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +1639 -1634
- package/src/components/AppBasePage.vue +867 -866
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +66 -66
- package/src/components/AppCompAudio.vue +261 -256
- package/src/components/AppCompBranchButtons.vue +508 -508
- package/src/components/AppCompButtonProgress.vue +137 -132
- package/src/components/AppCompCarousel.vue +342 -336
- package/src/components/AppCompContainer.vue +29 -29
- package/src/components/AppCompInputCheckBoxNx.vue +325 -323
- package/src/components/AppCompInputDropdownNx.vue +302 -299
- package/src/components/AppCompInputRadioNx.vue +287 -284
- package/src/components/AppCompInputTextNx.vue +156 -153
- package/src/components/AppCompInputTextTableNx.vue +205 -202
- package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
- package/src/components/AppCompInputTextToFillNx.vue +316 -313
- package/src/components/AppCompJauge.vue +81 -81
- package/src/components/AppCompMenu.vue +6 -1
- package/src/components/AppCompMenuItem.vue +246 -240
- package/src/components/AppCompNavigation.vue +977 -972
- package/src/components/AppCompNoteCall.vue +167 -161
- package/src/components/AppCompNoteCredit.vue +496 -491
- package/src/components/AppCompPlayBarNext.vue +2290 -2288
- package/src/components/AppCompPopUpNext.vue +508 -504
- package/src/components/AppCompQuizNext.vue +515 -510
- package/src/components/AppCompQuizRecall.vue +355 -350
- package/src/components/AppCompSVGNext.vue +346 -346
- package/src/components/AppCompSettingsMenu.vue +177 -172
- package/src/components/AppCompTableOfContent.vue +433 -427
- package/src/components/AppCompVideoPlayer.vue +377 -377
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +55 -55
- package/src/composables/useIdleDetector.js +56 -56
- package/src/composables/useQuiz.js +89 -89
- package/src/composables/useTimer.js +172 -172
- package/src/directives/nvdaFix.js +53 -53
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +493 -476
- package/src/module/stores/appStore.js +960 -947
- package/src/module/xapi/ADL.js +520 -520
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/launch.js +157 -157
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper.js +1895 -1895
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/analytics.js +34 -34
- package/src/plugins/bus.js +12 -8
- package/src/plugins/gsap.js +17 -17
- package/src/plugins/helper.js +355 -358
- package/src/plugins/i18n.js +27 -26
- package/src/plugins/idb.js +227 -227
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +57 -57
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +344 -344
- package/src/shared/validators.js +1018 -1018
- package/tests/component/AppBaseButton.spec.js +53 -0
- package/tests/component/pinia.spec.js +24 -0
- package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
- package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
- package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
- package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
- package/tests/unit/AppCompInputTextNx.spec.js +44 -0
- package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
- package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
- package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
- package/tests/unit/AppCompQuizNext.spec.js +114 -0
- package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
- package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
- package/vitest.config.js +28 -19
- package/vitest.setup.js +28 -0
- 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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.$bus.$off('
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
* @
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (
|
|
176
|
-
if (
|
|
177
|
-
if (
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.$bus.$on('
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
* @
|
|
187
|
-
* @
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
this.$bus.$emit('
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
// {
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
* @
|
|
233
|
-
*
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
*
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
this.
|
|
246
|
-
this.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const videoElement =
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
flex-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
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>
|