fcad-core-dragon 2.0.1 → 2.0.2-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.editorconfig +33 -33
  2. package/.eslintignore +29 -29
  3. package/.eslintrc.cjs +81 -81
  4. package/CHANGELOG +17 -1
  5. package/bk.scss +117 -117
  6. package/package.json +1 -1
  7. package/src/$locales/en.json +18 -4
  8. package/src/$locales/fr.json +17 -3
  9. package/src/assets/data/onboardingMessages.json +47 -47
  10. package/src/components/AppBase.vue +36 -341
  11. package/src/components/AppBaseErrorDisplay.vue +438 -438
  12. package/src/components/AppBaseFlipCard.vue +84 -84
  13. package/src/components/AppBaseModule.vue +16 -21
  14. package/src/components/AppBasePage.vue +45 -14
  15. package/src/components/AppBasePopover.vue +41 -41
  16. package/src/components/AppBaseSkeleton.vue +45 -0
  17. package/src/components/AppCompAudio.vue +12 -3
  18. package/src/components/AppCompButtonProgress.vue +13 -2
  19. package/src/components/AppCompCarousel.vue +12 -4
  20. package/src/components/AppCompInputCheckBoxNx.vue +324 -0
  21. package/src/components/AppCompInputDropdownNx.vue +295 -0
  22. package/src/components/AppCompInputRadioNx.vue +264 -0
  23. package/src/components/AppCompInputTextNx.vue +148 -0
  24. package/src/components/AppCompInputTextTableNx.vue +198 -0
  25. package/src/components/AppCompInputTextToFillDropdownNx.vue +291 -0
  26. package/src/components/AppCompInputTextToFillNx.vue +277 -0
  27. package/src/components/AppCompJauge.vue +11 -4
  28. package/src/components/AppCompMenu.vue +7 -14
  29. package/src/components/AppCompMenuItem.vue +7 -5
  30. package/src/components/AppCompNavigation.vue +21 -21
  31. package/src/components/AppCompNoteCall.vue +1 -0
  32. package/src/components/AppCompNoteCredit.vue +2 -1
  33. package/src/components/AppCompPlayBarNext.vue +94 -41
  34. package/src/components/AppCompPlayBarProgress.vue +82 -82
  35. package/src/components/AppCompPopUpNext.vue +6 -6
  36. package/src/components/AppCompQuiz.vue +500 -0
  37. package/src/components/AppCompQuizRecall.vue +113 -66
  38. package/src/components/AppCompSettingsMenu.vue +172 -172
  39. package/src/components/AppCompTableOfContent.vue +39 -10
  40. package/src/components/AppCompVideoPlayer.vue +1 -1
  41. package/src/components/AppCompViewDisplay.vue +6 -6
  42. package/src/composables/useQuiz.js +62 -179
  43. package/src/directives/nvdaFix.js +53 -0
  44. package/src/externalComps/ModuleView.vue +22 -22
  45. package/src/externalComps/SummaryView.vue +91 -91
  46. package/src/main.js +227 -30
  47. package/src/mixins/$mediaMixins.js +1 -11
  48. package/src/module/stores/appStore.js +29 -11
  49. package/src/module/xapi/Crypto/Hasher.js +241 -241
  50. package/src/module/xapi/Crypto/WordArray.js +278 -278
  51. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  52. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  53. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  54. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  55. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  56. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  57. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  58. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  59. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  60. package/src/module/xapi/Crypto/index.js +53 -53
  61. package/src/module/xapi/Statement/activity.js +47 -47
  62. package/src/module/xapi/Statement/agent.js +55 -55
  63. package/src/module/xapi/Statement/group.js +26 -26
  64. package/src/module/xapi/Statement/index.js +259 -259
  65. package/src/module/xapi/Statement/statement.js +253 -253
  66. package/src/module/xapi/Statement/statementRef.js +23 -23
  67. package/src/module/xapi/Statement/substatement.js +22 -22
  68. package/src/module/xapi/Statement/verb.js +36 -36
  69. package/src/module/xapi/activitytypes.js +17 -17
  70. package/src/module/xapi/utils.js +167 -167
  71. package/src/module/xapi/verbs.js +294 -294
  72. package/src/module/xapi/xapiStatement.js +444 -444
  73. package/src/plugins/bus.js +8 -8
  74. package/src/plugins/gsap.js +14 -14
  75. package/src/plugins/i18n.js +44 -44
  76. package/src/plugins/idb.js +1 -1
  77. package/src/plugins/save.js +37 -37
  78. package/src/plugins/scorm.js +287 -287
  79. package/src/plugins/xapi.js +11 -11
  80. package/src/public/index.html +33 -33
  81. package/src/shared/generalfuncs.js +134 -0
  82. package/src/shared/validators.js +308 -234
  83. package/src/components/AppCompInputCheckBoxNext.vue +0 -205
  84. package/src/components/AppCompInputDropdownNext.vue +0 -201
  85. package/src/components/AppCompInputRadioNext.vue +0 -158
  86. package/src/components/AppCompInputTextNext.vue +0 -124
  87. package/src/components/AppCompInputTextTableNext.vue +0 -142
  88. package/src/components/AppCompInputTextToFillDropdownNext.vue +0 -238
  89. package/src/components/AppCompInputTextToFillNext.vue +0 -171
  90. package/src/components/AppCompQuizNext.vue +0 -2908
