fcad-core-dragon 2.0.0-beta.3 → 2.0.0-beta.5

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 +13 -0
  5. package/README.md +71 -71
  6. package/bk.scss +117 -117
  7. package/package.json +8 -8
  8. package/src/$locales/en.json +145 -143
  9. package/src/$locales/fr.json +107 -105
  10. package/src/assets/data/onboardingMessages.json +47 -47
  11. package/src/components/AppBase.vue +1150 -1054
  12. package/src/components/AppBaseButton.test.js +22 -0
  13. package/src/components/AppBaseButton.vue +93 -87
  14. package/src/components/AppBaseErrorDisplay.vue +438 -438
  15. package/src/components/AppBaseFlipCard.vue +84 -84
  16. package/src/components/AppBaseModule.vue +1657 -1673
  17. package/src/components/AppBasePage.vue +742 -779
  18. package/src/components/AppBasePopover.vue +41 -41
  19. package/src/components/AppCompAudio.vue +265 -234
  20. package/src/components/AppCompBranchButtons.vue +556 -552
  21. package/src/components/AppCompButtonProgress.vue +121 -126
  22. package/src/components/AppCompCarousel.vue +328 -298
  23. package/src/components/AppCompInputCheckBoxNext.vue +200 -195
  24. package/src/components/AppCompInputDropdownNext.vue +201 -159
  25. package/src/components/AppCompInputRadioNext.vue +152 -152
  26. package/src/components/AppCompInputTextNext.vue +125 -106
  27. package/src/components/AppCompInputTextTableNext.vue +142 -141
  28. package/src/components/AppCompInputTextToFillDropdownNext.vue +238 -230
  29. package/src/components/AppCompInputTextToFillNext.vue +171 -171
  30. package/src/components/AppCompJauge.vue +74 -74
  31. package/src/components/AppCompMenu.vue +25 -10
  32. package/src/components/AppCompMenuItem.vue +228 -228
  33. package/src/components/AppCompNavigation.vue +972 -960
  34. package/src/components/AppCompNoteCall.vue +159 -133
  35. package/src/components/AppCompNoteCredit.vue +490 -292
  36. package/src/components/AppCompPlayBar.vue +1217 -1218
  37. package/src/components/AppCompPlayBarNext.vue +2060 -2052
  38. package/src/components/AppCompPlayBarProgress.vue +82 -82
  39. package/src/components/AppCompPopUpNext.vue +500 -503
  40. package/src/components/AppCompQuizNext.vue +2908 -2904
  41. package/src/components/AppCompQuizRecall.vue +298 -276
  42. package/src/components/AppCompSVGNext.vue +347 -347
  43. package/src/components/AppCompSettingsMenu.vue +172 -172
  44. package/src/components/AppCompTableOfContent.vue +386 -387
  45. package/src/components/AppCompTranscript.vue +24 -24
  46. package/src/components/AppCompVideoPlayer.vue +368 -368
  47. package/src/components/BaseModule.vue +55 -72
  48. package/src/components/tests__/AppBaseButton.spec.js +53 -0
  49. package/src/composables/useQuiz.js +206 -206
  50. package/src/externalComps/ModuleView.vue +22 -22
  51. package/src/externalComps/SummaryView.vue +91 -91
  52. package/src/main.js +272 -272
  53. package/src/mixins/$mediaMixins.js +819 -819
  54. package/src/mixins/timerMixin.js +155 -155
  55. package/src/module/stores/appStore.js +954 -893
  56. package/src/module/xapi/ADL.js +380 -376
  57. package/src/module/xapi/Crypto/Hasher.js +241 -241
  58. package/src/module/xapi/Crypto/WordArray.js +278 -278
  59. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  60. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  61. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  62. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  63. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  64. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  65. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  66. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  67. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  68. package/src/module/xapi/Statement/agent.js +55 -55
  69. package/src/module/xapi/Statement/index.js +259 -259
  70. package/src/module/xapi/Statement/statement.js +253 -253
  71. package/src/module/xapi/launch.js +157 -157
  72. package/src/module/xapi/utils.js +167 -167
  73. package/src/module/xapi/verbs.js +294 -294
  74. package/src/module/xapi/wrapper.js +1963 -1963
  75. package/src/module/xapi/xapiStatement.js +444 -444
  76. package/src/plugins/bus.js +8 -8
  77. package/src/plugins/gsap.js +14 -14
  78. package/src/plugins/helper.js +355 -308
  79. package/src/plugins/i18n.js +44 -44
  80. package/src/plugins/idb.js +227 -219
  81. package/src/plugins/save.js +37 -37
  82. package/src/plugins/scorm.js +287 -287
  83. package/src/plugins/xapi.js +11 -11
  84. package/src/public/index.html +33 -33
  85. package/src/router/index.js +48 -43
  86. package/src/router/routes.js +312 -312
  87. package/src/shared/generalfuncs.js +210 -210
  88. package/src/shared/validators.js +926 -1069
  89. package/vitest.config.js +19 -0
  90. package/vite.config.js +0 -27
