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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +13 -18
  2. package/bk.scss +117 -0
  3. package/package.json +23 -39
  4. package/src/$locales/en.json +30 -16
  5. package/src/$locales/fr.json +29 -16
  6. package/src/components/AppBase.vue +740 -305
  7. package/src/components/AppBaseButton.vue +33 -5
  8. package/src/components/AppBaseErrorDisplay.vue +43 -35
  9. package/src/components/AppBaseModule.vue +447 -623
  10. package/src/components/AppBasePage.vue +37 -25
  11. package/src/components/AppCompAudio.vue +266 -0
  12. package/src/components/AppCompBranchButtons.vue +52 -63
  13. package/src/components/AppCompButtonProgress.vue +1 -16
  14. package/src/components/AppCompCarousel.vue +43 -39
  15. package/src/components/AppCompInputCheckBox.vue +9 -3
  16. package/src/components/AppCompInputDropdown.vue +2 -4
  17. package/src/components/AppCompInputRadio.vue +8 -15
  18. package/src/components/AppCompInputTextTable.vue +15 -12
  19. package/src/components/AppCompInputTextToFillDropdown.vue +16 -14
  20. package/src/components/AppCompInputTextToFillText.vue +2 -2
  21. package/src/components/AppCompJauge.vue +13 -1
  22. package/src/components/AppCompMenu.vue +203 -10
  23. package/src/components/AppCompMenuItem.vue +20 -3
  24. package/src/components/AppCompNavigation.vue +351 -355
  25. package/src/components/AppCompNoteCall.vue +62 -47
  26. package/src/components/AppCompNoteCredit.vue +182 -79
  27. package/src/components/AppCompPlayBar.vue +975 -1023
  28. package/src/components/AppCompPlayBarProgress.vue +73 -0
  29. package/src/components/AppCompPopUp.vue +175 -114
  30. package/src/components/AppCompQuiz.vue +67 -81
  31. package/src/components/AppCompQuizRecall.vue +32 -5
  32. package/src/components/AppCompSVG.vue +66 -40
  33. package/src/components/AppCompSettingsMenu.vue +6 -8
  34. package/src/components/AppCompTableOfContent.vue +166 -45
  35. package/src/components/AppCompVideoPlayer.vue +154 -110
  36. package/src/components/BaseModule.vue +21 -17
  37. package/src/main.js +124 -88
  38. package/src/mixins/$mediaMixins.js +827 -0
  39. package/src/mixins/$pageMixins.js +65 -109
  40. package/src/mixins/$quizMixins.js +12 -26
  41. package/src/mixins/timerMixin.js +8 -9
  42. package/src/module/store.js +187 -68
  43. package/src/module/xapi/ADL.js +90 -53
  44. package/src/module/xapi/Crypto/Hasher.js +8 -8
  45. package/src/module/xapi/Crypto/WordArray.js +6 -6
  46. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
  47. package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
  48. package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
  49. package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
  50. package/src/module/xapi/Crypto/encoders/Base.js +7 -7
  51. package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
  52. package/src/module/xapi/Crypto/encoders/Hex.js +2 -2
  53. package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
  54. package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
  55. package/src/module/xapi/Statement/index.js +1 -1
  56. package/src/module/xapi/launch.js +10 -10
  57. package/src/module/xapi/utils.js +17 -17
  58. package/src/module/xapi/wrapper.js +123 -50
  59. package/src/module/xapi/xapiStatement.js +29 -29
  60. package/src/plugins/helper.js +8 -9
  61. package/src/plugins/i18n.js +23 -10
  62. package/src/plugins/scorm.js +14 -14
  63. package/src/router/index.js +3 -4
  64. package/src/router/routes.js +10 -30
  65. package/src/shared/generalfuncs.js +31 -24
  66. package/src/shared/validators.js +730 -20
  67. package/.prettierrc.js +0 -5
  68. package/babel.config.js +0 -3
  69. package/src/components/AppBaseDragChoice.vue +0 -91
  70. package/src/components/AppBaseDropZone.vue +0 -112
  71. package/src/components/AppCompBif.vue +0 -120
  72. package/src/components/AppCompDragAndDrop.vue +0 -339
  73. package/src/components/AppCompInputAssociation.vue +0 -332
  74. package/src/components/AppCompMediaPlayer.vue +0 -397
  75. package/src/plugins/timeManager.js +0 -77
  76. package/src/routes_bckp.js +0 -313
  77. package/src/routes_static.js +0 -344
  78. package/vue.config.js +0 -83
