fcad-core-dragon 2.0.0-beta.1 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{.eslintrc.js → .eslintrc.cjs} +13 -18
- package/bk.scss +117 -0
- package/package.json +23 -39
- package/src/$locales/en.json +30 -16
- package/src/$locales/fr.json +29 -16
- package/src/components/AppBase.vue +740 -305
- package/src/components/AppBaseButton.vue +33 -5
- package/src/components/AppBaseErrorDisplay.vue +43 -35
- package/src/components/AppBaseModule.vue +447 -623
- package/src/components/AppBasePage.vue +37 -25
- package/src/components/AppCompAudio.vue +266 -0
- package/src/components/AppCompBranchButtons.vue +52 -63
- package/src/components/AppCompButtonProgress.vue +1 -16
- package/src/components/AppCompCarousel.vue +43 -39
- package/src/components/AppCompInputCheckBox.vue +9 -3
- package/src/components/AppCompInputDropdown.vue +2 -4
- package/src/components/AppCompInputRadio.vue +8 -15
- package/src/components/AppCompInputTextTable.vue +15 -12
- package/src/components/AppCompInputTextToFillDropdown.vue +16 -14
- package/src/components/AppCompInputTextToFillText.vue +2 -2
- package/src/components/AppCompJauge.vue +13 -1
- package/src/components/AppCompMenu.vue +203 -10
- package/src/components/AppCompMenuItem.vue +20 -3
- package/src/components/AppCompNavigation.vue +351 -355
- package/src/components/AppCompNoteCall.vue +62 -47
- package/src/components/AppCompNoteCredit.vue +182 -79
- package/src/components/AppCompPlayBar.vue +975 -1023
- package/src/components/AppCompPlayBarProgress.vue +73 -0
- package/src/components/AppCompPopUp.vue +175 -114
- package/src/components/AppCompQuiz.vue +67 -81
- package/src/components/AppCompQuizRecall.vue +32 -5
- package/src/components/AppCompSVG.vue +66 -40
- package/src/components/AppCompSettingsMenu.vue +6 -8
- package/src/components/AppCompTableOfContent.vue +166 -45
- package/src/components/AppCompVideoPlayer.vue +154 -110
- package/src/components/BaseModule.vue +21 -17
- package/src/main.js +124 -88
- package/src/mixins/$mediaMixins.js +827 -0
- package/src/mixins/$pageMixins.js +65 -109
- package/src/mixins/$quizMixins.js +12 -26
- package/src/mixins/timerMixin.js +8 -9
- package/src/module/store.js +187 -68
- package/src/module/xapi/ADL.js +90 -53
- package/src/module/xapi/Crypto/Hasher.js +8 -8
- package/src/module/xapi/Crypto/WordArray.js +6 -6
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
- package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
- package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
- package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
- package/src/module/xapi/Crypto/encoders/Base.js +7 -7
- package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
- package/src/module/xapi/Crypto/encoders/Hex.js +2 -2
- package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
- package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
- package/src/module/xapi/Statement/index.js +1 -1
- package/src/module/xapi/launch.js +10 -10
- package/src/module/xapi/utils.js +17 -17
- package/src/module/xapi/wrapper.js +123 -50
- package/src/module/xapi/xapiStatement.js +29 -29
- package/src/plugins/helper.js +8 -9
- package/src/plugins/i18n.js +23 -10
- package/src/plugins/scorm.js +14 -14
- package/src/router/index.js +3 -4
- package/src/router/routes.js +10 -30
- package/src/shared/generalfuncs.js +31 -24
- package/src/shared/validators.js +730 -20
- package/.prettierrc.js +0 -5
- package/babel.config.js +0 -3
- package/src/components/AppBaseDragChoice.vue +0 -91
- package/src/components/AppBaseDropZone.vue +0 -112
- package/src/components/AppCompBif.vue +0 -120
- package/src/components/AppCompDragAndDrop.vue +0 -339
- package/src/components/AppCompInputAssociation.vue +0 -332
- package/src/components/AppCompMediaPlayer.vue +0 -397
- package/src/plugins/timeManager.js +0 -77
- package/src/routes_bckp.js +0 -313
- package/src/routes_static.js +0 -344
- package/vue.config.js +0 -83
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
*@ Description: This component is used as main container to display application and containt to display
|
|
3
3
|
*@ What it does: The component fetch the data for the page to display on navigation.
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
*@Note :Must be used
|
|
6
6
|
-->
|
|
7
7
|
|
|
8
8
|
<template>
|
|
9
9
|
<div fluid class="module">
|
|
10
|
+
<div
|
|
11
|
+
id="page_info_section"
|
|
12
|
+
aria-labelledby="page_info"
|
|
13
|
+
aria-live="true"
|
|
14
|
+
></div>
|
|
10
15
|
<a class="skip-link" href="" @click.prevent="skipToMain">
|
|
11
16
|
{{ $t('message.skip_content') }}
|
|
12
17
|
</a>
|
|
@@ -23,17 +28,10 @@
|
|
|
23
28
|
<!------------------------ Nav for FullMode ---------------->
|
|
24
29
|
|
|
25
30
|
<app-comp-navigation
|
|
26
|
-
:app-status="
|
|
31
|
+
:app-status="appReady"
|
|
27
32
|
:auto-navigate="theNavigationBetweenActivity"
|
|
28
33
|
/>
|
|
29
34
|
</b-navbar>
|
|
30
|
-
|
|
31
|
-
<portal-target
|
|
32
|
-
v-show="$store.state.$appStore.isDr"
|
|
33
|
-
id="DR_anchors_progress_wrapper"
|
|
34
|
-
name="DR_anchors_progress"
|
|
35
|
-
></portal-target>
|
|
36
|
-
|
|
37
35
|
<base-module :m-data="$data">
|
|
38
36
|
<b-container
|
|
39
37
|
id="wrapper-content"
|
|
@@ -41,21 +39,11 @@
|
|
|
41
39
|
:class="{ active: moduleConfig.videoFull }"
|
|
42
40
|
class="scroll-bar"
|
|
43
41
|
>
|
|
44
|
-
<div
|
|
45
|
-
v-show="!appReady"
|
|
46
|
-
id="Loading"
|
|
47
|
-
key="Loading"
|
|
48
|
-
:class="{ fadeIn: !appReady && changePage }"
|
|
49
|
-
>
|
|
50
|
-
<!-- App ready: {{ appReady }} -->
|
|
51
|
-
<div class="overlay" />
|
|
52
|
-
<div class="box-loading-anime disableBlur">
|
|
53
|
-
<img src="../assets/img/BeanEater-1s-200px.svg" alt="loading" />
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
42
|
<div class="box">
|
|
57
|
-
<router-view
|
|
43
|
+
<router-view ref="main" :key="$route.fullPath" />
|
|
44
|
+
<!-- <router-view v-show="appReady" ref="main" :key="$route.fullPath" /> -->
|
|
58
45
|
</div>
|
|
46
|
+
|
|
59
47
|
<portal-target
|
|
60
48
|
id="primary_nav_wrapper"
|
|
61
49
|
name="primary_nav"
|
|
@@ -64,43 +52,9 @@
|
|
|
64
52
|
</base-module>
|
|
65
53
|
|
|
66
54
|
<!------------------POPUP QUICK -------------------------->
|
|
67
|
-
<app-comp-pop-up
|
|
55
|
+
<app-comp-pop-up #content />
|
|
68
56
|
<!------------------END USE POP UP-------------------------->
|
|
69
|
-
|
|
70
|
-
<!-- <b-modal
|
|
71
|
-
id="splashscreen"
|
|
72
|
-
ref="splashscreen"
|
|
73
|
-
:title="$t('label.splashscreen')"
|
|
74
|
-
size="lg"
|
|
75
|
-
ok-only
|
|
76
|
-
no-stacking
|
|
77
|
-
:ok-title="$t('user_settings.close')"
|
|
78
|
-
:header-close-label="$t('button.closePopUp')"
|
|
79
|
-
>
|
|
80
|
-
<span>
|
|
81
|
-
{{ $t('message.splash_welcome') }}
|
|
82
|
-
</span>
|
|
83
|
-
<b-form-group
|
|
84
|
-
id="splash-form"
|
|
85
|
-
v-slot="{ ariaDescribedby }"
|
|
86
|
-
:label="$t('user_settings.title')"
|
|
87
|
-
>
|
|
88
|
-
<b-form-checkbox
|
|
89
|
-
v-for="(setting, index) in Object.entries(settingsSelected)"
|
|
90
|
-
:key="index"
|
|
91
|
-
v-model="settingsSelected[setting[0]]"
|
|
92
|
-
:aria-describedby="ariaDescribedby"
|
|
93
|
-
switch
|
|
94
|
-
stacked
|
|
95
|
-
size="lg"
|
|
96
|
-
:name="`parameter-${setting[0]}`"
|
|
97
|
-
:text-field="$t(`user_settings.${setting[0]}`)"
|
|
98
|
-
>
|
|
99
|
-
<span>{{ $t(`user_settings.${setting[0]}`) }}</span>
|
|
100
|
-
</b-form-checkbox>
|
|
101
|
-
</b-form-group>
|
|
102
|
-
</b-modal> -->
|
|
103
|
-
<!------------------END Settings splash-screen-------------------------->
|
|
57
|
+
|
|
104
58
|
<!--------------RIGHT SIDEBAR (for display of extra contents)------------>
|
|
105
59
|
|
|
106
60
|
<b-sidebar
|
|
@@ -108,12 +62,22 @@
|
|
|
108
62
|
id="right-sidebar"
|
|
109
63
|
ref="right-sidebar"
|
|
110
64
|
:key="dynamicSidebarContent._id"
|
|
65
|
+
:sidebar-class="{
|
|
66
|
+
'v-media': dynamicSidebarContent._context === 'ctxTranscript'
|
|
67
|
+
}"
|
|
111
68
|
v-bind="dynamicSidebarContent._props"
|
|
112
69
|
:visible="rightSidebarVisible"
|
|
113
70
|
:aria-label="dynamicSidebarContent._label"
|
|
114
|
-
@hidden="
|
|
71
|
+
@hidden="
|
|
72
|
+
closeSidebar(
|
|
73
|
+
dynamicSidebarContent._context,
|
|
74
|
+
dynamicSidebarContent._container
|
|
75
|
+
? dynamicSidebarContent._container
|
|
76
|
+
: null
|
|
77
|
+
)
|
|
78
|
+
"
|
|
115
79
|
>
|
|
116
|
-
<template #header="{hide}">
|
|
80
|
+
<template #header="{ hide }">
|
|
117
81
|
<button @click="hide">
|
|
118
82
|
<svg width="32" height="32">
|
|
119
83
|
<use href="#close-square-icon" fill="red" />
|
|
@@ -129,6 +93,7 @@
|
|
|
129
93
|
>
|
|
130
94
|
<component
|
|
131
95
|
:is="dynamicSidebarContent._component"
|
|
96
|
+
class="v-media"
|
|
132
97
|
v-bind="{ ...dynamicSidebarContent._comProps }"
|
|
133
98
|
/>
|
|
134
99
|
</transition>
|
|
@@ -154,12 +119,7 @@ export default {
|
|
|
154
119
|
type: Object,
|
|
155
120
|
default: () => {
|
|
156
121
|
return {
|
|
157
|
-
|
|
158
|
-
title: null, // edit to set the title of the lesson
|
|
159
|
-
description: null,
|
|
160
|
-
introActive: false, // Controle use INTODUCTION page in the Lesson. set to false if there is no introduction
|
|
161
|
-
concluActive: false, // Controle use CONCLUSION page in the Lesson. set to false if there is no conclusion
|
|
162
|
-
allowNavigationToActivity: false, // set Previous/Next can allow navigation between activities. Set to false if do not want navigation between activities with Previous/Next,É
|
|
122
|
+
allowNavigationToActivity: false, // set Previous/Next can allow navigation between activities. Set to false if do not want navigation between activities with Previous/Next
|
|
163
123
|
main: ''
|
|
164
124
|
}
|
|
165
125
|
}
|
|
@@ -171,7 +131,6 @@ export default {
|
|
|
171
131
|
videoFull: null,
|
|
172
132
|
routeData: [],
|
|
173
133
|
changePage: false,
|
|
174
|
-
appReady: false,
|
|
175
134
|
randKey: Math.floor(Math.random() * 10001),
|
|
176
135
|
popupIsOpen: null,
|
|
177
136
|
hidePlayBar: false, // Controle visibility of the play bar. set to true to hide play bar
|
|
@@ -190,8 +149,11 @@ export default {
|
|
|
190
149
|
infocusTabIndex: null,
|
|
191
150
|
transcriptVisible: false,
|
|
192
151
|
transcriptContent: null,
|
|
152
|
+
transcriptContainer: null,
|
|
193
153
|
branchingVisible: false,
|
|
194
|
-
mediaToShow: null
|
|
154
|
+
// mediaToShow: null,
|
|
155
|
+
lessonCompletionStatus: false,
|
|
156
|
+
checkedDataFromServer: 0
|
|
195
157
|
}
|
|
196
158
|
},
|
|
197
159
|
computed: {
|
|
@@ -208,37 +170,36 @@ export default {
|
|
|
208
170
|
'getConnectionInfo',
|
|
209
171
|
'hasMediaElOrTimeline',
|
|
210
172
|
'getModuleChildren',
|
|
211
|
-
'getAllActivities',
|
|
212
173
|
'getPageData',
|
|
213
174
|
'getMenuSettings',
|
|
214
175
|
'getCurrentSection',
|
|
215
176
|
'getRouteHistory',
|
|
216
177
|
'getOnboardingEnabled',
|
|
217
|
-
'getApplicationSettings'
|
|
178
|
+
'getApplicationSettings',
|
|
179
|
+
'getDataFromServer',
|
|
180
|
+
'getSkipMenu' //Must Check This LINE WITH CYNTHIA
|
|
218
181
|
]),
|
|
219
182
|
isMenu() {
|
|
220
183
|
return this.$route.name === 'menu'
|
|
221
184
|
},
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (this.getAppStatus === 'loading') readyState = false
|
|
225
|
-
else if (this.getAppStatus === 'ready') readyState = true
|
|
226
|
-
return readyState
|
|
185
|
+
appReady() {
|
|
186
|
+
return this.getAppStatus === 'ready' ? true : false
|
|
227
187
|
},
|
|
228
188
|
|
|
229
189
|
hasMedia() {
|
|
230
190
|
return typeof this.hasMediaElOrTimeline === 'object'
|
|
231
191
|
},
|
|
192
|
+
activityHasChanged() {
|
|
193
|
+
const rd = this.routeData.toReversed()
|
|
232
194
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
return lang
|
|
195
|
+
if (
|
|
196
|
+
rd.length <= 1 ||
|
|
197
|
+
rd[1].activity_ref !== this.$route.meta.activity_ref
|
|
198
|
+
)
|
|
199
|
+
return true
|
|
200
|
+
else return false
|
|
241
201
|
},
|
|
202
|
+
|
|
242
203
|
/**
|
|
243
204
|
* @description Set the id the module
|
|
244
205
|
*/
|
|
@@ -285,7 +246,6 @@ export default {
|
|
|
285
246
|
*/
|
|
286
247
|
theNavigationBetweenActivity() {
|
|
287
248
|
let navBwteenActivity = false
|
|
288
|
-
|
|
289
249
|
if (this.moduleConfig.allowNavigationToActivity)
|
|
290
250
|
navBwteenActivity = this.moduleConfig.allowNavigationToActivity
|
|
291
251
|
return navBwteenActivity
|
|
@@ -319,8 +279,8 @@ export default {
|
|
|
319
279
|
if (this.transcriptVisible) {
|
|
320
280
|
_label =
|
|
321
281
|
this.$i18n.locale === 'fr'
|
|
322
|
-
? '
|
|
323
|
-
: '
|
|
282
|
+
? 'Contenu de la transcription'
|
|
283
|
+
: 'Content of the transcript'
|
|
324
284
|
|
|
325
285
|
sidebarSettings = {
|
|
326
286
|
_props: {
|
|
@@ -332,8 +292,11 @@ export default {
|
|
|
332
292
|
no_close_on_backdrop: false
|
|
333
293
|
},
|
|
334
294
|
_component: AppCompTranscript,
|
|
335
|
-
_comProps: {
|
|
295
|
+
_comProps: {
|
|
296
|
+
content: this.transcriptContent
|
|
297
|
+
},
|
|
336
298
|
_context: 'ctxTranscript',
|
|
299
|
+
_container: this.transcriptContainer,
|
|
337
300
|
_label,
|
|
338
301
|
_id: 'transcript'
|
|
339
302
|
}
|
|
@@ -380,7 +343,7 @@ export default {
|
|
|
380
343
|
},
|
|
381
344
|
$route: {
|
|
382
345
|
handler() {
|
|
383
|
-
this.mediaToShow = this.formateData(this.getCurrentPage)
|
|
346
|
+
//this.mediaToShow = this.formateData(this.getCurrentPage)
|
|
384
347
|
this.getFocusables().then((r) => {
|
|
385
348
|
//Pressing Tab or Shit + tab should make it possible to cycle focus through them
|
|
386
349
|
if (!r) return
|
|
@@ -391,121 +354,44 @@ export default {
|
|
|
391
354
|
//update the routeChangeCounter when navigation
|
|
392
355
|
if (this.routeChangeCounter < 3) this.routeChangeCounter += 1
|
|
393
356
|
|
|
394
|
-
/*
|
|
395
|
-
|
|
357
|
+
/*
|
|
358
|
+
*Start Timer on New activities
|
|
396
359
|
*/
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (
|
|
400
|
-
|
|
401
|
-
this.getModuleInfo.packageType === 'xapi' &&
|
|
402
|
-
this.getConnectionInfo &&
|
|
403
|
-
this.getConnectionInfo.actor &&
|
|
404
|
-
this.getConnectionInfo.remote
|
|
405
|
-
) {
|
|
406
|
-
const stmt = {
|
|
407
|
-
id: `${this.$route.name.split('.')[0]}`,
|
|
408
|
-
description: `${this.$route.name.split('.')[0]} de ${
|
|
409
|
-
this.getModuleInfo.id
|
|
410
|
-
}`,
|
|
411
|
-
definition: `${this.$route.name.split('.')[0]} de ${
|
|
412
|
-
this.getModuleInfo.id
|
|
413
|
-
}`
|
|
414
|
-
}
|
|
360
|
+
|
|
361
|
+
const trackedRouteType = ['introduction', 'conclusion', 'normal']
|
|
362
|
+
if (trackedRouteType.includes(this.$route.meta.type)) {
|
|
363
|
+
if (this.$route.name.includes('.')) return
|
|
415
364
|
|
|
416
365
|
//Start the timer every time that there is a new activity
|
|
417
|
-
if (
|
|
418
|
-
|
|
419
|
-
this.$route.meta.type === 'introduction'
|
|
420
|
-
) {
|
|
421
|
-
if (this.timerState === 'started') this.stopTimer('activity') // reset the activity timer
|
|
422
|
-
this.startTimer('activity')
|
|
423
|
-
}
|
|
366
|
+
if (this.timerState === 'started') this.stopTimer('activity') // reset the activity timer
|
|
367
|
+
this.startTimer('activity')
|
|
424
368
|
|
|
425
|
-
|
|
369
|
+
//Send statement when activity as changed
|
|
370
|
+
if (this.activityHasChanged)
|
|
371
|
+
this.sendStartStatement({ id: this.$route.meta.activity_ref })
|
|
426
372
|
}
|
|
427
373
|
this.transcriptVisible = false
|
|
428
374
|
},
|
|
429
375
|
immediate: true
|
|
430
376
|
},
|
|
431
|
-
|
|
432
|
-
* @description Defined in Store
|
|
433
|
-
*/
|
|
434
|
-
getAppStatus(newValue) {
|
|
435
|
-
if (newValue === 'ready') {
|
|
436
|
-
this.appReady = true
|
|
437
|
-
} else {
|
|
438
|
-
this.appReady = false
|
|
439
|
-
}
|
|
440
|
-
},
|
|
377
|
+
|
|
441
378
|
/**
|
|
442
379
|
* @description Defined in Store Watch all completed activities to send a lesson completion statement
|
|
443
380
|
*/
|
|
444
381
|
getAllCompleted: {
|
|
445
382
|
handler() {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
this.
|
|
449
|
-
|
|
450
|
-
this.
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
completedSize = completedSize + value[1].length
|
|
456
|
-
})
|
|
457
|
-
const completedState = this.$xapi._getLessonStatus(
|
|
458
|
-
this.getConnectionInfo.actor.mbox.replace('mailto:', ''),
|
|
459
|
-
this.getConnectionInfo.activity_id
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
if (
|
|
463
|
-
completedSize === this.getAllActivities.pageSize &&
|
|
464
|
-
!completedState.completion
|
|
465
|
-
) {
|
|
466
|
-
let text
|
|
467
|
-
//Defining the text to display for stmt description and definition
|
|
468
|
-
switch (this.$i18n.locale) {
|
|
469
|
-
case 'fr':
|
|
470
|
-
if (this.getModuleInfo.courseID)
|
|
471
|
-
text = `Le ${this.getModuleInfo.id} de ${this.getModuleInfo.courseID}`
|
|
472
|
-
else text = `Le ${this.getModuleInfo.id}`
|
|
473
|
-
break
|
|
474
|
-
case 'en':
|
|
475
|
-
if (this.getModuleInfo.courseID)
|
|
476
|
-
text = `The ${this.getModuleInfo.id} of ${this.getModuleInfo.courseID}`
|
|
477
|
-
else text = `The ${this.getModuleInfo.id}`
|
|
478
|
-
break
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Retrive only user data in lessons front its interaction that we want to send to the LRS.
|
|
482
|
-
// Note: User Settings are sent on a different URI and statement
|
|
483
|
-
const {
|
|
484
|
-
isFistTime,
|
|
485
|
-
userSettings,
|
|
486
|
-
...lessonsData
|
|
487
|
-
} = this.getUserInteraction
|
|
488
|
-
|
|
489
|
-
this.sendXapiStatement({
|
|
490
|
-
verb: 'completed',
|
|
491
|
-
definition: text,
|
|
492
|
-
description: text,
|
|
493
|
-
extension: [
|
|
494
|
-
{
|
|
495
|
-
id: 'user-data',
|
|
496
|
-
content: {
|
|
497
|
-
routeHistory: this.getRouteHistory,
|
|
498
|
-
...lessonsData
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
],
|
|
502
|
-
duration: this.lessonDuration,
|
|
503
|
-
completion: true
|
|
504
|
-
})
|
|
505
|
-
}
|
|
383
|
+
// Check once the server to set the completion status of the lesson
|
|
384
|
+
if (this.getDataFromServer && this.checkedDataFromServer < 1) {
|
|
385
|
+
const { completedState } = this.getDataFromServer
|
|
386
|
+
|
|
387
|
+
this.lessonCompletionStatus =
|
|
388
|
+
!completedState || !completedState.completion
|
|
389
|
+
? false
|
|
390
|
+
: completedState.completion
|
|
391
|
+
this.checkedDataFromServer += 1
|
|
506
392
|
}
|
|
507
393
|
|
|
508
|
-
|
|
394
|
+
if (!this.lessonCompletionStatus) this.sendCompletionStatus('LESSON')
|
|
509
395
|
},
|
|
510
396
|
immediate: true
|
|
511
397
|
},
|
|
@@ -516,17 +402,17 @@ export default {
|
|
|
516
402
|
if (this.timerState !== 'started') return
|
|
517
403
|
//send a statement every x time (second)
|
|
518
404
|
if (
|
|
519
|
-
|
|
520
|
-
this.elapsedTime %
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
405
|
+
this.timerState === 'started' &&
|
|
406
|
+
this.elapsedTime % 500 === 0 &&
|
|
407
|
+
this.getModuleInfo.packageType === 'xapi' &&
|
|
408
|
+
this.getConnectionInfo &&
|
|
409
|
+
this.getConnectionInfo.actor &&
|
|
410
|
+
this.getConnectionInfo.remote &&
|
|
411
|
+
this.getDataFromServer
|
|
525
412
|
) {
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
)[0]
|
|
413
|
+
const { lessonPosition } = this.getDataFromServer
|
|
414
|
+
|
|
415
|
+
const lastReached = lessonPosition.length ? lessonPosition[0] : ''
|
|
530
416
|
|
|
531
417
|
//only Send savepoint statement when 3 navigation happen and the route is different from last saved
|
|
532
418
|
if (this.routeChangeCounter >= 3 && lastReached !== this.$route.name) {
|
|
@@ -550,7 +436,7 @@ export default {
|
|
|
550
436
|
verb: 'progressed',
|
|
551
437
|
definition: text,
|
|
552
438
|
description: text,
|
|
553
|
-
|
|
439
|
+
extensions: [
|
|
554
440
|
{
|
|
555
441
|
id: 'ending-point',
|
|
556
442
|
content: this.$route.name
|
|
@@ -559,48 +445,44 @@ export default {
|
|
|
559
445
|
duration: this.lessonDuration
|
|
560
446
|
}
|
|
561
447
|
|
|
562
|
-
this
|
|
448
|
+
this.$bus.$emit('send-xapi-statement', stmt)
|
|
563
449
|
this.routeChangeCounter = 0 //reset counter after saving
|
|
564
450
|
}
|
|
565
451
|
}
|
|
566
452
|
}
|
|
567
453
|
},
|
|
568
|
-
|
|
454
|
+
beforeUnmount() {
|
|
569
455
|
//Communication events
|
|
570
|
-
this.$bus.$off('
|
|
571
|
-
|
|
572
|
-
this.$bus.$off('
|
|
573
|
-
|
|
574
|
-
this.$bus.$off('start-onboarding')
|
|
575
|
-
|
|
576
|
-
this.$bus.$off('
|
|
577
|
-
|
|
578
|
-
this.$bus.$off('update-page')
|
|
579
|
-
|
|
580
|
-
this.$bus.$off('videoFullScreen')
|
|
581
|
-
|
|
582
|
-
this.$bus.$off('save-to-scorm')
|
|
583
|
-
|
|
584
|
-
this.$bus.$off('send-xapi-statement')
|
|
585
|
-
|
|
586
|
-
this.$bus.$off('launch-xapi-resource')
|
|
587
|
-
|
|
588
|
-
this.$bus.$off('fire-exit-event', this.endLesson)
|
|
589
|
-
|
|
456
|
+
this.$bus.$off('close-sidebar', this.closeSidebar)
|
|
457
|
+
this.$bus.$off('open-sidebar', this.openSidebar)
|
|
458
|
+
this.$bus.$off('open-popup', this.openPopup)
|
|
459
|
+
this.$bus.$off('close-popup', this.closePopup)
|
|
460
|
+
this.$bus.$off('start-onboarding', this.startOnboarding)
|
|
461
|
+
this.$bus.$off('videoFullScreen', this.onVideoFullScreen)
|
|
462
|
+
this.$bus.$off('save-to-scorm', this.saveToScorm)
|
|
463
|
+
this.$bus.$off('launch-xapi-resource', this.launchResource)
|
|
590
464
|
this.$bus.$off('update-route-history', this.updateRouteHistory)
|
|
591
|
-
|
|
592
465
|
this.$bus.$off('update-content', this.updateContent)
|
|
593
|
-
|
|
594
466
|
this.$bus.$off('show-transcript', this.openTranscript)
|
|
595
|
-
|
|
467
|
+
this.$bus.$off('send-completion-event', this.sendCompletionStatus)
|
|
468
|
+
this.$bus.$off('send-starting-event', this.sendStartStatement)
|
|
469
|
+
//nav mouseleave event
|
|
470
|
+
let nav = document.getElementById('navTool')
|
|
471
|
+
if (nav) {
|
|
472
|
+
nav.removeEventListener('mouseleave', this.onNavMouseleave)
|
|
473
|
+
}
|
|
474
|
+
//sidebar scroll event
|
|
475
|
+
const rightSidebar = this.getRightSidebar()
|
|
476
|
+
if (rightSidebar) {
|
|
477
|
+
rightSidebar.removeEventListener('scroll', this.handleRightSidebarScroll)
|
|
478
|
+
}
|
|
479
|
+
//keayboard listener
|
|
596
480
|
document.removeEventListener('keydown', this.handleKeyboardControls)
|
|
597
481
|
},
|
|
598
482
|
created() {
|
|
599
|
-
//TODO: get course name from app settings
|
|
600
|
-
// Correct animation slide off when transcript off
|
|
601
|
-
// correct toggle view hide branching content
|
|
602
483
|
let lessonLabel, lessonNumber, lessonTitle, titleString
|
|
603
|
-
|
|
484
|
+
|
|
485
|
+
//this.mediaToShow = this.formateData(this.getCurrentPage)
|
|
604
486
|
lessonLabel = this.$t('text.lesson')
|
|
605
487
|
lessonNumber = this.moduleConfig.id.replace('module_', '')
|
|
606
488
|
lessonTitle = this.theTitle
|
|
@@ -618,50 +500,27 @@ export default {
|
|
|
618
500
|
this.$store.commit('UPDATE_INTRO_STATE', this.theIntroIsActivated)
|
|
619
501
|
|
|
620
502
|
//Communication events
|
|
621
|
-
this.$bus.$on('open-popup',
|
|
622
|
-
this.openPopup(data)
|
|
623
|
-
})
|
|
624
|
-
|
|
625
|
-
this.$bus.$on('close-popup', () => {
|
|
626
|
-
this.closePopup()
|
|
627
|
-
})
|
|
503
|
+
this.$bus.$on('open-popup', this.openPopup)
|
|
628
504
|
|
|
629
|
-
this.$bus.$on('
|
|
630
|
-
this.startOnboarding(data)
|
|
631
|
-
})
|
|
505
|
+
this.$bus.$on('close-popup', this.closePopup)
|
|
632
506
|
|
|
633
|
-
this.$bus.$on('
|
|
634
|
-
this.openSidebar(data)
|
|
635
|
-
})
|
|
636
|
-
|
|
637
|
-
this.$bus.$on('close-sidebar', (data) => {
|
|
638
|
-
this.closeSidebar(data)
|
|
639
|
-
})
|
|
507
|
+
this.$bus.$on('start-onboarding', this.startOnboarding)
|
|
640
508
|
|
|
641
|
-
this.$bus.$on('
|
|
642
|
-
this.videoFull = value
|
|
643
|
-
})
|
|
509
|
+
this.$bus.$on('open-sidebar', this.openSidebar)
|
|
644
510
|
|
|
645
|
-
this.$bus.$on('
|
|
646
|
-
this.saveToScorm()
|
|
647
|
-
})
|
|
511
|
+
this.$bus.$on('close-sidebar', this.closeSidebar)
|
|
648
512
|
|
|
649
|
-
this.$bus.$on(
|
|
650
|
-
'send-xapi-statement',
|
|
651
|
-
(statement, cb = null, option = false) => {
|
|
652
|
-
this.sendXapiStatement(statement, cb, option)
|
|
653
|
-
}
|
|
654
|
-
)
|
|
513
|
+
this.$bus.$on('videoFullScreen', this.onVideoFullScreen)
|
|
655
514
|
|
|
656
|
-
this.$bus.$on('
|
|
657
|
-
this.launchResource(res)
|
|
658
|
-
})
|
|
515
|
+
this.$bus.$on('save-to-scorm', this.saveToScorm)
|
|
659
516
|
|
|
660
|
-
this.$bus.$on('
|
|
517
|
+
this.$bus.$on('launch-xapi-resource', this.launchResource)
|
|
661
518
|
|
|
662
519
|
this.$bus.$on('update-route-history', this.updateRouteHistory)
|
|
663
520
|
this.$bus.$on('update-content', this.updateContent)
|
|
664
521
|
this.$bus.$on('show-transcript', this.openTranscript)
|
|
522
|
+
this.$bus.$on('send-completion-event', this.sendCompletionStatus)
|
|
523
|
+
this.$bus.$on('send-starting-event', this.sendStartStatement)
|
|
665
524
|
|
|
666
525
|
this.initLesson()
|
|
667
526
|
|
|
@@ -681,7 +540,12 @@ export default {
|
|
|
681
540
|
|
|
682
541
|
let nav = document.getElementById('navTool')
|
|
683
542
|
|
|
684
|
-
nav.addEventListener('mouseleave',
|
|
543
|
+
nav.addEventListener('mouseleave', this.onNavMouseleave)
|
|
544
|
+
|
|
545
|
+
document.addEventListener('keydown', this.handleKeyboardControls)
|
|
546
|
+
},
|
|
547
|
+
methods: {
|
|
548
|
+
onNavMouseleave() {
|
|
685
549
|
let widgetOpen = document.getElementsByClassName('open')
|
|
686
550
|
|
|
687
551
|
if (widgetOpen.length == 0) {
|
|
@@ -691,15 +555,14 @@ export default {
|
|
|
691
555
|
} else {
|
|
692
556
|
this.closeDelay = true
|
|
693
557
|
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
methods: {
|
|
558
|
+
},
|
|
559
|
+
onVideoFullScreen(value) {
|
|
560
|
+
this.videoFull = value
|
|
561
|
+
},
|
|
699
562
|
/* Get All Element related to the media return a list of all DOM element*/
|
|
700
563
|
async getFocusables() {
|
|
701
|
-
return new Promise(function(resolve, reject) {
|
|
702
|
-
setTimeout(function() {
|
|
564
|
+
return new Promise(function (resolve, reject) {
|
|
565
|
+
setTimeout(function () {
|
|
703
566
|
const getAllFocusables = () => {
|
|
704
567
|
const listItems = document.querySelectorAll('.v-media')
|
|
705
568
|
if (!listItems || !listItems.length) return null
|
|
@@ -716,17 +579,16 @@ export default {
|
|
|
716
579
|
*
|
|
717
580
|
*/
|
|
718
581
|
async handleKeyboardControls(evt) {
|
|
719
|
-
let {
|
|
582
|
+
let { code } = evt
|
|
720
583
|
|
|
721
|
-
if (
|
|
584
|
+
if (code === 'Escape' && this.rightSidebarVisible)
|
|
722
585
|
return (this.rightSidebarVisible = false)
|
|
723
586
|
|
|
724
587
|
//==========================================
|
|
725
|
-
const videoRange = await this.getFocusables()
|
|
588
|
+
/*const videoRange = await this.getFocusables()
|
|
726
589
|
if (videoRange && videoRange.indexOf(document.activeElement) === -1)
|
|
727
590
|
return
|
|
728
|
-
|
|
729
|
-
this.$bus.$emit('play-media', key)
|
|
591
|
+
this.$bus.$emit('play-media', code)*/
|
|
730
592
|
},
|
|
731
593
|
|
|
732
594
|
/**
|
|
@@ -737,13 +599,13 @@ export default {
|
|
|
737
599
|
*/
|
|
738
600
|
openSidebar(obj) {
|
|
739
601
|
if (!obj || !obj.ctx || !obj.e) return
|
|
740
|
-
|
|
602
|
+
const wrapper = obj.w ? obj.w : null
|
|
741
603
|
this.lastInFocus = document.activeElement
|
|
742
604
|
const { ctx, e: content } = obj
|
|
743
605
|
|
|
744
606
|
switch (ctx) {
|
|
745
607
|
case 'ctxTranscript':
|
|
746
|
-
this.openTranscript(content)
|
|
608
|
+
this.openTranscript(content, wrapper)
|
|
747
609
|
break
|
|
748
610
|
|
|
749
611
|
case 'ctxBranching':
|
|
@@ -771,14 +633,14 @@ export default {
|
|
|
771
633
|
* @param {String} ctx - context in which side bar was opened and will now be closed
|
|
772
634
|
*
|
|
773
635
|
*/
|
|
774
|
-
closeSidebar(ctx) {
|
|
636
|
+
closeSidebar(ctx, wrapper = null) {
|
|
775
637
|
//delay animation
|
|
776
638
|
this.rightSidebarVisible = false // this will allow to run the animation of sidebar closing 1rst
|
|
777
639
|
|
|
778
640
|
this.resetFocus(this.lastInFocus)
|
|
779
641
|
switch (ctx) {
|
|
780
642
|
case 'ctxTranscript':
|
|
781
|
-
this.closeTranscript()
|
|
643
|
+
this.closeTranscript(wrapper)
|
|
782
644
|
break
|
|
783
645
|
|
|
784
646
|
case 'ctxBranching':
|
|
@@ -835,9 +697,17 @@ export default {
|
|
|
835
697
|
* @param {Object} c content to display in the sidebar
|
|
836
698
|
*/
|
|
837
699
|
|
|
838
|
-
openTranscript(c) {
|
|
700
|
+
openTranscript(c, container) {
|
|
839
701
|
if (!c) return (this.transcriptVisible = false)
|
|
840
|
-
|
|
702
|
+
//Changer le contenu du transcript
|
|
703
|
+
if (this.transcriptContent !== c) this.transcriptContent = c
|
|
704
|
+
//Changer le format du Container du media
|
|
705
|
+
if (this.transcriptContainer && this.transcriptContainer !== container) {
|
|
706
|
+
/*console.log('transcriptContainer est différent de container')
|
|
707
|
+
console.log({ ancien: this.transcriptContainer, nouveau: container })
|
|
708
|
+
console.log('agrandir ancien container et réduire nouveau container')*/
|
|
709
|
+
}
|
|
710
|
+
this.transcriptContainer = container
|
|
841
711
|
this.transcriptVisible = true
|
|
842
712
|
},
|
|
843
713
|
/**
|
|
@@ -845,13 +715,13 @@ export default {
|
|
|
845
715
|
* @summary reset the value of the transcriptContent and transcripVisibility
|
|
846
716
|
* @fires 'transcript-hidden' to AppCompPlaybar
|
|
847
717
|
*/
|
|
848
|
-
closeTranscript() {
|
|
718
|
+
closeTranscript(container = this.transcriptContainer) {
|
|
849
719
|
setTimeout(() => {
|
|
850
720
|
this.transcriptContent = null
|
|
851
721
|
this.transcriptVisible = false
|
|
852
722
|
}, 300)
|
|
853
723
|
this.$bus.$emit('transcript-hidden')
|
|
854
|
-
this.$bus.$emit('resize-media', 'lg')
|
|
724
|
+
this.$bus.$emit('resize-media', 'lg', container)
|
|
855
725
|
},
|
|
856
726
|
|
|
857
727
|
/**
|
|
@@ -904,8 +774,9 @@ export default {
|
|
|
904
774
|
return new Promise((res) => {
|
|
905
775
|
this.$bus.$emit('set-comp-status', 'appBaseModule', 'loading')
|
|
906
776
|
this.$store.dispatch('updateCurrentTimeline', '')
|
|
907
|
-
this.$store.dispatch('
|
|
777
|
+
this.$store.dispatch('updateCurrentMediaElements', [])
|
|
908
778
|
this.$store.dispatch('updateCurrentPage', {})
|
|
779
|
+
this.$bus.$emit('set-comp-status', 'appBaseModule', 'ready')
|
|
909
780
|
res(this.$store.state.$appStore.compStatusTracker)
|
|
910
781
|
})
|
|
911
782
|
},
|
|
@@ -1014,16 +885,15 @@ export default {
|
|
|
1014
885
|
case 'xapi':
|
|
1015
886
|
{
|
|
1016
887
|
//Check if there is a user connection
|
|
888
|
+
|
|
1017
889
|
if (
|
|
1018
890
|
this.getConnectionInfo &&
|
|
1019
891
|
this.getConnectionInfo.actor &&
|
|
1020
|
-
this.getConnectionInfo.remote
|
|
892
|
+
this.getConnectionInfo.remote &&
|
|
893
|
+
this.getDataFromServer
|
|
1021
894
|
) {
|
|
1022
|
-
|
|
1023
|
-
const bookmark =
|
|
1024
|
-
this.getConnectionInfo.actor.mbox.replace('mailto:', ''),
|
|
1025
|
-
this.getConnectionInfo.activity_id
|
|
1026
|
-
)[0]
|
|
895
|
+
const { lessonPosition } = this.getDataFromServer
|
|
896
|
+
const bookmark = lessonPosition.length ? lessonPosition[0] : ''
|
|
1027
897
|
// if none stored in the LMS redirect to the 1st page
|
|
1028
898
|
if (
|
|
1029
899
|
!this.bookmarkActive ||
|
|
@@ -1045,10 +915,6 @@ export default {
|
|
|
1045
915
|
? this.$router.push({ name: this.$route.name })
|
|
1046
916
|
: this.$router.push({ name: 'menu' })
|
|
1047
917
|
}
|
|
1048
|
-
// set the current page
|
|
1049
|
-
this.fetchPage().then((res) => {
|
|
1050
|
-
//this.mediaToShw = this.formateData(res)
|
|
1051
|
-
})
|
|
1052
918
|
}
|
|
1053
919
|
break
|
|
1054
920
|
}
|
|
@@ -1144,15 +1010,10 @@ export default {
|
|
|
1144
1010
|
!this.theIntroIsActivated
|
|
1145
1011
|
)
|
|
1146
1012
|
toName = 'menu'
|
|
1147
|
-
// next({ name: toName }) //navigate to correct path
|
|
1148
1013
|
}
|
|
1149
1014
|
//When User inter path to module
|
|
1150
1015
|
else if (to.name === 'module') {
|
|
1151
|
-
//Intro is not active set navigation path to menu
|
|
1152
|
-
//if (!this.theIntroIsActivated) toName = 'menu'
|
|
1153
|
-
//Intro active set navigation path to introduction
|
|
1154
1016
|
toName = 'menu'
|
|
1155
|
-
// next({ name: toName }) //navigate to correct path
|
|
1156
1017
|
}
|
|
1157
1018
|
//Default redirection
|
|
1158
1019
|
else {
|
|
@@ -1162,10 +1023,6 @@ export default {
|
|
|
1162
1023
|
//===================== Handeling a page request from Store=========================== ====//
|
|
1163
1024
|
//get the page from store
|
|
1164
1025
|
this.fetchPage().then((res) => {
|
|
1165
|
-
this.$bus.$emit('set-comp-status', 'AppbaseModule', 'ready')
|
|
1166
|
-
|
|
1167
|
-
// this.mediaToShw = this.formateData(res)
|
|
1168
|
-
|
|
1169
1026
|
//Save current page has bookmark in scorm
|
|
1170
1027
|
if (
|
|
1171
1028
|
this.getModuleInfo.packageType === 'scorm' &&
|
|
@@ -1198,193 +1055,6 @@ export default {
|
|
|
1198
1055
|
if (this.routeData.length >= 4) this.routeData.shift()
|
|
1199
1056
|
},
|
|
1200
1057
|
|
|
1201
|
-
/**
|
|
1202
|
-
* @description Send a custom statement to the lrs. Receive some custom params and build a statement to send
|
|
1203
|
-
* @param {Object} stmtObj
|
|
1204
|
-
* @param {Function} cb
|
|
1205
|
-
*/
|
|
1206
|
-
sendXapiStatement(stmtObj, cb = null, withFetch = false) {
|
|
1207
|
-
cb = cb || null
|
|
1208
|
-
const crsParams = this.getModuleInfo
|
|
1209
|
-
|
|
1210
|
-
if (
|
|
1211
|
-
this.getConnectionInfo &&
|
|
1212
|
-
this.getConnectionInfo.actor &&
|
|
1213
|
-
this.getConnectionInfo.remote
|
|
1214
|
-
) {
|
|
1215
|
-
const {
|
|
1216
|
-
id,
|
|
1217
|
-
result = null,
|
|
1218
|
-
definition,
|
|
1219
|
-
objectType,
|
|
1220
|
-
type,
|
|
1221
|
-
description,
|
|
1222
|
-
verb,
|
|
1223
|
-
extension,
|
|
1224
|
-
duration,
|
|
1225
|
-
completion
|
|
1226
|
-
} = stmtObj
|
|
1227
|
-
//unload
|
|
1228
|
-
//define the activity id
|
|
1229
|
-
let activityId
|
|
1230
|
-
//=========================== activity ID of Object ==========================================
|
|
1231
|
-
/*
|
|
1232
|
-
* Define the acitivity id of the stmt according to following:
|
|
1233
|
-
* The statement is sent at component/ element level : there is Id and Id is not null (id of the element)
|
|
1234
|
-
* The statement is sent at module level. there is no id
|
|
1235
|
-
* the Statement must be sent at the course level: There course_id exist and id is the course_id
|
|
1236
|
-
*/
|
|
1237
|
-
|
|
1238
|
-
if (id && id !== crsParams.courseID)
|
|
1239
|
-
activityId = `${this.getConnectionInfo.activity_id}/${id}`
|
|
1240
|
-
else if (crsParams.courseID && id === crsParams.courseID)
|
|
1241
|
-
activityId = this.getConnectionInfo.activity_id.replace(
|
|
1242
|
-
`/${crsParams.id}`,
|
|
1243
|
-
''
|
|
1244
|
-
)
|
|
1245
|
-
else activityId = `${this.getConnectionInfo.activity_id}`
|
|
1246
|
-
|
|
1247
|
-
//define the statement object
|
|
1248
|
-
let stmt = {
|
|
1249
|
-
actor: this.getConnectionInfo.actor,
|
|
1250
|
-
verb: (() => {
|
|
1251
|
-
if (verb && this.$xapi.verbs[verb.trim()])
|
|
1252
|
-
return this.$xapi.verbs[verb.trim()]
|
|
1253
|
-
else {
|
|
1254
|
-
//check if the agent exist in the learning record for this activity
|
|
1255
|
-
// to determine wether he is lunching the activity or resuming the activity
|
|
1256
|
-
if (
|
|
1257
|
-
this.$xapi._getAgent(
|
|
1258
|
-
this.getConnectionInfo.actor.mbox.replace('mailto:', ''),
|
|
1259
|
-
activityId
|
|
1260
|
-
)
|
|
1261
|
-
)
|
|
1262
|
-
return this.$xapi.verbs.resumed
|
|
1263
|
-
else return this.$xapi.verbs.started
|
|
1264
|
-
}
|
|
1265
|
-
})(),
|
|
1266
|
-
object: {
|
|
1267
|
-
id: activityId,
|
|
1268
|
-
definition: {
|
|
1269
|
-
name: {
|
|
1270
|
-
[this.displayLang]: `${definition}`
|
|
1271
|
-
},
|
|
1272
|
-
description: {
|
|
1273
|
-
[this.displayLang]: `${description}`,
|
|
1274
|
-
type: type || 'http://activitystrea.ms/schema/1.0/page'
|
|
1275
|
-
}
|
|
1276
|
-
},
|
|
1277
|
-
objectType: objectType || 'Activity'
|
|
1278
|
-
},
|
|
1279
|
-
context: {
|
|
1280
|
-
contextActivities: {}
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
//===================== contextActivity parent =====================//
|
|
1284
|
-
/*
|
|
1285
|
-
Define parent in the contextActivity
|
|
1286
|
-
When:
|
|
1287
|
-
1- when we have the id (parent === module)
|
|
1288
|
-
2- when with have no id but we have a course_id (parent is cours_id)
|
|
1289
|
-
*/
|
|
1290
|
-
const activityParent = (() => {
|
|
1291
|
-
if (crsParams.courseID && !id)
|
|
1292
|
-
return {
|
|
1293
|
-
id: this.getConnectionInfo.activity_id.replace(
|
|
1294
|
-
`/${crsParams.id}`,
|
|
1295
|
-
''
|
|
1296
|
-
),
|
|
1297
|
-
objectType: objectType || 'Activity'
|
|
1298
|
-
}
|
|
1299
|
-
else if (id && id !== crsParams.courseID)
|
|
1300
|
-
return {
|
|
1301
|
-
id: `${this.getConnectionInfo.activity_id}`,
|
|
1302
|
-
objectType: objectType || 'Activity'
|
|
1303
|
-
}
|
|
1304
|
-
else return null
|
|
1305
|
-
})()
|
|
1306
|
-
|
|
1307
|
-
// Add the parent key of the context activity
|
|
1308
|
-
if (activityParent)
|
|
1309
|
-
stmt.context.contextActivities['parent'] = [activityParent]
|
|
1310
|
-
|
|
1311
|
-
//===================== contextActivity grouping =====================//
|
|
1312
|
-
|
|
1313
|
-
//Defining the Grouping of the context activity
|
|
1314
|
-
const activityGrouping = (() => {
|
|
1315
|
-
if (
|
|
1316
|
-
activityParent &&
|
|
1317
|
-
crsParams.courseID &&
|
|
1318
|
-
activityParent.id.includes(crsParams.id)
|
|
1319
|
-
)
|
|
1320
|
-
return {
|
|
1321
|
-
id: this.getConnectionInfo.activity_id.replace(
|
|
1322
|
-
`/${crsParams.id}`,
|
|
1323
|
-
''
|
|
1324
|
-
)
|
|
1325
|
-
}
|
|
1326
|
-
else return null
|
|
1327
|
-
})()
|
|
1328
|
-
//Adding the grouping of the context activity
|
|
1329
|
-
if (activityGrouping)
|
|
1330
|
-
stmt.context.contextActivities['grouping'] = [activityGrouping]
|
|
1331
|
-
|
|
1332
|
-
//add result data to statement
|
|
1333
|
-
if (result) stmt['result'] = result
|
|
1334
|
-
|
|
1335
|
-
// Add duration info to statment when activity is complete
|
|
1336
|
-
if (verb === 'completed' || verb === 'suspended' || verb === 'exited') {
|
|
1337
|
-
if (!stmt.result) stmt.result = {} // check if exist
|
|
1338
|
-
|
|
1339
|
-
let d
|
|
1340
|
-
|
|
1341
|
-
duration
|
|
1342
|
-
? (d = `PT${duration.split(':')[0]}H${duration.split(':')[1]}M${
|
|
1343
|
-
duration.split(':')[2]
|
|
1344
|
-
}S`)
|
|
1345
|
-
: (d = `PT${this.activityDuration.split(':')[0]}H${
|
|
1346
|
-
this.activityDuration.split(':')[1]
|
|
1347
|
-
}M${this.activityDuration.split(':')[2]}S`)
|
|
1348
|
-
|
|
1349
|
-
stmt.result['duration'] = d
|
|
1350
|
-
//Set the completion status of the result
|
|
1351
|
-
if (completion) stmt.result['completion'] = completion
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
// ===================== Extension of the Object definition =====================//
|
|
1355
|
-
if (
|
|
1356
|
-
extension &&
|
|
1357
|
-
extension.constructor === Array &&
|
|
1358
|
-
extension.length > 0
|
|
1359
|
-
) {
|
|
1360
|
-
//Validate each entry given in the extension Array
|
|
1361
|
-
extension.forEach((e) => {
|
|
1362
|
-
//entry must be of type Object
|
|
1363
|
-
if (e.constructor !== Object)
|
|
1364
|
-
throw new Error(`'${e}' is not a valid value. Must be Object`)
|
|
1365
|
-
|
|
1366
|
-
//Entry Must have id and content keys
|
|
1367
|
-
const validKey = ['id', 'content']
|
|
1368
|
-
Object.keys(e).forEach((key) => {
|
|
1369
|
-
if (!validKey.includes(key))
|
|
1370
|
-
throw new Error(`Not valid key '${key}' for entry ${e}`)
|
|
1371
|
-
|
|
1372
|
-
//id must be a String
|
|
1373
|
-
if (key === 'id' && e[key] && e[key].constructor !== String)
|
|
1374
|
-
throw new Error(`'${key}' must be of type String`)
|
|
1375
|
-
})
|
|
1376
|
-
|
|
1377
|
-
stmt.object.definition['extensions'] = {
|
|
1378
|
-
...stmt.object.definition['extensions'],
|
|
1379
|
-
[`${this.getConnectionInfo.activity_id}/${e.id}`]: e.content
|
|
1380
|
-
}
|
|
1381
|
-
})
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
this.$xapi._sendStatement(stmt, cb, withFetch)
|
|
1385
|
-
}
|
|
1386
|
-
},
|
|
1387
|
-
|
|
1388
1058
|
/**
|
|
1389
1059
|
* @description Helper fonction to launch extra ressource from this activity. Extra ressource is another lesson.
|
|
1390
1060
|
* @param {Object} res - Data of the ressource to launch
|
|
@@ -1411,67 +1081,12 @@ export default {
|
|
|
1411
1081
|
res.url = `${baseDomain}/${res.url}`
|
|
1412
1082
|
|
|
1413
1083
|
const newUrlToLaunch = `${res.url}?endpoint=${endpoint}&auth=${auth}&actor=${actor}®istration=${registration}&activity_id=${activity_id}`
|
|
1414
|
-
|
|
1415
|
-
this
|
|
1084
|
+
this.routeChangeCounter = 0 //reset counter after saving
|
|
1085
|
+
this.$bus.$emit('fire-exit-event', () => {
|
|
1416
1086
|
window.location.replace(newUrlToLaunch)
|
|
1417
1087
|
})
|
|
1418
1088
|
},
|
|
1419
1089
|
|
|
1420
|
-
/**
|
|
1421
|
-
* @param {Function} cb
|
|
1422
|
-
* @param {Bool} option - set if must use Fetch API or not. default = false
|
|
1423
|
-
*/
|
|
1424
|
-
endLesson(cb, option = false) {
|
|
1425
|
-
let text
|
|
1426
|
-
//Defining the text to display for stmt description and definition
|
|
1427
|
-
switch (this.$i18n.locale) {
|
|
1428
|
-
case 'fr':
|
|
1429
|
-
if (this.getModuleInfo.courseID)
|
|
1430
|
-
text = `Le ${this.getModuleInfo.id} de ${this.getModuleInfo.courseID}`
|
|
1431
|
-
else text = `Le ${this.getModuleInfo.id}`
|
|
1432
|
-
break
|
|
1433
|
-
case 'en':
|
|
1434
|
-
if (this.getModuleInfo.courseID)
|
|
1435
|
-
text = `The ${this.getModuleInfo.id} of ${this.getModuleInfo.courseID}`
|
|
1436
|
-
else text = `The ${this.getModuleInfo.id}`
|
|
1437
|
-
break
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
// Retrieve only user data in lessons front its interaction that we want to send to the LRS.
|
|
1441
|
-
// Note: User Settings are sent on a different URI and statement
|
|
1442
|
-
const {
|
|
1443
|
-
isFistTime,
|
|
1444
|
-
userSettings,
|
|
1445
|
-
...lessonsData
|
|
1446
|
-
} = this.getUserInteraction
|
|
1447
|
-
|
|
1448
|
-
const stmt = {
|
|
1449
|
-
verb: 'exited',
|
|
1450
|
-
definition: text,
|
|
1451
|
-
description: text,
|
|
1452
|
-
extension: [
|
|
1453
|
-
{
|
|
1454
|
-
id: 'ending-point',
|
|
1455
|
-
content: this.$route.name
|
|
1456
|
-
},
|
|
1457
|
-
{
|
|
1458
|
-
id: 'user-data',
|
|
1459
|
-
content: {
|
|
1460
|
-
routeHistory: this.getRouteHistory,
|
|
1461
|
-
...lessonsData
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
],
|
|
1465
|
-
duration: this.lessonDuration
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
this.sendXapiStatement(stmt, null, option)
|
|
1469
|
-
|
|
1470
|
-
if (this.timerState === 'started') setTimeout(() => this.stopTimer(), 0) //clear the timer
|
|
1471
|
-
|
|
1472
|
-
if (cb) cb()
|
|
1473
|
-
},
|
|
1474
|
-
|
|
1475
1090
|
/**
|
|
1476
1091
|
* @description start the tutorial for first time users
|
|
1477
1092
|
* @requires '../assets/data/onboardingMessages.json'
|
|
@@ -1487,13 +1102,17 @@ export default {
|
|
|
1487
1102
|
this.nextOnboarding('message_1')
|
|
1488
1103
|
} catch (e) {
|
|
1489
1104
|
//fetch default values for popups
|
|
1490
|
-
this.onboardingMessages =
|
|
1105
|
+
this.onboardingMessages = import(
|
|
1106
|
+
'../assets/data/onboardingMessages.json'
|
|
1107
|
+
)
|
|
1491
1108
|
this.nextOnboarding('message_1')
|
|
1492
1109
|
}
|
|
1493
1110
|
}
|
|
1494
1111
|
} else {
|
|
1495
1112
|
if (this.getOnboardingEnabled) {
|
|
1496
|
-
this.onboardingMessages =
|
|
1113
|
+
this.onboardingMessages = import(
|
|
1114
|
+
'../assets/data/onboardingMessages.json'
|
|
1115
|
+
)
|
|
1497
1116
|
this.nextOnboarding('message_1')
|
|
1498
1117
|
}
|
|
1499
1118
|
}
|
|
@@ -1518,8 +1137,6 @@ export default {
|
|
|
1518
1137
|
this.toolTipTarget = messages[postNextMessage].value.target
|
|
1519
1138
|
}
|
|
1520
1139
|
}
|
|
1521
|
-
|
|
1522
|
-
//check if next message exist
|
|
1523
1140
|
//check if the next message is a popup or other componant
|
|
1524
1141
|
if (messages[nextMessage].type == 'popup-avert') {
|
|
1525
1142
|
this.openPopup(messages[nextMessage])
|
|
@@ -1531,29 +1148,20 @@ export default {
|
|
|
1531
1148
|
* @description- Skip directly to the main containt*
|
|
1532
1149
|
* main content is Node with page ID by default
|
|
1533
1150
|
* Main Content can be defined by front-end using prop "main" in Module.vue of project
|
|
1151
|
+
* @fire {event} 'move-to-target' to AppBase
|
|
1534
1152
|
*/
|
|
1535
1153
|
|
|
1536
1154
|
skipToMain() {
|
|
1537
1155
|
const { main } = this.moduleConfig // check for main input
|
|
1538
|
-
let skipTo = document.querySelector(`#wrapper-content`) //default definition of main element
|
|
1539
|
-
|
|
1540
|
-
if (main) {
|
|
1541
|
-
let targetEl = document.querySelector(`#${main}`) // search for node element specified as main
|
|
1542
1156
|
|
|
1543
|
-
|
|
1544
|
-
}
|
|
1545
|
-
let targetTop = skipTo.offsetTop
|
|
1157
|
+
let skipTo = main ? main : 'wrapper-content' // search for node element specified as main
|
|
1546
1158
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1159
|
+
//fires event
|
|
1160
|
+
this.$bus.$emit('move-to-target', skipTo, {
|
|
1161
|
+
top: 100, // offset target top to 100 pixel
|
|
1549
1162
|
left: 0,
|
|
1550
|
-
behavior: 'auto'
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
//Allowing accessibility control with keyboard
|
|
1554
|
-
skipTo.setAttribute('tabIndex', -1)
|
|
1555
|
-
window.scrollTo(scrollOpt)
|
|
1556
|
-
this.resetFocus(skipTo)
|
|
1163
|
+
behavior: 'auto'
|
|
1164
|
+
})
|
|
1557
1165
|
},
|
|
1558
1166
|
|
|
1559
1167
|
/**
|
|
@@ -1703,7 +1311,6 @@ export default {
|
|
|
1703
1311
|
* @param {HTMLElement} e - element that will get focus
|
|
1704
1312
|
*/
|
|
1705
1313
|
resetFocus(e) {
|
|
1706
|
-
//console.log('set focus IN ', { from: document.activeElement, to: e })
|
|
1707
1314
|
if (e) e.focus()
|
|
1708
1315
|
},
|
|
1709
1316
|
|
|
@@ -1752,76 +1359,293 @@ export default {
|
|
|
1752
1359
|
this.closeDelay = false
|
|
1753
1360
|
clearTimeout(this.timeOut)
|
|
1754
1361
|
}
|
|
1362
|
+
},
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* @description Method to handle to sanding of completion status to LRS
|
|
1366
|
+
* determine wether to send a completion for an activity or the Lesson
|
|
1367
|
+
* Completion is sent once
|
|
1368
|
+
* @param {String} context - 'ACTIVITY | LESSON'
|
|
1369
|
+
*/
|
|
1370
|
+
|
|
1371
|
+
sendCompletionStatus(context = null) {
|
|
1372
|
+
if (
|
|
1373
|
+
!this.getModuleInfo.packageType === 'xapi' ||
|
|
1374
|
+
!this.getConnectionInfo ||
|
|
1375
|
+
!this.getConnectionInfo.actor ||
|
|
1376
|
+
!this.getConnectionInfo.remote ||
|
|
1377
|
+
!this.getDataFromServer ||
|
|
1378
|
+
!context
|
|
1379
|
+
)
|
|
1380
|
+
return
|
|
1381
|
+
|
|
1382
|
+
let stmt = null
|
|
1383
|
+
|
|
1384
|
+
switch (context) {
|
|
1385
|
+
case 'ACTIVITY': {
|
|
1386
|
+
let text
|
|
1387
|
+
let completedSize = 0
|
|
1388
|
+
Object.entries(this.getAllCompleted).forEach((value) => {
|
|
1389
|
+
completedSize = completedSize + value[1].length
|
|
1390
|
+
})
|
|
1391
|
+
let recordInServerLength,
|
|
1392
|
+
thisActivityProgressLength,
|
|
1393
|
+
thisActivityLength
|
|
1394
|
+
|
|
1395
|
+
const { userProgress } = this.getDataFromServer
|
|
1396
|
+
const thisActivityServerState =
|
|
1397
|
+
userProgress[this.$route.meta.activity_ref]
|
|
1398
|
+
|
|
1399
|
+
if (thisActivityServerState)
|
|
1400
|
+
recordInServerLength = Object.keys(thisActivityServerState).length
|
|
1401
|
+
|
|
1402
|
+
thisActivityProgressLength = this.getAllCompleted[
|
|
1403
|
+
this.$route.meta.activity_ref
|
|
1404
|
+
]
|
|
1405
|
+
? this.getAllCompleted[this.$route.meta.activity_ref].length
|
|
1406
|
+
: 0
|
|
1407
|
+
|
|
1408
|
+
//get The total length of the current activity
|
|
1409
|
+
thisActivityLength = this.getAllActivities(
|
|
1410
|
+
this.getCurrentPage.activityRef
|
|
1411
|
+
).pageSize
|
|
1412
|
+
|
|
1413
|
+
if (thisActivityProgressLength !== thisActivityLength) return
|
|
1414
|
+
if (thisActivityProgressLength == recordInServerLength) return
|
|
1415
|
+
|
|
1416
|
+
//Defining the text to display for stmt description and definition
|
|
1417
|
+
const id = this.getCurrentPage.activityRef
|
|
1418
|
+
let aName = ''
|
|
1419
|
+
switch (true) {
|
|
1420
|
+
case id == 'A00':
|
|
1421
|
+
aName = 'Introduction'
|
|
1422
|
+
break
|
|
1423
|
+
case id == 'A99':
|
|
1424
|
+
aName = 'Conclusion'
|
|
1425
|
+
break
|
|
1426
|
+
|
|
1427
|
+
default: {
|
|
1428
|
+
let d = id.replace('A', '').trim()
|
|
1429
|
+
d = parseInt(d)
|
|
1430
|
+
aName = `${this.$t('text.activity')} ${d}`
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
switch (this.$i18n.locale) {
|
|
1435
|
+
case 'fr':
|
|
1436
|
+
if (this.getModuleInfo.courseID)
|
|
1437
|
+
text = `${aName} de ${this.getModuleInfo.id} `
|
|
1438
|
+
else text = `Le ${this.getModuleInfo.id}`
|
|
1439
|
+
break
|
|
1440
|
+
case 'en':
|
|
1441
|
+
if (this.getModuleInfo.courseID)
|
|
1442
|
+
text = `${aName} of ${this.getModuleInfo.id}`
|
|
1443
|
+
else text = `The ${this.getModuleInfo.id}`
|
|
1444
|
+
break
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Retrive only user data in lessons front its interaction that we want to send to the LRS.
|
|
1448
|
+
// Note: User Settings are sent on a different URI and statement
|
|
1449
|
+
const { isFistTime, userSettings, ...lessonsData } =
|
|
1450
|
+
this.getUserInteraction
|
|
1451
|
+
|
|
1452
|
+
stmt = {
|
|
1453
|
+
id: this.getCurrentPage.activityRef,
|
|
1454
|
+
verb: 'completed',
|
|
1455
|
+
definition: text,
|
|
1456
|
+
description: text,
|
|
1457
|
+
extensions: [
|
|
1458
|
+
{
|
|
1459
|
+
id: 'user-data',
|
|
1460
|
+
content: {
|
|
1461
|
+
routeHistory: this.getRouteHistory,
|
|
1462
|
+
...lessonsData
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
],
|
|
1466
|
+
duration: this.activityDuration,
|
|
1467
|
+
completion: true
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
break
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
case 'LESSON': {
|
|
1474
|
+
//======================================================
|
|
1475
|
+
let completedSize = 0
|
|
1476
|
+
Object.entries(this.getAllCompleted).forEach((value) => {
|
|
1477
|
+
completedSize = completedSize + value[1].length
|
|
1478
|
+
})
|
|
1479
|
+
|
|
1480
|
+
if (completedSize === this.getAllActivities().pageSize) {
|
|
1481
|
+
let text
|
|
1482
|
+
//Defining the text to display for stmt description and definition
|
|
1483
|
+
switch (this.$i18n.locale) {
|
|
1484
|
+
case 'fr':
|
|
1485
|
+
if (this.getModuleInfo.courseID)
|
|
1486
|
+
text = `Le ${this.getModuleInfo.id} de ${this.getModuleInfo.courseID}`
|
|
1487
|
+
else text = `Le ${this.getModuleInfo.id}`
|
|
1488
|
+
break
|
|
1489
|
+
case 'en':
|
|
1490
|
+
if (this.getModuleInfo.courseID)
|
|
1491
|
+
text = `The ${this.getModuleInfo.id} of ${this.getModuleInfo.courseID}`
|
|
1492
|
+
else text = `The ${this.getModuleInfo.id}`
|
|
1493
|
+
break
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// Retrive only user data in lessons front its interaction that we want to send to the LRS.
|
|
1497
|
+
// Note: User Settings are sent on a different URI and statement
|
|
1498
|
+
const { isFistTime, userSettings, ...lessonsData } =
|
|
1499
|
+
this.getUserInteraction
|
|
1500
|
+
|
|
1501
|
+
stmt = {
|
|
1502
|
+
verb: 'completed',
|
|
1503
|
+
definition: text,
|
|
1504
|
+
description: text,
|
|
1505
|
+
extensions: [
|
|
1506
|
+
{
|
|
1507
|
+
id: 'user-data',
|
|
1508
|
+
content: {
|
|
1509
|
+
routeHistory: this.getRouteHistory,
|
|
1510
|
+
...lessonsData
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
],
|
|
1514
|
+
duration: this.lessonDuration,
|
|
1515
|
+
completion: true
|
|
1516
|
+
}
|
|
1517
|
+
this.lessonCompletionStatus = true // set the status of this lesson as completed
|
|
1518
|
+
let completedState = {
|
|
1519
|
+
duration: this.lessonDuration,
|
|
1520
|
+
completion: this.lessonCompletionStatus
|
|
1521
|
+
}
|
|
1522
|
+
this.$store.dispatch('updateDataFetchFromServer', {
|
|
1523
|
+
completedState
|
|
1524
|
+
})
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
break
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (!stmt) return
|
|
1531
|
+
|
|
1532
|
+
this.$bus.$emit('send-xapi-statement', stmt)
|
|
1533
|
+
},
|
|
1534
|
+
/**
|
|
1535
|
+
* @description Method handle start event of activity to LRS
|
|
1536
|
+
* Check if the activity is already initiated in The LRS record to determine wether it a start or a resume
|
|
1537
|
+
* @param {Object} a - Data of the activity
|
|
1538
|
+
* @fires 'send-xapi-statement' to APPBASE
|
|
1539
|
+
*/
|
|
1540
|
+
|
|
1541
|
+
sendStartStatement(a) {
|
|
1542
|
+
if (!a || !this.getDataFromServer) return
|
|
1543
|
+
const { id } = a
|
|
1544
|
+
let aName = null
|
|
1545
|
+
|
|
1546
|
+
switch (true) {
|
|
1547
|
+
case id == 'A00':
|
|
1548
|
+
aName = 'Introduction'
|
|
1549
|
+
break
|
|
1550
|
+
case id == 'A99':
|
|
1551
|
+
aName = 'Conclusion'
|
|
1552
|
+
break
|
|
1553
|
+
|
|
1554
|
+
default: {
|
|
1555
|
+
let d = id.replace('A', '').trim()
|
|
1556
|
+
d = parseInt(d)
|
|
1557
|
+
aName = `${this.$t('text.activity')} ${d}`
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
let text =
|
|
1562
|
+
this.$i18n.locale == 'fr'
|
|
1563
|
+
? `${aName} de ${this.getModuleInfo.id}`
|
|
1564
|
+
: `${aName} of ${this.getModuleInfo.id}`
|
|
1565
|
+
|
|
1566
|
+
/*
|
|
1567
|
+
*Determine if activity as been initialized. Activity is initialized when it is in the serverRecords
|
|
1568
|
+
*/
|
|
1569
|
+
const { userProgress } = this.getDataFromServer
|
|
1570
|
+
const thisActivityServerState = userProgress[id] ? userProgress[id] : {}
|
|
1571
|
+
const recordInServerLength = Object.keys(thisActivityServerState).length
|
|
1572
|
+
|
|
1573
|
+
const stmt = {
|
|
1574
|
+
id,
|
|
1575
|
+
verb: recordInServerLength ? 'resumed' : 'initialized', //determine verb of the statement to send
|
|
1576
|
+
definition: text,
|
|
1577
|
+
description: text
|
|
1578
|
+
}
|
|
1579
|
+
setTimeout(() => this.$bus.$emit('send-xapi-statement', stmt), 500)
|
|
1755
1580
|
}
|
|
1756
1581
|
}
|
|
1757
1582
|
}
|
|
1758
1583
|
</script>
|
|
1759
1584
|
<style lang="scss">
|
|
1760
|
-
|
|
1585
|
+
.module {
|
|
1586
|
+
width: 100%;
|
|
1587
|
+
height: 100%;
|
|
1588
|
+
min-height: 100vh;
|
|
1589
|
+
position: relative;
|
|
1590
|
+
display: flex;
|
|
1591
|
+
flex-direction: row;
|
|
1592
|
+
align-items: stretch;
|
|
1593
|
+
}
|
|
1761
1594
|
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1595
|
+
.skip-link {
|
|
1596
|
+
position: absolute;
|
|
1597
|
+
left: -999px;
|
|
1598
|
+
top: auto;
|
|
1599
|
+
width: 1px;
|
|
1600
|
+
height: 1px;
|
|
1601
|
+
overflow: hidden;
|
|
1602
|
+
z-index: -999;
|
|
1603
|
+
|
|
1604
|
+
&:focus,
|
|
1605
|
+
&:active {
|
|
1606
|
+
left: auto;
|
|
1766
1607
|
top: auto;
|
|
1767
|
-
width:
|
|
1768
|
-
height:
|
|
1769
|
-
overflow:
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
top: auto;
|
|
1776
|
-
width: 30%;
|
|
1777
|
-
height: auto;
|
|
1778
|
-
overflow: auto;
|
|
1779
|
-
margin: 10px 35%;
|
|
1780
|
-
padding: 5px;
|
|
1781
|
-
text-align: center;
|
|
1782
|
-
font-size: 1.2em;
|
|
1783
|
-
z-index: 999;
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
/********** FM **********/
|
|
1787
|
-
#Loading {
|
|
1788
|
-
width: 100%;
|
|
1789
|
-
position: fixed;
|
|
1790
|
-
z-index: 99999;
|
|
1791
|
-
left: 0;
|
|
1792
|
-
opacity: 0;
|
|
1793
|
-
display: none;
|
|
1794
|
-
transition: opacity 0.5s;
|
|
1795
|
-
pointer-events: none;
|
|
1608
|
+
width: 30%;
|
|
1609
|
+
height: auto;
|
|
1610
|
+
overflow: auto;
|
|
1611
|
+
margin: 10px 35%;
|
|
1612
|
+
padding: 5px;
|
|
1613
|
+
text-align: center;
|
|
1614
|
+
font-size: 1.2em;
|
|
1615
|
+
z-index: 999;
|
|
1796
1616
|
}
|
|
1797
1617
|
}
|
|
1798
|
-
|
|
1799
|
-
#
|
|
1800
|
-
width:
|
|
1801
|
-
|
|
1802
|
-
|
|
1618
|
+
/********** FM **********/
|
|
1619
|
+
#Loading {
|
|
1620
|
+
width: 100%;
|
|
1621
|
+
position: fixed;
|
|
1622
|
+
z-index: 99999;
|
|
1623
|
+
left: 0;
|
|
1624
|
+
opacity: 0;
|
|
1803
1625
|
display: none;
|
|
1626
|
+
transition: opacity 0.5s;
|
|
1804
1627
|
pointer-events: none;
|
|
1805
|
-
|
|
1806
|
-
@media screen and (max-width: 1100px) {
|
|
1807
|
-
display: block;
|
|
1808
|
-
pointer-events: all;
|
|
1809
|
-
position: absolute;
|
|
1810
|
-
top: 6px;
|
|
1811
|
-
right: 10px;
|
|
1812
|
-
z-index: 10;
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
svg {
|
|
1816
|
-
transition: transform 0.5s ease-out;
|
|
1817
|
-
transform: rotate(180deg);
|
|
1818
|
-
|
|
1819
|
-
&[data-hide='true'] {
|
|
1820
|
-
transform: rotate(0deg);
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
1628
|
}
|
|
1629
|
+
|
|
1630
|
+
// #Loading {
|
|
1631
|
+
// position: absolute;
|
|
1632
|
+
// width: 100%;
|
|
1633
|
+
// display: flex;
|
|
1634
|
+
// flex-direction: column;
|
|
1635
|
+
// //width: 33%;
|
|
1636
|
+
// height: 40%;
|
|
1637
|
+
// top: 50%;
|
|
1638
|
+
// align-items: center;
|
|
1639
|
+
// justify-content: center;
|
|
1640
|
+
// background-color: rgba($color: #000000, $alpha: 0.7);
|
|
1641
|
+
// border-radius: 10px;
|
|
1642
|
+
// box-shadow: 2px 2px 4px 0px rgba($color: #000000, $alpha: 0.2);
|
|
1643
|
+
// z-index: 99999;
|
|
1644
|
+
// pointer-events: none;
|
|
1645
|
+
// }
|
|
1824
1646
|
#right-sidebar {
|
|
1647
|
+
-webkit-box-shadow: -2px 1px 6px -1px rgb(0 0 0 / 40%);
|
|
1648
|
+
box-shadow: -2px 1px 6px -1px rgb(0 0 0 / 40%);
|
|
1825
1649
|
.b-sidebar-header button .app-icon {
|
|
1826
1650
|
width: 20px;
|
|
1827
1651
|
height: 20px;
|