package/src/main.js CHANGED
@@ -20,8 +20,9 @@ import AppCompNoteCall from './components/AppCompNoteCall.vue'
20
20
  import AppCompNoteCredit from './components/AppCompNoteCredit.vue'
21
21
  import AppCompVideoPlayer from './components/AppCompVideoPlayer.vue'
22
22
  import AppCompPlayBarNext from './components/AppCompPlayBarNext.vue'
23
- import AppCompQuizNext from './components/AppCompQuizNext.vue'
23
+ import AppCompQuiz from './components/AppCompQuiz.vue'
24
24
  import AppCompQuizRecall from './components/AppCompQuizRecall.vue'
25
+ import AppBaseSkeleton from './components/AppBaseSkeleton.vue'
25
26
 
26
27
  import GsapPlugin from './plugins/gsap'
27
28
  import eventBus from './plugins/bus'
@@ -37,6 +38,7 @@ import axios from 'axios'
37
38
  import AppCompSVGNext from './components/AppCompSVGNext.vue'
38
39
  import VueSafeTeleport from 'vue-safe-teleport'
39
40
  import { FocusTrap } from 'focus-trap-vue'
41
+ import nvdaFix from './directives/nvdaFix.js'
40
42
 
41
43
  const pinia = createPinia()
42
44
 
