fcad-core-dragon 2.0.0-beta.9 → 2.0.0
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.
- package/CHANGELOG +11 -0
- package/package.json +1 -1
- package/src/components/AppBase.vue +130 -3
- package/src/components/AppBaseModule.vue +15 -18
- package/src/components/AppCompBranchButtons.vue +5 -5
- package/src/components/AppCompMenuItem.vue +238 -228
- package/src/components/AppCompTableOfContent.vue +2 -0
- package/src/mixins/timerMixin.js +195 -155
- package/src/module/stores/appStore.js +3 -3
- package/src/module/xapi/ADL.js +0 -1
- package/src/module/xapi/wrapper.js +0 -1
- package/src/router/index.js +5 -0
package/CHANGELOG
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
2.0.0(31 mars 2025)
|
|
2
|
+
Menu : ponctuation des titres selon la langue (fr/en)
|
|
3
|
+
|
|
4
|
+
Fenêtre latérale : Le minimum de "branches" est maintenant 1 (était 2)
|
|
5
|
+
|
|
6
|
+
Fenêtre latérale : Bogue quand on la referme avec espace
|
|
7
|
+
|
|
8
|
+
Nouvelle fonctionnalité : Idle detector
|
|
9
|
+
|
|
10
|
+
Fenêtre progression : Bogue avec le path "activité 99", maintenant "conclusion"
|
|
11
|
+
|
|
1
12
|
2.0.0-beta.9(03 mars 2025)
|
|
2
13
|
Correction de bogues (conflit toc/notes avec popup, ouverture simultanée de toc et notes)
|
|
3
14
|
|
package/package.json
CHANGED
|
@@ -62,10 +62,18 @@ import { mapState, mapActions } from 'pinia'
|
|
|
62
62
|
import { useAppStore } from '../module/stores/appStore.js'
|
|
63
63
|
import { timerMixin } from '../mixins/timerMixin'
|
|
64
64
|
import { validateAppContent } from '../shared/validators'
|
|
65
|
-
|
|
65
|
+
import { computed } from 'vue'
|
|
66
66
|
import mobileDetect from 'mobile-detect'
|
|
67
67
|
export default {
|
|
68
68
|
mixins: [timerMixin],
|
|
69
|
+
provide() {
|
|
70
|
+
return {
|
|
71
|
+
elapsedIdleTime: computed(() => this.elapsedIdleTime),
|
|
72
|
+
timerCurrentState: computed(() => this.timerCurrentState),
|
|
73
|
+
lessonDuration: computed(() => this.lessonDuration),
|
|
74
|
+
activityDuration: computed(() => this.activityDuration)
|
|
75
|
+
}
|
|
76
|
+
},
|
|
69
77
|
props: {
|
|
70
78
|
appConfig: {
|
|
71
79
|
type: Object,
|
|
@@ -221,6 +229,20 @@ export default {
|
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
231
|
}
|
|
232
|
+
//===========FOR TESTING TIMER TIMEOUT====//
|
|
233
|
+
// elapsedIdleTime: {
|
|
234
|
+
// handler() {
|
|
235
|
+
// console.log(
|
|
236
|
+
// '⌛:',
|
|
237
|
+
// this.elapsedIdleTime,
|
|
238
|
+
// this.timerCurrentState,
|
|
239
|
+
// this.activityDuration
|
|
240
|
+
// )
|
|
241
|
+
|
|
242
|
+
// //max idle time allowed 300s (5 min)
|
|
243
|
+
// },
|
|
244
|
+
// immediate: true
|
|
245
|
+
// }
|
|
224
246
|
},
|
|
225
247
|
|
|
226
248
|
created() {
|
|
@@ -254,6 +276,9 @@ export default {
|
|
|
254
276
|
this.$bus.$on('fire-exit-event', this.endLesson)
|
|
255
277
|
this.$bus.$on('reset-focus-on', this.resetFocus)
|
|
256
278
|
this.$bus.$on('move-to-target', this.moveTo)
|
|
279
|
+
this.$bus.$on('start-timer', this.startAppTimer)
|
|
280
|
+
this.$bus.$on('start-idle-detector', this.setIdleDetector)
|
|
281
|
+
this.$bus.$on('stop-idle-detector', this.unsetIdleDetector)
|
|
257
282
|
},
|
|
258
283
|
beforeMount() {
|
|
259
284
|
window.addEventListener(
|
|
@@ -278,7 +303,9 @@ export default {
|
|
|
278
303
|
this.$bus.$off('reset-focus-on', this.resetFocus)
|
|
279
304
|
this.$bus.$off('send-xapi-statement', this.sendXapiStatements)
|
|
280
305
|
this.$bus.$off('move-to-target', this.moveTo)
|
|
281
|
-
|
|
306
|
+
this.$bus.$off('start-idle-detector', this.setIdleDetector)
|
|
307
|
+
this.$bus.$off('stop-idle-detector', this.unsetIdleDetector)
|
|
308
|
+
this.$bus.$off('start-timer', this.startAppTimer)
|
|
282
309
|
if (this.getAppConfigs.remote) this.unsubscribeToSetConfig() //stop watching changing in store to save to local storage
|
|
283
310
|
},
|
|
284
311
|
methods: {
|
|
@@ -294,6 +321,31 @@ export default {
|
|
|
294
321
|
'setMobileState',
|
|
295
322
|
'setCurrentBrowser'
|
|
296
323
|
]),
|
|
324
|
+
startAppTimer() {
|
|
325
|
+
if (this.timerCurrentState == 'started') this.stopTimer('activity')
|
|
326
|
+
this.startTimer('activity')
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
setIdleDetector() {
|
|
330
|
+
setTimeout(() => {
|
|
331
|
+
this.startIdleTimer() //start the iddle dectection
|
|
332
|
+
document.addEventListener('mousedown', this.resetIdleTimer, false) //set eventlisteners
|
|
333
|
+
document.addEventListener('mousemove', this.resetIdleTimer, false) //set eventlisteners
|
|
334
|
+
document.addEventListener('keypress', this.resetIdleTimer, false) //set eventlisteners
|
|
335
|
+
document.addEventListener('touchmove', this.resetIdleTimer, false) //set eventlisteners
|
|
336
|
+
}, 1000)
|
|
337
|
+
},
|
|
338
|
+
/*
|
|
339
|
+
* stop the idle timer and remove hidle-dector listeners
|
|
340
|
+
*/
|
|
341
|
+
unsetIdleDetector() {
|
|
342
|
+
this.stopIdleTimer()
|
|
343
|
+
document.removeEventListener('mousedown', this.resetIdleTimer, false)
|
|
344
|
+
document.removeEventListener('mousemove', this.resetIdleTimer, false)
|
|
345
|
+
document.removeEventListener('keypress', this.resetIdleTimer, false)
|
|
346
|
+
document.removeEventListener('touchmove', this.resetIdleTimer, false)
|
|
347
|
+
},
|
|
348
|
+
|
|
297
349
|
setInitialLoadingDone() {
|
|
298
350
|
this.initialLoading = false
|
|
299
351
|
},
|
|
@@ -422,6 +474,80 @@ export default {
|
|
|
422
474
|
|
|
423
475
|
const { routeHistory = [], ...userProgress } = userData
|
|
424
476
|
|
|
477
|
+
const completedState = lessonStatus || {}
|
|
478
|
+
const lessonPosition = savedPoint || ''
|
|
479
|
+
const applicationSettings = preferredSettings || {}
|
|
480
|
+
//====FOR TESTING====//
|
|
481
|
+
// console.log('💾 RECORDS ', {
|
|
482
|
+
// userProgress,
|
|
483
|
+
// routeHistory,
|
|
484
|
+
// lessonPosition,
|
|
485
|
+
// completedState,
|
|
486
|
+
// playbarValues,
|
|
487
|
+
// applicationSettings
|
|
488
|
+
// })
|
|
489
|
+
//Update the App Store data
|
|
490
|
+
this.updateDataFetchFromServer({
|
|
491
|
+
userProgress,
|
|
492
|
+
routeHistory,
|
|
493
|
+
lessonPosition,
|
|
494
|
+
completedState,
|
|
495
|
+
playbarValues,
|
|
496
|
+
applicationSettings
|
|
497
|
+
}).then(() => {
|
|
498
|
+
this.updateTracker('appBase_fetch', 'ready')
|
|
499
|
+
})
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
async fetchDataFromServerOptimized() {
|
|
503
|
+
this.updateTracker('appBase_fetch', 'loading')
|
|
504
|
+
if (this.getModuleInfo.packageType !== 'xapi') return
|
|
505
|
+
if (!this.getConnectionInfo || !this.getConnectionInfo.remote) return
|
|
506
|
+
|
|
507
|
+
const actorMbox = this.getConnectionInfo.actor.mbox.replace('mailto:', '')
|
|
508
|
+
const activityId = this.getConnectionInfo.activity_id
|
|
509
|
+
const _url = new URL(activityId)
|
|
510
|
+
const parentID = `${_url.origin}/${this.getModuleInfo.courseID}` // redefining activity id for statement
|
|
511
|
+
|
|
512
|
+
const lessonProgressParam = { email: actorMbox, activityId }
|
|
513
|
+
const lessonStateParam = {
|
|
514
|
+
email: actorMbox,
|
|
515
|
+
activityId,
|
|
516
|
+
verb: 'completed'
|
|
517
|
+
}
|
|
518
|
+
const lessonPosionParam = {
|
|
519
|
+
email: actorMbox,
|
|
520
|
+
activityId
|
|
521
|
+
}
|
|
522
|
+
const playbarParam = {
|
|
523
|
+
email: actorMbox,
|
|
524
|
+
activityId,
|
|
525
|
+
verb: 'played'
|
|
526
|
+
}
|
|
527
|
+
const preferencesParam = {
|
|
528
|
+
email: actorMbox,
|
|
529
|
+
activityId: parentID,
|
|
530
|
+
verb: 'preferred'
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const fetchParams = [
|
|
534
|
+
lessonProgressParam,
|
|
535
|
+
lessonStateParam,
|
|
536
|
+
lessonPosionParam,
|
|
537
|
+
playbarParam,
|
|
538
|
+
preferencesParam
|
|
539
|
+
]
|
|
540
|
+
|
|
541
|
+
const {
|
|
542
|
+
userData,
|
|
543
|
+
savedPoint,
|
|
544
|
+
preferredSettings,
|
|
545
|
+
playbarValues,
|
|
546
|
+
lessonStatus
|
|
547
|
+
} = await this.$xapi._getBulkData(fetchParams)
|
|
548
|
+
|
|
549
|
+
const { routeHistory = [], ...userProgress } = userData
|
|
550
|
+
|
|
425
551
|
const completedState = lessonStatus || {}
|
|
426
552
|
const lessonPosition = savedPoint || ''
|
|
427
553
|
const applicationSettings = preferredSettings || {}
|
|
@@ -929,7 +1055,8 @@ export default {
|
|
|
929
1055
|
this.setApplicationSettings({}) // resetting store record for settings
|
|
930
1056
|
this.setRouteHistory([]) // resetting store record for all last visited pages
|
|
931
1057
|
|
|
932
|
-
this.$bus.$emit('stop-timer')
|
|
1058
|
+
// this.$bus.$emit('stop-timer')
|
|
1059
|
+
this.stopTimer()
|
|
933
1060
|
if (this.getModuleInfo.packageType !== 'xapi') return
|
|
934
1061
|
|
|
935
1062
|
if (!this.getConnectionInfo || this.getConnectionInfo.remote == false)
|
|
@@ -104,7 +104,6 @@ import { fileAssets } from '../shared/generalfuncs.js'
|
|
|
104
104
|
import { mapState, mapActions } from 'pinia'
|
|
105
105
|
import { useAppStore } from '../module/stores/appStore'
|
|
106
106
|
import BaseModule from './BaseModule.vue'
|
|
107
|
-
import { timerMixin } from '../mixins/timerMixin'
|
|
108
107
|
import AppCompContainer from './AppCompContainer.vue'
|
|
109
108
|
import { defineAsyncComponent } from 'vue'
|
|
110
109
|
//import
|
|
@@ -113,7 +112,7 @@ export default {
|
|
|
113
112
|
BaseModule,
|
|
114
113
|
AppCompContainer
|
|
115
114
|
},
|
|
116
|
-
|
|
115
|
+
inject: ['elapsedIdleTime', 'timerCurrentState', 'lessonDuration'],
|
|
117
116
|
props: {
|
|
118
117
|
moduleConfig: {
|
|
119
118
|
type: Object,
|
|
@@ -135,8 +134,6 @@ export default {
|
|
|
135
134
|
popupIsOpen: false,
|
|
136
135
|
hidePlayBar: false, // Controle visibility of the play bar. set to true to hide play bar
|
|
137
136
|
stmt: null, // holder for xapi statememt,
|
|
138
|
-
moduleTimer: 0, //tracker for overall time spent the lesson
|
|
139
|
-
activityTimer: 0, // tracker for the time spent on activity,
|
|
140
137
|
routeChangeCounter: 0,
|
|
141
138
|
toolTipTarget: '', //for the tool tip,
|
|
142
139
|
onboardingMessages: {}, //for the onboarding @todo replace with default file
|
|
@@ -262,7 +259,6 @@ export default {
|
|
|
262
259
|
|
|
263
260
|
return mainEl
|
|
264
261
|
},
|
|
265
|
-
|
|
266
262
|
dynamicSidebarContent() {
|
|
267
263
|
if (
|
|
268
264
|
!this.transcriptVisible &&
|
|
@@ -367,13 +363,12 @@ export default {
|
|
|
367
363
|
*/
|
|
368
364
|
|
|
369
365
|
const trackedRouteType = ['introduction', 'conclusion', 'normal']
|
|
366
|
+
|
|
370
367
|
if (trackedRouteType.includes(this.$route.meta.type)) {
|
|
371
368
|
if (this.$route.name.includes('.')) return
|
|
372
369
|
|
|
373
370
|
//Start the timer every time that there is a new activity
|
|
374
|
-
|
|
375
|
-
this.startTimer('activity')
|
|
376
|
-
|
|
371
|
+
this.$bus.$emit('start-timer')
|
|
377
372
|
//Send statement when activity as changed
|
|
378
373
|
if (this.activityHasChanged)
|
|
379
374
|
this.sendStartStatement({ id: this.$route.meta.activity_ref })
|
|
@@ -408,13 +403,12 @@ export default {
|
|
|
408
403
|
/**
|
|
409
404
|
* @description Defined in timerMixin Watch The epalsed time for autosaving the user reached position
|
|
410
405
|
*/
|
|
411
|
-
|
|
406
|
+
elapsedIdleTime: {
|
|
412
407
|
handler() {
|
|
413
|
-
if (this.
|
|
408
|
+
if (this.timerCurrentState !== 'started') return
|
|
414
409
|
//send a statement every x time (second)
|
|
415
410
|
if (
|
|
416
|
-
this.
|
|
417
|
-
this.elapsedTime % 500 === 0 &&
|
|
411
|
+
this.elapsedIdleTime % 500 === 0 &&
|
|
418
412
|
this.getModuleInfo.packageType === 'xapi' &&
|
|
419
413
|
this.getConnectionInfo &&
|
|
420
414
|
this.getConnectionInfo.actor &&
|
|
@@ -486,6 +480,7 @@ export default {
|
|
|
486
480
|
this.$bus.$off('show-transcript', this.openTranscript)
|
|
487
481
|
this.$bus.$off('send-completion-event', this.sendCompletionStatus)
|
|
488
482
|
this.$bus.$off('send-starting-event', this.sendStartStatement)
|
|
483
|
+
|
|
489
484
|
//nav mouseleave event
|
|
490
485
|
let nav = document.getElementById('navTool')
|
|
491
486
|
if (nav) {
|
|
@@ -549,7 +544,9 @@ export default {
|
|
|
549
544
|
this.$bus.$on('send-completion-event', this.sendCompletionStatus)
|
|
550
545
|
this.$bus.$on('send-starting-event', this.sendStartStatement)
|
|
551
546
|
|
|
552
|
-
this.initLesson()
|
|
547
|
+
this.initLesson().then(() => {
|
|
548
|
+
this.$bus.$emit('start-idle-detector')
|
|
549
|
+
})
|
|
553
550
|
|
|
554
551
|
if (this.navigationHistory.length != 0) {
|
|
555
552
|
this.routeData = this.navigationHistory
|
|
@@ -664,11 +661,11 @@ export default {
|
|
|
664
661
|
closeSidebar(ctx, wrapper = null) {
|
|
665
662
|
//delay animation
|
|
666
663
|
this.rightSidebarVisible = false //
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
664
|
+
setTimeout(() => {
|
|
665
|
+
const rSidebar = document.querySelector('#right-sidebar') // the sidebar
|
|
666
|
+
rSidebar.setAttribute('style', 'display:none')
|
|
667
|
+
rSidebar.dispatchEvent(this.rightSidebarEvent) //this will allow to run the animation of sidebar closing 1rst
|
|
668
|
+
}, 100)
|
|
672
669
|
this.resetFocus(this.lastInFocus)
|
|
673
670
|
switch (ctx) {
|
|
674
671
|
case 'ctxTranscript':
|
|
@@ -122,9 +122,9 @@ export default {
|
|
|
122
122
|
let errMsg = false
|
|
123
123
|
|
|
124
124
|
//card must have at least 2 elements
|
|
125
|
-
if (dataArray.length <
|
|
125
|
+
if (dataArray.length < 1) {
|
|
126
126
|
isValid = false
|
|
127
|
-
errMsg = `\n card require at least
|
|
127
|
+
errMsg = `\n card require at least 1 element`
|
|
128
128
|
} else {
|
|
129
129
|
for (let el of dataArray) {
|
|
130
130
|
//Each element in card must be of type Object and can not be empty
|
|
@@ -148,10 +148,10 @@ export default {
|
|
|
148
148
|
let isValid = true
|
|
149
149
|
if (import.meta.env.DEV) {
|
|
150
150
|
let errMsg = false
|
|
151
|
-
//card must have at least
|
|
152
|
-
if (dataArray.length <
|
|
151
|
+
//card must have at least 1 elements
|
|
152
|
+
if (dataArray.length < 1) {
|
|
153
153
|
isValid = false
|
|
154
|
-
errMsg = `\n customButton require at least
|
|
154
|
+
errMsg = `\n customButton require at least 1 element`
|
|
155
155
|
} else {
|
|
156
156
|
for (let el of dataArray) {
|
|
157
157
|
//Each element in card must be of type Object and can not be empty
|
|
@@ -1,228 +1,238 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
@ Description: This component is used to display and create the link's to all the activity creation in module.
|
|
3
|
-
@ What it does: Goes trougth all the activity in the router and create a card that open the Table of content (appCompTableOfContent) that display the anchor.Display the title and subtitle enter in menu.json. Must be used with AppCompTableOfContent and AppCompMenu.
|
|
4
|
-
-->
|
|
5
|
-
<template>
|
|
6
|
-
<v-row v-if="activities.length" class="box-msa">
|
|
7
|
-
<v-col
|
|
8
|
-
v-for="activity of activities"
|
|
9
|
-
:key="activity.id"
|
|
10
|
-
cols="6"
|
|
11
|
-
class="menu-section-activity"
|
|
12
|
-
:aria-describedby="activity.id + '-subMenu'"
|
|
13
|
-
>
|
|
14
|
-
<router-link
|
|
15
|
-
:id="activity.id + '-subMenu'"
|
|
16
|
-
:title="activity.title"
|
|
17
|
-
:data-test="`item-menu-${activity.id}`"
|
|
18
|
-
class="btn-menu"
|
|
19
|
-
:to="{
|
|
20
|
-
name: createRoutes(activity.id)
|
|
21
|
-
}"
|
|
22
|
-
@click="startActivity(activity)"
|
|
23
|
-
>
|
|
24
|
-
<div class="menu-card" tag="article">
|
|
25
|
-
<v-row>
|
|
26
|
-
<div v-if="activity.subtitle === undefined" class="title">
|
|
27
|
-
<h4 v-html="activity.title"></h4>
|
|
28
|
-
</div>
|
|
29
|
-
<div v-else class="title">
|
|
30
|
-
<h4
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (key
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
1
|
+
<!--
|
|
2
|
+
@ Description: This component is used to display and create the link's to all the activity creation in module.
|
|
3
|
+
@ What it does: Goes trougth all the activity in the router and create a card that open the Table of content (appCompTableOfContent) that display the anchor.Display the title and subtitle enter in menu.json. Must be used with AppCompTableOfContent and AppCompMenu.
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<v-row v-if="activities.length" class="box-msa">
|
|
7
|
+
<v-col
|
|
8
|
+
v-for="activity of activities"
|
|
9
|
+
:key="activity.id"
|
|
10
|
+
cols="6"
|
|
11
|
+
class="menu-section-activity"
|
|
12
|
+
:aria-describedby="activity.id + '-subMenu'"
|
|
13
|
+
>
|
|
14
|
+
<router-link
|
|
15
|
+
:id="activity.id + '-subMenu'"
|
|
16
|
+
:title="activity.title"
|
|
17
|
+
:data-test="`item-menu-${activity.id}`"
|
|
18
|
+
class="btn-menu"
|
|
19
|
+
:to="{
|
|
20
|
+
name: createRoutes(activity.id)
|
|
21
|
+
}"
|
|
22
|
+
@click="startActivity(activity)"
|
|
23
|
+
>
|
|
24
|
+
<div class="menu-card" tag="article">
|
|
25
|
+
<v-row>
|
|
26
|
+
<div v-if="activity.subtitle === undefined" class="title">
|
|
27
|
+
<h4 v-html="activity.title"></h4>
|
|
28
|
+
</div>
|
|
29
|
+
<div v-else class="title">
|
|
30
|
+
<h4
|
|
31
|
+
v-html="
|
|
32
|
+
`${activity.title}${cardTitleSeparator}${activity.subtitle}`
|
|
33
|
+
"
|
|
34
|
+
></h4>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div class="cnt-time">
|
|
38
|
+
<svg :aria-label="$t('label.timer')">
|
|
39
|
+
<use href="#clock-icon" />
|
|
40
|
+
</svg>
|
|
41
|
+
<p class="time">
|
|
42
|
+
{{ activity.time || '00:00' }}
|
|
43
|
+
</p>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="box-gauge">
|
|
46
|
+
<app-comp-jauge
|
|
47
|
+
:max-value="getActivitySize(activity.id)"
|
|
48
|
+
:value="getPageComplete(activity.id)"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
</v-row>
|
|
52
|
+
</div>
|
|
53
|
+
</router-link>
|
|
54
|
+
</v-col>
|
|
55
|
+
</v-row>
|
|
56
|
+
</template>
|
|
57
|
+
<script>
|
|
58
|
+
// ...
|
|
59
|
+
import { mapState } from 'pinia'
|
|
60
|
+
import { useAppStore } from '../module/stores/appStore'
|
|
61
|
+
import AppCompJauge from './AppCompJauge.vue'
|
|
62
|
+
export default {
|
|
63
|
+
components: {
|
|
64
|
+
AppCompJauge
|
|
65
|
+
},
|
|
66
|
+
computed: {
|
|
67
|
+
...mapState(useAppStore, [
|
|
68
|
+
'getAllActivities',
|
|
69
|
+
'getAllActivitiesState',
|
|
70
|
+
'getAllCompleted',
|
|
71
|
+
'getAppConfigs',
|
|
72
|
+
'getMenuSettings',
|
|
73
|
+
'getModuleInfo'
|
|
74
|
+
]),
|
|
75
|
+
/*
|
|
76
|
+
* Apply different punctuation for colons. No space before a colon in english, one space before a colon in french
|
|
77
|
+
*/
|
|
78
|
+
cardTitleSeparator() {
|
|
79
|
+
return this.getAppConfigs.lang === 'en' ? ': ' : ' : '
|
|
80
|
+
},
|
|
81
|
+
activities() {
|
|
82
|
+
// get the data for list of the page for this module from the store and
|
|
83
|
+
// get the route of the page from the router to build the menu with its routes
|
|
84
|
+
let count = 0
|
|
85
|
+
const collection = []
|
|
86
|
+
|
|
87
|
+
this.getAllActivities().list.forEach((value, key) => {
|
|
88
|
+
let theActivity,
|
|
89
|
+
activityTitle,
|
|
90
|
+
activitySubTitle,
|
|
91
|
+
activityTime = null,
|
|
92
|
+
activityPath = ''
|
|
93
|
+
|
|
94
|
+
if (this.menuInfo[key]) {
|
|
95
|
+
const { title, subTitle, time } = this.menuInfo[key] //get time subTitle time from menu info
|
|
96
|
+
//set the title
|
|
97
|
+
if (title && title != ' ') activityTitle = title
|
|
98
|
+
//set the subtitle
|
|
99
|
+
if (subTitle && subTitle != ' ') activitySubTitle = subTitle
|
|
100
|
+
//set the time
|
|
101
|
+
if (time && time != ' ') activityTime = time
|
|
102
|
+
}
|
|
103
|
+
// This is the Introduction
|
|
104
|
+
if (key === 'A00') {
|
|
105
|
+
activityTitle = activityTitle || this.$t('text.introduction')
|
|
106
|
+
activityPath = 'introduction'
|
|
107
|
+
} else if (key === 'A99') {
|
|
108
|
+
activityTitle = activityTitle || this.$t('text.conclusion')
|
|
109
|
+
activityPath = 'conclusion'
|
|
110
|
+
} else {
|
|
111
|
+
count++
|
|
112
|
+
activityTitle =
|
|
113
|
+
activityTitle || `${this.$t('text.activity')} ${count}`
|
|
114
|
+
activityPath = `activite_${count}`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
theActivity = {
|
|
118
|
+
id: key,
|
|
119
|
+
title: activityTitle,
|
|
120
|
+
subtitle: activitySubTitle,
|
|
121
|
+
time: activityTime,
|
|
122
|
+
path: activityPath,
|
|
123
|
+
data: value
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
collection.push(theActivity) // push the activity in the collection
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return collection
|
|
130
|
+
},
|
|
131
|
+
menuInfo() {
|
|
132
|
+
let menuInfo = this.getMenuSettings
|
|
133
|
+
return menuInfo
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
mounted() {},
|
|
137
|
+
methods: {
|
|
138
|
+
/**
|
|
139
|
+
* @description Gives gives length of completed page for an activity
|
|
140
|
+
* @param {String} idActivity
|
|
141
|
+
* @returns {Number}
|
|
142
|
+
*/
|
|
143
|
+
getPageComplete(idActivity) {
|
|
144
|
+
/// give all the page that are complete to the gauge
|
|
145
|
+
if (idActivity) {
|
|
146
|
+
let completed = []
|
|
147
|
+
if (this.getAllCompleted[idActivity]) {
|
|
148
|
+
completed = this.getAllCompleted[idActivity] //get all completed page for the activity
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return completed.length
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
/**
|
|
155
|
+
* @description give the size of the state of the activity
|
|
156
|
+
* @param {String} activityID
|
|
157
|
+
* @returns {Number} size
|
|
158
|
+
*/
|
|
159
|
+
getActivitySize(activityID) {
|
|
160
|
+
/*
|
|
161
|
+
* Note:
|
|
162
|
+
* Assaging by reference would change the property of the getters when the valued are changed in the new object in. * To prevent this behaviour we will make a deep copy of the getter
|
|
163
|
+
* cf: https://reactgo.com/javascript-clone-object/
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
const allActivitiesState = JSON.parse(
|
|
167
|
+
JSON.stringify(this.getAllActivitiesState)
|
|
168
|
+
)
|
|
169
|
+
let size = allActivitiesState[activityID]
|
|
170
|
+
? allActivitiesState[activityID].size
|
|
171
|
+
: 0
|
|
172
|
+
|
|
173
|
+
return size
|
|
174
|
+
},
|
|
175
|
+
createRoutes(data) {
|
|
176
|
+
let activity
|
|
177
|
+
|
|
178
|
+
if (data.charAt(1) == '0' || data.charAt(1) == 0) {
|
|
179
|
+
activity = data.substr(2)
|
|
180
|
+
} else {
|
|
181
|
+
activity = data.substr(1)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (activity == 0 && activity == '0') return `introduction`
|
|
185
|
+
if (activity == '99') return `conclusion`
|
|
186
|
+
else return `activite_${activity}`
|
|
187
|
+
},
|
|
188
|
+
startActivity(a) {
|
|
189
|
+
//this.$bus.$emit('send-starting-event', a)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
</script>
|
|
194
|
+
<style lang="scss">
|
|
195
|
+
.menu-card {
|
|
196
|
+
width: 100%;
|
|
197
|
+
padding: 24px;
|
|
198
|
+
|
|
199
|
+
.v-row {
|
|
200
|
+
margin: 0;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
|
|
203
|
+
.title {
|
|
204
|
+
flex-grow: 4;
|
|
205
|
+
width: 80%;
|
|
206
|
+
margin-bottom: 30px;
|
|
207
|
+
|
|
208
|
+
h3 {
|
|
209
|
+
text-align: left;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.cnt-time {
|
|
214
|
+
display: flex;
|
|
215
|
+
flex-direction: row;
|
|
216
|
+
align-items: center;
|
|
217
|
+
margin-bottom: 20px;
|
|
218
|
+
|
|
219
|
+
svg {
|
|
220
|
+
width: 18px;
|
|
221
|
+
height: 18px;
|
|
222
|
+
margin-right: 10px;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.box-gauge {
|
|
227
|
+
display: block;
|
|
228
|
+
width: 100%;
|
|
229
|
+
|
|
230
|
+
.box-g {
|
|
231
|
+
display: flex;
|
|
232
|
+
flex-direction: row;
|
|
233
|
+
align-items: center;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
</style>
|
package/src/mixins/timerMixin.js
CHANGED
|
@@ -1,155 +1,195 @@
|
|
|
1
|
-
/*
|
|
2
|
-
@ Description: Mixins to extends general fonctionnalities in a page of an activity. Add time tracker in activities
|
|
3
|
-
@ Note: .
|
|
4
|
-
*/
|
|
5
|
-
export const timerMixin = {
|
|
6
|
-
data() {
|
|
7
|
-
return {
|
|
8
|
-
lessonTimeCounter: 0,
|
|
9
|
-
activityTimeCounter: 0,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
break
|
|
32
|
-
|
|
33
|
-
case 'lesson':
|
|
34
|
-
this.lessonInterval = setInterval(() => {
|
|
35
|
-
this.lessonTimeCounter += 1
|
|
36
|
-
}, 1000)
|
|
37
|
-
break
|
|
38
|
-
|
|
39
|
-
default:
|
|
40
|
-
this.fullInterval = setInterval(() => {
|
|
41
|
-
this.activityTimeCounter += 1
|
|
42
|
-
this.lessonTimeCounter += 1
|
|
43
|
-
}, 1000)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
this.
|
|
59
|
-
} else if (op
|
|
60
|
-
clearInterval(this.lessonInterval)
|
|
61
|
-
this.lessonTimeCounter = 0
|
|
62
|
-
} else {
|
|
63
|
-
clearInterval(this.
|
|
64
|
-
clearInterval(this.
|
|
65
|
-
clearInterval(this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.
|
|
69
|
-
this.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
clearInterval(this.
|
|
82
|
-
} else {
|
|
83
|
-
clearInterval(this.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
1
|
+
/*
|
|
2
|
+
@ Description: Mixins to extends general fonctionnalities in a page of an activity. Add time tracker in activities
|
|
3
|
+
@ Note: .
|
|
4
|
+
*/
|
|
5
|
+
export const timerMixin = {
|
|
6
|
+
data() {
|
|
7
|
+
return {
|
|
8
|
+
lessonTimeCounter: 0,
|
|
9
|
+
activityTimeCounter: 0,
|
|
10
|
+
activityInterval: null,
|
|
11
|
+
lessonInterval: null,
|
|
12
|
+
fullInterval: null,
|
|
13
|
+
timerState: 'stopped',
|
|
14
|
+
idleCounter: 0,
|
|
15
|
+
idleTimeoutID: null,
|
|
16
|
+
idleTimeout: 5 * 60000, //5 mins
|
|
17
|
+
idleTimer: null
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
methods: {
|
|
21
|
+
/* Start the timer */
|
|
22
|
+
startTimer(op) {
|
|
23
|
+
if (op && !['activity', 'lesson'].includes(op))
|
|
24
|
+
throw new Error('this is not a valid option for the timer')
|
|
25
|
+
if (this.timerState == 'stopped') {
|
|
26
|
+
switch (op) {
|
|
27
|
+
case 'activity':
|
|
28
|
+
this.activityInterval = setInterval(() => {
|
|
29
|
+
this.activityTimeCounter += 1
|
|
30
|
+
}, 1000)
|
|
31
|
+
break
|
|
32
|
+
|
|
33
|
+
case 'lesson':
|
|
34
|
+
this.lessonInterval = setInterval(() => {
|
|
35
|
+
this.lessonTimeCounter += 1
|
|
36
|
+
}, 1000)
|
|
37
|
+
break
|
|
38
|
+
|
|
39
|
+
default:
|
|
40
|
+
this.fullInterval = setInterval(() => {
|
|
41
|
+
this.activityTimeCounter += 1
|
|
42
|
+
this.lessonTimeCounter += 1
|
|
43
|
+
}, 1000)
|
|
44
|
+
}
|
|
45
|
+
this.timerState = 'started'
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
/* Stop the timer and reset thimer to zero */
|
|
49
|
+
stopTimer(op) {
|
|
50
|
+
if (op && !['activity', 'lesson'].includes(op))
|
|
51
|
+
throw new Error('this is not a valid parameter')
|
|
52
|
+
|
|
53
|
+
if (this.timerState == 'started') {
|
|
54
|
+
if (op === 'activity' && this.activityTimeCounter > 0) {
|
|
55
|
+
this.lessonTimeCounter =
|
|
56
|
+
this.lessonTimeCounter + this.activityTimeCounter //add to the lesson counter
|
|
57
|
+
clearInterval(this.activityInterval) //clear the interval
|
|
58
|
+
this.activityTimeCounter = 0 // reset the counter
|
|
59
|
+
} else if (op == 'lesson' && this.lessonTimeCounter > 0) {
|
|
60
|
+
clearInterval(this.lessonInterval)
|
|
61
|
+
this.lessonTimeCounter = 0
|
|
62
|
+
} else {
|
|
63
|
+
clearInterval(this.activityInterval) //clear the interval
|
|
64
|
+
clearInterval(this.fullInterval)
|
|
65
|
+
clearInterval(this.lessonInterval)
|
|
66
|
+
clearInterval(this.elapsedCounter)
|
|
67
|
+
|
|
68
|
+
this.activityTimeCounter = 0
|
|
69
|
+
this.lessonTimeCounter = 0
|
|
70
|
+
this.elapsedCounter = 0
|
|
71
|
+
}
|
|
72
|
+
this.timerState = 'stopped'
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
/* Pause the timer but does not reset to zero*/
|
|
76
|
+
pauseTimer(op) {
|
|
77
|
+
if (op && !['activity', 'lesson'].includes(op))
|
|
78
|
+
throw new Error('this is not a valid parameter')
|
|
79
|
+
|
|
80
|
+
if (op === 'activity' && this.activityTimeCounter > 0) {
|
|
81
|
+
clearInterval(this.activityInterval)
|
|
82
|
+
} else if (op === 'lesson') {
|
|
83
|
+
clearInterval(this.lessonInterval)
|
|
84
|
+
} else {
|
|
85
|
+
clearInterval(this.fullInterval)
|
|
86
|
+
clearInterval(this.elapsedCounter)
|
|
87
|
+
}
|
|
88
|
+
this.timerState = 'stopped'
|
|
89
|
+
},
|
|
90
|
+
/*
|
|
91
|
+
* Inialize the idle Timer,
|
|
92
|
+
* create a timer to trick time passed since idle is start
|
|
93
|
+
* create a countdown to pause activity timer
|
|
94
|
+
*/
|
|
95
|
+
startIdleTimer() {
|
|
96
|
+
this.idleTimer = setInterval(() => {
|
|
97
|
+
this.idleCounter += 1
|
|
98
|
+
}, 1000)
|
|
99
|
+
this.idleTimeoutID = setTimeout(
|
|
100
|
+
() => this.pauseTimer('activity'),
|
|
101
|
+
this.idleTimeout
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
/*
|
|
105
|
+
* reset idle timer .
|
|
106
|
+
* retart the activity counter
|
|
107
|
+
*/
|
|
108
|
+
resetIdleTimer() {
|
|
109
|
+
this.stopIdleTimer()
|
|
110
|
+
if (this.timerState == 'stopped') this.startTimer('activity')
|
|
111
|
+
this.startIdleTimer()
|
|
112
|
+
},
|
|
113
|
+
/**
|
|
114
|
+
* clear idle timer
|
|
115
|
+
* clear idle countdown
|
|
116
|
+
* reset idle counter
|
|
117
|
+
*/
|
|
118
|
+
stopIdleTimer() {
|
|
119
|
+
this.idleCounter = 0
|
|
120
|
+
clearInterval(this.idleTimer)
|
|
121
|
+
clearTimeout(this.idleTimeoutID)
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/* Format seconds to ISO 8601 format */
|
|
125
|
+
formatToISOString(seconds) {
|
|
126
|
+
let d = new Date(null) //create a default date ref
|
|
127
|
+
d.setSeconds(seconds) //set the time with passed numbers of seconds
|
|
128
|
+
//Example of ISO 8601 time format of 24 chars: "1970-01-04T14:50:00.000Z"
|
|
129
|
+
let ISOTime = d.toISOString()
|
|
130
|
+
|
|
131
|
+
return ISOTime // YYYY-MM-DDTHH:mm:ss.sssZ
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/* Parse a ISO string time period to the format of HH:mm:ss */
|
|
135
|
+
ISOTimeParser(seconds) {
|
|
136
|
+
let ISOTimePeriod = this.formatToISOString(seconds).substring(8, 19) // only the time portion of it
|
|
137
|
+
ISOTimePeriod = ISOTimePeriod.split('T')
|
|
138
|
+
const DDToHrs = (parseInt(ISOTimePeriod[0]) - 1) * 24 // convert xxT to hrs
|
|
139
|
+
const periodOfTime = ISOTimePeriod[1] ? ISOTimePeriod[1].split(':') : null
|
|
140
|
+
|
|
141
|
+
if (!periodOfTime) return '00:00:00'
|
|
142
|
+
|
|
143
|
+
let timeString = ''
|
|
144
|
+
let HH = (DDToHrs + parseInt(periodOfTime[0])).toString()
|
|
145
|
+
let mm = periodOfTime[1]
|
|
146
|
+
let ss = periodOfTime[2]
|
|
147
|
+
|
|
148
|
+
if (HH.length === 1) HH = `0${HH}`
|
|
149
|
+
|
|
150
|
+
timeString = `${HH}:${mm}:${ss}`
|
|
151
|
+
|
|
152
|
+
return timeString
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
beforeUnmount() {
|
|
156
|
+
this.$bus.$off('timer-start', this.startTimer)
|
|
157
|
+
this.$bus.$off('timer-tart', this.stopTimer)
|
|
158
|
+
this.$bus.$off('timer-pause', this.pauseTimer)
|
|
159
|
+
this.$bus.$off('stop-idle-timer', this.stopIdleTimer)
|
|
160
|
+
this.$bus.$off('start-idle-timer', this.startIdleTimer)
|
|
161
|
+
},
|
|
162
|
+
mounted() {
|
|
163
|
+
this.$bus.$on('timer-start', this.startTimer)
|
|
164
|
+
this.$bus.$on('timer-stop', this.stopTimer)
|
|
165
|
+
this.$bus.$on('timer-pause', this.pauseTimer)
|
|
166
|
+
this.$bus.$on('start-idle-timer', this.startIdleTimer)
|
|
167
|
+
this.$bus.$on('stop-idle-timer', this.stopIdleTimer)
|
|
168
|
+
},
|
|
169
|
+
computed: {
|
|
170
|
+
activityDuration() {
|
|
171
|
+
let duration
|
|
172
|
+
if (this.activityTimeCounter > 0)
|
|
173
|
+
duration = this.ISOTimeParser(this.activityTimeCounter)
|
|
174
|
+
else duration = '00:00:00'
|
|
175
|
+
|
|
176
|
+
return duration
|
|
177
|
+
},
|
|
178
|
+
lessonDuration() {
|
|
179
|
+
this.lessonTimeCounter = this.lessonTimeCounter + this.activityTimeCounter
|
|
180
|
+
|
|
181
|
+
let duration = this.ISOTimeParser(this.lessonTimeCounter)
|
|
182
|
+
return duration
|
|
183
|
+
},
|
|
184
|
+
elapsedTime() {
|
|
185
|
+
const eTime = this.elapsedCounter
|
|
186
|
+
return eTime
|
|
187
|
+
},
|
|
188
|
+
elapsedIdleTime() {
|
|
189
|
+
return this.idleCounter
|
|
190
|
+
},
|
|
191
|
+
timerCurrentState() {
|
|
192
|
+
return this.timerState
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -628,7 +628,7 @@ export const useAppStore = defineStore('$appStore', {
|
|
|
628
628
|
this.introAcitve = status
|
|
629
629
|
},
|
|
630
630
|
|
|
631
|
-
setAppDebugMode(
|
|
631
|
+
setAppDebugMode(bool) {
|
|
632
632
|
this.appDebugMode = bool
|
|
633
633
|
},
|
|
634
634
|
|
|
@@ -641,7 +641,7 @@ export const useAppStore = defineStore('$appStore', {
|
|
|
641
641
|
setDeviceType(device) {
|
|
642
642
|
this.deviceType = device
|
|
643
643
|
},
|
|
644
|
-
setErrorMenu(
|
|
644
|
+
setErrorMenu(error) {
|
|
645
645
|
this.errMenuSetting = error
|
|
646
646
|
},
|
|
647
647
|
setApplicationSettings(settings) {
|
|
@@ -652,7 +652,7 @@ export const useAppStore = defineStore('$appStore', {
|
|
|
652
652
|
this.mediaVolume = volume
|
|
653
653
|
},
|
|
654
654
|
|
|
655
|
-
setMediaMuted(
|
|
655
|
+
setMediaMuted(bool) {
|
|
656
656
|
this.mediaMuted = bool
|
|
657
657
|
},
|
|
658
658
|
|
package/src/module/xapi/ADL.js
CHANGED
|
@@ -1726,7 +1726,6 @@ export function xapiwrapper(ADL) {
|
|
|
1726
1726
|
* @param {boolean} strictCallbacks Callback must be executed and first param is error or null if no error
|
|
1727
1727
|
* @return {Objec} containing the status of last promise request and Array of the responses;
|
|
1728
1728
|
*/
|
|
1729
|
-
|
|
1730
1729
|
ADL.XHR_request = function (
|
|
1731
1730
|
lrs,
|
|
1732
1731
|
urls,
|
package/src/router/index.js
CHANGED
|
@@ -33,6 +33,11 @@ router.beforeResolve((to, from, next) => {
|
|
|
33
33
|
if (from.name !== 'module')
|
|
34
34
|
app.config.globalProperties.$bus.$emit('update-route-history', from)
|
|
35
35
|
app.config.globalProperties.$bus.$emit('update-content', to, from, next)
|
|
36
|
+
//hidle detector should be stopped in menu
|
|
37
|
+
if (to.name == 'menu') {
|
|
38
|
+
app.config.globalProperties.$bus.$emit('timer-stop')
|
|
39
|
+
app.config.globalProperties.$bus.$emit('stop-idle-detector')
|
|
40
|
+
}
|
|
36
41
|
|
|
37
42
|
if (noNavigationToMenu && to.name == 'menu') {
|
|
38
43
|
const r = app.config.globalProperties.$helper.getRoutesFromVueRouter() //This return all the routes defined for the module
|