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

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 (49) hide show
  1. package/README.md +1 -1
  2. package/package.json +7 -9
  3. package/src/$locales/en.json +39 -15
  4. package/src/$locales/fr.json +48 -23
  5. package/src/components/AppBase.vue +245 -266
  6. package/src/components/AppBaseErrorDisplay.vue +29 -0
  7. package/src/components/AppBaseModule.vue +485 -232
  8. package/src/components/AppBasePage.vue +28 -54
  9. package/src/components/AppCompBif.vue +120 -0
  10. package/src/components/AppCompBranchButtons.vue +31 -30
  11. package/src/components/AppCompButtonProgress.vue +35 -46
  12. package/src/components/AppCompCarousel.vue +154 -247
  13. package/src/components/AppCompJauge.vue +1 -2
  14. package/src/components/AppCompMediaPlayer.vue +106 -74
  15. package/src/components/AppCompMenu.vue +87 -81
  16. package/src/components/AppCompMenuItem.vue +48 -90
  17. package/src/components/AppCompNavigation.vue +949 -0
  18. package/src/components/AppCompNoteCall.vue +126 -0
  19. package/src/components/AppCompNoteCredit.vue +164 -0
  20. package/src/components/AppCompPlayBar.vue +864 -1085
  21. package/src/components/AppCompPopUp.vue +26 -27
  22. package/src/components/AppCompPopover.vue +27 -0
  23. package/src/components/AppCompQuiz.vue +27 -36
  24. package/src/components/AppCompQuizRecall.vue +250 -0
  25. package/src/components/AppCompSVG.vue +309 -0
  26. package/src/components/AppCompSettingsMenu.vue +1 -0
  27. package/src/components/AppCompTableOfContent.vue +140 -85
  28. package/src/components/AppCompTranscript.vue +19 -0
  29. package/src/components/AppCompVideoPlayer.vue +336 -0
  30. package/src/components/BaseModule.vue +24 -105
  31. package/src/main.js +18 -9
  32. package/src/mixins/$pageMixins.js +106 -28
  33. package/src/mixins/timerMixin.js +31 -7
  34. package/src/module/store.js +33 -12
  35. package/src/module/xapi/Crypto/encoders/Hex.js +2 -1
  36. package/src/module/xapi/wrapper.js +4 -4
  37. package/src/plugins/gsap.js +4 -1
  38. package/src/plugins/helper.js +53 -18
  39. package/src/plugins/idb.js +1 -0
  40. package/src/public/index.html +1 -1
  41. package/src/router/index.js +41 -0
  42. package/src/router/routes.js +337 -0
  43. package/src/routes_bckp.js +313 -0
  44. package/src/routes_static.js +344 -0
  45. package/src/shared/generalfuncs.js +79 -4
  46. package/src/shared/validators.js +249 -0
  47. package/src/components/AppCompNavigationFull.vue +0 -1791
  48. package/src/components/AppCompToolTip.vue +0 -94
  49. package/src/routes.js +0 -734
@@ -19,18 +19,12 @@
19
19
  id="video"
20
20
  ref="video"
21
21
  tabindex="0"
22
- class="m-video"
22
+ class="v-media m-video"
23
23
  :poster="media.mPoster"
24
24
  @loadedmetadata="updateMediaData($event)"
25
25
  @load="() => {}"
26
26
  @error="() => {}"
27
27
  >
28
- <!-- <source
29
- v-for="(aMedia, index) in media.mSources"
30
- :key="index"
31
- :src="`${aMedia.src}`"
32
- :type="`${media.mType}/${aMedia.type}`"
33
- /> -->
34
28
  <source
35
29
  v-for="(aMedia, index) in media.mSources"
36
30
  :key="index"
@@ -49,7 +43,7 @@
49
43
  v-if="media && media.mType === 'audio'"
50
44
  ref="audio"
51
45
  tabindex="0"
52
- class="m-audio"
46
+ class="v-media m-audio"
53
47
  :poster="media.mPoster"
54
48
  @loadedmetadata="updateMediaData($event)"
55
49
  >
@@ -70,14 +64,24 @@
70
64
  <div
71
65
  v-if="media && hasAnimation"
72
66
  id="anim-box"
67
+ ref="anim-box"
73
68
  class="anim-canvas"
