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