@@ -4,21 +4,21 @@
4
4
  Once the HTMLMediaElement tag is created, the component save the HTMLMediaElement to store and
5
5
  make it available to all other component that will need id
6
6
  -->
7
-
8
7
  <template>
9
- <div
8
+ <section
10
9
  v-if="vidData"
11
- id="app-video-player"
12
- class="v-media __media-container"
10
+ :id="'video_' + id"
11
+ ref="$media-container"
12
+ class="v-media __media-container app-video-player"
13
13
  :class="[{ FS: fullScreen }, CCBrowser]"
14
- tabindex="0"
14
+ aria-label="Video"
15
15
  >
16
- <b-row v-show="hasErr.length" class="warning-error">
16
+ <b-row v-if="hasErr.length" class="warning-error">
17
17
  <b-col class="box-error">
18
18
  <div class="yellow-box">
19
19
  <h2>
20
20
  <b-icon icon="exclamation-triangle" />
21
- Erreure: COMPOSANT VIDEO
21
+ Erreur: COMPOSANT VIDEO
22
22
  </h2>
23
23
  </div>
24
24
  <div class="box">
@@ -28,22 +28,31 @@
28
28
  <br />
29
29
  </p>
30
30
  <ul>
31
- <li v-for="(err, index) in hasErr" :key="`error_type_${index}`">
32
- {{ err }}
33
- </li>
31
+ <li
32
+ v-for="(err, index) in hasErr"
33
+ :key="`error_type_${index}`"
34
+ v-html="err"
35
+ />
34
36
  </ul>
35
37
  </div>
36
38
  </b-col>
37
39
  </b-row>
38
40
  <!------------------video section --------------------------->
39
- <template v-show="!hasErr.length">
40
- <div class="vid-wrapper" @click="handlePlayBack">
41
- <div class="playback-animation">
42
- <svg class="app-icons-svg">
43
- <use v-show="!isPlaying" href="#play-icon" />
44
- <use v-show="isPlaying" href="#pause-icon" />
45
- </svg>
41
+
42
+ <template v-else>
43
+ <div class="vid-wrapper">
44
+ <!--Vid-skeleton-->
45
+ <div
46
+ v-if="isLoading || hasSourceLoadingError"
47
+ class="skeleton"
48
+ :class="{ isloading: isLoading, error: hasSourceLoadingError }"
49
+ >
50
+ <p v-if="hasSourceLoadingError">
51
+ Une erreur s'est produite lors du chargement de la vidéo, veuillez
52
+ réessayer
53
+ </p>
46
54
  </div>
55
+ <!--End vid-sleleton-->
47
56
  <video
48
57
  :id="id"
49
58
  ref="m-video"
@@ -55,6 +64,7 @@
55
64
  :key="index"
56
65
  :src="`${aSource.src}`"
57
66
  :type="`video/${aSource.type}`"
67
+ @error="errorHandling($event)"
58
68
  />
59
69
  <track
60
70
  v-for="(subtitle, index) in vidSubtitles"
@@ -65,9 +75,14 @@
65
75
  />
66
76
  </video>
67
77
  </div>
68
- <app-comp-play-bar v-if="$vidElement" :media-to-play="$vidElement" />
78
+ <app-comp-play-bar
79
+ v-if="$vidElement && !isLoading && !hasSourceLoadingError"
80
+ :ref="`plyr_${id}`"
81
+ :media-to-play="$vidElement"
82
+ @resize-video="resizeVideo"
83
+ />
69
84
  </template>
70
- </div>
85
+ </section>
71
86
  </template>
72
87
 
73
88
  <script>
