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

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 (72) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/CHANGELOG +9 -0
  3. package/package.json +26 -12
  4. package/src/$locales/en.json +37 -87
  5. package/src/$locales/fr.json +17 -106
  6. package/src/assets/data/onboardingMessages.json +1 -1
  7. package/src/components/AppBase.vue +241 -143
  8. package/src/components/AppBaseButton.vue +2 -6
  9. package/src/components/AppBaseErrorDisplay.vue +193 -183
  10. package/src/components/AppBaseFlipCard.vue +1 -0
  11. package/src/components/AppBaseModule.vue +195 -225
  12. package/src/components/AppBasePage.vue +519 -64
  13. package/src/components/AppBasePopover.vue +41 -0
  14. package/src/components/AppCompAudio.vue +32 -64
  15. package/src/components/AppCompBranchButtons.vue +52 -71
  16. package/src/components/AppCompButtonProgress.vue +12 -18
  17. package/src/components/AppCompCarousel.vue +102 -0
  18. package/src/components/{AppCompInputCheckBox.vue → AppCompInputCheckBoxNext.vue} +56 -94
  19. package/src/components/AppCompInputDropdownNext.vue +159 -0
  20. package/src/components/{AppCompInputRadio.vue → AppCompInputRadioNext.vue} +53 -63
  21. package/src/components/AppCompInputTextNext.vue +106 -0
  22. package/src/components/AppCompInputTextTableNext.vue +141 -0
  23. package/src/components/AppCompInputTextToFillDropdownNext.vue +230 -0
  24. package/src/components/{AppCompInputTextToFillText.vue → AppCompInputTextToFillNext.vue} +71 -64
  25. package/src/components/AppCompJauge.vue +16 -9
  26. package/src/components/AppCompMenu.vue +50 -29
  27. package/src/components/AppCompMenuItem.vue +52 -15
  28. package/src/components/AppCompNavigation.vue +225 -211
  29. package/src/components/AppCompNoteCall.vue +22 -30
  30. package/src/components/AppCompNoteCredit.vue +45 -20
  31. package/src/components/AppCompPlayBar.vue +55 -108
  32. package/src/components/AppCompPlayBarNext.vue +2052 -0
  33. package/src/components/AppCompPlayBarProgress.vue +10 -1
  34. package/src/components/AppCompPopUpNext.vue +503 -0
  35. package/src/components/{AppCompQuiz.vue → AppCompQuizNext.vue} +632 -703
  36. package/src/components/AppCompQuizRecall.vue +74 -75
  37. package/src/components/{AppCompSVG.vue → AppCompSVGNext.vue} +111 -99
  38. package/src/components/AppCompSettingsMenu.vue +11 -8
  39. package/src/components/AppCompTableOfContent.vue +78 -76
  40. package/src/components/AppCompTranscript.vue +5 -0
  41. package/src/components/AppCompVideoPlayer.vue +30 -42
  42. package/src/components/BaseModule.vue +1 -0
  43. package/src/composables/useQuiz.js +206 -0
  44. package/src/externalComps/ModuleView.vue +22 -0
  45. package/src/externalComps/SummaryView.vue +91 -0
  46. package/src/main.js +99 -90
  47. package/src/mixins/$mediaMixins.js +13 -21
  48. package/src/mixins/timerMixin.js +1 -1
  49. package/src/module/stores/appStore.js +901 -0
  50. package/src/module/xapi/ADL.js +8 -4
  51. package/src/plugins/bus.js +7 -2
  52. package/src/plugins/gsap.js +4 -7
  53. package/src/plugins/helper.js +33 -13
  54. package/src/plugins/i18n.js +2 -2
  55. package/src/plugins/idb.js +44 -29
  56. package/src/plugins/save.js +1 -1
  57. package/src/plugins/scorm.js +2 -2
  58. package/src/plugins/xapi.js +2 -2
  59. package/src/public/index.html +22 -10
  60. package/src/router/index.js +13 -10
  61. package/src/router/routes.js +20 -25
  62. package/src/shared/generalfuncs.js +33 -18
  63. package/src/shared/validators.js +116 -6
  64. package/src/components/AppCompInputDropdown.vue +0 -182
  65. package/src/components/AppCompInputTextBox.vue +0 -91
  66. package/src/components/AppCompInputTextTable.vue +0 -158
  67. package/src/components/AppCompInputTextToFillDropdown.vue +0 -257
  68. package/src/components/AppCompPopUp.vue +0 -583
  69. package/src/components/AppCompPopover.vue +0 -27
  70. package/src/mixins/$pageMixins.js +0 -415
  71. package/src/mixins/$quizMixins.js +0 -442
  72. package/src/module/store.js +0 -1014