74
69
  :class="[animationType]"
70
+ role="figure"
71
+ aria-roledescription="Animation"
75
72
  >
73
+ <p class="sr-only">{{ mediaA11Y.srTxt }}</p>
74
+ <a class="skip-link" href="" @click.prevent="skipTo('playbar-play')">
75
+ {{ mediaA11Y.skipToTxt }}
76
+ </a>
76
77
  <slot ref="animScene" name="drawingCanvas">
77
78
  You have an animation to create
78
79
  </slot>
79
80
  </div>
80
- <portal-target name="playbar-portal"></portal-target>
81
+ <!-- <div v-if="mData"> -->
82
+ <app-comp-play-bar :media-to-play="mData" />
83
+ <!-- </div> -->
84
+ <!-- <portal-target :key="$route.fullPath" name="playbar-portal"></portal-target> -->
81
85
  </div>
82
86
  </template>
83
87
 
@@ -106,13 +110,18 @@ export default {
106
110
  timeline: null,
107
111
  subtitleMenuButtons: [],
108
112
  subtitlesMenu: null,
109
- randKey: `kid_${Math.floor(Math.random() * 1001)}`,
110
113
  fsHeigh: null,
111
114
  isMedia: false
115
+ //currentMediaElement: null
112
116
  }
113
117
  },
114
118
  computed: {
115
- ...mapGetters(['hasMediaElOrTimeline', 'getCurrentBrowser']),
119
+ ...mapGetters([
120
+ 'hasMediaElOrTimeline',
121
+ 'getCurrentBrowser',
122
+ 'getCurrentPage'
123
+ ]),
124
+
116
125
  setTimeout() {
117
126
  return window.setTimeout
118
127
  },
@@ -149,20 +158,32 @@ export default {
149
158
  } else {
150
159
  return 'chrome'
151
160
  }
161
+ },
162
+
163
+ mediaA11Y() {
164
+ let m = {
165
+ srTxt: '',
166
+ skipToTxt: ''
167
+ }
168
+
169
+ this.$i18n.locale === 'fr'
170
+ ? (m = {
171
+ srTxt:
172
+ "Le contenu d'apprentissage sera seulement disponible à la fin de la narration. Appuyez le bouton jouer, pour lancer l'animation.",
173
+ skipToTxt: 'Passer au controleur de media'
174
+ })
175
+ : (m = {
176
+ srTxt:
177
+ 'The learning content will be available only at the end of the narration. Press the play button to start the animation',
178
+ skipToTxt: 'Skip to play-bar'
179
+ })
180
+
181
+ return m
152
182
  }
153
183
  },