@@ -91,24 +106,33 @@ export default {
91
106
 
92
107
  data() {
93
108
  return {
94
- id: null,
109
+ id: this.vidData.id,
95
110
  vidSources: [],
96
111
  vidSubtitles: [],
97
112
  vidPoster: null,
98
113
  vidTranscript: null,
114
+ hasSourceLoadingError: false,
99
115
  hasErr: validateVideoData(this.vidData),
100
116
  isSet: false,
101
- isPlaying: false,
102
- playClicked: false
117
+ playClicked: false,
118
+ vidFocus: false,
119
+ vidElm: null,
120
+ vidElmFocus: false,
121
+ mediaContainer: null
103
122
  }
104
123
  },
105
124
  computed: {
106
- ...mapGetters(['getCurrentBrowser']),
125
+ ...mapGetters(['getCurrentBrowser', 'getCurrentPage']),
126
+ //Return true if the video is loading (to show the loading display)
127
+ isLoading() {
128
+ if (!this.isSet && !this.hasSourceLoadingError) return true
129
+ else return false
130
+ },
107
131
 
108
132
  setTimeout() {
109
133
  return window.setTimeout
110
134
  },
111
-
135
+ //Return current browser
112
136
  CCBrowser() {
113
137
  let browser = this.getCurrentBrowser
114
138
 
@@ -118,35 +142,18 @@ export default {
118
142
  return 'chrome'
119
143
  }
120
144
  },
121
-
122
- mediaA11Y() {
123
- let m = {
124
- srTxt: '',
125
- skipToTxt: ''
126
- }
127
-
128
- this.$i18n.locale === 'fr'
129
- ? (m = {
130
- srTxt:
131
- "Le contenu d'apprentissage sera seulement disponible à la fin de la narration. Appuyez le bouton jouer, pour lancer l'animation.",
132
- skipToTxt: 'Passer au controleur de media'
133
- })
134
- : (m = {
135
- srTxt:
136
- 'The learning content will be available only at the end of the narration. Press the play button to start the animation',
137
- skipToTxt: 'Skip to play-bar'
138
- })
139
-
140
- return m
141
- },
145
+ //Return the videoElement (used in the playbar)
142
146
  $vidElement() {
143
147
  if (!this.isSet) return null
144
- const { mTranscript, mSubtitles } = this.vidData
148
+ const { id, mTranscript, mSubtitles } = this.vidData
149
+ const mElement = this.$refs['m-video']
145
150
  return {
151
+ id,
146
152
  mTranscript,
147
153
  mType: 'video',
148
154
  mSubtitles,
149
- mElement: this.$refs['m-video']
155
+ mElement,
156
+ mMediaContainer: this.$refs['$media-container']
150
157
  }
151
158
  }
152
159
  },
@@ -155,25 +162,23 @@ export default {
155
162
  this.initVideo()
156
163
  },
157
164
 
158
- beforeDestroy() {
165
+ beforeUnmount() {
159
166
  this.$bus.$off('hide-playback', this.hideAnimation)
160
167
  this.$bus.$off('resize-media', this.resizeVideo)
161
168
  },
162
169
 
163
170
  methods: {
171
+ errorHandling(e) {
172
+ this.hasSourceLoadingError = true
173
+ },
164
174
  initVideo() {
165
- const { mSources, mPoster, mSubtitles, mTranscript } = this.vidData
166
-
175
+ const { mSources, mPoster, mSubtitles, mTranscript, id } = this.vidData
176
+ if (id) this.id = id
167
177
  if (mSources) this.vidSources = mSources
168
178
  if (mPoster) this.vidPoster = mPoster
169
179
  if (mSubtitles) this.vidSubtitles = mSubtitles
170
180
  if (mTranscript) this.vidTranscript = mTranscript
171
181
 
172
- setTimeout(() => {
173
- this.isSet = true
174
- this.isPlaying = !this.$vidElement.mElement.paused
175
- this.vidPaused = this.$vidElement.mElement.paused
176
- }, 200)
177
182
  this.$bus.$on('resize-media', this.resizeVideo)
178
183
  this.$bus.$on('hide-playback', this.hideAnimation)
179
184
  },
@@ -187,15 +192,28 @@ export default {
187
192
  //dispatch loading status of for this component
188
193
  this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'loading')
189
194
  this.$bus.$emit('update-media-duration')
195
+ //Should Check that the the media Element is unique im Media Liste
196
+ const { mElements } = this.getCurrentPage
197
+
198
+ const hasEntry = mElements.findIndex((media) => media.id === e.target.id)
190
199
 
191
- this.$store
192
- .dispatch('updateCurrentMediaElement', e.target)
193
- .then(() => {
194
- this.$bus.$emit('update-page')
195
- })
196
- .then(() =>
197
- this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
200
+ if (hasEntry !== -1) {
201
+ // Should report Error to Console and Component template about this media
202
+ const errmsg = `Cet élément a le même ID q'un autre media. Vous ne devez pas avoir de médias avec le même ID dans une page.`
203
+
204
+ console.warn(
205
+ `%c WARNING!>>> You cannot use the same ID in your media elements`,
206
+ 'background: orange; color: white; display: block; border-radius:5px; margin:5px;'
198
207
  )
208
+
209
+ this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
210
+ return this.hasErr.push(errmsg)
211
+ }
212
+
213
+ this.$store.dispatch('updateCurrentMediaElements', e.target).then(() => {
214
+ this.isSet = true
215
+ this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
216
+ })
199
217
  },
200
218
  /**
201
219
  *@description - small animation over the playback button
@@ -203,7 +221,7 @@ export default {
203
221
  *@emits play-media - To AppComPLayBar
204
222
  */
205
223
  runPlaybackAnimation() {
206
- this.isPlaying = !this.$vidElement.mElement.paused
224
+ //this.isPlaying = !this.$vidElement.mElement.paused
207
225
 
208
226
  let animation = this.$gsap.fromTo(
209
227
  '.playback-animation',
@@ -223,16 +241,16 @@ export default {
223
241
  * @emits play-media to AppComplayBar
224
242
  *
225
243
  */
226
- handlePlayBack() {
244
+ /*handlePlayBack(evtType) {
227
245
  this.runPlaybackAnimation()
228
- this.$bus.$emit('play-media', 'k')
229
- },
246
+ this.$bus.$emit('play-media', 'KeyK')
247
+ },*/
230
248
  /**
231
249
  * @description hide the animation when the media is played with click on playbar play button
232
250
  * this ensures that animation does not run every time user controls the media via the playbar controls
233
251
  */
234
- hideAnimation() {
235
- if (this.playClicked) return
252
+ hideAnimation(type = null) {
253
+ if (this.playClicked && !type) return
236
254
  this.runPlaybackAnimation()
237
255
  this.playClicked = true
238
256
  },
@@ -269,13 +287,23 @@ export default {
269
287
  * @description -set the size of the video element when transcript is open or closed
270
288
  */
271
289
 
272
- resizeVideo(size) {
273
- let defaultSize = 98
290
+ resizeVideo(size, container) {
291
+ let defaultSize = 100
274
292
  if (size == 'sm') defaultSize = 68
275
- const videoElement = document.querySelector('.__media-container')
293
+ //const videoElement = document.querySelector('.__media-container')
294
+ const videoElement = this.$vidElement.mMediaContainer
276
295
  setTimeout(() => {
277
296
  videoElement.style.width = `${defaultSize}%`
278
297
  }, 100)
298
+ },
299
+
300
+ spaceKeyPreventDefault(evt) {
301
+ let { code } = evt
302
+ if (code == 'Space') {
303
+ //console.log('\x1b[43mspace i')
304
+ evt.preventDefault()
305
+ this.handlePlayBack('spacebar')
306
+ }
279
307
  }
280
308
  }
281
309
  }
@@ -283,54 +311,70 @@ export default {
283
311
  <style lang="scss">
284
312
  $widthVideo: 100%;
285
313
 
286
- .__media-container,
287
- .vid-wrapper {
314
+ .app-video-player {
288
315
  display: flex;
289
- align-items: center;
290
- justify-content: center;
291
- }
292
-
293
- .__media-container {
294
- width: 98%;
295
- user-select: none;
296
- overflow: hidden;
297
- max-width: 900px;
298
- border-radius: 5px;
299
- background: #000;
300
-
316
+ flex-direction: row;
317
+ flex-wrap: wrap;
301
318
  position: relative;
302
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
303
- transition: all 0.3s ease;
319
+ align-items: center;
304
320
 
305
- video,
306
321
  .vid-wrapper {
307
- width: $widthVideo;
322
+ width: 100%;
323
+ position: relative;
324
+ overflow: hidden;
325
+
326
+ .playback-button {
327
+ position: absolute;
328
+ top: 0;
329
+ left: 0;
330
+ width: 100%;
331
+ height: 100%;
332
+
333
+ display: flex;
334
+ flex-flow: column wrap;
335
+ justify-content: center;
336
+ align-items: center;
337
+ background-color: transparent;
338
+ }
339
+
340
+ video {
341
+ width: 100%;
342
+ height: auto;
343
+ display: block;
344
+ user-select: none;
345
+ }
308
346
  }
347
+ }
309
348
 
310
- .playback-animation {
311
- cursor: pointer;
312
- position: absolute;
349
+ //Skeleton animation style
350
+ .skeleton {
351
+ height: 100%;
352
+ width: 100%;
353
+ position: absolute;
354
+ z-index: 3;
355
+ &.isloading {
356
+ animation: skeleton-loading 1s linear infinite alternate;
357
+ }
358
+ &.error {
313
359
  display: flex;
314
- align-items: center;
315
360
  justify-content: center;
316
-
317
- -webkit-box-shadow: 7px 5px 7px 0px rgba(0, 0, 0, 0.24);
318
- box-shadow: 7px 5px 7px 0px rgba(0, 0, 0, 0.24);
319
- background-color: #007bff;
320
- width: 75px;
321
- height: 75px;
322
- border-radius: 50%;
323
-
324
- .app-icons-svg {
325
- width: 40px;
326
- height: 40px;
327
- }
361
+ align-items: center;
362
+ //colors
363
+ background-color: hsl(200, 20%, 80%);
364
+ }
365
+ &.audio {
366
+ height: 277px;
367
+ max-width: 600px;
328
368
  }
329
369
  }
330
- .__media-container.fullscreen {
331
- max-width: 100%;
332
- width: 100%;
333
- height: 100vh;
334
- border-radius: 0px;
370
+
371
+ @keyframes skeleton-loading {
372
+ //colors
373
+ 0% {
374
+ background-color: hsl(200, 20%, 80%);
375
+ }
376
+ 100% {
377
+ background-color: hsl(200, 20%, 95%);
378
+ }
335
379
  }
336
380
  </style>
@@ -5,18 +5,17 @@
5
5
  -->
6
6
  <template>
7
7
  <div v-if="mData" class="module-wrapper">
8
- <div
8
+ <!-- <div
9
9
  class="overlay-close-widget"
10
10
  :class="{ blockOverlay: over }"
11
11
  @click="overlayClose()"
12
- ></div>
12
+ ></div> -->
13
13
  <slot name="mTitle" />
14
14
  <slot>Module</slot>
15
15
  </div>
16
16
  </template>
17
17
 
18
18
  <script>
19
- import { mapGetters } from 'vuex'
20
19
  export default {
21
20
  props: {
22
21
  mData: {
@@ -27,30 +26,35 @@ export default {
27
26
 
28
27
  data() {
29
28
  return {
30
- data: null,
31
- over: false
29
+ data: null
30
+ // over: false
32
31
  // back: null
33
32
  }
34
33
  },
35
34
 
36
- computed: {
37
- ...mapGetters(['getModuleInfo', 'getConnectionInfo'])
38
- },
39
- watch: {
40
- getConnectionInfo: {
41
- immediate: true,
42
- handler() {
43
- if (!this.getConnectionInfo) return
44
- // this.$bus.$emit('set-user-progress')
45
- }
46
- }
35
+ mounted() {
36
+ // this.$bus.$on('toggle-widget', () => {
37
+ // this.over = true
38
+ // })
39
+ // this.$bus.$on('close-widget', () => {
40
+ // this.over = false
41
+ // })
47
42
  },
48
43
  created() {},
49
- methods: {}
44
+ methods: {
45
+ // overlayClose() {
46
+ // this.$bus.$emit('close-widget')
47
+ // }
48
+ }
50
49
  }
51
50
  </script>
52
51
 
53
52
  <style lang="scss" scoped>
53
+ .module-wrapper {
54
+ width: 100%;
55
+ height: 100%;
56
+ min-height: 100vh;
57
+ }
54
58
  .overlay-close-widget {
55
59
  position: absolute;
56
60
  top: 0;