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,960 +1,972 @@
1
- <!-- About this Component--
2
- @ Description: This component is used to display and create the navigation in el+ mode.
3
- @ What it does: Create the link to back back to the menu. Create link to the all the anchor to the current activity and display the curent anchor and the button next page and previous page. Teleport are use to place the element in the rigth place in the main frame.
4
- -->
5
- <template>
6
- <div v-if="navigationRoutes" class="app-ctrl">
7
- <div id="tool-bar">
8
- <router-link
9
- id="btn_back_summary"
10
- :to="{ name: homeLanding.route }"
11
- class="btn"
12
- :title="homeLanding.title"
13
- >
14
- <template #default>
15
- <svg class="home-icon">
16
- <use href="#home-icon" />
17
- </svg>
18
-
19
- <span class="toolbar_label">{{ homeLanding.title }}</span>
20
- </template>
21
- </router-link>
22
- <div class="ctn-w">
23
- <app-base-button
24
- id="toc"
25
- :title="$t('button.toc')"
26
- :aria-label="$t('button.toc')"
27
- @click="openToc()"
28
- >
29
- <svg class="toc-icon">
30
- <use href="#toc-icon" />
31
- </svg>
32
- <span class="toolbar_label">{{ $t('button.toc') }}</span>
33
- </app-base-button>
34
- <app-comp-table-of-content />
35
- </div>
36
- <div class="ctn-w">
37
- <app-base-button
38
- id="btn_infos"
39
- :title="$t('button.info')"
40
- :aria-label="$t('button.info')"
41
- :disabled="noInfo"
42
- :class="{ md_disabled: noInfo }"
43
- @click="openCreditNote()"
44
- >
45
- <svg class="info-icon">
46
- <use href="#info-icon" />
47
- </svg>
48
- <span class="toolbar_label">{{ $t('button.info') }}</span>
49
- </app-base-button>
50
- <app-comp-note-credit />
51
- </div>
52
- </div>
53
-
54
- <!-- Navigation principale -->
55
-
56
- <SafeTeleport to="#primary_nav_wrapper">
57
- <template v-if="!isMenu">
58
- <app-base-button
59
- id="btn_previous_page"
60
- ref="btn_previous_page"
61
- class="nav_main_btn primary_nav_btn"
62
- :title="$t('button.go_to_previous_page')"
63
- :aria-label="$t('button.go_to_previous_page')"
64
- :is-active="!backAllowed"
65
- @click="goPrevious"
66
- >
67
- <svg>
68
- <use href="#fleche-gauche-icon" />
69
- </svg>
70
- </app-base-button>
71
- <div class="pagination">
72
- <span aria-hidden="true">
73
- Page {{ pagination.counter }} / {{ pagination.total }}
74
- </span>
75
- <span class="sr-only">
76
- Page {{ pagination.counter }} {{ pagination.$W }}
77
- {{ pagination.total }}
78
- </span>
79
- </div>
80
- <app-base-button
81
- id="btn_next_page"
82
- ref="btn_next_page"
83
- class="nav_main_btn primary_nav_btn"
84
- :aria-label="$t('button.go_to_next_page')"
85
- :title="$t('button.go_to_next_page')"
86
- :is-active="!nextAllowed"
87
- @click="goNext"
88
- >
89
- <svg>
90
- <use href="#fleche-droite-icon"></use>
91
- </svg>
92
- </app-base-button>
93
- </template>
94
- </SafeTeleport>
95
- <!---------------------------- TEST ------------------------->
96
- <!---------------------------- END_ACTIVITY_POPUP SECTION ------------------------->
97
-
98
- <SafeTeleport
99
- v-if="endActivityPopup && getEndPopUp"
100
- defer
101
- to="#end-activity"
102
- >
103
- <div class="popup-bottom-buttons">
104
- <app-base-button
105
- :aria-label="endActivityPopup.backLabel"
106
- :title="endActivityPopup.backLabel"
107
- class="popup_primary_nav_btn btn btn-reserve"
108
- @click="navigateTo(endActivityPopup.backRoute)"
109
- >
110
- {{ endActivityPopup.backLabel }}
111
- </app-base-button>
112
-
113
- <app-base-button
114
- :aria-label="endActivityPopup.nextLabel"
115
- :title="endActivityPopup.nextLabel"
116
- class="popup_primary_nav_btn btn btn-main"
117
- @click="navigateTo(endActivityPopup.nextRoute)"
118
- >
119
- {{ endActivityPopup.nextLabel }}
120
- </app-base-button>
121
- </div>
122
- </SafeTeleport>
123
- <!---------------------------- END END_ACTIVITY_POPUP SECTION ------------------------->
124
- </div>
125
- </template>
126
-
127
- <script>
128
- import { mapState, mapActions } from 'pinia'
129
- import { useAppStore } from '../module/stores/appStore'
130
- export default {
131
- props: {
132
- autoNavigate: { type: Boolean, default: false }
133
- },
134
-
135
- data() {
136
- return {
137
- hasMedia: false,
138
- mediaType: null,
139
- mediaPlaybackEnd: false,
140
- navigation: {},
141
- currentPath: null,
142
- navigationRoutes: null,
143
- currentActivity: null,
144
- currentPageId: null,
145
- anchors: [],
146
- pageInfo: null,
147
- nextActivity: null,
148
- nextActivityRoute: null
149
- }
150
- },
151
- computed: {
152
- ...mapState(useAppStore, [
153
- 'getCurrentPage',
154
- 'getModuleInfo',
155
- 'getIntroStatus',
156
- 'getUserInteraction',
157
- 'getAnchorsForActivity',
158
- 'getAppConfigs',
159
- 'getBifChoice',
160
- 'getMenuSettings',
161
- 'getWidgetOpen',
162
- 'getDataNoteCredit',
163
- 'getPopStatus',
164
- 'getEndPopUp'
165
- ]),
166
- noInfo() {
167
- if (this.getDataNoteCredit.note || this.getDataNoteCredit.credit) {
168
- return false
169
- } else return true
170
- },
171
-
172
- /// MUST BE MODIFY BECAUSE MENU IS VIEW NOW
173
- pagination() {
174
- let p = { counter: null, total: null }
175
-
176
- p['$W'] = this.$i18n.locale === 'fr' ? 'de' : 'of'
177
-
178
- p.counter =
179
- this.navigationRoutes.cat.findIndex(
180
- (route) => route._ref === this.$route.meta.id
181
- ) + 1
182
-
183
- p.total = this.navigationRoutes.cat.length
184
-
185
- return p
186
- },
187
-
188
- backAllowed() {
189
- let isAllowed = true // by default
190
- const { all_routes } = this.$helper.getRoutesFromVueRouter() // get all route of all activities
191
- const theFirstRoute = all_routes[0].children[0] // get first route of 1st activity
192
-
193
- //Prevent back to previous when 1st page of the 1st group of activities
194
- if (theFirstRoute.name === this.$route.name) isAllowed = false
195
-
196
- return isAllowed
197
- },
198
- nextAllowed() {
199
- let isAllowed
200
-
201
- //const { auto_next_activity } = this.getAppConfigs
202
- const { all_routes } = this.$helper.getRoutesFromVueRouter() // get all route of all activities
203
- let lastRoute = null
204
-
205
- if (!this.autoNavigate) {
206
- lastRoute = this.navigationRoutes.cat.toReversed()[0] //get last route in courant route catalogue
207
- //block navigation at the end of an activity (last route of current activity)
208
- isAllowed = lastRoute._namedRoute === this.$route.name ? false : true
209
- } else {
210
- lastRoute = all_routes.toReversed()[0].children.toReversed()[0] // get the last route of last activity
211
-
212
- //block navigation when current route is last route and there is no next lesson
213
- isAllowed =
214
- lastRoute.name === this.$route.name && !this.nextLesson ? false : true
215
- }
216
-
217
- return isAllowed
218
- },
219
-
220
- isMenu() {
221
- return this.$route.name === 'menu'
222
- },
223
-
224
- nextLesson() {
225
- const { linkedResource } = this.getAppConfigs
226
- if (linkedResource) return linkedResource
227
- else return null
228
- },
229
-
230
- previousLabel() {
231
- let label =
232
- this.$route.meta.id !== 'P01' ? this.$t('button.previous') : ''
233
-
234
- return label
235
- },
236
-
237
- nextLabel() {
238
- const lastRoute = this.navigationRoutes.cat.toReversed()[0] // get the last route by reversing the order of the navigation catalog
239
- let label =
240
- this.$route.meta.id !== lastRoute._ref
241
- ? this.$t('button.go_to_next_page')
242
- : this.$t('button.carousel_next')
243
- return label
244
- },
245
-
246
- homeLanding() {
247
- let { no_menu: noMenu } = this.getAppConfigs
248
- const activityRoutes = this.$helper.getRoutesFromVueRouter()
249
- //Get the 1st route from the list of routes
250
- const bckRoute = activityRoutes.meta.children[0]
251
- return noMenu
252
- ? {
253
- title: this.$t('button.go_to_first_page'),
254
- route: bckRoute._namedRoute
255
- }
256
- : { title: this.$t('button.menu'), route: 'menu' }
257
- },
258
-
259
- endActivityPopup() {
260
- let content = null
261
-
262
- let { no_menu: noMenu } = this.getAppConfigs
263
-
264
- if (!this.nextActivity) return
265
-
266
- switch (true) {
267
- // Next is an activity and there is no menu
268
- case this.nextActivity.type_of_next === 'ACTIVITY' && noMenu: {
269
- const activityRoutes = this.$helper.getRoutesFromVueRouter(
270
- this.$route.meta.activity_ref
271
- )
272
- //Get the 1st route from the list of routes of this current activity
273
- const bckRoute = activityRoutes.meta.children[0]
274
- content = {
275
- nextLabel: this.$t('button.next_activity'),
276
- backLabel: this.$t('button.go_to_first_page'),
277
- nextRoute: this.nextActivity._namedRoute,
278
- backRoute: bckRoute._namedRoute,
279
- nextEnabled: true,
280
- backEnabled: true
281
- }
282
- break
283
- }
284
- // Next is an activity and there is a menu
285
- case this.nextActivity.type_of_next === 'ACTIVITY' && !noMenu:
286
- content = {
287
- nextLabel: this.$t('button.next_activity'),
288
- backLabel: this.$t('button.go_to_menu'),
289
- nextRoute: this.nextActivity._namedRoute,
290
- backRoute: 'menu',
291
- nextEnabled: true,
292
- backEnabled: true
293
- }
294
- break
295
- // Next is a lesson and there is no menu
296
- case this.nextActivity.type_of_next === 'LESSON' && noMenu: {
297
- const activityRoutes = this.$helper.getRoutesFromVueRouter()
298
- //Get the 1st route from the list of routes
299
- const bckRoute = activityRoutes.meta.children[0]
300
- content = {
301
- nextLabel: this.$t('button.go_to_lesson'),
302
- backLabel: this.$t('button.go_to_first_page'),
303
- nextRoute: this.nextLesson,
304
- backRoute: bckRoute._namedRoute,
305
- nextEnabled: true,
306
- //backEnabled: (() => (isSingleActivity ? false : true))() //show back button of the endActivity popup when it is not a single activity.
307
- backEnabled: true //show back button of the endActivity popup when it is not a single activity.
308
- }
309
- break
310
- }
311
- // Next is a lesson and there is a menu
312
- case this.nextActivity.type_of_next === 'LESSON' && !noMenu:
313
- content = {
314
- nextLabel: this.$t('button.go_to_lesson'),
315
- backLabel: this.$t('button.go_to_menu'),
316
- nextRoute: this.nextLesson,
317
- backRoute: 'menu',
318
- nextEnabled: true,
319
- backEnabled: true
320
- }
321
- break
322
- }
323
-
324
- return content
325
- }
326
- },
327
- watch: {
328
- $route: {
329
- handler() {
330
- this.setNavigation() // reset the navigation context when route change
331
-
332
- setTimeout(() => {
333
- this.initPage()
334
- }, 200)
335
- },
336
- deep: true
337
- }
338
- },
339
- beforeUnmount() {
340
- this.$bus.$off('mediaPlaybackEnded', this.onMediaEnded)
341
- this.$bus.$off('quizNoAnswer', this.showPopupNoAnswer)
342
- },
343
- mounted() {
344
- this.$bus.$on('mediaPlaybackEnded', this.onMediaEnded)
345
- this.$bus.$on('quizNoAnswer', this.showPopupNoAnswer)
346
-
347
- this.setNavigation()
348
- setTimeout(() => {
349
- this.initPage()
350
- }, 200)
351
- },
352
- methods: {
353
- ...mapActions(useAppStore, ['updateNextActivity']),
354
- initPage() {
355
- this.setNextActivity()
356
- this.setPreviousActivity()
357
- },
358
-
359
- getActivity() {
360
- const { all_routes } = this.$helper.getRoutesFromVueRouter()
361
- const activity = all_routes.find(
362
- (route) =>
363
- route.meta.id === this.$router.currentRoute.value.meta.activity_ref
364
- )
365
- return activity
366
- },
367
- onMediaEnded() {
368
- this.mediaPlaybackEnd = true
369
- },
370
- navigateTo(path) {
371
- if (path.constructor === Object)
372
- return this.$bus.$emit(
373
- 'launch-xapi-resource',
374
- this.$helper.getNextLessonEnv(this.nextLesson)
375
- )
376
-
377
- if (!path) return this.$bus.$emit('close-popup')
378
-
379
- this.$router.push({
380
- name: path
381
- })
382
- },
383
- /** *****************Nav Methods*********************** */
384
- // TO TEST!!!!
385
- getNextPageBifurcationNochoice(activity, page) {
386
- let menu = this.getMenuSettings
387
- let bif
388
- //return if the page you are on is a declare as branching in menuSetting
389
- menu[activity].anchors.forEach((value) => {
390
- if (value.pageRef == page && value.branching) {
391
- bif = true
392
- }
393
- })
394
-
395
- return bif
396
- },
397
- // TO TEST!!!!
398
- getFirtsPageBif(activity, page) {
399
- let data
400
-
401
- // play string to make path sentence
402
- if (page.indexOf('0') == 1) {
403
- data = `page_${page.substring(page.indexOf('P') + 2)}`
404
- } else {
405
- data = `page_${page.substring(page.indexOf('P') + 1)}`
406
- }
407
- let menu = this.getMenuSettings
408
- let back = false
409
-
410
- //return if the page you are on is a declare as branching in menuSetting
411
- menu[activity].anchors.forEach((value) => {
412
- if (value.branching && value.branching.end == data) {
413
- back = true
414
- }
415
- })
416
-
417
- return back
418
- },
419
- // TO TEST!!!!
420
- getPageNavBif(activity, page) {
421
- let menu = this.getMenuSettings
422
- let data
423
-
424
- // return data star and end page
425
- menu[activity].anchors.forEach((value) => {
426
- if (value.pageRef == page && value.branching) {
427
- data = value.branching
428
- }
429
- })
430
-
431
- return data
432
- },
433
- // TO TEST!!!!
434
- getPageNavBifStart(activity, page) {
435
- let data
436
-
437
- // play string to make path sentence
438
- if (page.indexOf('0') == 1) {
439
- data = `page_${page.substring(page.indexOf('P') + 2)}`
440
- } else {
441
- data = `page_${page.substring(page.indexOf('P') + 1)}`
442
- }
443
- let menu = this.getMenuSettings
444
- let back
445
-
446
- //return the start page of bif
447
- menu[activity].anchors.forEach((value) => {
448
- if (value.branching && value.branching.end == data) {
449
- back = value.branching.start
450
- }
451
- })
452
-
453
- return back
454
- },
455
- /** @description Method to handle navigation to next content.
456
- * At the end of Navigation:
457
- * - Emit completion status for activity
458
- * - Opens a popup at the end Of activity or Lesson
459
- * @fires send-completion-event - completion of ACTIVITY
460
- * @fires 'close-widget' - close credit widget
461
- *
462
- */
463
- goNext() {
464
- const pageIndex = this.navigationRoutes.cat.findIndex(
465
- (route) => route._ref === this.$route.meta.id
466
- )
467
- const next = pageIndex + 1
468
-
469
- //=====================BIF HANDLING =============================
470
- // check if you did made a choice
471
- if (
472
- !this.getBifChoice &&
473
- this.getNextPageBifurcationNoChoice(
474
- this.$route.meta.activity_ref,
475
- this.$route.meta.id
476
- )
477
- ) {
478
- // create path to make the jump
479
- return this.$router.push({
480
- name: `${this.navigationRoutes.cat[0]._namedRoute}.${
481
- this.getPageNavBif(
482
- this.$route.meta.activity_ref,
483
- this.$route.meta.id
484
- ).end
485
- }`
486
- })
487
- }
488
- //=====================END BIF HANDLING =============================
489
-
490
- if (!this.nextActivity) return
491
- //Handle navigation to page, activity or Lesson
492
- switch (true) {
493
- // Next navigate lead to next page of the current liste
494
- case this.nextActivity.type_of_next === 'PAGE':
495
- this.$router.push({
496
- name: `${this.navigationRoutes.cat[next]._namedRoute}`
497
- })
498
- break
499
- //Next navigation will lead next activity
500
- case this.nextActivity.type_of_next === 'ACTIVITY': {
501
- const endPopup = {
502
- type: 'popup-endActivity',
503
- value: {
504
- title: `${this.$t('popup.text_what_to_do')}`
505
- }
506
- }
507
-
508
- this.openPopup(endPopup)
509
-
510
- this.nextActivityRoute = this.nextActivity._namedRoute
511
-
512
- if (this.getModuleInfo.packageType !== 'xapi') return
513
-
514
- this.$bus.$emit('send-completion-event', 'ACTIVITY')
515
- break
516
- }
517
- //Next navigation will lead next activity
518
- case this.nextActivity.type_of_next === 'LESSON': {
519
- const endPopup = {
520
- type: 'popup-endActivity',
521
- value: {
522
- template: `<p>${this.$t('popup.text_what_to_do')}</p>`,
523
- title: ''
524
- }
525
- }
526
-
527
- this.openPopup(endPopup)
528
-
529
- this.nextActivityRoute = this.nextLesson
530
-
531
- if (this.getModuleInfo.packageType !== 'xapi') return
532
- this.$bus.$emit('send-completion-event', 'ACTIVITY')
533
-
534
- break
535
- }
536
- }
537
- },
538
-
539
- //=============================================================
540
- goPrevious() {
541
- // ==================normal navigation ====================
542
-
543
- const pageIndex = this.navigationRoutes.cat.findIndex(
544
- (route) => route._ref === this.$route.meta.id
545
- )
546
- const previous = pageIndex - 1
547
-
548
- // validate if ther a branching and did you make a choice
549
- if (
550
- !this.getBifChoice &&
551
- this.getFirtsPageBif(this.$route.meta.activity_ref, this.$route.meta.id)
552
- ) {
553
- // create path to make the jump
554
- if (
555
- this.getPageNavBifStart(
556
- this.$route.meta.activity_ref,
557
- this.$route.meta.id
558
- ) == 'page_1'
559
- ) {
560
- this.$router.push({
561
- name: `${this.navigationRoutes.cat[0]._namedRoute}`
562
- })
563
- } else {
564
- this.$router.push({
565
- name: `${this.navigationRoutes.cat[0]._namedRoute}.${this.getPageNavBifStart(
566
- this.$route.meta.activity_ref,
567
- this.$route.meta.id
568
- )}`
569
- })
570
- }
571
-
572
- // back to previous page of module (module.page_xx)
573
- } else if (pageIndex > 0 && this.navigationRoutes.cat[previous]._path) {
574
- this.$router.push({
575
- name: `${this.navigationRoutes.cat[previous]._namedRoute}`
576
- })
577
- // this is the last page
578
- } else {
579
- // if allow navigation to activity active, navigate to previous activity
580
- if (this.autoNavigate && this.previousActivity) {
581
- this.$router.push({ name: this.previousActivity._namedRoute })
582
- } else this.$router.push('/')
583
- }
584
- },
585
- setNavigation() {
586
- // Close the info widget before navigation
587
- if (this.getWidgetOpen) {
588
- this.$bus.$emit('close-widget')
589
- }
590
- // current Route is module
591
- this.currentActivity = this.$router.currentRoute.value.meta.activity_ref
592
- this.currentPageId = this.$router.currentRoute.value.meta.id
593
-
594
- const { all_routes, meta } = this.$helper.getRoutesFromVueRouter()
595
-
596
- if (this.$router.currentRoute.value.name === 'module') {
597
- this.navigationRoutes = {
598
- context: 'module',
599
- cat: meta.children
600
- }
601
- }
602
- // Navigation catalog is the introduction
603
- else if (
604
- this.$router.currentRoute.value.meta.type === 'introduction' ||
605
- this.$router.currentRoute.value.meta.type === 'menu'
606
- ) {
607
- const a = all_routes.find(
608
- (route) =>
609
- route.meta.id ===
610
- this.$router.currentRoute.value.meta.activity_ref.$folderName
611
- )
612
-
613
- const b = all_routes.find(
614
- (route) =>
615
- route.meta.id === this.$router.currentRoute.value.meta.activity_ref
616
- )
617
- if (a) {
618
- let r = []
619
- //if Intro acitive get all the routes for indroduction or just the menu
620
- this.getIntroStatus
621
- ? r.push(...a.meta.children)
622
- : r.push(
623
- a.meta.children.find(
624
- (el) => !el._namedRoute.includes('introduction')
625
- )
626
- )
627
- this.navigationRoutes = {
628
- context: 'introduction',
629
- cat: r
630
- }
631
- } else if (b) {
632
- let r = []
633
- //if Intro acitive get all the routes for indroduction or just the menu
634
- this.getIntroStatus
635
- ? r.push(...b.meta.children)
636
- : r.push(
637
- b.meta.children.find(
638
- (el) => !el._namedRoute.includes('introduction')
639
- )
640
- )
641
- this.navigationRoutes = {
642
- context: 'introduction',
643
- cat: r
644
- }
645
- }
646
- }
647
- // Navigation catalog is pages of Activity ||menu
648
- else {
649
- const a = all_routes.find(
650
- (route) =>
651
- route.meta.id ===
652
- this.$router.currentRoute.value.meta.activity_ref.$folderName
653
- )
654
-
655
- // on refresh $forderName doesn't existe
656
- const b = all_routes.find(
657
- (route) =>
658
- route.meta.id === this.$router.currentRoute.value.meta.activity_ref
659
- )
660
-
661
- if (a) {
662
- this.navigationRoutes = {
663
- context: 'normal',
664
- cat: a.meta.children
665
- }
666
- } else if (b) {
667
- this.navigationRoutes = {
668
- context: 'normal',
669
- cat: b.meta.children
670
- }
671
- }
672
- }
673
- }, // END setNaviagation
674
- /**
675
- *@description: set the next activity to for the Navigation
676
- */
677
- setNextActivity() {
678
- if (this.navigationRoutes) {
679
- // get all the activities routes from router
680
- const { meta } = this.$helper.getRoutesFromVueRouter()
681
- const allRoutes = meta.children
682
-
683
- // get the index of the current route
684
- const current = this.navigationRoutes.cat.findIndex(
685
- (route) => route._ref === this.$route.meta.id
686
- )
687
-
688
- switch (this.navigationRoutes.context) {
689
- case 'introduction': {
690
- //Reached the last page in Intro
691
- if (current === this.navigationRoutes.cat.length - 1) {
692
- //there is a next activity
693
- if (allRoutes[1])
694
- return (this.nextActivity = {
695
- ...allRoutes[1],
696
- type_of_next: 'ACTIVITY'
697
- })
698
-
699
- //There is a next lesson
700
- if (this.nextLesson)
701
- return (this.nextActivity = {
702
- ...this.nextLesson,
703
- type_of_next: 'LESSON'
704
- })
705
-
706
- //There is nothing
707
- this.nextActivity = null
708
- } else {
709
- //Other pages in Intro
710
- this.nextActivity = {
711
- ...this.navigationRoutes.cat[current + 1],
712
- type_of_next: 'PAGE'
713
- }
714
- }
715
- break
716
- }
717
- case 'normal': {
718
- // find the current activity ref in the router catalog
719
-
720
- const currentRouteIndex = allRoutes.findIndex(
721
- (route) => route._ref === this.$route.meta.activity_ref
722
- )
723
-
724
- // this is the Last page in current activity
725
- if (
726
- current === this.navigationRoutes.cat.length - 1 &&
727
- allRoutes[currentRouteIndex + 1]
728
- ) {
729
- this.nextActivity = {
730
- ...allRoutes[currentRouteIndex + 1],
731
- type_of_next: 'ACTIVITY'
732
- } // get the next activity in the router catalog
733
- }
734
- // this is in the same activity return the next page of the activity
735
- else if (this.navigationRoutes.cat[current + 1]) {
736
- this.nextActivity = {
737
- ...this.navigationRoutes.cat[current + 1],
738
- type_of_next: 'PAGE'
739
- }
740
- }
741
- // There is no other activity after the current one
742
- else if (this.nextLesson)
743
- this.nextActivity = { ...this.nextLesson, type_of_next: 'LESSON' }
744
- else this.nextActivity = null
745
-
746
- break
747
- }
748
- }
749
- } else this.nextActivity = null
750
-
751
- this.updateNextActivity(this.nextActivity)
752
- },
753
- /**
754
- * @description: set previous activity for the navigation
755
- */
756
- setPreviousActivity() {
757
- if (this.autoNavigate && this.navigationRoutes) {
758
- // get all the activities routes from router
759
- const { all_routes: allRoutes } = this.$helper.getRoutesFromVueRouter()
760
-
761
- // get the index of the current route
762
- const current = this.navigationRoutes.cat.findIndex(
763
- (route) => route._ref === this.$route.meta.id
764
- )
765
-
766
- switch (this.navigationRoutes.context) {
767
- case 'introduction': {
768
- // This is the first page in the introduction
769
- if (current === 0) {
770
- this.previousActivity = null
771
- } else {
772
- this.previousActivity = {
773
- ...this.navigationRoutes.cat[current - 1],
774
- type_of_previous: 'PAGE'
775
- }
776
- }
777
- break
778
- }
779
- case 'normal': {
780
- // get the index of the current route
781
- const currentRouteIndex = allRoutes.findIndex(
782
- (route) => route.meta.id === this.$route.meta.activity_ref
783
- )
784
- // this is the 1st page of the 1st activity in the router catalogue
785
- // return point is introduction
786
- if (current === 0 && currentRouteIndex === 1) {
787
- const _length = allRoutes[0].children.length
788
- this.previousActivity = {
789
- ...allRoutes[0].meta.children[_length - 1],
790
- type_of_previous: 'ACTIVITY'
791
- } // get last page of the introduction navigation context
792
- }
793
- // This is the 1st page in another activity in the router catalogue
794
- else if (allRoutes[currentRouteIndex - 1] && current === 0) {
795
- const _length = allRoutes[currentRouteIndex - 1].children.length
796
- this.previousActivity = {
797
- ...allRoutes[currentRouteIndex - 1].meta.children[_length - 1],
798
- type_of_previous: 'ACTIVITY'
799
- } // previous is last page of previous activity
800
- }
801
- // This is a simply another page in an activity
802
- else
803
- this.previousActivity = {
804
- ...this.navigationRoutes.cat[current - 1],
805
- type_of_previous: 'PAGE'
806
- }
807
- break
808
- }
809
- }
810
- } else this.previousActivity = null
811
- },
812
- openPopup(popUpData) {
813
- this.$bus.$emit('open-popup', popUpData)
814
- },
815
-
816
- /**
817
- * @description - send information to open toc
818
- */ openToc() {
819
- this.$bus.$emit('toggle-widget', 'toc')
820
- this.$bus.$emit('info-activity', this.getCurrentPage.activityRef)
821
- },
822
- /**
823
- * @description - send information to open credit and note
824
- */
825
- openCreditNote() {
826
- this.$bus.$emit('toggle-widget', 'noteCredit')
827
- }
828
- }
829
- }
830
- </script>
831
-
832
- <style lang="scss">
833
- .app-nav {
834
- transition: width 0.5s;
835
- position: fixed !important;
836
- top: 0;
837
- left: 0;
838
- z-index: 10;
839
- display: flex;
840
- flex-direction: column;
841
- flex-wrap: wrap;
842
- align-content: start !important;
843
- width: 67px;
844
- height: 100vh;
845
-
846
- .app-ctrl {
847
- display: flex;
848
- flex-direction: column;
849
- align-items: start;
850
- width: 100%;
851
-
852
- #tool-bar {
853
- width: 100%;
854
- padding: 32px 0;
855
- display: flex;
856
- flex-direction: column;
857
- align-items: center;
858
-
859
- & .ctn-w > button,
860
- & > a {
861
- //display: block;
862
- width: 80px;
863
- height: 80px;
864
- padding: 0 !important;
865
- margin-bottom: 24px;
866
- display: flex;
867
- flex-direction: column;
868
- align-content: center;
869
- justify-content: center;
870
- position: relative;
871
-
872
- .home-icon {
873
- width: 22px;
874
- height: 24px;
875
- }
876
-
877
- .toc-icon {
878
- width: 41px;
879
- height: 22px;
880
- }
881
-
882
- .info-icon {
883
- width: 24px;
884
- height: 24px;
885
- }
886
-
887
- span {
888
- &.toolbar_label {
889
- display: block;
890
- transition: opacity 0.5s;
891
- visibility: hidden;
892
- opacity: 0;
893
- }
894
- }
895
-
896
- &.active_toc,
897
- &.active_infos {
898
- &::before {
899
- content: '';
900
- display: block;
901
- position: absolute;
902
- height: 100%;
903
- width: 4px;
904
- left: -16px;
905
- }
906
- }
907
- }
908
-
909
- .ctn-w {
910
- position: relative;
911
- }
912
- }
913
- }
914
-
915
- &:focus,
916
- &:hover,
917
- &.show {
918
- width: 115px !important;
919
-
920
- #tool-bar {
921
- & .ctn-w > button,
922
- a {
923
- span.toolbar_label {
924
- visibility: visible;
925
- opacity: 1;
926
- }
927
- }
928
- }
929
- }
930
- }
931
-
932
- #primary_nav_wrapper {
933
- display: flex;
934
- flex-direction: row;
935
- flex-wrap: wrap;
936
- align-content: center;
937
- justify-content: center;
938
- margin: 25px 0;
939
-
940
- #btn_previous_page,
941
- #btn_next_page {
942
- border-radius: 50px;
943
- width: 45px;
944
- height: 45px;
945
- display: flex;
946
- justify-content: center;
947
-
948
- /*svg {
949
- width: 100%;
950
- height: auto;
951
- }*/
952
- }
953
-
954
- .pagination {
955
- display: flex;
956
- align-items: center;
957
- justify-content: center;
958
- }
959
- }
960
- </style>
1
+ <!-- About this Component--
2
+ @ Description: This component is used to display and create the navigation in el+ mode.
3
+ @ What it does: Create the link to back back to the menu. Create link to the all the anchor to the current activity and display the curent anchor and the button next page and previous page. Teleport are use to place the element in the rigth place in the main frame.
4
+ -->
5
+ <template>
6
+ <div v-if="navigationRoutes" class="app-ctrl">
7
+ <div id="tool-bar">
8
+ <router-link
9
+ id="btn_back_summary"
10
+ :to="{ name: homeLanding.route }"
11
+ class="btn"
12
+ :title="homeLanding.title"
13
+ >
14
+ <template #default>
15
+ <svg class="home-icon">
16
+ <use href="#home-icon" />
17
+ </svg>
18
+
19
+ <span class="toolbar_label">{{ $t('button.menu') }}</span>
20
+ </template>
21
+ </router-link>
22
+ <div class="ctn-w">
23
+ <app-base-button
24
+ id="toc"
25
+ :title="$t('button.toc')"
26
+ :aria-label="$t('button.toc')"
27
+ @click="openToc()"
28
+ >
29
+ <svg class="toc-icon">
30
+ <use href="#toc-icon" />
31
+ </svg>
32
+ <span class="toolbar_label">{{ $t('button.toc') }}</span>
33
+ </app-base-button>
34
+ <app-comp-table-of-content />
35
+ </div>
36
+ <div class="ctn-w">
37
+ <app-base-button
38
+ id="btn_infos"
39
+ :title="$t('button.info')"
40
+ :aria-label="$t('button.info')"
41
+ :aria-disabled="!noInfo"
42
+ :class="{ md_disabled: !noInfo }"
43
+ @click="openCreditsNotes()"
44
+ >
45
+ <svg class="info-icon">
46
+ <use href="#info-icon" />
47
+ </svg>
48
+ <span class="toolbar_label">{{ $t('button.info') }}</span>
49
+ </app-base-button>
50
+ <app-comp-note-credit />
51
+ </div>
52
+ </div>
53
+
54
+ <!-- Navigation principale -->
55
+
56
+ <SafeTeleport to="#primary_nav_wrapper">
57
+ <template v-if="!isMenu">
58
+ <app-base-button
59
+ id="btn_previous_page"
60
+ ref="btn_previous_page"
61
+ class="nav_main_btn primary_nav_btn"
62
+ :title="$t('button.go_to_previous_page')"
63
+ :aria-label="$t('button.go_to_previous_page')"
64
+ :is-disabled="!backAllowed"
65
+ @click="goPrevious"
66
+ >
67
+ <svg>
68
+ <use href="#fleche-gauche-icon" />
69
+ </svg>
70
+ </app-base-button>
71
+ <div class="pagination">
72
+ <span aria-hidden="true">
73
+ Page {{ pagination.counter }} / {{ pagination.total }}
74
+ </span>
75
+ <span class="sr-only">
76
+ Page {{ pagination.counter }} {{ pagination.$W }}
77
+ {{ pagination.total }}
78
+ </span>
79
+ </div>
80
+ <app-base-button
81
+ id="btn_next_page"
82
+ ref="btn_next_page"
83
+ class="nav_main_btn primary_nav_btn"
84
+ :aria-label="$t('button.go_to_next_page')"
85
+ :title="$t('button.go_to_next_page')"
86
+ :is-disabled="!nextAllowed"
87
+ @click="goNext"
88
+ >
89
+ <svg>
90
+ <use href="#fleche-droite-icon"></use>
91
+ </svg>
92
+ </app-base-button>
93
+ </template>
94
+ </SafeTeleport>
95
+ <!---------------------------- TEST ------------------------->
96
+ <!---------------------------- END_ACTIVITY_POPUP SECTION ------------------------->
97
+
98
+ <SafeTeleport
99
+ v-if="endActivityPopup && getEndPopUp"
100
+ defer
101
+ to="#end-activity"
102
+ >
103
+ <div class="popup-bottom-buttons">
104
+ <app-base-button
105
+ :aria-label="endActivityPopup.backLabel"
106
+ :title="endActivityPopup.backLabel"
107
+ class="popup_primary_nav_btn btn btn-reserve"
108
+ @click="navigateTo(endActivityPopup.backRoute)"
109
+ >
110
+ {{ endActivityPopup.backLabel }}
111
+ </app-base-button>
112
+
113
+ <app-base-button
114
+ :aria-label="endActivityPopup.nextLabel"
115
+ :title="endActivityPopup.nextLabel"
116
+ class="popup_primary_nav_btn btn btn-main"
117
+ @click="navigateTo(endActivityPopup.nextRoute)"
118
+ >
119
+ {{ endActivityPopup.nextLabel }}
120
+ </app-base-button>
121
+ </div>
122
+ </SafeTeleport>
123
+ <!---------------------------- END END_ACTIVITY_POPUP SECTION ------------------------->
124
+ </div>
125
+ </template>
126
+
127
+ <script>
128
+ import { mapState, mapActions } from 'pinia'
129
+ import { useAppStore } from '../module/stores/appStore'
130
+ export default {
131
+ props: {
132
+ autoNavigate: { type: Boolean, default: false }
133
+ },
134
+
135
+ data() {
136
+ return {
137
+ hasMedia: false,
138
+ mediaType: null,
139
+ mediaPlaybackEnd: false,
140
+ navigation: {},
141
+ currentPath: null,
142
+ navigationRoutes: null,
143
+ currentActivity: null,
144
+ currentPageId: null,
145
+ anchors: [],
146
+ pageInfo: null,
147
+ nextActivity: null,
148
+ nextActivityRoute: null,
149
+ notes: null,
150
+ credits: null
151
+ }
152
+ },
153
+ computed: {
154
+ ...mapState(useAppStore, [
155
+ 'getCurrentPage',
156
+ 'getModuleInfo',
157
+ 'getIntroStatus',
158
+ 'getUserInteraction',
159
+ 'getAnchorsForActivity',
160
+ 'getAppConfigs',
161
+ 'getBifChoice',
162
+ 'getMenuSettings',
163
+ 'getWidgetOpen',
164
+ 'getPopStatus',
165
+ 'getEndPopUp',
166
+ 'getNotes',
167
+ 'getCredits'
168
+ ]),
169
+ noInfo() {
170
+ return this.infoNotes.length > 0 || this.infoCredits.length > 0
171
+ },
172
+ infoNotes() {
173
+ let active = this.$route.meta.activity_ref
174
+ let page = this.$route.meta.id
175
+ let notes = this.getNotes(active, page)
176
+ return notes
177
+ },
178
+ infoCredits() {
179
+ let active = this.$route.meta.activity_ref
180
+ let page = this.$route.meta.id
181
+ let credits = this.getCredits(active, page)
182
+ return credits
183
+ },
184
+ /// MUST BE MODIFY BECAUSE MENU IS VIEW NOW
185
+ pagination() {
186
+ let p = { counter: null, total: null }
187
+
188
+ p['$W'] = this.$i18n.locale === 'fr' ? 'de' : 'of'
189
+
190
+ p.counter =
191
+ this.navigationRoutes.cat.findIndex(
192
+ (route) => route._ref === this.$route.meta.id
193
+ ) + 1
194
+
195
+ p.total = this.navigationRoutes.cat.length
196
+
197
+ return p
198
+ },
199
+
200
+ backAllowed() {
201
+ let isAllowed = true // by default
202
+ const { all_routes } = this.$helper.getRoutesFromVueRouter() // get all route of all activities
203
+ const theFirstRoute = all_routes[0].children[0] // get first route of 1st activity
204
+
205
+ //Prevent back to previous when 1st page of the 1st group of activities
206
+ if (theFirstRoute.name === this.$route.name) isAllowed = false
207
+
208
+ return isAllowed
209
+ },
210
+ nextAllowed() {
211
+ let isAllowed
212
+
213
+ //const { auto_next_activity } = this.getAppConfigs
214
+ const { all_routes } = this.$helper.getRoutesFromVueRouter() // get all route of all activities
215
+ let lastRoute = null
216
+
217
+ if (!this.autoNavigate) {
218
+ lastRoute = this.navigationRoutes.cat.toReversed()[0] //get last route in courant route catalogue
219
+ //block navigation at the end of an activity (last route of current activity)
220
+ isAllowed = lastRoute._namedRoute === this.$route.name ? false : true
221
+ } else {
222
+ lastRoute = all_routes.toReversed()[0].children.toReversed()[0] // get the last route of last activity
223
+
224
+ //block navigation when current route is last route and there is no next lesson
225
+ isAllowed =
226
+ lastRoute.name === this.$route.name && !this.nextLesson ? false : true
227
+ }
228
+
229
+ return isAllowed
230
+ },
231
+
232
+ isMenu() {
233
+ return this.$route.name === 'menu'
234
+ },
235
+
236
+ nextLesson() {
237
+ const { linkedResource } = this.getAppConfigs
238
+ if (linkedResource) return linkedResource
239
+ else return null
240
+ },
241
+
242
+ previousLabel() {
243
+ let label =
244
+ this.$route.meta.id !== 'P01' ? this.$t('button.previous') : ''
245
+
246
+ return label
247
+ },
248
+
249
+ nextLabel() {
250
+ const lastRoute = this.navigationRoutes.cat.toReversed()[0] // get the last route by reversing the order of the navigation catalog
251
+ let label =
252
+ this.$route.meta.id !== lastRoute._ref
253
+ ? this.$t('button.go_to_next_page')
254
+ : this.$t('button.carousel_next')
255
+ return label
256
+ },
257
+
258
+ homeLanding() {
259
+ let { no_menu: noMenu } = this.getAppConfigs
260
+ const activityRoutes = this.$helper.getRoutesFromVueRouter()
261
+ //Get the 1st route from the list of routes
262
+ const bckRoute = activityRoutes.meta.children[0]
263
+ const btnObj = {
264
+ title: noMenu
265
+ ? this.$t('button.go_to_first_page')
266
+ : this.$t('button.go_to_menu'),
267
+ route: noMenu ? bckRoute._namedRoute : 'menu'
268
+ }
269
+ return btnObj
270
+ },
271
+
272
+ endActivityPopup() {
273
+ let content = null
274
+
275
+ let { no_menu: noMenu } = this.getAppConfigs
276
+
277
+ if (!this.nextActivity) return
278
+
279
+ switch (true) {
280
+ // Next is an activity and there is no menu
281
+ case this.nextActivity.type_of_next === 'ACTIVITY' && noMenu: {
282
+ const activityRoutes = this.$helper.getRoutesFromVueRouter(
283
+ this.$route.meta.activity_ref
284
+ )
285
+ //Get the 1st route from the list of routes of this current activity
286
+ const bckRoute = activityRoutes.meta.children[0]
287
+ content = {
288
+ nextLabel: this.$t('button.next_activity'),
289
+ backLabel: this.$t('button.go_to_first_page'),
290
+ nextRoute: this.nextActivity._namedRoute,
291
+ backRoute: bckRoute._namedRoute,
292
+ nextEnabled: true,
293
+ backEnabled: true
294
+ }
295
+ break
296
+ }
297
+ // Next is an activity and there is a menu
298
+ case this.nextActivity.type_of_next === 'ACTIVITY' && !noMenu:
299
+ content = {
300
+ nextLabel: this.$t('button.next_activity'),
301
+ backLabel: this.$t('button.go_to_menu'),
302
+ nextRoute: this.nextActivity._namedRoute,
303
+ backRoute: 'menu',
304
+ nextEnabled: true,
305
+ backEnabled: true
306
+ }
307
+ break
308
+ // Next is a lesson and there is no menu
309
+ case this.nextActivity.type_of_next === 'LESSON' && noMenu: {
310
+ const activityRoutes = this.$helper.getRoutesFromVueRouter()
311
+ //Get the 1st route from the list of routes
312
+ const bckRoute = activityRoutes.meta.children[0]
313
+ content = {
314
+ nextLabel: this.$t('button.go_to_lesson'),
315
+ backLabel: this.$t('button.go_to_first_page'),
316
+ nextRoute: this.nextLesson,
317
+ backRoute: bckRoute._namedRoute,
318
+ nextEnabled: true,
319
+ //backEnabled: (() => (isSingleActivity ? false : true))() //show back button of the endActivity popup when it is not a single activity.
320
+ backEnabled: true //show back button of the endActivity popup when it is not a single activity.
321
+ }
322
+ break
323
+ }
324
+ // Next is a lesson and there is a menu
325
+ case this.nextActivity.type_of_next === 'LESSON' && !noMenu:
326
+ content = {
327
+ nextLabel: this.$t('button.go_to_lesson'),
328
+ backLabel: this.$t('button.go_to_menu'),
329
+ nextRoute: this.nextLesson,
330
+ backRoute: 'menu',
331
+ nextEnabled: true,
332
+ backEnabled: true
333
+ }
334
+ break
335
+ }
336
+
337
+ return content
338
+ }
339
+ },
340
+ watch: {
341
+ $route: {
342
+ handler() {
343
+ this.setNavigation() // reset the navigation context when route change
344
+
345
+ setTimeout(() => {
346
+ this.initPage()
347
+ }, 200)
348
+ },
349
+ deep: true
350
+ }
351
+ },
352
+ beforeUnmount() {
353
+ this.$bus.$off('mediaPlaybackEnded', this.onMediaEnded)
354
+ this.$bus.$off('quizNoAnswer', this.showPopupNoAnswer)
355
+ this.$bus.$off('open-notes', this.openCreditsNotes)
356
+ },
357
+ mounted() {
358
+ this.$bus.$on('mediaPlaybackEnded', this.onMediaEnded)
359
+ this.$bus.$on('quizNoAnswer', this.showPopupNoAnswer)
360
+ this.$bus.$on('open-notes', this.openCreditsNotes)
361
+
362
+ this.setNavigation()
363
+ setTimeout(() => {
364
+ this.initPage()
365
+ }, 200)
366
+ },
367
+ methods: {
368
+ ...mapActions(useAppStore, ['updateNextActivity']),
369
+ initPage() {
370
+ this.setNextActivity()
371
+ this.setPreviousActivity()
372
+ },
373
+
374
+ getActivity() {
375
+ const { all_routes } = this.$helper.getRoutesFromVueRouter()
376
+ const activity = all_routes.find(
377
+ (route) =>
378
+ route.meta.id === this.$router.currentRoute.value.meta.activity_ref
379
+ )
380
+ return activity
381
+ },
382
+ onMediaEnded() {
383
+ this.mediaPlaybackEnd = true
384
+ },
385
+ navigateTo(path) {
386
+ if (path.constructor === Object)
387
+ return this.$bus.$emit(
388
+ 'launch-xapi-resource',
389
+ this.$helper.getNextLessonEnv(this.nextLesson)
390
+ )
391
+
392
+ this.$router.push({
393
+ name: path
394
+ })
395
+ },
396
+ /** *****************Nav Methods*********************** */
397
+ // TO TEST!!!!
398
+ getNextPageBifurcationNochoice(activity, page) {
399
+ let menu = this.getMenuSettings
400
+ let bif
401
+ //return if the page you are on is a declare as branching in menuSetting
402
+ menu[activity].anchors.forEach((value) => {
403
+ if (value.pageRef == page && value.branching) {
404
+ bif = true
405
+ }
406
+ })
407
+
408
+ return bif
409
+ },
410
+ // TO TEST!!!!
411
+ getFirtsPageBif(activity, page) {
412
+ let data
413
+
414
+ // play string to make path sentence
415
+ if (page.indexOf('0') == 1) {
416
+ data = `page_${page.substring(page.indexOf('P') + 2)}`
417
+ } else {
418
+ data = `page_${page.substring(page.indexOf('P') + 1)}`
419
+ }
420
+ let menu = this.getMenuSettings
421
+ let back = false
422
+
423
+ //return if the page you are on is a declare as branching in menuSetting
424
+ menu[activity].anchors.forEach((value) => {
425
+ if (value.branching && value.branching.end == data) {
426
+ back = true
427
+ }
428
+ })
429
+
430
+ return back
431
+ },
432
+ // TO TEST!!!!
433
+ getPageNavBif(activity, page) {
434
+ let menu = this.getMenuSettings
435
+ let data
436
+
437
+ // return data star and end page
438
+ menu[activity].anchors.forEach((value) => {
439
+ if (value.pageRef == page && value.branching) {
440
+ data = value.branching
441
+ }
442
+ })
443
+
444
+ return data
445
+ },
446
+ // TO TEST!!!!
447
+ getPageNavBifStart(activity, page) {
448
+ let data
449
+
450
+ // play string to make path sentence
451
+ if (page.indexOf('0') == 1) {
452
+ data = `page_${page.substring(page.indexOf('P') + 2)}`
453
+ } else {
454
+ data = `page_${page.substring(page.indexOf('P') + 1)}`
455
+ }
456
+ let menu = this.getMenuSettings
457
+ let back
458
+
459
+ //return the start page of bif
460
+ menu[activity].anchors.forEach((value) => {
461
+ if (value.branching && value.branching.end == data) {
462
+ back = value.branching.start
463
+ }
464
+ })
465
+
466
+ return back
467
+ },
468
+ /** @description Method to handle navigation to next content.
469
+ * At the end of Navigation:
470
+ * - Emit completion status for activity
471
+ * - Opens a popup at the end Of activity or Lesson
472
+ * @fires send-completion-event - completion of ACTIVITY
473
+ * @fires 'close-widget' - close credit widget
474
+ *
475
+ */
476
+ goNext() {
477
+ const pageIndex = this.navigationRoutes.cat.findIndex(
478
+ (route) => route._ref === this.$route.meta.id
479
+ )
480
+ const next = pageIndex + 1
481
+
482
+ //=====================BIF HANDLING =============================
483
+ // check if you did made a choice
484
+ if (
485
+ !this.getBifChoice &&
486
+ this.getNextPageBifurcationNoChoice(
487
+ this.$route.meta.activity_ref,
488
+ this.$route.meta.id
489
+ )
490
+ ) {
491
+ // create path to make the jump
492
+ return this.$router.push({
493
+ name: `${this.navigationRoutes.cat[0]._namedRoute}.${
494
+ this.getPageNavBif(
495
+ this.$route.meta.activity_ref,
496
+ this.$route.meta.id
497
+ ).end
498
+ }`
499
+ })
500
+ }
501
+ //=====================END BIF HANDLING =============================
502
+ if (!this.nextActivity) return
503
+ //Handle navigation to page, activity or Lesson
504
+ switch (true) {
505
+ // Next navigate lead to next page of the current liste
506
+ case this.nextActivity.type_of_next === 'PAGE':
507
+ this.$router.push({
508
+ name: `${this.navigationRoutes.cat[next]._namedRoute}`
509
+ })
510
+ break
511
+ //Next navigation will lead next activity
512
+ case this.nextActivity.type_of_next === 'ACTIVITY': {
513
+ const endPopup = {
514
+ type: 'popup-endActivity',
515
+ value: {
516
+ title: `${this.$t('popup.text_what_to_do')}`
517
+ }
518
+ }
519
+
520
+ this.openPopup(endPopup)
521
+
522
+ this.nextActivityRoute = this.nextActivity._namedRoute
523
+
524
+ if (this.getModuleInfo.packageType !== 'xapi') return
525
+
526
+ this.$bus.$emit('send-completion-event', 'ACTIVITY')
527
+ break
528
+ }
529
+ //Next navigation will lead next activity
530
+ case this.nextActivity.type_of_next === 'LESSON': {
531
+ const endPopup = {
532
+ type: 'popup-endActivity',
533
+ value: {
534
+ template: `<p>${this.$t('popup.text_what_to_do')}</p>`,
535
+ title: ''
536
+ }
537
+ }
538
+
539
+ this.openPopup(endPopup)
540
+
541
+ this.nextActivityRoute = this.nextLesson
542
+
543
+ if (this.getModuleInfo.packageType !== 'xapi') return
544
+ this.$bus.$emit('send-completion-event', 'ACTIVITY')
545
+
546
+ break
547
+ }
548
+ }
549
+ },
550
+
551
+ //=============================================================
552
+ goPrevious() {
553
+ // ==================normal navigation ====================
554
+
555
+ const pageIndex = this.navigationRoutes.cat.findIndex(
556
+ (route) => route._ref === this.$route.meta.id
557
+ )
558
+ const previous = pageIndex - 1
559
+
560
+ // validate if ther a branching and did you make a choice
561
+ if (
562
+ !this.getBifChoice &&
563
+ this.getFirtsPageBif(this.$route.meta.activity_ref, this.$route.meta.id)
564
+ ) {
565
+ // create path to make the jump
566
+ if (
567
+ this.getPageNavBifStart(
568
+ this.$route.meta.activity_ref,
569
+ this.$route.meta.id
570
+ ) == 'page_1'
571
+ ) {
572
+ this.$router.push({
573
+ name: `${this.navigationRoutes.cat[0]._namedRoute}`
574
+ })
575
+ } else {
576
+ this.$router.push({
577
+ name: `${this.navigationRoutes.cat[0]._namedRoute}.${this.getPageNavBifStart(
578
+ this.$route.meta.activity_ref,
579
+ this.$route.meta.id
580
+ )}`
581
+ })
582
+ }
583
+
584
+ // back to previous page of module (module.page_xx)
585
+ } else if (pageIndex > 0 && this.navigationRoutes.cat[previous]._path) {
586
+ this.$router.push({
587
+ name: `${this.navigationRoutes.cat[previous]._namedRoute}`
588
+ })
589
+ // this is the last page
590
+ } else {
591
+ // if allow navigation to activity active, navigate to previous activity
592
+ if (this.autoNavigate && this.previousActivity) {
593
+ this.$router.push({ name: this.previousActivity._namedRoute })
594
+ } else this.$router.push('/')
595
+ }
596
+ },
597
+ setNavigation() {
598
+ // current Route is module
599
+ this.currentActivity = this.$router.currentRoute.value.meta.activity_ref
600
+ this.currentPageId = this.$router.currentRoute.value.meta.id
601
+
602
+ const { all_routes, meta } = this.$helper.getRoutesFromVueRouter()
603
+
604
+ if (this.$router.currentRoute.value.name === 'module') {
605
+ this.navigationRoutes = {
606
+ context: 'module',
607
+ cat: meta.children
608
+ }
609
+ }
610
+ // Navigation catalog is the introduction
611
+ else if (
612
+ this.$router.currentRoute.value.meta.type === 'introduction' ||
613
+ this.$router.currentRoute.value.meta.type === 'menu'
614
+ ) {
615
+ const a = all_routes.find(
616
+ (route) =>
617
+ route.meta.id ===
618
+ this.$router.currentRoute.value.meta.activity_ref.$folderName
619
+ )
620
+
621
+ const b = all_routes.find(
622
+ (route) =>
623
+ route.meta.id === this.$router.currentRoute.value.meta.activity_ref
624
+ )
625
+ if (a) {
626
+ let r = []
627
+ //if Intro acitive get all the routes for indroduction or just the menu
628
+ this.getIntroStatus
629
+ ? r.push(...a.meta.children)
630
+ : r.push(
631
+ a.meta.children.find(
632
+ (el) => !el._namedRoute.includes('introduction')
633
+ )
634
+ )
635
+ this.navigationRoutes = {
636
+ context: 'introduction',
637
+ cat: r
638
+ }
639
+ } else if (b) {
640
+ let r = []
641
+ //if Intro acitive get all the routes for indroduction or just the menu
642
+ this.getIntroStatus
643
+ ? r.push(...b.meta.children)
644
+ : r.push(
645
+ b.meta.children.find(
646
+ (el) => !el._namedRoute.includes('introduction')
647
+ )
648
+ )
649
+ this.navigationRoutes = {
650
+ context: 'introduction',
651
+ cat: r
652
+ }
653
+ }
654
+ }
655
+ // Navigation catalog is pages of Activity ||menu
656
+ else {
657
+ const a = all_routes.find(
658
+ (route) =>
659
+ route.meta.id ===
660
+ this.$router.currentRoute.value.meta.activity_ref.$folderName
661
+ )
662
+
663
+ // on refresh $forderName doesn't existe
664
+ const b = all_routes.find(
665
+ (route) =>
666
+ route.meta.id === this.$router.currentRoute.value.meta.activity_ref
667
+ )
668
+
669
+ if (a) {
670
+ this.navigationRoutes = {
671
+ context: 'normal',
672
+ cat: a.meta.children
673
+ }
674
+ } else if (b) {
675
+ this.navigationRoutes = {
676
+ context: 'normal',
677
+ cat: b.meta.children
678
+ }
679
+ }
680
+ }
681
+ }, // END setNaviagation
682
+ /**
683
+ *@description: set the next activity to for the Navigation
684
+ */
685
+ setNextActivity() {
686
+ if (this.navigationRoutes) {
687
+ // get all the activities routes from router
688
+ const { meta } = this.$helper.getRoutesFromVueRouter()
689
+ const allRoutes = meta.children
690
+
691
+ // get the index of the current route
692
+ const current = this.navigationRoutes.cat.findIndex(
693
+ (route) => route._ref === this.$route.meta.id
694
+ )
695
+
696
+ switch (this.navigationRoutes.context) {
697
+ case 'introduction': {
698
+ //Reached the last page in Intro
699
+ if (current === this.navigationRoutes.cat.length - 1) {
700
+ //there is a next activity
701
+ if (allRoutes[1])
702
+ return (this.nextActivity = {
703
+ ...allRoutes[1],
704
+ type_of_next: 'ACTIVITY'
705
+ })
706
+
707
+ //There is a next lesson
708
+ if (this.nextLesson)
709
+ return (this.nextActivity = {
710
+ ...this.nextLesson,
711
+ type_of_next: 'LESSON'
712
+ })
713
+
714
+ //There is nothing
715
+ this.nextActivity = null
716
+ } else {
717
+ //Other pages in Intro
718
+ this.nextActivity = {
719
+ ...this.navigationRoutes.cat[current + 1],
720
+ type_of_next: 'PAGE'
721
+ }
722
+ }
723
+ break
724
+ }
725
+ case 'normal': {
726
+ // find the current activity ref in the router catalog
727
+
728
+ const currentRouteIndex = allRoutes.findIndex(
729
+ (route) => route._ref === this.$route.meta.activity_ref
730
+ )
731
+
732
+ // this is the Last page in current activity
733
+ if (
734
+ current === this.navigationRoutes.cat.length - 1 &&
735
+ allRoutes[currentRouteIndex + 1]
736
+ ) {
737
+ this.nextActivity = {
738
+ ...allRoutes[currentRouteIndex + 1],
739
+ type_of_next: 'ACTIVITY'
740
+ } // get the next activity in the router catalog
741
+ }
742
+ // this is in the same activity return the next page of the activity
743
+ else if (this.navigationRoutes.cat[current + 1]) {
744
+ this.nextActivity = {
745
+ ...this.navigationRoutes.cat[current + 1],
746
+ type_of_next: 'PAGE'
747
+ }
748
+ }
749
+ // There is no other activity after the current one
750
+ else if (this.nextLesson)
751
+ this.nextActivity = { ...this.nextLesson, type_of_next: 'LESSON' }
752
+ else this.nextActivity = null
753
+
754
+ break
755
+ }
756
+ }
757
+ } else this.nextActivity = null
758
+
759
+ this.updateNextActivity(this.nextActivity)
760
+ },
761
+ /**
762
+ * @description: set previous activity for the navigation
763
+ */
764
+ setPreviousActivity() {
765
+ if (this.autoNavigate && this.navigationRoutes) {
766
+ // get all the activities routes from router
767
+ const { all_routes: allRoutes } = this.$helper.getRoutesFromVueRouter()
768
+
769
+ // get the index of the current route
770
+ const current = this.navigationRoutes.cat.findIndex(
771
+ (route) => route._ref === this.$route.meta.id
772
+ )
773
+
774
+ switch (this.navigationRoutes.context) {
775
+ case 'introduction': {
776
+ // This is the first page in the introduction
777
+ if (current === 0) {
778
+ this.previousActivity = null
779
+ } else {
780
+ this.previousActivity = {
781
+ ...this.navigationRoutes.cat[current - 1],
782
+ type_of_previous: 'PAGE'
783
+ }
784
+ }
785
+ break
786
+ }
787
+ case 'normal': {
788
+ // get the index of the current route
789
+ const currentRouteIndex = allRoutes.findIndex(
790
+ (route) => route.meta.id === this.$route.meta.activity_ref
791
+ )
792
+ // this is the 1st page of the 1st activity in the router catalogue
793
+ // return point is introduction
794
+ if (current === 0 && currentRouteIndex === 1) {
795
+ const _length = allRoutes[0].children.length
796
+ this.previousActivity = {
797
+ ...allRoutes[0].meta.children[_length - 1],
798
+ type_of_previous: 'ACTIVITY'
799
+ } // get last page of the introduction navigation context
800
+ }
801
+ // This is the 1st page in another activity in the router catalogue
802
+ else if (allRoutes[currentRouteIndex - 1] && current === 0) {
803
+ const _length = allRoutes[currentRouteIndex - 1].children.length
804
+ this.previousActivity = {
805
+ ...allRoutes[currentRouteIndex - 1].meta.children[_length - 1],
806
+ type_of_previous: 'ACTIVITY'
807
+ } // previous is last page of previous activity
808
+ }
809
+ // This is a simply another page in an activity
810
+ else
811
+ this.previousActivity = {
812
+ ...this.navigationRoutes.cat[current - 1],
813
+ type_of_previous: 'PAGE'
814
+ }
815
+ break
816
+ }
817
+ }
818
+ } else this.previousActivity = null
819
+ },
820
+ openPopup(popUpData) {
821
+ this.$bus.$emit('open-popup', popUpData)
822
+ },
823
+
824
+ /**
825
+ * @description - send information to open toc
826
+ */ openToc() {
827
+ this.$bus.$emit('toggle-widget', { type: 'toc', content: null })
828
+ this.$bus.$emit('info-activity', this.getCurrentPage.activityRef)
829
+ },
830
+ /**
831
+ * @description - send information to open credit and note
832
+ */
833
+ openCreditsNotes() {
834
+ if (!this.noInfo) return
835
+ this.$bus.$emit('toggle-widget', {
836
+ type: 'noteCredit',
837
+ content: { notes: this.infoNotes, credits: this.infoCredits }
838
+ })
839
+ }
840
+ }
841
+ }
842
+ </script>
843
+
844
+ <style lang="scss">
845
+ .app-nav {
846
+ transition: width 0.5s;
847
+ position: fixed !important;
848
+ top: 0;
849
+ left: 0;
850
+ z-index: 10;
851
+ display: flex;
852
+ flex-direction: column;
853
+ flex-wrap: wrap;
854
+ align-content: start !important;
855
+ width: 67px;
856
+ height: 100vh;
857
+
858
+ .app-ctrl {
859
+ display: flex;
860
+ flex-direction: column;
861
+ align-items: start;
862
+ width: 100%;
863
+
864
+ #tool-bar {
865
+ width: 100%;
866
+ padding: 32px 0;
867
+ display: flex;
868
+ flex-direction: column;
869
+ align-items: center;
870
+
871
+ & .ctn-w > button,
872
+ & > a {
873
+ //display: block;
874
+ width: 80px;
875
+ height: 80px;
876
+ padding: 0 !important;
877
+ margin-bottom: 24px;
878
+ display: flex;
879
+ flex-direction: column;
880
+ align-content: center;
881
+ justify-content: center;
882
+ position: relative;
883
+
884
+ .home-icon {
885
+ width: 22px;
886
+ height: 24px;
887
+ }
888
+
889
+ .toc-icon {
890
+ width: 41px;
891
+ height: 22px;
892
+ }
893
+
894
+ .info-icon {
895
+ width: 24px;
896
+ height: 24px;
897
+ }
898
+
899
+ span {
900
+ &.toolbar_label {
901
+ display: block;
902
+ transition: opacity 0.5s;
903
+ visibility: hidden;
904
+ opacity: 0;
905
+ }
906
+ }
907
+
908
+ &.active_toc,
909
+ &.active_infos {
910
+ &::before {
911
+ content: '';
912
+ display: block;
913
+ position: absolute;
914
+ height: 100%;
915
+ width: 4px;
916
+ left: -16px;
917
+ }
918
+ }
919
+ }
920
+
921
+ .ctn-w {
922
+ position: relative;
923
+ }
924
+ }
925
+ }
926
+
927
+ &:focus,
928
+ &:hover,
929
+ &.show {
930
+ width: 115px !important;
931
+
932
+ #tool-bar {
933
+ & .ctn-w > button,
934
+ a {
935
+ span.toolbar_label {
936
+ visibility: visible;
937
+ opacity: 1;
938
+ }
939
+ }
940
+ }
941
+ }
942
+ }
943
+
944
+ #primary_nav_wrapper {
945
+ display: flex;
946
+ flex-direction: row;
947
+ flex-wrap: wrap;
948
+ align-content: center;
949
+ justify-content: center;
950
+ margin: 25px 0;
951
+
952
+ #btn_previous_page,
953
+ #btn_next_page {
954
+ border-radius: 50px;
955
+ width: 45px;
956
+ height: 45px;
957
+ display: flex;
958
+ justify-content: center;
959
+
960
+ /*svg {
961
+ width: 100%;
962
+ height: auto;
963
+ }*/
964
+ }
965
+
966
+ .pagination {
967
+ display: flex;
968
+ align-items: center;
969
+ justify-content: center;
970
+ }
971
+ }
972
+ </style>