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

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 (163) hide show
  1. package/.editorconfig +6 -31
  2. package/.prettierrc +11 -0
  3. package/.vscode/extensions.json +8 -0
  4. package/.vscode/settings.json +16 -0
  5. package/CHANGELOG +153 -0
  6. package/README.md +28 -43
  7. package/documentation/.vitepress/config.js +114 -0
  8. package/documentation/api-examples.md +49 -0
  9. package/documentation/composants/app-base-button.md +58 -0
  10. package/documentation/composants/app-base-error-display.md +59 -0
  11. package/documentation/composants/app-base-popover.md +68 -0
  12. package/documentation/composants/app-comp-audio.md +75 -0
  13. package/documentation/composants/app-comp-branch-buttons.md +111 -0
  14. package/documentation/composants/app-comp-button-progress.md +53 -0
  15. package/documentation/composants/app-comp-carousel.md +53 -0
  16. package/documentation/composants/app-comp-container.md +53 -0
  17. package/documentation/composants/app-comp-input-checkbox-next.md +42 -0
  18. package/documentation/composants/app-comp-input-dropdown-next.md +34 -0
  19. package/documentation/composants/app-comp-input-radio-next.md +39 -0
  20. package/documentation/composants/app-comp-input-text-next.md +35 -0
  21. package/documentation/composants/app-comp-input-text-table-next.md +34 -0
  22. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -0
  23. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -0
  24. package/documentation/composants/app-comp-jauge.md +31 -0
  25. package/documentation/composants/app-comp-menu-item.md +55 -0
  26. package/documentation/composants/app-comp-menu.md +29 -0
  27. package/documentation/composants/app-comp-navigation.md +41 -0
  28. package/documentation/composants/app-comp-note-call.md +53 -0
  29. package/documentation/composants/app-comp-note-credit.md +53 -0
  30. package/documentation/composants/app-comp-play-bar-next.md +53 -0
  31. package/documentation/composants/app-comp-pop-up-next.md +93 -0
  32. package/documentation/composants/app-comp-quiz-next.md +235 -0
  33. package/documentation/composants/app-comp-quiz-recall.md +53 -0
  34. package/documentation/composants/app-comp-svg-next.md +53 -0
  35. package/documentation/composants/app-comp-table-of-content.md +50 -0
  36. package/documentation/composants/app-comp-video-player.md +82 -0
  37. package/documentation/composants.md +46 -0
  38. package/documentation/composants_critiques/ModelPageComposant.md +53 -0
  39. package/documentation/composants_critiques/app-base-module.md +43 -0
  40. package/documentation/composants_critiques/app-base-page.md +48 -0
  41. package/documentation/composants_critiques/app-base.md +311 -0
  42. package/documentation/composants_critiques/main.md +15 -0
  43. package/documentation/demarrage.md +50 -0
  44. package/documentation/deploiement.md +58 -0
  45. package/documentation/index.md +33 -0
  46. package/documentation/markdown-examples.md +85 -0
  47. package/documentation/public/npm_version.png +0 -0
  48. package/documentation/public/vite.svg +15 -0
  49. package/documentation/public/vuejs.svg +2 -0
  50. package/documentation/public/vuetify.svg +6 -0
  51. package/eslint.config.js +60 -0
  52. package/package.json +43 -47
  53. package/src/$locales/en.json +86 -108
  54. package/src/$locales/fr.json +66 -127
  55. package/src/assets/data/onboardingMessages.json +1 -1
  56. package/src/components/AppBase.vue +960 -405
  57. package/src/components/AppBaseButton.test.js +21 -0
  58. package/src/components/AppBaseButton.vue +42 -10
  59. package/src/components/AppBaseErrorDisplay.vue +207 -189
  60. package/src/components/AppBaseFlipCard.vue +1 -0
  61. package/src/components/AppBaseModule.vue +769 -977
  62. package/src/components/AppBasePage.vue +635 -81
  63. package/src/components/AppBasePopover.vue +41 -0
  64. package/src/components/AppBaseSkeleton.vue +66 -0
  65. package/src/components/AppCompAudio.vue +256 -0
  66. package/src/components/AppCompBranchButtons.vue +79 -153
  67. package/src/components/AppCompButtonProgress.vue +21 -36
  68. package/src/components/AppCompCarousel.vue +231 -87
  69. package/src/components/{AppCompTranscript.vue → AppCompContainer.vue} +12 -2
  70. package/src/components/AppCompInputCheckBoxNx.vue +323 -0
  71. package/src/components/AppCompInputDropdownNx.vue +299 -0
  72. package/src/components/AppCompInputRadioNx.vue +284 -0
  73. package/src/components/AppCompInputTextNx.vue +153 -0
  74. package/src/components/AppCompInputTextTableNx.vue +202 -0
  75. package/src/components/AppCompInputTextToFillDropdownNx.vue +340 -0
  76. package/src/components/AppCompInputTextToFillNx.vue +313 -0
  77. package/src/components/AppCompJauge.vue +36 -10
  78. package/src/components/AppCompMenu.vue +246 -32
  79. package/src/components/AppCompMenuItem.vue +87 -21
  80. package/src/components/AppCompNavigation.vue +470 -447
  81. package/src/components/AppCompNoteCall.vue +93 -58
  82. package/src/components/AppCompNoteCredit.vue +423 -96
  83. package/src/components/AppCompPlayBarNext.vue +2288 -0
  84. package/src/components/AppCompPopUpNext.vue +504 -0
  85. package/src/components/AppCompQuizNext.vue +510 -0
  86. package/src/components/AppCompQuizRecall.vue +199 -99
  87. package/src/components/AppCompSVGNext.vue +346 -0
  88. package/src/components/AppCompSettingsMenu.vue +17 -16
  89. package/src/components/AppCompTableOfContent.vue +262 -99
  90. package/src/components/AppCompVideoPlayer.vue +183 -142
  91. package/src/components/BaseModule.vue +8 -20
  92. package/src/components/tests__/AppBaseButton.spec.js +53 -0
  93. package/src/components/tests__/useTimer.spec.js +91 -0
  94. package/src/composables/useIdleDetector.js +56 -0
  95. package/src/composables/useQuiz.js +89 -0
  96. package/src/composables/useTimer.js +172 -0
  97. package/src/directives/nvdaFix.js +53 -0
  98. package/src/externalComps/ModuleView.vue +22 -0
  99. package/src/externalComps/SummaryView.vue +91 -0
  100. package/src/main.js +397 -148
  101. package/src/module/stores/appStore.js +947 -0
  102. package/src/module/xapi/ADL.js +241 -60
  103. package/src/module/xapi/Crypto/Hasher.js +8 -8
  104. package/src/module/xapi/Crypto/WordArray.js +6 -6
  105. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
  106. package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
  107. package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
  108. package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
  109. package/src/module/xapi/Crypto/encoders/Base.js +7 -7
  110. package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
  111. package/src/module/xapi/Crypto/encoders/Hex.js +2 -2
  112. package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
  113. package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
  114. package/src/module/xapi/Statement/index.js +3 -3
  115. package/src/module/xapi/launch.js +10 -10
  116. package/src/module/xapi/utils.js +17 -17
  117. package/src/module/xapi/wrapper.js +219 -214
  118. package/src/module/xapi/xapiStatement.js +29 -29
  119. package/src/plugins/analytics.js +34 -0
  120. package/src/plugins/bus.js +7 -2
  121. package/src/plugins/gsap.js +5 -7
  122. package/src/plugins/helper.js +97 -34
  123. package/src/plugins/i18n.js +13 -18
  124. package/src/plugins/idb.js +45 -30
  125. package/src/plugins/save.js +1 -1
  126. package/src/plugins/scorm.js +15 -15
  127. package/src/plugins/xapi.js +2 -2
  128. package/src/public/index.html +22 -10
  129. package/src/router/index.js +29 -13
  130. package/src/router/routes.js +29 -54
  131. package/src/shared/generalfuncs.js +186 -30
  132. package/src/shared/validators.js +809 -40
  133. package/vitest.config.js +19 -0
  134. package/.eslintignore +0 -29
  135. package/.eslintrc.js +0 -86
  136. package/.prettierrc.js +0 -5
  137. package/babel.config.js +0 -3
  138. package/src/components/AppBaseDragChoice.vue +0 -91
  139. package/src/components/AppBaseDropZone.vue +0 -112
  140. package/src/components/AppCompBif.vue +0 -120
  141. package/src/components/AppCompDragAndDrop.vue +0 -339
  142. package/src/components/AppCompInputAssociation.vue +0 -332
  143. package/src/components/AppCompInputCheckBox.vue +0 -227
  144. package/src/components/AppCompInputDropdown.vue +0 -184
  145. package/src/components/AppCompInputRadio.vue +0 -169
  146. package/src/components/AppCompInputTextBox.vue +0 -91
  147. package/src/components/AppCompInputTextTable.vue +0 -155
  148. package/src/components/AppCompInputTextToFillDropdown.vue +0 -255
  149. package/src/components/AppCompInputTextToFillText.vue +0 -164
  150. package/src/components/AppCompMediaPlayer.vue +0 -397
  151. package/src/components/AppCompPlayBar.vue +0 -1319
  152. package/src/components/AppCompPopUp.vue +0 -522
  153. package/src/components/AppCompPopover.vue +0 -27
  154. package/src/components/AppCompQuiz.vue +0 -2989
  155. package/src/components/AppCompSVG.vue +0 -309
  156. package/src/mixins/$pageMixins.js +0 -459
  157. package/src/mixins/$quizMixins.js +0 -456
  158. package/src/mixins/timerMixin.js +0 -156
  159. package/src/module/store.js +0 -895
  160. package/src/plugins/timeManager.js +0 -77
  161. package/src/routes_bckp.js +0 -313
  162. package/src/routes_static.js +0 -344
  163. package/vue.config.js +0 -83