@@ -11,10 +11,13 @@
11
11
  <div v-if="error" id="sidebar-submenu" :class="{ isOpen: open }">
12
12
  <app-base-button
13
13
  id="close-toc"
14
+ class="btn-ghost"
14
15
  :title="$t('button.closePopUp')"
15
16
  @click="close()"
16
17
  >
17
- X
18
+ <svg>
19
+ <use href="#close-square-icon" />
20
+ </svg>
18
21
  </app-base-button>
19
22
 
20
23
  <p class="t-act" v-html="title"></p>
@@ -29,7 +32,7 @@
29
32
  <div class="box-anchor">
30
33
  <p class="t-toc">{{ $t('text.toc') }}</p>
31
34
 
32
- <b-link
35
+ <a
33
36
  v-for="(anchor, index) of anchors"
34
37
  :key="anchor.title"
35
38
  class="toc-item"
@@ -37,10 +40,20 @@
37
40
  @click="closeAndNextPage(anchor.path)"
38
41
  >
39
42
  <div class="box-text-anchor">
40
- <p class="anchor-t" v-html="anchor.title"></p>
41
- <p class="state">{{ $t('text.complete') }}</p>
43
+ <div class="text-anchor">
44
+ <p class="anchor-t" v-html="anchor.title"></p>
45
+ <p class="state">
46
+ <svg>
47
+ <use href="#check-toc" />
48
+ </svg>
49
+ {{ $t('text.complete') }}
50
+ </p>
51
+ </div>
52
+ <svg>
53
+ <use href="#chevronD-icon" />
54
+ </svg>
42
55
  </div>
43
- </b-link>
56
+ </a>
44
57
  </div>
45
58
  </div>
46
59
  <div v-else id="sidebar-submenu" :class="{ isOpen: open }">
@@ -52,8 +65,8 @@
52
65
  </template>
53
66
 
54
67
  <script>