@@ -73,9 +75,10 @@ export default {
73
75
  app.component('AppBasePopover', AppBasePopover)
74
76
  app.component('AppCompVideoPlayer', AppCompVideoPlayer)
75
77
  app.component('AppCompPlayBarNext', AppCompPlayBarNext)
76
- app.component('AppCompQuizNext', AppCompQuizNext)
78
+ app.component('AppCompQuiz', AppCompQuiz)
77
79
  app.component('AppIconsNext', AppCompSVGNext)
78
80
  app.component('AppCompQuizRecall', AppCompQuizRecall)
81
+ app.component('AppBaseSkeleton', AppBaseSkeleton)
79
82
  app.component(AppCompViewDisplay)
80
83
 
81
84
  if (!options)
@@ -87,12 +90,12 @@ export default {
87
90
  const errorMissing = {
88
91
  i18n: { a: 'Internationalisation', b: 'vue-I18n' },
89
92
  menuSettings: { a: 'Menu Settings file', b: 'menuSettings' }
90
- /// nom de composante avec l'error avec la string comme en haut
91
93
  }
92
94
 
93
95
  /*Watch for appBase data received in the store
94
96
  *To set connection Info
95
97
  *To set the connection with indexDB
98
+ *To fetch data from servers /storage
96
99
  *To set document lang
97
100
  */
98
101
  const appStore = useAppStore()
@@ -104,32 +107,90 @@ export default {
104
107
  args, // array of parameters passed to the action
105
108
  after // hook after the action returns or resolves
106
109
  }) => {
107
- after((result) => {
108
- if (name !== 'setAppConfigs') return
110
+ after(async (result) => {
111
+ if (name !== 'initializeApp') return
109
112
  let { appConfigs } = appStore.$state
113
+ let { remote = false, specification } = appConfigs
114
+ const c = await configConnection(appConfigs)
115
+ store.lrsConfig = c
116
+ const { $scorm, $xapi, $idb } = app.config.globalProperties
117
+ if (specification == 'xapi') {
118
+ const {
119
+ auth,
120
+ endpoint,
121
+ registration = $xapi.ruuid()
122
+ } = store.lrsConfig
123
+
124
+ const config = {
125
+ auth,
126
+ endpoint,
127
+ registration,
128
+ activity_platform: `SIPI_organizationId`
129
+ }
130
+
131
+ $xapi._configLRS(config) // configure and launch LRS
132
+ }
133
+
110
134
  //Configure connection with the Data from appBase
111
- configConnection(appConfigs).then((c) => {
112
- //Save LRS CONFIGURATION TO STORE
113
- appStore.lrsConfig = c
114
- // Subscribe to changes in the state to save progress in localStorage when application is not in remote mode
115
- const remote = c ? c.remote : false
116
-
117
- if (!remote)
118
- app.config.globalProperties.$idb
119
- .openDB()
120
- .then(() =>
121
- app.config.globalProperties.$idb.saveState(
122
- appStore,
123
- appConfigs.idb_id
124
- )
125
- )
135
+ //===================================================
136
+
137
+ const activity_id = c ? c.activity_id : null
138
+
139
+ //Redefine actor with value from connection informtion
140
+ if (c && c.actor) appConfigs.actor = c.actor
141
+
142
+ appConfigs.remote = c && c.remote ? c.remote : false
143
+
144
+ //========================FETCHING EXISTING DATA AND SETTING STORE STATE===========================
145
+ /*Fetch user existing data from the server
146
+ and set/update initial state of the store
147
+ */
148
+ const res = await fetchDatasFromServer({
149
+ $scorm,
150
+ $xapi,
151
+ $idb,
152
+ activity_id,
153
+ ...appConfigs
126
154
  })
155
+ //Update the store state
156
+
157
+ if (res) {
158
+ const {
159
+ routeHistory,
160
+ userSettings = {},
161
+ progress,
162
+ playbarValues = null,
163
+ lessonPosition = null,
164
+ completedState = null
165
+ } = res
166
+
167
+ appStore.setUserMetaData(progress)
168
+ appStore.setRouteHistory(routeHistory)
169
+ appStore.setApplicationSettings(userSettings)
170
+
171
+ if (playbarValues)
172
+ appStore.$state.mediaPlaybarValues = playbarValues
173
+
174
+ if (lessonPosition) appStore.setLessonPosition(lessonPosition)
175
+ if (completedState) appStore.setCompletionState(completedState)
176
+ }
177
+ //Open Connection to IDB and Save the store state to localDB
178
+ let dbStoreActive = window.location.hostname == 'localhost' && !remote
179
+
180
+ if (dbStoreActive) {
181
+ console.log(`🏬 IDB Store active!`)
182
+ await $idb.openDB()
183
+ await $idb.saveState(appStore, appConfigs.idb_id)
184
+ }
185
+ //Update Loaging state
186
+ appStore.userDataLoaded = true
187
+
127
188
  //Set document lang attribute (wcag request)
128
189
  document.documentElement.setAttribute('lang', appConfigs.lang)
129
190
  })
130
191
  }
131
192
  )
132
-
193
+ app.directive('nvda-fix', nvdaFix)
133
194
  app.provide('unsubscribeToSetConfig', unsubscribeToSetConfig)
134
195
 
135
196
  /* Check that required options are provided */
@@ -172,22 +233,23 @@ export default {
172
233
  /* Local Method to configurate the connection info */
173
234
  const configConnection = async (data) => {
174
235
  let connectionInfo
236
+
175
237
  switch (true) {
176
238
  case window.location.origin.includes('cegepadistance.ca'): {
177
239
  if (data.specification == 'scorm') {
178
- return (connectionInfo = {
240
+ connectionInfo = {
179
241
  remote: true,
180
242
  endpoint: 'scorm'
181
- })
243
+ }
182
244
  } else {
183
245
  try {
184
246
  // Accessing User && Activity Info from Url parametters
185
247
  const queryString = window.location.search
186
248
  const urlParams = new URLSearchParams(queryString)
187
-
249
+ let c // Initialize connection object
188
250
  //Get User && Activity info
189
251
  if (urlParams.get('actor') && urlParams.get('activity_id')) {
190
- connectionInfo = {
252
+ c = {
191
253
  actor: JSON.parse(urlParams.get('actor')),
192
254
  activity_id: urlParams.get('activity_id'),
193
255
  endpoint: urlParams.get('endpoint'),
@@ -200,9 +262,8 @@ export default {
200
262
 
201
263
  const { basicauth } = request.data
202
264
 
203
- connectionInfo.auth = basicauth
204
-
205
- return connectionInfo
265
+ c.auth = basicauth
266
+ connectionInfo = c
206
267
  } catch (err) {
207
268
  console.error('DOWNLOAD ERROR: 💥', err)
208
269
  }
@@ -243,12 +304,148 @@ export default {
243
304
  remote,
244
305
  ...endpointConfig
245
306
  }
246
-
247
- return connectionInfo
307
+ break
248
308
  }
249
309
  }
310
+
311
+ return connectionInfo
250
312
  }
313
+ /* Local Method for data fetching */
314
+ const fetchDatasFromServer = async (config) => {
315
+ const {
316
+ $scorm,
317
+ $xapi,
318
+ $idb,
319
+ actor,
320
+ activity_id,
321
+ crs_id,
322
+ idb_id,
323
+ specification,
324
+ remote
325
+ } = config
326
+
327
+ let server = remote ? specification : 'local'
328
+ //falback to idb when scorm and origin is localhost
329
+ if (specification == 'scorm' && window.location.hostname == 'localhost')
330
+ server = 'local'
331
+
332
+ let data = null
333
+
334
+ switch (server) {
335
+ case 'scorm': {
336
+ if ($scorm.initialized) {
337
+ const lessonStatus = $scorm.GetValue('cmi.core.lesson_status', true)
338
+ if (lessonStatus === 'unknown') {
339
+ $scorm.setValue('cmi.core.lesson_status', 'incomplete')
340
+ $scorm.Commit()
341
+ }
251
342
 
343
+ const suspendData = $scorm.GetValue('cmi.suspend_data')
344
+ if (suspendData !== '') {
345
+ try {
346
+ const parsed = JSON.parse(suspendData.replace(/\\/g, ''))
347
+ const {
348
+ userData: progress,
349
+ routeHistory,
350
+ userSettings
351
+ } = parsed
352
+
353
+ const lessonPosition = this.$scorm.GetValue(
354
+ 'cmi.core.lesson_location',
355
+ false
356
+ )
357
+
358
+ data = {
359
+ progress,
360
+ routeHistory,
361
+ lessonPosition,
362
+ userSettings
363
+ }
364
+ } catch (err) {
365
+ console.error('❌ Failed to parse suspend_data:', err)
366
+ }
367
+ }
368
+ }
369
+ break
370
+ }
371
+
372
+ case 'xapi': {
373
+ try {
374
+ const actorMbox = actor.mbox.replace('mailto:', '')
375
+ const activityId = activity_id
376
+ const _url = new URL(activityId)
377
+ const parentID = `${_url.origin}/${crs_id}` // redefining activity id for statement
378
+
379
+ const lessonProgressParam = { email: actorMbox, activityId }
380
+ const lessonStateParam = {
381
+ email: actorMbox,
382
+ activityId,
383
+ verb: 'completed'
384
+ }
385
+ const lessonPosionParam = {
386
+ email: actorMbox,
387
+ activityId
388
+ }
389
+ const playbarParam = {
390
+ email: actorMbox,
391
+ activityId,
392
+ verb: 'played'
393
+ }
394
+ const preferencesParam = {
395
+ email: actorMbox,
396
+ activityId: parentID,
397
+ verb: 'preferred'
398
+ }
399
+
400
+ const fetchParams = [
401
+ lessonProgressParam,
402
+ lessonStateParam,
403
+ lessonPosionParam,
404
+ playbarParam,
405
+ preferencesParam
406
+ ]
407
+
408
+ const {
409
+ userData,
410
+ savedPoint,
411
+ preferredSettings,
412
+ playbarValues,
413
+ lessonStatus
414
+ } = await $xapi._getBulkData(fetchParams)
415
+
416
+ const { routeHistory = [], ...progress } = userData
417
+
418
+ const completedState = lessonStatus || {}
419
+ const lessonPosition = savedPoint || ''
420
+ const userSettings = preferredSettings || {}
421
+
422
+ data = {
423
+ progress,
424
+ routeHistory,
425
+ lessonPosition,
426
+ completedState,
427
+ playbarValues,
428
+ userSettings
429
+ }
430
+ } catch (err) {
431
+ throw new Error(err)
432
+ }
433
+ break
434
+ }
435
+ default: {
436
+ try {
437
+ await $idb.openDB() //open the new instance in indexDB
438
+ const res = await $idb.getFromDB(idb_id) // get existing Data from db
439
+
440
+ if (res && res.$record) data = res.$record
441
+ } catch (err) {
442
+ console.error('❌ Failed get Data from IndexDB:', err)
443
+ }
444
+ }
445
+ }
446
+
447
+ return data
448
+ }
252
449
  //=================================END SET LRS INFO ====================================
253
450
  window.setDebugMode = (mode) => {
254
451
  appStore.appDebugMode = mode
@@ -66,7 +66,7 @@ const $extendsMedia = {
66
66
  'hasMediaElOrTimeline',
67
67
  'getCurrentMediaDuration',
68
68
  'getCurrentBrowser',
69
- 'getDataFromServer',
69
+
70
70
  'getAutoplayEnabled',
71
71
  'getMediaVolume',
72
72
  'getModuleInfo',
@@ -129,16 +129,6 @@ const $extendsMedia = {
129
129
  // return this.getMediaVolume
130
130
  return this.getMediaPlaybarValues('volume')
131
131
  },
132
-
133
- // mediaVolume() {
134
- // let vol = 0.5
135
- // if (!this.getDataFromServer) return vol
136
-
137
- // const {pbValues} = this.getDataFromServer
138
- // if(pbValues && pbValues.volume) vol = parseFloat(pbValues.volume)
139
- // console.log("My Vol...", vol)
140
- // return vol
141
- // },
142
132
  //media from store
143
133
  mediaMuted() {
144
134
  return this.getMediaMuted
@@ -50,9 +50,10 @@ export const useAppStore = defineStore('$appStore', {
50
50
  compStatusTracker: [],
51
51
  dataFromServer: null,
52
52
  errMenuSetting: null,
53
- endPopUp: false
54
- //_allFiles: [],
55
- //allModuleRoutes: null
53
+ endPopUp: false,
54
+ userDataLoaded: false,
55
+ lessonPosition: [],
56
+ completedState: {}
56
57
  }
57
58
  },
58
59
  //=======================GETTERS
@@ -80,6 +81,9 @@ export const useAppStore = defineStore('$appStore', {
80
81
  return state.mediaMuted
81
82
  },
82
83
 
84
+ getUserDataStatus: (state) => {
85
+ return state.userDataLoaded
86
+ },
83
87
  /**
84
88
  * @description: Check if automatic video playback is enabled in application settings
85
89
  */
@@ -363,6 +367,7 @@ export const useAppStore = defineStore('$appStore', {
363
367
  return (activity = null, id = null) => {
364
368
  if (!activity || !id) return null
365
369
  const allInteraction = this.getUserInteraction
370
+
366
371
  if (!allInteraction[activity] || !allInteraction[activity][id])
367
372
  return {}
368
373
 
@@ -381,7 +386,7 @@ export const useAppStore = defineStore('$appStore', {
381
386
  let allStates
382
387
  let temp = []
383
388
 
384
- if (state.userMetaData[aKey]) {
389
+ if (state.userMetaData && state.userMetaData[aKey]) {
385
390
  const activity = Object.entries(state.userMetaData[aKey])
386
391
 
387
392
  // get all the completed acitivities
@@ -612,6 +617,13 @@ export const useAppStore = defineStore('$appStore', {
612
617
 
613
618
  getEndPopUp: (state) => {
614
619
  return state.endPopUp
620
+ },
621
+
622
+ getLessonPosition(state) {
623
+ return state.lessonPosition
624
+ },
625
+ getCompletionState(state) {
626
+ return state.completedState
615
627
  }
616
628
  },
617
629
  //=======================ACTIONS
@@ -647,6 +659,12 @@ export const useAppStore = defineStore('$appStore', {
647
659
  setApplicationSettings(settings) {
648
660
  this.applicationSettings = settings
649
661
  },
662
+ setLessonPosition(newData) {
663
+ this.lessonPosition = newData
664
+ },
665
+ setCompletionState(newData) {
666
+ this.completedState = newData
667
+ },
650
668
 
651
669
  setMediaVolume(volume) {
652
670
  this.mediaVolume = volume
@@ -672,7 +690,7 @@ export const useAppStore = defineStore('$appStore', {
672
690
  },
673
691
 
674
692
  /* set/update the Application configuration*/
675
- async setAppConfigs(data) {
693
+ async initializeApp(data) {
676
694
  this.appConfigs = data
677
695
  const {
678
696
  id,
@@ -904,12 +922,12 @@ export const useAppStore = defineStore('$appStore', {
904
922
  this.sideBIsOpen = data
905
923
  },
906
924
 
907
- async updateDataFetchFromServer(data) {
908
- if (!data || data.constructor !== Object) return
909
- Object.keys(data).forEach((k) => {
910
- this.dataFromServer = { ...this.dataFromServer, [k]: data[k] }
911
- })
912
- },
925
+ // async updateDataFetchFromServer(data) {
926
+ // if (!data || data.constructor !== Object) return
927
+ // Object.keys(data).forEach((k) => {
928
+ // this.dataFromServer = { ...this.dataFromServer, [k]: data[k] }
929
+ // })
930
+ // },
913
931
 
914
932
  updatePreviousActivity(data) {
915
933
  this.prevActivity = data