fcad-core-dragon 2.1.0 → 2.1.2

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