154
184
  watch: {
155
- media(val, oldVal) {
185
+ media() {
156
186
  let mElement = this.$refs[this.media.mType]
157
- if (oldVal === null && val !== oldVal) {
158
- //this.$store.dispatch('updateAppStatus', 'loading')
159
- //this.$store.dispatch('updateCurrentMediaElement', mElement)
160
- //this.fecthFromServer(this.media.mSources)
161
- // for (let m of this.media.mSources) {
162
- // this.checkRessource(m.src)
163
- // }
164
- }
165
-
166
187
  /* Handeling media subtitles
167
188
  * if there is any subtitle, create the subtitle menu
168
189
  */
@@ -208,6 +229,7 @@ export default {
208
229
  if (this.media) {
209
230
  window.addEventListener('resize', this.caclWindowHeight)
210
231
  }
232
+ this.currentMediaElement
211
233
  },
212
234
 
213
235
  beforeDestroy() {
@@ -293,13 +315,75 @@ export default {
293
315
  .catch((err) => {
294
316
  err
295
317
  })
318
+ },
319
+ /**
320
+ * @description- Skip directly to the specify containt/region*
321
+ * main content is Node with defined ID
322
+ * @param {String} targetID- Css selector for the target element
323
+ */
324
+
325
+ skipTo(targetID) {
326
+ let skipTo = document.querySelector(`#wrapper-content`) //default definition of main element
327
+
328
+ if (targetID) {
329
+ let targetEl = document.querySelector(`#${targetID}`) // search for node element specified as main
330
+
331
+ if (targetEl) skipTo = targetEl
332
+ }
333
+ let targetTop = skipTo.offsetTop
334
+
335
+ let scrollOpt = {
336
+ top: targetTop - 100,
337
+ left: 0,
338
+ behavior: 'auto' //auto
339
+ }
340
+
341
+ //Allowing accessibility control with keyboard
342
+ skipTo.setAttribute('tabIndex', -1)
343
+ window.scrollTo(scrollOpt)
344
+ skipTo.focus()
345
+ },
346
+
347
+ async setMediaElement() {
348
+ const { type } = await this.getCurrentPage
349
+ if (!['pg_media, pg_animation'].includes(type)) return null
350
+
351
+ let mediaObject = {}
352
+ switch (type) {
353
+ case 'pg_media': {
354
+ const {
355
+ animation,
356
+ type,
357
+ mediaData: { mSources, mType, mSubtitle, mPoster, mTranscript },
358
+ timeline,
359
+ mElement
360
+ } = this.getCurrentPage
361
+
362
+ mediaObject = {
363
+ animation,
364
+ type,
365
+ mediaData: { mSources, mType, mSubtitle, mPoster, mTranscript },
366
+ timeline,
367
+ mElement
368
+ }
369
+
370
+ break
371
+ }
372
+ case 'pg_animation': {
373
+ const { animation, type, timeline } = this.getCurrentPage
374
+ mediaObject = { animation, type, timeline }
375
+ break
376
+ }
377
+ }
378
+
379
+ return mediaObject
296
380
  }
297
381
  }
298
382
  }
299
383
  </script>
300
384
  <style lang="scss">
301
385
  $widthVideo: 100%;
302
- $widthAudioAnimation: 100%;
386
+ $widthAudioAnimation: 50%;
303
387
 
304
388
  %animation {
305
389
  position: absolute;
@@ -310,56 +394,4 @@ $widthAudioAnimation: 100%;
310
394
  z-index: 1;
311
395
  overflow: hidden;
312
396
  }
313
-
314
- .app-media-player {
315
- position: relative;
316
- overflow: hidden;
317
-
318
- &.AudioAnimation {
319
- width: $widthAudioAnimation;
320
- .anim-canvas {
321
- @extend %animation;
322
- }
323
- }
324
-
325
- &.FS,
326
- &.animationOnly {
327
- width: 100%;
328
- position: absolute;
329
- top: 0;
330
- left: 0;
331
-
332
- video {
333
- min-width: 100% !important;
334
- min-height: 100% !important;
335
- position: absolute !important;
336
- top: 50% !important;
337
- left: 50% !important;
338
- transform: translate(-50%, -50%) !important;
339
- z-index: 9;
340
- }
341
-
342
- .anim-canvas {
343
- @extend %animation;
344
- }
345
- }
346
-
347
- &.FS,
348
- &.AudioAnimation {
349
- width: 100%;
350
- position: absolute;
351
- top: 0;
352
- left: 0;
353
-
354
- .anim-canvas {
355
- @extend %animation;
356
- }
357
- }
358
- }
359
-
360
- video {
361
- &:focus-visible {
362
- outline-color: transparent;
363
- }
364
- }
365
397
  </style>
@@ -1,30 +1,32 @@
1
1
  <!--
2
2
  @ Description: This component is used to create the menu and the ard that will bring you back where you were before the menu.
3
- @ What it does: Look in the store what was the last page you saw before you came to the menu and create the the link for the card. It display AppCompMenuItem and AppCompTableOfContent
3
+ @ What it does: Look in the store what was the last page you saw before you came to the menu and create the the link for the card. It display's AppCompMenuItem
4
4
  -->
5
5
  <template>
6
6
  <div class="menu-container">
7
- <b-row>
8
- <b-col md="4" class="wrapper-lasRoute">
9
- <b-link
10
- id="btn_last_route"
11
- class="nav_last_route"
12
- :to="{ name: GoToLastRoute() }"
13
- :title="handelLabel"
14
- >
15
- <div class="box-menu">
16
- <slot name="inside-last-route" />
17
-
18
- <div class="vsl-tmp">
19
- <p>
20
- {{ handelLabel }}
21
- </p>
22
- </div>
23
- </div>
24
- </b-link>
7
+ <b-row align-v="center" align-h="center">
8
+ <b-col md="10" class="wrapper-lasRoute">
9
+ <div class="box-menu">
10
+ <h1>{{ getLessonid }}</h1>
11
+ <h2 v-html="getLessonTitle"></h2>
12
+ <p class="lesson-progess">{{ lessonProg() }}</p>
13
+ <b-link
14
+ id="btn_last_route"
15
+ class="nav_last_route"
16
+ :title="handelLabel"
17
+ :to="{ name: GoToLastRoute() }"
18
+ >
19
+ {{ handelLabel }}
20
+ </b-link>
21
+ </div>
25
22
  </b-col>
23
+ <b-col
24
+ md="10"
25
+ class="wrapper-MenuItem"
26
+ title="table of content for this lesson"
27
+ >
28
+ <h3>{{ $t('text.activity_title') }}</h3>
26
29
 
27
- <b-col md="8" class="wrapper-MenuItem">
28
30
  <app-comp-menu-item></app-comp-menu-item>
29
31
  </b-col>
30
32
  </b-row>
@@ -36,15 +38,23 @@ import { mapGetters } from 'vuex'
36
38
 
37
39
  export default {
38
40
  name: 'AppCompMenu',
41
+ data() {
42
+ return {
43
+ totalAttempts: 0
44
+ }
45
+ },
39
46
  computed: {
40
47
  ...mapGetters([
41
48
  'getRouteHistory',
42
49
  'getAllActivitiesState',
50
+ 'getAllCompleted',
51
+ 'getModuleInfo',
52
+ 'getMenuSettings',
43
53
  'getAllCompleted'
44
54
  ]),
45
55
  handelLabel() {
46
56
  let label = ''
47
- const { allActivitiesState, allCompleted } = this.progressWithoutMenu()
57
+ const { allActivitiesState, allCompleted } = this.progressWithMenu()
48
58
 
49
59
  let activity = Object.values(allActivitiesState)
50
60
  let completeActivity = Object.values(allCompleted)
@@ -59,6 +69,21 @@ export default {
59
69
  }
60
70
  }
61
71
  return label
72
+ },
73
+ getLessonid() {
74
+ // ATTENDRE UX*UI poure voir si on garde le concept ou pas
75
+ let lessonLabel = this.$t('text.lesson')
76
+ let lessonId = 99
77
+ return `${lessonLabel} ${lessonId}`
78
+ },
79
+ getLessonTitle() {
80
+ let lessonT
81
+ if (this.getMenuSettings.lessonTitle) {
82
+ lessonT = this.getMenuSettings.lessonTitle
83
+ } else {
84
+ lessonT = 'Entrer un titre à votre dans menu setting svp.'
85
+ }
86
+ return `${lessonT}`
62
87
  }
63
88
  },
64
89
  created() {},
@@ -69,14 +94,14 @@ export default {
69
94
  let path
70
95
 
71
96
  //Get all activity state (menu not included)
72
- const { allActivitiesState } = this.progressWithoutMenu()
97
+ const { allActivitiesState } = this.progressWithMenu()
73
98
  let state = Object.values(allActivitiesState)
74
99
 
75
100
  if (this.getRouteHistory.length) {
76
101
  if (this.getRouteHistory.length == 1) {
77
102
  lastRoute = this.getRouteHistory[0]
78
103
  } else {
79
- lastRoute = this.getRouteHistory[this.getRouteHistory.length - 2]
104
+ lastRoute = this.getRouteHistory[this.getRouteHistory.length - 1]
80
105
  }
81
106
  }
82
107
 
@@ -90,6 +115,7 @@ export default {
90
115
  if (lastRoute) path = this.createdRoute(lastRoute)
91
116
  return path
92
117
  },
118
+
93
119
  noProgress(obj) {
94
120
  //validator to see if anything was seen before
95
121
  for (let i = 0; i < obj.length; i++) {
@@ -104,54 +130,27 @@ export default {
104
130
  let page
105
131
  let path
106
132
 
107
- if (
108
- (newRoute.activity_ref && newRoute.activity_ref.charAt(1) == '0') ||
109
- (newRoute.activity_ref && newRoute.activity_ref.charAt(1) == 0)
110
- ) {
111
- activity = newRoute.activity_ref.substr(2)
133
+ const typesToCatch = ['menu', 'introduction', 'conclusion']
134
+ //Define activity
135
+ if (typesToCatch.includes(newRoute.type)) {
136
+ activity = newRoute.type
112
137
  } else {
113
- activity = newRoute.activity_ref.substr(1)
138
+ activity =
139
+ newRoute.activity_ref.charAt(1) == '0'
140
+ ? `activite_${newRoute.activity_ref.substring(2)}`
141
+ : `activite_${newRoute.activity_ref.substring(1)}`
114
142
  }
115
- if (newRoute.activity_ref && newRoute.activity_ref !== 'A00') {
116
- // create link for page when you are in the intro
117
- if (newRoute.id.charAt(1) == '0' || newRoute.id.charAt(1) == 0) {
118
- page = newRoute.id.substr(2)
119
- } else {
120
- page = newRoute.id.substr(1)
121
- }
122
- // create link for any ativity
123
- if (page == '1' || page == 1) {
124
- path = `activite_${activity}`
125
- } else {
126
- path = `activite_${activity}.page_${page}`
127
- }
128
- } else {
129
- // create link for page when you are not in the intro
130
- let str = newRoute.id.substr(newRoute.id.indexOf('-') + 1)
131
143
 
132
- if (str.charAt(1) == '0' || str.charAt(1) == 0) {
133
- page = str.substr(2)
134
- } else {
135
- page = str.substr(1)
136
- }
144
+ //Define page
145
+ const idToIgnore = ['P01', 'pg_menu']
146
+ page = idToIgnore.includes(newRoute.id) ? ' ' : newRoute.id.substring(2)
147
+
148
+ //Define path
149
+ path = page !== ' ' ? `${activity}.page_${page}` : activity
137
150
 
138
- // CREATE LINK if it was the intro
139
- if (page == '1' || page == 1) {
140
- path = `introduction`
141
- } else if (page == '0' || page == 0) {
142
- path = `menu`
143
- } else {
144
- path = `introduction.page_${page}`
145
- }
146
- }
147
151
  return path
148
152
  },
149
- progressWithoutMenu() {
150
- //get the page which is defined as the menu
151
- const toIgnore = this.$helper
152
- .getRoutesFromVueRouter('A00')
153
- .all_routes.find((el) => el.name === 'menu').meta.id
154
-
153
+ progressWithMenu() {
155
154
  /*
156
155
  * Note:
157
156
  * Assaging by reference would change the property of the getters when the valued are changed in the new object in. * To prevent this behaviour we will make a deep copy of the getter
@@ -172,31 +171,38 @@ export default {
172
171
  ) {
173
172
  let completedIntro = allCompleted[activity]
174
173
 
175
- //remove the "menu page from the completed in intro
176
- completedIntro = completedIntro.filter(
177
- (el) => Object.keys(el)[0] !== toIgnore
178
- )
179
-
180
174
  if (!completedIntro.length) completedIntro = []
181
175
 
182
176
  allCompleted[activity] = completedIntro
183
-
184
- // Remove menu page from the activity state tracking
185
- let introState = allActivitiesState[activity].progressions
186
- if (introState.length) {
187
- introState = introState.filter(
188
- (el) => Object.keys(el)[0] !== toIgnore
189
- )
190
-
191
- allActivitiesState[activity].progressions = introState
192
- allActivitiesState[activity].size -= 1
193
- }
194
177
  }
195
178
  }
179
+
196
180
  return {
197
181
  allActivitiesState,
198
182
  allCompleted
199
183
  }
184
+ },
185
+ lessonProg() {
186
+ let lstActivity = Object.keys(this.getAllActivitiesState)
187
+ let progress = null
188
+ let info = this.progressWithMenu()
189
+ let page = null
190
+ let pageCmplt = null
191
+
192
+ lstActivity.forEach((element) => {
193
+ //Get the total of all the page
194
+ page += info.allActivitiesState[element].size
195
+
196
+ if (info.allCompleted[element]) {
197
+ // GEt all page complete
198
+ pageCmplt += Object.values(info.allCompleted[element]).length
199
+ }
200
+ })
201
+
202
+ // Create pourcentage complete
203
+ progress = Math.round((pageCmplt * 100) / page)
204
+
205
+ return `${progress}%`
200
206
  }
201
207
  }
202
208
  }