@@ -0,0 +1,947 @@
1
+ import { defineStore } from 'pinia'
2
+ import { fileAssets } from '../../shared/generalfuncs.js'
3
+ import { mappedFiles } from '../../router/routes.js'
4
+ const allFiles = fileAssets.getActivities()
5
+
6
+ export const useAppStore = defineStore('$appStore', {
7
+ //=======================STATE
8
+ state: () => {
9
+ return {
10
+ status: '',
11
+ anchorStatus: null,
12
+ thisModule: { activities: mappedFiles },
13
+ appDebugMode: false, //
14
+ currentActivity: null,
15
+ currentPage: null,
16
+ currentBranchPage: null,
17
+ currentBranching: null,
18
+ currentSection: null,
19
+ currentPageMediaElements: [], // array containing all media of the page
20
+ currentMediaDuration: null,
21
+ currentPageTimeline: null,
22
+ bookmark: null,
23
+ currentBrowser: null,
24
+ isMobile: null,
25
+ deviceType: null,
26
+ introAcitve: null,
27
+ userMetaData: {},
28
+ appConfigs: null,
29
+ menuSetting: null,
30
+ dataNc: {},
31
+ wigIsOpen: false,
32
+ popIsOpen: false,
33
+ sideBIsOpen: false,
34
+ applicationSettings: {},
35
+ routeHistory: null,
36
+ mediaVolume: 0.5,
37
+ mediaMuted: false,
38
+ mediaSubtitles: false,
39
+ mediaPlaybarValues: {
40
+ subtitles: false,
41
+ volume: 0.5
42
+ },
43
+ navA11y: false,
44
+ showPrimaryCtrl: true,
45
+ showNavLeftCtrl: true,
46
+ showNavRightCtrl: true,
47
+ prevActivity: null,
48
+ nextActivity: null,
49
+ lrsConfig: null,
50
+ compStatusTracker: [],
51
+ dataFromServer: null,
52
+ errMenuSetting: null,
53
+ endPopUp: false,
54
+ userDataLoaded: false,
55
+ lessonPosition: [],
56
+ completedState: {}
57
+ }
58
+ },
59
+ //=======================GETTERS
60
+ getters: {
61
+ getAppDebugMode: (state) => {
62
+ return state.appDebugMode
63
+ },
64
+ /**
65
+ * @description: Check if subtitles are enabled or not
66
+ */
67
+ getMediaSubtitles: (state) => {
68
+ return state.mediaSubtitles
69
+ },
70
+ /**
71
+ * @description: get media volume for the playbar
72
+ */
73
+ getMediaVolume: (state) => {
74
+ return state.mediaVolume
75
+ },
76
+
77
+ /**
78
+ * @description: get media muted for the playbar
79
+ */
80
+ getMediaMuted: (state) => {
81
+ return state.mediaMuted
82
+ },
83
+
84
+ getUserDataStatus: (state) => {
85
+ return state.userDataLoaded
86
+ },
87
+ /**
88
+ * @description: Check if automatic video playback is enabled in application settings
89
+ */
90
+
91
+ getAutoplayEnabled: (state) => {
92
+ return state.applicationSettings.autoplay
93
+ ? state.applicationSettings.autoplay
94
+ : null
95
+ },
96
+
97
+ /**
98
+ * @description: Check if onboarding is enabled in application settings
99
+ */
100
+ getOnboardingEnabled: (state) => {
101
+ return state.applicationSettings.onboarding
102
+ ? state.applicationSettings.onboarding
103
+ : null
104
+ },
105
+
106
+ /**
107
+ * @description: Check if auto-show subtitles is enabled in application settings
108
+ */
109
+ getShowSubtitles: (state) => {
110
+ return state.applicationSettings.subtitles
111
+ ? state.applicationSettings.subtitles
112
+ : null
113
+ },
114
+
115
+ /**
116
+ * @description: Check if auto-show subtitles is enabled in application settings
117
+ */
118
+ getBookmarkEnabled: (state) => {
119
+ return state.applicationSettings.bookmark
120
+ ? state.applicationSettings.bookmark
121
+ : null
122
+ },
123
+
124
+ /**
125
+ * @description: Getter to return the app status
126
+ */
127
+ getAppStatus: (state) => {
128
+ return !state.userDataLoaded || state.compStatusTracker.length
129
+ ? 'loading'
130
+ : 'ready'
131
+ },
132
+
133
+ /**
134
+ * @description: Getter to return the compStatusTracker
135
+ */
136
+ getCompStatusTracker: (state) => {
137
+ return state.compStatusTracker
138
+ },
139
+
140
+ getErrorMenu: (state) => {
141
+ return state.errMenuSetting
142
+ },
143
+ getSkipMenu: (state) => {
144
+ if (state.appConfigs.noMenu) return state.appConfigs.noMenu
145
+ else return false
146
+ },
147
+ getNotes() {
148
+ return (actId, pageId) => {
149
+ let pageNotes = []
150
+ let pageID = `${actId}/${pageId}`
151
+ const pagesList = allFiles.filter((page) => {
152
+ return page.name.includes(pageID)
153
+ })
154
+
155
+ for (const p of pagesList) {
156
+ if (p.content.data().notes) {
157
+ pageNotes.push({ [p.content.data().id]: p.content.data().notes })
158
+ }
159
+ }
160
+
161
+ return pageNotes
162
+ }
163
+ },
164
+ getCredits() {
165
+ return (actId, pageId) => {
166
+ let pageCredits = []
167
+ let pageID = `${actId}/${pageId}`
168
+ const pagesList = allFiles.filter((page) => {
169
+ return page.name.includes(pageID)
170
+ })
171
+
172
+ for (const p of pagesList) {
173
+ if (p.content.data().credits)
174
+ pageCredits.push(...p.content.data().credits)
175
+ }
176
+ return pageCredits
177
+ }
178
+ },
179
+ /**
180
+ * @description: Getter to return the content of a page
181
+ * @param {String} activity_Id: ID of the activity
182
+ * @param {String} page_Id: ID of the page
183
+ * @return {Object} : data of the search result
184
+ */
185
+ getPageData: (state) => {
186
+ return (activity_Id, page_Id) => {
187
+ //Must have activity_Id, page_Id params
188
+ if (!activity_Id || !page_Id)
189
+ throw new Error(
190
+ ` ⚠️ Invalid call to getPageData \n 🚩 Missing required arguments`
191
+ )
192
+
193
+ if (state.thisModule.activities.get(activity_Id)) {
194
+ let page
195
+
196
+ //handle the case for branching and normal pages
197
+ if (page_Id.length > 3) {
198
+ const splitted = page_Id.split('_')
199
+
200
+ page = state.thisModule.activities
201
+ .get(activity_Id)
202
+ .get(splitted[0])
203
+ .get(page_Id)
204
+ } else {
205
+ const p = state.thisModule.activities.get(activity_Id).get(page_Id)
206
+ if (p)
207
+ switch (p.constructor) {
208
+ //Branching root page
209
+ case Map:
210
+ page = p.get(page_Id)
211
+ break
212
+ //Normal page
213
+ default:
214
+ page = state.thisModule.activities
215
+ .get(activity_Id)
216
+ .get(page_Id)
217
+ break
218
+ }
219
+ }
220
+
221
+ if (page) return page.content
222
+ }
223
+
224
+ return false
225
+ }
226
+ },
227
+ /**
228
+ * @description: Getter to return an activity of the module. The getter will return all the activities if no id is given
229
+ * @param id: id of a activity to return
230
+ */
231
+ getAllActivities: (state) => {
232
+ return (id) => {
233
+ if (
234
+ !Object.keys(state.thisModule).length ||
235
+ !state.thisModule.activities
236
+ )
237
+ return {}
238
+
239
+ if (!id)
240
+ return new Object({
241
+ list: state.thisModule.activities,
242
+ pageSize: (() => {
243
+ let size = 0
244
+ state.thisModule.activities.forEach((value, key) => {
245
+ const aSize = allFiles.filter((f) =>
246
+ f.name.includes(key)
247
+ ).length
248
+ size = size + aSize
249
+ })
250
+ return size
251
+ })()
252
+ })
253
+
254
+ return new Object({
255
+ list: state.thisModule.activities.get(id),
256
+ pageSize: allFiles.filter((f) => f.name.includes(id)).length
257
+ })
258
+ }
259
+ },
260
+
261
+ getAnchorStatus: (state) => {
262
+ return state.anchorStatus
263
+ },
264
+ getIntroStatus() {
265
+ let activities = this.getAllActivities().list
266
+ return activities.get('A00') ? true : false
267
+ },
268
+ getConcluStatus() {
269
+ let activities = this.getAllActivities().list
270
+ return activities.get('A99') ? true : false
271
+ },
272
+ getModuleInfo: (state) => {
273
+ if (state.thisModule.info) return state.thisModule.info
274
+ else return state.thisModule
275
+ },
276
+ getAppConfigs: (state) => {
277
+ if (state.appConfigs) return state.appConfigs
278
+ else return false
279
+ },
280
+ getRouteHistory: (state) => {
281
+ return state.routeHistory ? state.routeHistory : []
282
+ },
283
+ hasMediaElOrTimeline: (state) => {
284
+ return state.currentPageMediaElements.length || state.currentPageTimeline
285
+ },
286
+ /**
287
+ * @description: Getter to return the current page
288
+ * @returns: page {Object}
289
+ */
290
+ getCurrentPage: (state) => {
291
+ if (state.currentPage !== null) {
292
+ state.currentPage['mElements'] = state.currentPageMediaElements
293
+ state.currentPage['timeline'] = state.currentPageTimeline
294
+ }
295
+ return state.currentPage
296
+ },
297
+ /**
298
+ * @description: Getter to return the current branching page
299
+ * @returns: page {Object}
300
+ */
301
+ getCurrentBranching: (state) => {
302
+ return state.currentBranching
303
+ },
304
+ /**
305
+ * @description: Getter to return the current branch page in a branching
306
+ * @returns: page {Object}
307
+ */
308
+ getCurrentBranchPage: (state) => {
309
+ return state.currentBranchPage
310
+ },
311
+ /**
312
+ * @description: Getter to return the current Media Duration
313
+ */
314
+ getCurrentMediaDuration: (state) => {
315
+ return state.currentMediaDuration
316
+ },
317
+
318
+ /**
319
+ * @description: Getter to return the browser
320
+ */
321
+ getCurrentBrowser: (state) => {
322
+ return state.currentBrowser
323
+ },
324
+
325
+ getIsMobile: (state) => {
326
+ return state.isMobile
327
+ },
328
+ /**
329
+ * @description: Getter to return the type of device on which app is running (iosDevice, Android device or Desktop)
330
+ */
331
+ getDeviceType: (state) => {
332
+ return state.deviceType
333
+ },
334
+ /**
335
+ * @description: Getter to return the current enabled user settings
336
+ */
337
+ getApplicationSettings: (state) => {
338
+ return state.applicationSettings
339
+ },
340
+
341
+ /**
342
+ * @description: Getter to return the playbar values - Will return an attribut state by it key or the entire mediaPLaybar values
343
+ *
344
+ */
345
+ getMediaPlaybarValues: (state) => {
346
+ return (key) =>
347
+ key && state.mediaPlaybarValues[key]
348
+ ? state.mediaPlaybarValues[key]
349
+ : state.mediaPlaybarValues
350
+ },
351
+
352
+ /**
353
+ * @description: Getter to return the userMetadata
354
+ * * @returns: {Object}
355
+ */
356
+ getUserInteraction: (state) => {
357
+ return state.userMetaData
358
+ },
359
+
360
+ /**
361
+ * @description: Getter to return the interaction on a page
362
+ * * @returns: {Object} - userIneraction
363
+ */
364
+ getPageInteraction() {
365
+ return (activity = null, id = null) => {
366
+ if (!activity || !id) return null
367
+ const allInteraction = this.getUserInteraction
368
+
369
+ if (!allInteraction[activity] || !allInteraction[activity][id])
370
+ return {}
371
+
372
+ return allInteraction[activity][id] //allInteraction
373
+ }
374
+ },
375
+ /**
376
+ * @description: Getter to return all states of the activities
377
+ * @returns: progess {Object}
378
+ */
379
+ getAllActivitiesState(state) {
380
+ const activities = this.getAllActivities().list
381
+ let progress = {}
382
+ // search this acvitity in the userInteraction
383
+ activities.forEach((aValue, aKey) => {
384
+ let allStates
385
+ let temp = []
386
+
387
+ if (state.userMetaData && state.userMetaData[aKey]) {
388
+ const activity = Object.entries(state.userMetaData[aKey])
389
+
390
+ // get all the completed acitivities
391
+ allStates = activity.map((page) => {
392
+ // return only all the pages which state are completed or started
393
+ if (
394
+ page[1].userInteraction.state === 'completed' ||
395
+ page[1].userInteraction.state === 'started'
396
+ ) {
397
+ return {
398
+ ...allStates,
399
+ [page[0]]: page[1].userInteraction.state
400
+ }
401
+ }
402
+ })
403
+ }
404
+ if (allStates && allStates.length) {
405
+ temp = [...allStates]
406
+ }
407
+
408
+ // Getting the(Map type Object) length of the activities including the branching
409
+ let z = [] //placeholder
410
+ const getLength = (el) => {
411
+ if (el.size && el.size > 0) {
412
+ el.forEach((v) => {
413
+ // v is a map get its length with a recursive call to its elements
414
+ if (v && v.constructor === Map && v.size > 0) getLength(v)
415
+ else z.push(v) // v is an object add it to z
416
+ })
417
+ }
418
+ return z.length //return the length of the placeholder
419
+ }
420
+
421
+ // Update the progress with new values
422
+ progress = {
423
+ ...progress,
424
+ [aKey]: {
425
+ size: getLength(aValue),
426
+ progressions: [...temp]
427
+ }
428
+ }
429
+ })
430
+ return progress
431
+ },
432
+
433
+ /**
434
+ * @description: Getter to return only completed activities
435
+ * @returns: record {Object}
436
+ */
437
+ getAllCompleted(state) {
438
+ const allActivities = this.getAllActivitiesState
439
+ let record = {}
440
+
441
+ for (const key in allActivities) {
442
+ // get only activities that are completed
443
+ const completed = allActivities[key].progressions.filter((page) => {
444
+ if (page) return Object.values(page)[0] === 'completed'
445
+ })
446
+ // add the activity and its completed pages
447
+ if (completed.length)
448
+ record = {
449
+ ...record,
450
+ [key]: [...completed]
451
+ } // add the complete array to the record
452
+ }
453
+ return record
454
+ },
455
+
456
+ // for the quizzes
457
+ getAllQuizAnswers: (state) => {
458
+ return state.quizAnswers
459
+ },
460
+ getAllPollAnswers: (state) => {
461
+ return state.pollAnswers
462
+ },
463
+ getQuizAnswers: (state) => {
464
+ return (question_id) => {
465
+ const indexQuestion = state.quizAnswers.findIndex(function (element) {
466
+ return element.questionId == question_id
467
+ })
468
+ if (indexQuestion !== -1) {
469
+ return state.quizAnswers[indexQuestion]
470
+ }
471
+ return false
472
+ }
473
+ },
474
+ getPollAnswers: (state) => {
475
+ return (question_id) => {
476
+ const indexQuestion = state.pollAnswers.findIndex(function (element) {
477
+ return element.questionId == question_id
478
+ })
479
+ if (indexQuestion !== -1) {
480
+ return state.pollAnswers[indexQuestion]
481
+ }
482
+ return false
483
+ }
484
+ },
485
+ getBifChoice(state) {
486
+ const activities = this.getUserInteraction
487
+ let data = {}
488
+
489
+ let act = null
490
+ let page = null
491
+ for (const c in activities) {
492
+ act = c
493
+ const activity = state.userMetaData[c]
494
+ for (const b in activity) {
495
+ page = b
496
+ if (
497
+ activity[b] &&
498
+ [b].userInteractionactivity &&
499
+ [b].userInteraction.bifChoice
500
+ ) {
501
+ let choix = activity[b].userInteraction.bifChoice
502
+ data = { ...data, act, page, choix }
503
+ }
504
+ }
505
+ }
506
+
507
+ return data
508
+ },
509
+ /**
510
+ * @description: Getter to return the connection information of the app
511
+ * @returns: record {Object}
512
+ */
513
+ getConnectionInfo() {
514
+ return this.getLrsInfo
515
+ },
516
+
517
+ /**
518
+ * @description: Getter to return the menu setting
519
+ * @returns: record {Object}
520
+ */
521
+ getMenuSettings: (state) => {
522
+ return state.menuSetting
523
+ },
524
+
525
+ /**
526
+ * @description: Getter to return the menu setting
527
+ * @returns: record {Array}: liste of all the anchors by activity
528
+ */
529
+ getMenuAnchors(state) {
530
+ const menu = this.getMenuSettings
531
+ let anchors = []
532
+ for (const c in menu) {
533
+ anchors = [...anchors, { [c]: menu[c].anchors }]
534
+ }
535
+ return anchors
536
+ },
537
+
538
+ /**
539
+ * @description: Getter to return the menu setting
540
+ * @params ID {String}: id of the Activity
541
+ * @returns: record {ArrayOfObject}
542
+ */
543
+ getAnchorsForActivity(state) {
544
+ const allAnchors = this.getMenuAnchors
545
+ return (id) => {
546
+ const result = allAnchors.find((a) => a[id]) // find the object which has id
547
+
548
+ if (result) return result[id]
549
+ else return []
550
+ }
551
+ },
552
+
553
+ /**
554
+ * @description: Getter to return the current Section
555
+ * @returns: {Object}
556
+ */
557
+ getCurrentSection: (state) => {
558
+ if (state.currentSection) return state.currentSection
559
+ else return {}
560
+ },
561
+
562
+ getShowPrimaryCtrl: (state) => {
563
+ return state.showPrimaryCtrl
564
+ },
565
+
566
+ getShowNavLeftCtrl: (state) => {
567
+ return state.showNavLeftCtrl
568
+ },
569
+
570
+ getShowNavRightCtrl: (state) => {
571
+ return state.showNavRightCtrl
572
+ },
573
+
574
+ getLrsInfo: (state) => {
575
+ return state.lrsConfig
576
+ },
577
+
578
+ getNextActivity: (state) => {
579
+ return state.nextActivity
580
+ },
581
+
582
+ getPreviousActivity: (state) => {
583
+ return state.prevActivity
584
+ },
585
+
586
+ getDataFromServer: (state) => {
587
+ return state.dataFromServer
588
+ },
589
+ getDataNoteCredit: (state) => {
590
+ return state.dataNc
591
+ },
592
+ getWidgetOpen(state) {
593
+ return state.wigIsOpen
594
+ },
595
+ getPopupIsOpen(state) {
596
+ return state.popIsOpen
597
+ },
598
+ getSidebarIsOpen(state) {
599
+ return state.sideBIsOpen
600
+ },
601
+
602
+ getSettingsFromStore: (state) => {
603
+ return (sName = null) => {
604
+ if (!state.appConfigs || state.appConfigs[sName] == undefined)
605
+ return null
606
+
607
+ if (sName) return state.appConfigs[sName]
608
+ else return state.appConfigs
609
+ }
610
+ },
611
+
612
+ getA11yStatus: (state) => {
613
+ return state.navA11y
614
+ },
615
+
616
+ getEndPopUp: (state) => {
617
+ return state.endPopUp
618
+ },
619
+
620
+ getLessonPosition(state) {
621
+ return state.lessonPosition
622
+ },
623
+ getCompletionState(state) {
624
+ return state.completedState
625
+ }
626
+ },
627
+ //=======================ACTIONS
628
+ actions: {
629
+ updateAppStatus(status) {
630
+ this.status = status
631
+ },
632
+
633
+ updateAnchorStatus(status) {
634
+ this.anchorStatus = status
635
+ },
636
+
637
+ updateIntroStatus(status) {
638
+ this.introAcitve = status
639
+ },
640
+
641
+ setAppDebugMode(bool) {
642
+ this.appDebugMode = bool
643
+ },
644
+
645
+ setMobileState(status) {
646
+ this.isMobile = status
647
+ },
648
+ setCurrentBrowser(browser) {
649
+ this.currentBrowser = browser
650
+ },
651
+ setDeviceType(device) {
652
+ this.deviceType = device
653
+ },
654
+ setErrorMenu(error) {
655
+ this.errMenuSetting = error
656
+ },
657
+ setApplicationSettings(settings) {
658
+ this.applicationSettings = settings
659
+ },
660
+ setLessonPosition(newData) {
661
+ this.lessonPosition = newData
662
+ },
663
+ setCompletionState(newData) {
664
+ this.completedState = newData
665
+ },
666
+
667
+ setMediaVolume(volume) {
668
+ this.mediaVolume = volume
669
+ },
670
+
671
+ setMediaMuted(bool) {
672
+ this.mediaMuted = bool
673
+ },
674
+
675
+ setMediaSubtitles(subtitlesEnabled) {
676
+ this.mediaSubtitles = subtitlesEnabled
677
+ },
678
+
679
+ setMediaPlaybarValues(data) {
680
+ // update the playbar attributes
681
+ for (const k in data) {
682
+ this.mediaPlaybarValues[k] = data[k]
683
+ }
684
+ },
685
+
686
+ setAllFiles(data) {
687
+ this.allFiles = data
688
+ },
689
+
690
+ /* set/update the Application configuration*/
691
+ async initializeApp(data) {
692
+ this.appConfigs = data
693
+ const {
694
+ id,
695
+ crs_id: courseID,
696
+ idb_id: idbID,
697
+ specification: packageType
698
+ } = data
699
+ // Update the Module information when setting config
700
+ this.updateModule({
701
+ key: 'info',
702
+ data: { id, courseID, idbID, packageType }
703
+ })
704
+ },
705
+
706
+ setRouteHistory(data) {
707
+ this.routeHistory = data
708
+ },
709
+ /* set/update the Menu configuration*/
710
+ setMenu(data) {
711
+ this.menuSetting = data
712
+ },
713
+
714
+ /* set/update the Branching information*/
715
+ updateCurrentBranching(data) {
716
+ this.currentBranching = data
717
+ },
718
+ /* set/update the branch page information*/
719
+ updateCurrentBranchPage(data) {
720
+ this.currentBranchPage = data
721
+ },
722
+ /* set/update page information*/
723
+ updateCurrentPage({ activity_Id, page_Id }, caller = null) {
724
+ if (activity_Id && page_Id) {
725
+ let page
726
+ //id type case PXX_EXX_PXX
727
+ if (page_Id.length > 3) {
728
+ const splitted = page_Id.split('_')
729
+ page = this.thisModule.activities
730
+ .get(activity_Id)
731
+ .get(splitted[0])
732
+ .get(page_Id).content
733
+ } else {
734
+ const p = this.thisModule.activities.get(activity_Id).get(page_Id)
735
+
736
+ //this branching page if the constructor is a map
737
+ if (p && p.constructor === Map) {
738
+ page = this.thisModule.activities
739
+ .get(activity_Id)
740
+ .get(page_Id)
741
+ .get(page_Id).content
742
+ } else
743
+ page = this.thisModule.activities
744
+ .get(activity_Id)
745
+ .get(page_Id).content
746
+ }
747
+ this.currentPage = page
748
+ } else this.currentPage = {}
749
+ if (caller) console.log('🤙 updateCurrentPage called by', caller)
750
+ },
751
+ /**
752
+ @description update the module data.
753
+ If no key is passed update the entire module with new data. if key is passed add/update the key
754
+ @param {String} key : a key to add/update in the module
755
+ @param {String} data : data for the key to add/update in the module
756
+ */
757
+ updateModule({ key, data }) {
758
+ if (key || this.thisModule[key]) this.thisModule[key] = data
759
+ else this.thisModule = data
760
+ },
761
+
762
+ updateCurrentTimeline(data) {
763
+ this.currentPageTimeline = data
764
+ },
765
+
766
+ async updateCurrentMediaElements(data) {
767
+ if (data && data.constructor === Array && !data.length)
768
+ return (this.currentPageMediaElements = [])
769
+
770
+ const search = this.currentPageMediaElements.findIndex(
771
+ (e) => e.id == data.id
772
+ )
773
+ //If already existe update the current media information
774
+ if (search !== -1) return (this.currentPageMediaElements[search] = data)
775
+ //Add the ref in list
776
+ this.currentPageMediaElements.push(data)
777
+ },
778
+
779
+ updateCurrentMediaDuration(data) {
780
+ this.currentMediaDuration = data
781
+ },
782
+
783
+ updateCurrentSection(data) {
784
+ // update when new data
785
+ if (
786
+ !this.currentSection ||
787
+ this.currentSection.anchorName !== data.anchorName
788
+ )
789
+ this.currentSection = data
790
+ },
791
+ updateCurrentnoteCredit(data) {
792
+ this.dataNc = data
793
+ },
794
+ /**
795
+ * @description: Save user interaction on a page to store
796
+ * USe to Update existing userMetadata
797
+ */
798
+ updateUserMetaData(data) {
799
+ const _keys = Object.keys(data) // get all keys in the passed data
800
+ // ckeck if the Activity existe in the user data
801
+ if (data.activityRef) {
802
+ if (this.userMetaData[data.activityRef]) {
803
+ const activity = this.userMetaData[data.activityRef] // get targeted activity in the user data
804
+ const page = data.id // page id
805
+
806
+ // check if the page exist in the the activity
807
+ if (Object.keys(activity).includes(page)) {
808
+ // add entry for new key in user data for the target page
809
+ _keys.forEach((key) => {
810
+ // add add value of new key in the record : discart key=ActivityRef and key ===
811
+ if (key !== _keys[0] && key !== _keys[1]) {
812
+ // store the current user interaction in a variable
813
+ const oldValue = activity[page][key]
814
+
815
+ // update the user data by copying the old value and add new data
816
+ activity[page][key] = {
817
+ ...oldValue,
818
+ ...data[key]
819
+ }
820
+ }
821
+ })
822
+ } else {
823
+ /**
824
+ * add the page reference as an object to the user data
825
+ * and create entry for user interaction
826
+ * */
827
+ _keys.forEach((key) => {
828
+ if (key !== _keys[0] && key !== _keys[1]) {
829
+ activity[page] = {
830
+ [key]: data[key]
831
+ }
832
+ }
833
+ })
834
+ }
835
+ } else {
836
+ // create a new entry for the activity
837
+ this.userMetaData = {
838
+ ...this.userMetaData,
839
+ [data.activityRef]: {
840
+ [data.id]: {}
841
+ }
842
+ }
843
+
844
+ // Add data for each new page
845
+ _keys.forEach((key) => {
846
+ if (key !== _keys[0] && key !== _keys[1]) {
847
+ // add the page datas
848
+ this.userMetaData[data.activityRef][data.id] = {
849
+ [key]: data[key]
850
+ }
851
+ }
852
+ })
853
+ }
854
+ }
855
+ },
856
+
857
+ /* set/update the user data with existing record */
858
+ setUserMetaData(data) {
859
+ this.userMetaData = data
860
+ },
861
+
862
+ // to update the answers
863
+ saveAnswersQuiz(answers) {
864
+ const theAnswers = this.getAllQuizAnswers.findIndex(function (element) {
865
+ return element.questionId == answers.questionId
866
+ })
867
+ if (theAnswers == -1) {
868
+ // if the answer already existed
869
+ this.quizAnswers.push(answers)
870
+ } else {
871
+ this.quizAnswers[theAnswers] = answers
872
+ }
873
+ },
874
+
875
+ saveAnswersPoll(answers) {
876
+ const theAnswers = this.getAllPollAnswers.findIndex(function (element) {
877
+ return element.questionId == answers.questionId
878
+ })
879
+ if (theAnswers == -1) {
880
+ // if the answer already existed
881
+ this.pollAnswers.push(answers)
882
+ } else {
883
+ this.pollAnswers[theAnswers] = answers
884
+ }
885
+ },
886
+
887
+ setLrsConfig(data) {
888
+ if (data) this.lrsConfig = data
889
+ },
890
+
891
+ updateWidgetOpen(data) {
892
+ this.wigIsOpen = data
893
+ },
894
+ /**
895
+ * @description update the state of the popup -Cuminicate weither it is open or closes
896
+ */
897
+ updatepopIsOpen(data) {
898
+ this.popIsOpen = data
899
+ },
900
+ /**
901
+ * @description update the state of the sidebar -Cuminicate weither it is open or closes
902
+ */
903
+ updatesideBIsOpen(data) {
904
+ this.sideBIsOpen = data
905
+ },
906
+
907
+ updatePreviousActivity(data) {
908
+ this.prevActivity = data
909
+ },
910
+
911
+ updateNextActivity(data) {
912
+ this.nextActivity = data
913
+ },
914
+
915
+ updateNavA11y(data) {
916
+ this.navA11y = data
917
+ },
918
+
919
+ updateCompStatusTracker(data) {
920
+ if (!data) return
921
+
922
+ const { name, status } = data
923
+
924
+ if (!name || !status) return
925
+
926
+ let tracker = this.compStatusTracker
927
+
928
+ //search for the component
929
+ let target = tracker.find((e) => e == name)
930
+
931
+ //add component to tracking cue
932
+ if (!target && status == 'loading') tracker.push(name)
933
+
934
+ if (!tracker.length) return
935
+
936
+ //remove component to tracking cue
937
+ if (status == 'ready') {
938
+ const index = tracker.indexOf(target)
939
+
940
+ if (index >= 0) tracker = tracker.splice(index, 1)
941
+ }
942
+ },
943
+ updateEndPopUp(data) {
944
+ this.endPopUp = data
945
+ }
946
+ }
947
+ })