55
- import { mapGetters } from 'vuex'
56
-
68
+ import { mapState, mapActions } from 'pinia'
69
+ import { useAppStore } from '../module/stores/appStore'
57
70
  export default {
58
71
  data() {
59
72
  return {
@@ -67,7 +80,7 @@ export default {
67
80
  }
68
81
  },
69
82
  computed: {
70
- ...mapGetters([
83
+ ...mapState(useAppStore, [
71
84
  'getMenuSettings',
72
85
  'getAnchorsForActivity',
73
86
  'getBifChoice',
@@ -86,6 +99,7 @@ export default {
86
99
  this.$bus.$on('info-activity', this.onInfoActivity)
87
100
  },
88
101
  methods: {
102
+ ...mapActions(useAppStore, ['updateWidgetOpen']),
89
103
  onInfoActivity(data) {
90
104
  //should be condition to avoid event fireing multiple time on call
91
105
 
@@ -103,15 +117,15 @@ export default {
103
117
  },
104
118
  onCloseWidget(data) {
105
119
  this.open = false
106
- this.$store.dispatch('updateWidgetOpen', false)
120
+ this.updateWidgetOpen(false)
107
121
  this.reset()
108
122
  },
109
123
  onToggleWidget(data) {
110
124
  if (data == 'toc') {
111
125
  this.open = !this.open
112
- this.$store.dispatch('updateWidgetOpen', true)
126
+ this.updateWidgetOpen(true)
113
127
  } else {
114
- this.$store.dispatch('updateWidgetOpen', false)
128
+ this.updateWidgetOpen(false)
115
129
  this.open = false
116
130
  }
117
131
  },
@@ -133,17 +147,11 @@ export default {
133
147
  nb = activity.substr(2)
134
148
  else nb = activity.substr(1)
135
149
 
136
- this.title = `${this.$t('text.activity')} ${nb} : ${
137
- menuInfo[activity].subTitle
138
- }`
139
-
140
- let count = 0
150
+ this.title = `${this.$t('text.activity')} ${nb} : ${menuInfo[activity].subTitle}`
141
151
 
142
152
  //create anchors title and path
143
153
  if (menuInfo[activity] && menuInfo[activity].anchors) {
144
154
  menuInfo[activity].anchors.forEach((element) => {
145
- count++
146
-
147
155
  //
148
156
  let _path = null
149
157
  let b = null
@@ -219,12 +227,12 @@ export default {
219
227
  },
220
228
  close() {
221
229
  this.open = !this.open
222
- this.$store.dispatch('updateWidgetOpen', this.open)
230
+ this.updateWidgetOpen(this.open)
223
231
  },
224
232
  closeAndNextPage(NextPage) {
225
- this.$router.push({ name: NextPage })
226
233
  this.open = !this.open
227
- this.$store.dispatch('updateWidgetOpen', this.open)
234
+ this.updateWidgetOpen(this.open)
235
+ this.$router.push({ name: NextPage })
228
236
  },
229
237
  reset() {
230
238
  this.anchors = []
@@ -292,33 +300,61 @@ export default {
292
300
  position: absolute;
293
301
  left: 75px;
294
302
  top: 0;
303
+ max-height: 592px;
304
+ width: 476px;
295
305
  transition: left 0.5s ease-in-out;
296
306
 
297
- &::before {
298
- content: '';
307
+ &.isOpen {
299
308
  display: block;
300
- width: 0;
301
- height: 0;
302
- border-top: 10px solid transparent;
303
- border-bottom: 10px solid transparent;
304
- border-right: 10px solid;
309
+ }
305
310
 
311
+ #close-toc {
306
312
  position: absolute;
307
- left: -10px;
308
- top: 10px;
313
+ right: 24px;
314
+ margin-bottom: 0;
315
+ padding: 11px;
316
+ width: 48px;
317
+ height: 48px;
318
+ display: flex;
319
+ justify-content: center;
320
+
321
+ &:focus {
322
+ box-shadow: inherit !important;
323
+ }
309
324
  }
310
325
 
311
- &.isOpen {
312
- display: block;
313
- }
326
+ .box-prog-act {
327
+ width: 319px;
328
+ display: flex;
329
+ flex-direction: row;
330
+ margin-bottom: 16px;
331
+
332
+ p {
333
+ position: relative;
334
+ width: 100%;
314
335
 
315
- .b-sidebar-header {
316
- display: none;
317
- pointer-events: none;
336
+ span {
337
+ position: absolute;
338
+ right: 0;
339
+ top: 0;
340
+ }
341
+ }
318
342
  }
319
343
 
320
344
  .box-anchor {
345
+ max-height: 366px;
346
+ overflow-y: auto;
347
+ padding: 0 4px;
348
+
349
+ .t-toc {
350
+ margin-bottom: 16px;
351
+ }
352
+
321
353
  .toc-item {
354
+ display: block;
355
+ width: 95%;
356
+ margin-bottom: 8px;
357
+
322
358
  .state {
323
359
  display: none;
324
360
  }
@@ -327,52 +363,18 @@ export default {
327
363
  display: block;
328
364
  }
329
365
  }
330
- }
331
-
332
- .box-time {
333
- width: 100%;
334
- padding: 10px 20px;
335
- }
336
-
337
- .box-contenu {
338
- padding: 20px;
339
- @media screen and (max-width: 1100px) {
340
- padding: 0;
341
- }
342
-
343
- .box-ttl {
344
- @media screen and (max-width: 1100px) {
345
- padding: 15px 0px 10px 20px;
346
- }
347
- }
348
-
349
- .container-anchor {
350
- margin-top: 35px;
351
- margin-left: 35px;
352
- padding-left: 24px;
353
- padding-top: 14px;
354
- max-height: 75vh;
355
- overflow-y: auto;
356
-
357
- @media screen and (max-width: 1100px) {
358
- padding: 0;
359
- margin: 0;
360
- }
361
-
362
- .box-sa {
363
- margin-bottom: 16px;
364
366
 
365
- @media screen and (max-width: 1100px) {
366
- padding: 19px 20px 19px 47px;
367
- margin-bottom: 0;
368
- }
369
- }
367
+ .box-text-anchor {
368
+ display: flex;
369
+ flex-direction: row;
370
+ align-items: center;
371
+ justify-content: space-between;
370
372
  }
371
373
  }
372
374
  }
373
375
  }
374
376
 
375
- .navbar {
377
+ .app-nav {
376
378
  &.show,
377
379
  &:hover {
378
380
  .ctn-w {
@@ -13,7 +13,12 @@ export default {
13
13
  </script>
14
14
 
15
15
  <style lang="scss">
16
+ .b-sidebar-body {
17
+ width: 100%;
18
+ }
19
+
16
20
  #transcript-container {
21
+ width: 100%;
17
22
  padding: 30px;
18
23
  }
19
24
  </style>
@@ -13,30 +13,13 @@
13
13
  :class="[{ FS: fullScreen }, CCBrowser]"
14
14
  aria-label="Video"
15
15
  >
16
- <b-row v-if="hasErr.length" class="warning-error">
17
- <b-col class="box-error">
18
- <div class="yellow-box">
19
- <h2>
20
- <b-icon icon="exclamation-triangle" />
21
- Erreur: COMPOSANT VIDEO
22
- </h2>
23
- </div>
24
- <div class="box">
25
- <p>
26
- Vous avez une/des erreur(s) pour la création de votre composant.
27
- Veuillez corriger les erreurs ci-dessous:
28
- <br />
29
- </p>
30
- <ul>
31
- <li
32
- v-for="(err, index) in hasErr"
33
- :key="`error_type_${index}`"
34
- v-html="err"
35
- />
36
- </ul>
37
- </div>
38
- </b-col>
39
- </b-row>
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
+
40
23
  <!------------------video section --------------------------->
41
24
 
42
25
  <template v-else>
@@ -75,7 +58,7 @@
75
58
  />
76
59
  </video>
77
60
  </div>
78
- <app-comp-play-bar
61
+ <app-comp-play-bar-next
79
62
  v-if="$vidElement && !isLoading && !hasSourceLoadingError"
80
63
  :ref="`plyr_${id}`"
81
64
  :media-to-play="$vidElement"
@@ -86,10 +69,13 @@
86
69
  </template>
87
70
 
88
71
  <script>
89
- import { mapGetters } from 'vuex'
72
+ import { mapState, mapActions } from 'pinia'
73
+ import { useAppStore } from '../module/stores/appStore'
90
74
  import { validateVideoData } from '../shared/validators'
75
+ import AppCompPlayBarNext from './AppCompPlayBarNext.vue'
91
76
 
92
77
  export default {
78
+ components: { AppCompPlayBarNext },
93
79
  props: {
94
80
  vidData: {
95
81
  type: Object,
@@ -122,7 +108,7 @@ export default {
122
108
  }
123
109
  },
124
110
  computed: {
125
- ...mapGetters(['getCurrentBrowser', 'getCurrentPage']),
111
+ ...mapState(useAppStore, ['getCurrentBrowser', 'getCurrentPage']),
126
112
  //Return true if the video is loading (to show the loading display)
127
113
  isLoading() {
128
114
  if (!this.isSet && !this.hasSourceLoadingError) return true
@@ -168,6 +154,7 @@ export default {
168
154
  },
169
155
 
170
156
  methods: {
157
+ ...mapActions(useAppStore, ['updateCurrentMediaElements']),
171
158
  errorHandling(e) {
172
159
  this.hasSourceLoadingError = true
173
160
  },
@@ -190,12 +177,15 @@ export default {
190
177
  */
191
178
  updateMediaData(e) {
192
179
  //dispatch loading status of for this component
180
+
193
181
  this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'loading')
194
182
  this.$bus.$emit('update-media-duration')
195
183
  //Should Check that the the media Element is unique im Media Liste
196
184
  const { mElements } = this.getCurrentPage
197
185
 
198
- const hasEntry = mElements.findIndex((media) => media.id === e.target.id)
186
+ const hasEntry = mElements.findLastIndex(
187
+ (media) => media.id === e.target.id
188
+ )
199
189
 
200
190
  if (hasEntry !== -1) {
201
191
  // Should report Error to Console and Component template about this media
@@ -210,7 +200,7 @@ export default {
210
200
  return this.hasErr.push(errmsg)
211
201
  }
212
202
 
213
- this.$store.dispatch('updateCurrentMediaElements', e.target).then(() => {
203
+ this.updateCurrentMediaElements(e.target).then(() => {
214
204
  this.isSet = true
215
205
  this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
216
206
  })
@@ -222,18 +212,17 @@ export default {
222
212
  */
223
213
  runPlaybackAnimation() {
224
214
  //this.isPlaying = !this.$vidElement.mElement.paused
225
-
226
- let animation = this.$gsap.fromTo(
227
- '.playback-animation',
228
- { opacity: 1, scale: 0.8 },
229
- {
230
- opacity: 0,
231
- scale: 1.3,
232
- duration: 0.8,
233
- ease: 'power2.out'
234
- }
235
- )
236
- animation.play()
215
+ // let animation = this.$gsap.fromTo(
216
+ // '.playback-animation',
217
+ // { opacity: 1, scale: 0.8 },
218
+ // {
219
+ // opacity: 0,
220
+ // scale: 1.3,
221
+ // duration: 0.8,
222
+ // ease: 'power2.out'
223
+ // }
224
+ // )
225
+ // animation.play()
237
226
  },
238
227
 
239
228
  /**
@@ -300,7 +289,6 @@ export default {
300
289
  spaceKeyPreventDefault(evt) {
301
290
  let { code } = evt
302
291
  if (code == 'Space') {
303
- //console.log('\x1b[43mspace i')
304
292
  evt.preventDefault()
305
293
  this.handlePlayBack('spacebar')
306
294
  }
@@ -54,6 +54,7 @@ export default {
54
54
  width: 100%;
55
55
  height: 100%;
56
56
  min-height: 100vh;
57
+ padding: 0 145px;
57
58
  }
58
59
  .overlay-close-widget {
59
60
  position: absolute;
@@ -0,0 +1,206 @@
1
+ //This composable sould Extend the functionality of a the quiz
2
+
3
+ import i18n from '@/i18n' //Must import directly the local from project app because vue-18in does not work in legacy mode with composable
4
+
5
+ export function useQuiz(quiz) {
6
+ const { quizType, solution, showSolution, quizInputType } = quiz
7
+ const { t } = i18n.global
8
+
9
+ // /**
10
+ // * @param {String} inputId
11
+ // * @returns {Array} the class
12
+ // */
13
+ function classInput(inputId, optSelected = null) {
14
+ let theClass = []
15
+
16
+ switch (true) {
17
+ case ['choix_unique', 'reponse_ouverte'].includes(quizType.value):
18
+ if (inputId == quizInputType.value) {
19
+ theClass.push('reponseSelectionner')
20
+ }
21
+ break
22
+
23
+ case quizType.value == 'choix_mult':
24
+ if (containsValue(quizInputType.value, inputId)) {
25
+ theClass.push('reponseSelectionner')
26
+ }
27
+ }
28
+
29
+ if (showSolution.value) {
30
+ if (solution.value !== null) {
31
+ switch (true) {
32
+ case ['choix_unique', 'reponse_ouverte'].includes(quizType.value):
33
+ if (inputId == solution.value) {
34
+ theClass.push('correct_answer')
35
+ } else {
36
+ theClass.push('wrong_answer')
37
+ }
38
+ break
39
+
40
+ case quizType.value == 'choix_mult':
41
+ if (containsValue(solution.value, inputId)) {
42
+ theClass.push('correct_answer')
43
+ } else {
44
+ theClass.push('wrong_answer')
45
+ }
46
+ break
47
+ case quizType.value == 'dropdown':
48
+ if (!optSelected || !optSelected[inputId]) return
49
+ if (solution.value[inputId] == optSelected[inputId]) {
50
+ theClass.push('correct_answer')
51
+ } else {
52
+ theClass.push('wrong_answer')
53
+ }
54
+ break
55
+
56
+ case quizType.value == 'texte_troue_select':
57
+ if (typeof optSelected[inputId] !== 'undefined') {
58
+ if (
59
+ Object.values(solution.value[inputId])[0] ==
60
+ optSelected[inputId]
61
+ ) {
62
+ theClass.push('correct_answer')
63
+ } else {
64
+ theClass.push('wrong_answer')
65
+ }
66
+ }
67
+ break
68
+
69
+ case quizType.value == 'texte_tableau':
70
+ if (
71
+ containsValue(
72
+ solution.value[inputId].reponse_value,
73
+ optSelected[inputId]
74
+ )
75
+ ) {
76
+ theClass.push('correct_answer')
77
+ } else {
78
+ theClass.push('wrong_answer')
79
+ }
80
+ break
81
+ case quizType.value == 'texte_troue':
82
+ if (typeof optSelected[inputId] !== 'undefined') {
83
+ if (
84
+ containsValue(
85
+ Object.values(solution.value[inputId])[0],
86
+ optSelected[inputId]
87
+ )
88
+ ) {
89
+ theClass.push('correct_answer')
90
+ } else {
91
+ theClass.push('wrong_answer')
92
+ }
93
+ }
94
+ break
95
+ }
96
+ }
97
+ }
98
+ return theClass
99
+ }
100
+
101
+ function messageAccessibility(inputId, optSelected = null) {
102
+ let mess = ''
103
+ if (showSolution.value) {
104
+ if (solution.value !== null) {
105
+ switch (true) {
106
+ case ['choix_unique', 'reponse_ouverte'].includes(quizType.value):
107
+ if (inputId == solution.value) {
108
+ mess = `${t('quizState.goodAnswer')}`
109
+ } else {
110
+ mess = `${t('quizState.badAnswer')}`
111
+ }
112
+ break
113
+
114
+ case quizType.value == 'choix_mult':
115
+ if (containsValue(solution.value, inputId)) {
116
+ mess = `${t('quizState.goodAnswer')}`
117
+ } else {
118
+ mess = `${t('quizState.badAnswer')}`
119
+ }
120
+ break
121
+ case quizType.value == 'dropdown':
122
+ if (solution[inputId.value] == optSelected[inputId.value]) {
123
+ mess = `${t('quizState.goodAnswer')}`
124
+ } else {
125
+ mess = `${t('quizState.badAnswer')}`
126
+ }
127
+ break
128
+
129
+ case quizType.value == 'texte_troue_select':
130
+ if (typeof optSelected[inputId] !== 'undefined') {
131
+ if (
132
+ Object.values(solution.value[inputId])[0] ==
133
+ optSelected[inputId]
134
+ ) {
135
+ mess = `${t('quizState.goodAnswer')}`
136
+ } else {
137
+ mess = `${t('quizState.badAnswer')}`
138
+ }
139
+ }
140
+ break
141
+
142
+ case quizType.value == 'texte_tableau':
143
+ if (
144
+ containsValue(
145
+ solution.value[inputId].reponse_value,
146
+ optSelected[inputId]
147
+ )
148
+ ) {
149
+ mess = `${t('quizState.goodAnswer')}`
150
+ } else {
151
+ mess = `${t('quizState.badAnswer')}`
152
+ }
153
+ break
154
+ case quizType.value == 'texte_troue':
155
+ if (typeof optSelected[inputId] !== 'undefined') {
156
+ if (
157
+ containsValue(
158
+ Object.values(solution.value[inputId])[0],
159
+ optSelected[inputId]
160
+ )
161
+ ) {
162
+ mess = `${t('quizState.goodAnswer')}`
163
+ } else {
164
+ mess = `${t('quizState.badAnswer')}`
165
+ }
166
+ }
167
+ break
168
+ }
169
+ }
170
+ }
171
+ return mess
172
+ }
173
+
174
+ /**
175
+ * @description check if a values exists in a array
176
+ * @param {Array} array
177
+ * @param value
178
+ * @returns {Boolean}
179
+ */
180
+ function containsValue(array, value) {
181
+ return array.includes(value)
182
+ }
183
+
184
+ /**
185
+ * @description shuffles an array used to randomized the option order if shuffleAnswers is true
186
+ * @param {Array} array
187
+ * @returns {Array}
188
+ */
189
+ function shuffleArray(array) {
190
+ let newArray = []
191
+ let newArray2 = []
192
+
193
+ for (let i = 0; i < array.length; i++) {
194
+ const element = array[i]
195
+ newArray.push(element)
196
+ }
197
+ while (newArray.length > 0) {
198
+ let pos = Math.floor(newArray.length * Math.random())
199
+ newArray2.push(newArray[pos])
200
+ newArray.splice(pos, 1)
201
+ }
202
+ return newArray2
203
+ }
204
+
205
+ return { containsValue, shuffleArray, classInput, messageAccessibility }
206
+ }
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <app-base-module :module-config="$data" />
3
+ </template>
4
+
5
+ <script>
6
+ export default {
7
+ data() {
8
+ return {
9
+ id: 'module_99',
10
+ consigneBehavior: 'onHover', //Controle the behavior of desplaying instruction
11
+ bookmarkActive: true, // Controle the use of saved point
12
+ allowNavigationToActivity: null
13
+
14
+ //main:''// Edit to define the ID of the node that will be main. When skipping to main content in the page.
15
+ }
16
+ },
17
+ created() {
18
+ this.allowNavigationToActivity =
19
+ this.$helper.getSettingsFromStore('auto_next_activity')
20
+ }
21
+ }
22
+ </script>