@@ -1,308 +1,355 @@
1
- import routes from '../router/routes'
2
- import { useAppStore } from '../module/stores/appStore'
3
- import { name as fcadName, version as fcadVersion } from '../../package.json'
4
-
5
- import axios from 'axios'
6
-
7
- import locale_en from '../$locales/en.json'
8
- import locale_fr from '../$locales/fr.json'
9
-
10
- export default function helper(app, name) {
11
- Object.defineProperty(app.config.globalProperties, name, {
12
- value: {
13
- /***
14
- * @description: helper to get the collection of routes of activies from vue-Router.
15
- * @note If need more info from the router , use directly the vue-router
16
- * @param {String} ID [optional] - ID of an activity (ex: A01).
17
- * @return {Array} - return all routes for an activity Or all the routes of the module
18
- *
19
- */
20
- getRoutesFromVueRouter: (ID) => {
21
- //let all_routes
22
- //const appStore = useAppStore()
23
- //const routes = appStore.$state.allModuleRoutes
24
- //let { children: all_routes, meta } = routes[0]
25
- let { children: all_routes, meta } = routes[1].children[0]
26
-
27
- if (ID) {
28
- let f = all_routes.find((route) => route.meta.id === ID)
29
- if (f) {
30
- all_routes = f.children
31
- meta = f.meta
32
- }
33
- }
34
- return { all_routes, meta }
35
- },
36
- /** @description: helper to get the build timestamp. Useful for QA
37
- *
38
- */
39
- getBuildTime() {
40
- return document.documentElement.dataset.buildTime
41
- },
42
- /**
43
- * @description: helper to get the FCAD version full string. Useful for QA
44
- *
45
- */
46
- getFcadVersionString() {
47
- return `${fcadName} v${fcadVersion}`
48
- },
49
- /** @description: helper to get the FCAD version number. Useful for QA
50
- *
51
- */
52
- getFcadVersion() {
53
- return `${fcadVersion}`
54
- },
55
-
56
- /**
57
- * @description: helper to get App setting information from the store.
58
- * @param {String} sName - a key of specific value from the settings
59
- * ex: 'specification' to return the specification of the app: xapi |scorm
60
- * @return {Object|null} -
61
- */
62
- getSettingsFromStore(sName = null) {
63
- const store = useAppStore()
64
- if (
65
- !store.$state.appConfigs ||
66
- store.$state.appConfigs[sName] == undefined
67
- )
68
- return null
69
-
70
- if (sName) return store.$state.appConfigs[sName]
71
- else return store.$state.appConfigs
72
- },
73
-
74
- /**
75
- * @description: helper to get the menu information from the store.
76
- *
77
- */
78
- getMenuInfoFromStore() {
79
- const store = useAppStore()
80
- if (store.$state.menuSetting) return store.$state.menuSetting
81
- else return null
82
- },
83
- /***
84
- * @description Method to return a myme time of a document
85
- * @param {String} aType extension of the document exemple: doc , pdf ect...
86
- * @return {String} mime
87
- */
88
- mimeTypeFor(aType) {
89
- let mimeType = false
90
- switch (aType) {
91
- case 'doc':
92
- mimeType = 'application/msword'
93
- break
94
-
95
- case 'docx':
96
- mimeType =
97
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
98
- break
99
-
100
- case 'pdf':
101
- mimeType = 'application/pdf'
102
- break
103
-
104
- case 'txt':
105
- mimeType = 'text/plain'
106
- break
107
-
108
- case 'jpeg' || 'jpg':
109
- mimeType = 'image/jpeg'
110
- break
111
-
112
- case 'png':
113
- mimeType = 'image/png'
114
- break
115
- }
116
- return mimeType
117
- },
118
- /***
119
- * @description method to download a file.
120
- * @param {String} filePath url of the file to download
121
- * @param {String} outputName 'the default name for the download file'
122
- * @summary Use axios to simulate a file request from server.
123
- * Create <a> tag with downloadable link and download the element to user PC.
124
- * Remove a element after download
125
- * @usage:
126
- * - case 1: the file is in the public folder of your project. Use full path ex: downloadFile('./public/
127
- * myfile')
128
- * - case 2: file is in another folder in a src of your project. Use relative path. File is handle as webpack
129
- * module requirement with method require(). ex: downloadFile(require('@/accets/another_folder/** myfile'))
130
- *
131
- */
132
- async downloadFile(filepath, outputName) {
133
- try {
134
- if (filepath && filepath.constructor === String) {
135
- const res = await axios.get(filepath, { responseType: 'blob' }) // use http request
136
-
137
- const blob = new Blob([res.data], {
138
- type: res.data.type
139
- }) // create a blob element
140
-
141
- const alink = document.createElement('a') //create a element
142
-
143
- alink.href = URL.createObjectURL(blob) //create url reference for the object
144
- alink.download = outputName || `myFile` // set download name
145
- alink.click() //symulate click event to download the object
146
-
147
- URL.revokeObjectURL(alink.href) //release the url referrence of the object
148
- } else throw new Error('No URL provided for download')
149
- } catch (err) {
150
- // plugin has access to $bus through the Vue.proptotype
151
- const msg = this.getKeyFromLocal('message.err_download_file')
152
-
153
- app.config.globalProperties.$bus.$emit('open-popup', {
154
- type: 'popup-info',
155
- value: { text_1: `⚠️ ${msg}` }
156
- })
157
- console.error('DOWNLOAD ERROR: 💥', err)
158
- }
159
- },
160
- /***
161
- * @description - helper methode to retrive a word from a local file.
162
- * @param {String} key - key of the word to retrive ex: 'a.b.c'
163
- * @return {String } - return the value of the key ex: 'hello world' if exist or the key ('a.b.c')
164
- */
165
- getKeyFromLocal(key) {
166
- if (!key) throw new Error('Missing Key for search')
167
- const store = useAppStore()
168
- const lang = store.state.appConfigs.lang // get lang from the store
169
- const dic = { en: locale_en, fr: locale_fr } //contents of the json locales
170
- const keys = key.split('.')
171
-
172
- let value
173
- for (let i = 0; i < keys.length; i++) {
174
- let n = keys[i]
175
- if (i === 0 && dic[lang][n]) value = dic[lang][n]
176
- else if (value[n]) value = value[n]
177
- else return (value = key)
178
- }
179
-
180
- return value
181
- },
182
-
183
- /***
184
- * @description: Insert line break opportunities into a URL
185
- * @param {String} url - original url string to formate
186
- * @credit - https://css-tricks.com/better-line-breaks-for-long-urls/
187
- */
188
- breakUrlString(url) {
189
- // Split the URL into an array to distinguish double slashes from single slashes
190
- const doubleSlash = url.split('//')
191
-
192
- // Format the strings on either side of double slashes separately
193
- let formatted = doubleSlash
194
- .map(
195
- (str) =>
196
- // Insert a word break opportunity after a colon
197
- str
198
- .replace(/(?<after>:)/giu, '$1<wbr>')
199
- // Before a single slash, tilde, period, comma, hyphen, underline, question mark, number sign, or percent symbol
200
- .replace(/(?<before>[/~.,\-_?#%])/giu, '<wbr>$1')
201
- // Before and after an equals sign or ampersand
202
- .replace(/(?<beforeAndAfter>[=&])/giu, '<wbr>$1<wbr>')
203
- // Reconnect the strings with word break opportunities after double slashes
204
- )
205
- .join('//<wbr>')
206
-
207
- return formatted
208
- },
209
- /***
210
- * @description: Creates the right url and id for the next lesson, depending on the environment (projets/campus)
211
- * Works with full url (ex: https://projets...) or a partial url (ex: 109-101-MQ-60-TP/M1Intro/index.php) set in app.
212
- * When working locally, uses the projets.cegepadistance.ca environment
213
- * @param {Object} linkedResource - Setting from app.settings.js
214
- * @return {Object} - linkedResource object with the right url and id values according to current environment
215
- */
216
- getNextLessonEnv(linkedResource) {
217
- let courseLocation, host, courseId
218
- /**ID*/
219
- if (linkedResource.id.startsWith('http')) {
220
- courseId = linkedResource.id.split('lrs-xapi/')[1]
221
- } else {
222
- courseId = linkedResource.id
223
- if (courseId.startsWith('/')) {
224
- courseId = courseId.substring(1)
225
- }
226
- }
227
-
228
- /**URL*/
229
- //If the value is a complete url, split and keep the end only ex: 109-101-MQ-60-TP/M1Intro/index.php
230
- if (linkedResource.url.startsWith('http')) {
231
- courseLocation = linkedResource.url.split('lrs-xapi/')[1]
232
- } else {
233
- courseLocation = linkedResource.url
234
- if (courseLocation.startsWith('/')) {
235
- courseLocation = courseLocation.substring(1)
236
- }
237
- }
238
-
239
- host = import.meta.env.PROD
240
- ? window.location.host
241
- : 'projets.cegepadistance.ca'
242
-
243
- linkedResource.id = `https://${host}/lrs-xapi/${courseId}`
244
- linkedResource.url = `https://${host}/lrs-xapi/${courseLocation}`
245
-
246
- return linkedResource
247
- },
248
-
249
- //Method to get let last activity
250
- getlastItemInMap(map) {
251
- return [...map][map.size - 1]
252
- },
253
-
254
- /***
255
- * Method to format Time to ISOS String
256
- * @param {String} T - time Iso Time
257
- * @return {String} - time PT Format
258
- */
259
-
260
- formatToISOSTime(t) {
261
- if (!t) return
262
- let regex = new RegExp(':', 'gm')
263
- let T = !regex.test(t) ? this.formatTime(t) : t
264
-
265
- // let timeStr = `PT${t}S`
266
-
267
- let timeStr =
268
- T.match(regex).length > 1
269
- ? `PT${T.split(':')[0]}H${T.split(':')[1]}M${T.split(':')[2]}S`
270
- : `PT00H${T.split(':')[0]}M${T.split(':')[1]}S`
271
-
272
- return timeStr
273
- },
274
- /***
275
- * Method to format Time to ISOS String
276
- * @param {String} T - time Iso Time
277
- * @return {String} - time PT Format
278
- */
279
- formatTime(time) {
280
- let returnStr = ``
281
- // format a time from second to 00:00:00
282
- const option = { minimumIntegerDigits: 2, useGrouping: false }
283
- const minutes = Math.floor(time / 60).toLocaleString('en-US', option)
284
- const seconds = Math.floor(time - minutes * 60).toLocaleString(
285
- 'en-US',
286
- option
287
- )
288
- const hours = Math.floor(time / 3600).toLocaleString('en-US', option)
289
- if (hours == '00') {
290
- returnStr = `${minutes}:${seconds}`
291
- } else {
292
- returnStr = `${hours}:${minutes}:${seconds}`
293
- }
294
- return returnStr
295
- }, // END formatTime
296
-
297
- /**
298
- * @description check if a values exists in a array
299
- * @param {Array} array
300
- * @param value
301
- * @returns {Boolean}
302
- */
303
- containsValue(array, value) {
304
- return array.includes(value)
305
- }
306
- }
307
- })
308
- }
1
+ import routes from '../router/routes'
2
+ import { useAppStore } from '../module/stores/appStore'
3
+ import { name as fcadName, version as fcadVersion } from '../../package.json'
4
+
5
+ import axios from 'axios'
6
+
7
+ import locale_en from '../$locales/en.json'
8
+ import locale_fr from '../$locales/fr.json'
9
+
10
+ export default function helper(app, name) {
11
+ Object.defineProperty(app.config.globalProperties, name, {
12
+ value: {
13
+ /***
14
+ * @description: helper to get the collection of routes of activies from vue-Router.
15
+ * @note If need more info from the router , use directly the vue-router
16
+ * @param {String} ID [optional] - ID of an activity (ex: A01).
17
+ * @return {Array} - return all routes for an activity Or all the routes of the module
18
+ *
19
+ */
20
+ getRoutesFromVueRouter: (ID) => {
21
+ //let all_routes
22
+ //const appStore = useAppStore()
23
+ //const routes = appStore.$state.allModuleRoutes
24
+ //let { children: all_routes, meta } = routes[0]
25
+ let { children: all_routes, meta } = routes[1].children[0]
26
+
27
+ if (ID) {
28
+ let f = all_routes.find((route) => route.meta.id === ID)
29
+ if (f) {
30
+ all_routes = f.children
31
+ meta = f.meta
32
+ }
33
+ }
34
+ return { all_routes, meta }
35
+ },
36
+ /** @description: helper to get the build timestamp. Useful for QA
37
+ *
38
+ */
39
+ getBuildTime() {
40
+ return document.documentElement.dataset.buildTime
41
+ },
42
+ /** @description: helper to get the template version used by projgen to create this project. Useful for QA
43
+ *
44
+ */
45
+ getTemplateVersion() {
46
+ return document.documentElement.dataset.templateVersion
47
+ },
48
+ /**
49
+ * @description: helper to get the FCAD version full string. Useful for QA
50
+ *
51
+ */
52
+ getFcadVersionString() {
53
+ return `[${fcadName} v${fcadVersion}] [fcad-template v${this.getTemplateVersion()}] [build ${this.getBuildTime()}]`
54
+ },
55
+ /** @description: helper to get the FCAD version number. Useful for QA
56
+ *
57
+ */
58
+ getFcadVersion() {
59
+ return `${fcadVersion}`
60
+ },
61
+
62
+ /**
63
+ * @description: helper to get App setting information from the store.
64
+ * @param {String} sName - a key of specific value from the settings
65
+ * ex: 'specification' to return the specification of the app: xapi |scorm
66
+ * @return {Object|null} -
67
+ */
68
+ getSettingsFromStore(sName = null) {
69
+ const store = useAppStore()
70
+ if (
71
+ !store.$state.appConfigs ||
72
+ store.$state.appConfigs[sName] == undefined
73
+ )
74
+ return null
75
+
76
+ if (sName) return store.$state.appConfigs[sName]
77
+ else return store.$state.appConfigs
78
+ },
79
+
80
+ /**
81
+ * @description: helper to get the menu information from the store.
82
+ *
83
+ */
84
+ getMenuInfoFromStore() {
85
+ const store = useAppStore()
86
+ if (store.$state.menuSetting) return store.$state.menuSetting
87
+ else return null
88
+ },
89
+ /***
90
+ * @description Method to return a myme time of a document
91
+ * @param {String} aType extension of the document exemple: doc , pdf ect...
92
+ * @return {String} mime
93
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/MIME_types/Common_types
94
+ */
95
+ mimeTypeFor(aType) {
96
+ let mimeType = false
97
+
98
+ switch (true) {
99
+ case aType.endsWith('.doc'):
100
+ // Microsoft Word
101
+ mimeType = 'application/msword'
102
+ break
103
+
104
+ case aType.endsWith('.docx'):
105
+ // Microsoft Word (OpenXML)
106
+ mimeType =
107
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
108
+ break
109
+
110
+ case aType.endsWith('.pdf'):
111
+ //Adobe Portable Document Format (PDF)
112
+ mimeType = 'application/pdf'
113
+ break
114
+ case aType.endsWith('.odt'):
115
+ // OpenDocument text document
116
+ mimeType = 'application/vnd.oasis.opendocument.text'
117
+ break
118
+
119
+ case aType.endsWith('.txt'):
120
+ // Text, (generally ASCII or ISO 8859-n)
121
+ mimeType = 'text/plain'
122
+ break
123
+
124
+ case aType.endsWith('.jpeg') || aType.endsWith('.jpg'):
125
+ // JPEG images
126
+ mimeType = 'image/jpeg'
127
+ break
128
+
129
+ case aType.endsWith('.png'):
130
+ // Portable Network Graphics
131
+ mimeType = 'image/png'
132
+ break
133
+ case aType.endsWith('.csv'):
134
+ // Comma-separated values (CSV)
135
+ mimeType = 'text/csv'
136
+ break
137
+
138
+ case aType.endsWith('.xls'):
139
+ // Microsoft Excel
140
+ mimeType = 'application/vnd.ms-excel'
141
+ break
142
+ case aType.endsWith('.xlsx'):
143
+ // Microsoft Excel (OpenXML)
144
+ mimeType =
145
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
146
+ break
147
+ case aType.endsWith('.ppt'):
148
+ //Microsoft PowerPoint
149
+ mimeType = 'application/vnd.ms-powerpoint'
150
+ break
151
+ case aType.endsWith('.pptx'):
152
+ //Microsoft PowerPoint (OpenXML)
153
+ mimeType =
154
+ 'tapplication/vnd.openxmlformats-officedocument.presentationml.presentation'
155
+ break
156
+ case aType.endsWith('.htm') || aType.endsWith('.html'):
157
+ // HyperText Markup Language (HTML)
158
+ mimeType = 'text/html'
159
+ break
160
+ }
161
+
162
+ return mimeType
163
+ },
164
+ /***
165
+ * @description method to download a file.
166
+ * @param {String} filePath url of the file to download
167
+ * @param {String} outputName 'the default name for the download file'
168
+ * @summary Use axios to simulate a file request from server.
169
+ * Create <a> tag with downloadable link and download the element to user PC.
170
+ * Remove a element after download
171
+ * @usage:
172
+ * - case 1: the file is in the public folder of your project. Use full path ex: downloadFile('./public/
173
+ * myfile')
174
+ * - case 2: file is in another folder in a src of your project. Use relative path. import the file and provide it to the * downloader fonction ex: downloadFile(require('@/accets/another_folder
175
+ * )
176
+ *
177
+ */
178
+ async downloadFile(filepath, outputName) {
179
+ try {
180
+ if (filepath && filepath.constructor === String) {
181
+ const res = await axios.get(filepath, { responseType: 'blob' }) // use http request
182
+ console.log(' Download res: ', res)
183
+ const blob = new Blob([res.data], {
184
+ // type: res.data.type
185
+ type: this.mimeTypeFor(filepath)
186
+ }) // create a blob element
187
+
188
+ const alink = document.createElement('a') //create a element
189
+
190
+ alink.href = URL.createObjectURL(blob) //create url reference for the object
191
+ alink.download = outputName || `myFile` // set download name
192
+ alink.click() //symulate click event to download the object
193
+
194
+ URL.revokeObjectURL(alink.href) //release the url referrence of the object
195
+ } else throw new Error('No URL provided for download')
196
+ } catch (err) {
197
+ // plugin has access to $bus through the Vue.proptotype
198
+ const msg = this.getKeyFromLocal('message.err_download_file')
199
+
200
+ app.config.globalProperties.$bus.$emit('open-popup', {
201
+ type: 'popup-info',
202
+ value: { text_1: `⚠️ ${msg}` }
203
+ })
204
+ console.error('DOWNLOAD ERROR: 💥', err)
205
+ }
206
+ },
207
+ /***
208
+ * @description - helper methode to retrive a word from a local file.
209
+ * @param {String} key - key of the word to retrive ex: 'a.b.c'
210
+ * @return {String } - return the value of the key ex: 'hello world' if exist or the key ('a.b.c')
211
+ */
212
+ getKeyFromLocal(key) {
213
+ if (!key) throw new Error('Missing Key for search')
214
+ const store = useAppStore()
215
+ const lang = store.$state.appConfigs.lang // get lang from the store
216
+ const dic = { en: locale_en, fr: locale_fr } //contents of the json locales
217
+ const keys = key.split('.')
218
+
219
+ let value
220
+ for (let i = 0; i < keys.length; i++) {
221
+ let n = keys[i]
222
+ if (i === 0 && dic[lang][n]) value = dic[lang][n]
223
+ else if (value[n]) value = value[n]
224
+ else return (value = key)
225
+ }
226
+
227
+ return value
228
+ },
229
+
230
+ /***
231
+ * @description: Insert line break opportunities into a URL
232
+ * @param {String} url - original url string to formate
233
+ * @credit - https://css-tricks.com/better-line-breaks-for-long-urls/
234
+ */
235
+ breakUrlString(url) {
236
+ // Split the URL into an array to distinguish double slashes from single slashes
237
+ const doubleSlash = url.split('//')
238
+
239
+ // Format the strings on either side of double slashes separately
240
+ let formatted = doubleSlash
241
+ .map(
242
+ (str) =>
243
+ // Insert a word break opportunity after a colon
244
+ str
245
+ .replace(/(?<after>:)/giu, '$1<wbr>')
246
+ // Before a single slash, tilde, period, comma, hyphen, underline, question mark, number sign, or percent symbol
247
+ .replace(/(?<before>[/~.,\-_?#%])/giu, '<wbr>$1')
248
+ // Before and after an equals sign or ampersand
249
+ .replace(/(?<beforeAndAfter>[=&])/giu, '<wbr>$1<wbr>')
250
+ // Reconnect the strings with word break opportunities after double slashes
251
+ )
252
+ .join('//<wbr>')
253
+
254
+ return formatted
255
+ },
256
+ /***
257
+ * @description: Creates the right url and id for the next lesson, depending on the environment (projets/campus)
258
+ * Works with full url (ex: https://projets...) or a partial url (ex: 109-101-MQ-60-TP/M1Intro/index.php) set in app.
259
+ * When working locally, uses the projets.cegepadistance.ca environment
260
+ * @param {Object} linkedResource - Setting from app.settings.js
261
+ * @return {Object} - linkedResource object with the right url and id values according to current environment
262
+ */
263
+ getNextLessonEnv(linkedResource) {
264
+ let courseLocation, host, courseId
265
+ /**ID*/
266
+ if (linkedResource.id.startsWith('http')) {
267
+ courseId = linkedResource.id.split('lrs-xapi/')[1]
268
+ } else {
269
+ courseId = linkedResource.id
270
+ if (courseId.startsWith('/')) {
271
+ courseId = courseId.substring(1)
272
+ }
273
+ }
274
+
275
+ /**URL*/
276
+ //If the value is a complete url, split and keep the end only ex: 109-101-MQ-60-TP/M1Intro/index.php
277
+ if (linkedResource.url.startsWith('http')) {
278
+ courseLocation = linkedResource.url.split('lrs-xapi/')[1]
279
+ } else {
280
+ courseLocation = linkedResource.url
281
+ if (courseLocation.startsWith('/')) {
282
+ courseLocation = courseLocation.substring(1)
283
+ }
284
+ }
285
+
286
+ host = import.meta.env.PROD
287
+ ? window.location.host
288
+ : 'projets.cegepadistance.ca'
289
+
290
+ linkedResource.id = `https://${host}/lrs-xapi/${courseId}`
291
+ linkedResource.url = `https://${host}/lrs-xapi/${courseLocation}`
292
+
293
+ return linkedResource
294
+ },
295
+
296
+ //Method to get let last activity
297
+ getlastItemInMap(map) {
298
+ return [...map][map.size - 1]
299
+ },
300
+
301
+ /***
302
+ * Method to format Time to ISOS String
303
+ * @param {String} T - time Iso Time
304
+ * @return {String} - time PT Format
305
+ */
306
+
307
+ formatToISOSTime(t) {
308
+ if (!t) return
309
+ let regex = new RegExp(':', 'gm')
310
+ let T = !regex.test(t) ? this.formatTime(t) : t
311
+
312
+ // let timeStr = `PT${t}S`
313
+
314
+ let timeStr =
315
+ T.match(regex).length > 1
316
+ ? `PT${T.split(':')[0]}H${T.split(':')[1]}M${T.split(':')[2]}S`
317
+ : `PT00H${T.split(':')[0]}M${T.split(':')[1]}S`
318
+
319
+ return timeStr
320
+ },
321
+ /***
322
+ * Method to format Time to ISOS String
323
+ * @param {String} T - time Iso Time
324
+ * @return {String} - time PT Format
325
+ */
326
+ formatTime(time) {
327
+ let returnStr = ``
328
+ // format a time from second to 00:00:00
329
+ const option = { minimumIntegerDigits: 2, useGrouping: false }
330
+ const minutes = Math.floor(time / 60).toLocaleString('en-US', option)
331
+ const seconds = Math.floor(time - minutes * 60).toLocaleString(
332
+ 'en-US',
333
+ option
334
+ )
335
+ const hours = Math.floor(time / 3600).toLocaleString('en-US', option)
336
+ if (hours == '00') {
337
+ returnStr = `${minutes}:${seconds}`
338
+ } else {
339
+ returnStr = `${hours}:${minutes}:${seconds}`
340
+ }
341
+ return returnStr
342
+ }, // END formatTime
343
+
344
+ /**
345
+ * @description check if a values exists in a array
346
+ * @param {Array} array
347
+ * @param value
348
+ * @returns {Boolean}
349
+ */
350
+ containsValue(array, value) {
351
+ return array.includes(value)
352
+ }
353
+ }
354
+ })
355
+ }