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.
Files changed (78) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +13 -18
  2. package/bk.scss +117 -0
  3. package/package.json +23 -39
  4. package/src/$locales/en.json +30 -16
  5. package/src/$locales/fr.json +29 -16
  6. package/src/components/AppBase.vue +740 -305
  7. package/src/components/AppBaseButton.vue +33 -5
  8. package/src/components/AppBaseErrorDisplay.vue +43 -35
  9. package/src/components/AppBaseModule.vue +447 -623
  10. package/src/components/AppBasePage.vue +37 -25
  11. package/src/components/AppCompAudio.vue +266 -0
  12. package/src/components/AppCompBranchButtons.vue +52 -63
  13. package/src/components/AppCompButtonProgress.vue +1 -16
  14. package/src/components/AppCompCarousel.vue +43 -39
  15. package/src/components/AppCompInputCheckBox.vue +9 -3
  16. package/src/components/AppCompInputDropdown.vue +2 -4
  17. package/src/components/AppCompInputRadio.vue +8 -15
  18. package/src/components/AppCompInputTextTable.vue +15 -12
  19. package/src/components/AppCompInputTextToFillDropdown.vue +16 -14
  20. package/src/components/AppCompInputTextToFillText.vue +2 -2
  21. package/src/components/AppCompJauge.vue +13 -1
  22. package/src/components/AppCompMenu.vue +203 -10
  23. package/src/components/AppCompMenuItem.vue +20 -3
  24. package/src/components/AppCompNavigation.vue +351 -355
  25. package/src/components/AppCompNoteCall.vue +62 -47
  26. package/src/components/AppCompNoteCredit.vue +182 -79
  27. package/src/components/AppCompPlayBar.vue +975 -1023
  28. package/src/components/AppCompPlayBarProgress.vue +73 -0
  29. package/src/components/AppCompPopUp.vue +175 -114
  30. package/src/components/AppCompQuiz.vue +67 -81
  31. package/src/components/AppCompQuizRecall.vue +32 -5
  32. package/src/components/AppCompSVG.vue +66 -40
  33. package/src/components/AppCompSettingsMenu.vue +6 -8
  34. package/src/components/AppCompTableOfContent.vue +166 -45
  35. package/src/components/AppCompVideoPlayer.vue +154 -110
  36. package/src/components/BaseModule.vue +21 -17
  37. package/src/main.js +124 -88
  38. package/src/mixins/$mediaMixins.js +827 -0
  39. package/src/mixins/$pageMixins.js +65 -109
  40. package/src/mixins/$quizMixins.js +12 -26
  41. package/src/mixins/timerMixin.js +8 -9
  42. package/src/module/store.js +187 -68
  43. package/src/module/xapi/ADL.js +90 -53
  44. package/src/module/xapi/Crypto/Hasher.js +8 -8
  45. package/src/module/xapi/Crypto/WordArray.js +6 -6
  46. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
  47. package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
  48. package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
  49. package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
  50. package/src/module/xapi/Crypto/encoders/Base.js +7 -7
  51. package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
  52. package/src/module/xapi/Crypto/encoders/Hex.js +2 -2
  53. package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
  54. package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
  55. package/src/module/xapi/Statement/index.js +1 -1
  56. package/src/module/xapi/launch.js +10 -10
  57. package/src/module/xapi/utils.js +17 -17
  58. package/src/module/xapi/wrapper.js +123 -50
  59. package/src/module/xapi/xapiStatement.js +29 -29
  60. package/src/plugins/helper.js +8 -9
  61. package/src/plugins/i18n.js +23 -10
  62. package/src/plugins/scorm.js +14 -14
  63. package/src/router/index.js +3 -4
  64. package/src/router/routes.js +10 -30
  65. package/src/shared/generalfuncs.js +31 -24
  66. package/src/shared/validators.js +730 -20
  67. package/.prettierrc.js +0 -5
  68. package/babel.config.js +0 -3
  69. package/src/components/AppBaseDragChoice.vue +0 -91
  70. package/src/components/AppBaseDropZone.vue +0 -112
  71. package/src/components/AppCompBif.vue +0 -120
  72. package/src/components/AppCompDragAndDrop.vue +0 -339
  73. package/src/components/AppCompInputAssociation.vue +0 -332
  74. package/src/components/AppCompMediaPlayer.vue +0 -397
  75. package/src/plugins/timeManager.js +0 -77
  76. package/src/routes_bckp.js +0 -313
  77. package/src/routes_static.js +0 -344
  78. package/vue.config.js +0 -83
@@ -1,397 +0,0 @@
1
- <!--
2
- @ Description: This component is used to display media element (video/audio and Gsap timelined animation )
3
- @ What it does: The component create the HTMLMediaElement tag( video/audio) from data provided by user.
4
- Once the HTMLMediaElement tag is created, the component save the HTMLMediaElement to store and
5
- make it available to all other component that will need id
6
- -->
7
-
8
- <template>
9
- <div
10
- v-if="mData"
11
- id="contain-media-player"
12
- class="app-media-player"
13
- :class="[{ FS: fullScreen }, animationType, CCBrowser]"
14
- :style="{ height: [fsHeigh] }"
15
- >
16
- <!------------------video section --------------------------->
17
- <video
18
- v-if="media && media.mType === 'video'"
19
- id="video"
20
- ref="video"
21
- tabindex="0"
22
- class="v-media m-video"
23
- :poster="media.mPoster"
24
- @loadedmetadata="updateMediaData($event)"
25
- @load="() => {}"
26
- @error="() => {}"
27
- >
28
- <source
29
- v-for="(aMedia, index) in media.mSources"
30
- :key="index"
31
- :src="`${aMedia.src}`"
32
- :type="`${media.mType}/${aMedia.type}`"
33
- />
34
- <track
35
- v-if="media.mSubtitle"
36
- :src="media.mSubtitle.src"
37
- :srclang="media.mSubtitle.srclang"
38
- :label="media.mSubtitle.label"
39
- />
40
- </video>
41
- <!------------------ audio section --------------------------->
42
- <video
43
- v-if="media && media.mType === 'audio'"
44
- ref="audio"
45
- tabindex="0"
46
- class="v-media m-audio"
47
- :poster="media.mPoster"
48
- @loadedmetadata="updateMediaData($event)"
49
- >
50
- <source
51
- v-for="(aMedia, index) in media.mSources"
52
- :key="index"
53
- :src="`${aMedia.src}`"
54
- :type="`${media.mType}/${aMedia.type}`"
55
- />
56
- <track
57
- v-if="media.mSubtitle"
58
- :src="media.mSubtitle.src"
59
- :srclang="media.mSubtitle.srcLang"
60
- :label="media.mSubtitle.label"
61
- />
62
- </video>
63
- <!------------------animation section --------------------------->
64
- <div
65
- v-if="media && hasAnimation"
66
- id="anim-box"
67
- ref="anim-box"
68
- class="anim-canvas"
69
- :class="[animationType]"
70
- role="figure"
71
- aria-roledescription="Animation"
72
- >
73
- <p class="sr-only">{{ mediaA11Y.srTxt }}</p>
74
- <a class="skip-link" href="" @click.prevent="skipTo('playbar-play')">
75
- {{ mediaA11Y.skipToTxt }}
76
- </a>
77
- <slot ref="animScene" name="drawingCanvas">
78
- You have an animation to create
79
- </slot>
80
- </div>
81
- <!-- <div v-if="mData"> -->
82
- <app-comp-play-bar :media-to-play="mData" />
83
- <!-- </div> -->
84
- <!-- <portal-target :key="$route.fullPath" name="playbar-portal"></portal-target> -->
85
- </div>
86
- </template>
87
-
88
- <script>
89
- import { mapGetters } from 'vuex'
90
-
91
- export default {
92
- props: {
93
- mData: {
94
- type: Object,
95
- required: true
96
- },
97
- fullScreen: {
98
- type: Boolean,
99
- default: true
100
- },
101
- custom: {
102
- type: Boolean,
103
- default: false
104
- }
105
- },
106
-
107
- data() {
108
- return {
109
- media: null,
110
- timeline: null,
111
- subtitleMenuButtons: [],
112
- subtitlesMenu: null,
113
- fsHeigh: null,
114
- isMedia: false
115
- //currentMediaElement: null
116
- }
117
- },
118
- computed: {
119
- ...mapGetters([
120
- 'hasMediaElOrTimeline',
121
- 'getCurrentBrowser',
122
- 'getCurrentPage'
123
- ]),
124
-
125
- setTimeout() {
126
- return window.setTimeout
127
- },
128
-
129
- hasAnimation() {
130
- if (
131
- this.mData &&
132
- (this.mData.animation || this.mData.type === 'pg_animation')
133
- ) {
134
- return true
135
- } else return false
136
- },
137
- animationType() {
138
- let type = null
139
- if (this.mData) {
140
- if (this.mData.type === 'pg_animation') {
141
- type = 'animationOnly'
142
- }
143
- if (this.mData.type === 'pg_media') {
144
- if (this.mData.mediaData.mType === 'audio') {
145
- type = 'audioAnimation'
146
- } else if (this.mData.mediaData.mType === 'video') {
147
- type = 'videoAnimation'
148
- }
149
- }
150
- }
151
- return type
152
- },
153
- CCBrowser() {
154
- let browser = this.getCurrentBrowser
155
-
156
- if (browser === 'Safari') {
157
- return 'safari'
158
- } else {
159
- return 'chrome'
160
- }
161
- },
162
-
163
- mediaA11Y() {
164
- let m = {
165
- srTxt: '',
166
- skipToTxt: ''
167
- }
168
-
169
- this.$i18n.locale === 'fr'
170
- ? (m = {
171
- srTxt:
172
- "Le contenu d'apprentissage sera seulement disponible à la fin de la narration. Appuyez le bouton jouer, pour lancer l'animation.",
173
- skipToTxt: 'Passer au controleur de media'
174
- })
175
- : (m = {
176
- srTxt:
177
- 'The learning content will be available only at the end of the narration. Press the play button to start the animation',
178
- skipToTxt: 'Skip to play-bar'
179
- })
180
-
181
- return m
182
- }
183
- },
184
- watch: {
185
- media() {
186
- let mElement = this.$refs[this.media.mType]
187
- /* Handeling media subtitles
188
- * if there is any subtitle, create the subtitle menu
189
- */
190
- if (mElement && mElement.textTracks) {
191
- this.subtitlesMenu = []
192
- for (let i = 0; i < mElement.textTracks.length; i++) {
193
- this.subtitlesMenu.push({
194
- id: `subtitle-${mElement.textTracks[i].language}`,
195
- lang: mElement.textTracks[i].language,
196
- label: mElement.textTracks[i].label
197
- })
198
- }
199
- }
200
- },
201
-
202
- mData: {
203
- immediate: true,
204
- handler() {
205
- if (this.mData && this.mData.type == 'pg_media') {
206
- this.media = {
207
- mType: this.mData.mediaData.mType,
208
- mSources: [...this.mData.mediaData.mSources],
209
- mPoster: this.mData.mediaData.mPoster || '',
210
- mSubtitle: this.mData.mediaData.mSubtitle || null,
211
- mTranscript: this.mData.mediaData.mTranscript || null
212
- }
213
- }
214
- // Its an animation
215
- else if (this.mData && this.mData.type == 'pg_animation') {
216
- this.media = this.mData.animation
217
- }
218
- }
219
- }
220
- },
221
- mounted() {
222
- if (this.fullScreen) {
223
- this.fsHeigh = ` ${window.innerHeight - 55}px`
224
- } else {
225
- this.fsHeigh = `100%`
226
- }
227
-
228
- this.$bus.$emit('videoFullScreen', this.fullScreen)
229
- if (this.media) {
230
- window.addEventListener('resize', this.caclWindowHeight)
231
- }
232
- this.currentMediaElement
233
- },
234
-
235
- beforeDestroy() {
236
- this.$bus.$emit('videoFullScreen', false)
237
- window.removeEventListener('resize', this.caclWindowHeight)
238
- },
239
- methods: {
240
- hideSutitle() {
241
- return
242
- },
243
- showSubtitle() {
244
- return
245
- },
246
-
247
- /**
248
- * @description select available= subtitle for title for a media
249
- * @param {htmlElement} e
250
- */
251
- selectSubtitle(e) {
252
- let mElement = document.querySelector(
253
- `.app-media-player> ${this.media.mType}`
254
- )
255
- let subtitlesOption = this.$el.querySelectorAll('.subtitles-menu> a')
256
-
257
- //set all the subitle options as inactive
258
- for (let i = 0; i < subtitlesOption.length; i++) {
259
- subtitlesOption[i].setAttribute('data-state', 'inactive')
260
- subtitlesOption[i].className = 'sub_inactive'
261
- }
262
-
263
- for (let i = 0; i < mElement.textTracks.length; i++) {
264
- // For the 'subtitles-off' button, the first condition will never match so all will subtitles be turned off
265
- if (mElement.textTracks[i].language == e.target.lang) {
266
- mElement.textTracks[i].mode = 'showing'
267
- e.target.setAttribute('data-state', 'active')
268
- e.target.className = 'sub_active'
269
- } else {
270
- mElement.textTracks[i].mode = 'hidden'
271
- e.target.setAttribute('data-state', 'active')
272
- e.target.className = 'sub_active'
273
- }
274
- }
275
- },
276
-
277
- /**
278
- * @description update the information for the mediaElement in the store
279
- * @param {htmlElement} e
280
- * @fires update-page to AppBaseModule.vue
281
- */
282
- updateMediaData(e) {
283
- //dispatch loading status of for this component
284
- this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'loading')
285
-
286
- this.$store
287
- .dispatch('updateCurrentMediaElement', e.target)
288
- .then(() => {
289
- this.$bus.$emit('update-page')
290
- if (this.mData.timeline) {
291
- //Set the length (total duration) of the timeline to the duration of the media
292
- //Note: using totalDuration() or duration() will not work to define the duration to the length of the media.
293
- this.mData.timeline.set({}, {}, e.target.duration)
294
- }
295
- })
296
- .then(() =>
297
- this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
298
- )
299
- },
300
- caclWindowHeight() {
301
- document.getElementById(
302
- 'contain-media-player'
303
- ).style.height = `${window.innerHeight - 55}px`
304
- },
305
- // TODO: Checking that the media ressource exist/is available
306
- // true: create set the ressource src
307
- // false: disable play button, show on screen message media not available to user
308
- //
309
- async checkRessource(url) {
310
- this.axios
311
- .get(url, { responseType: 'blob' })
312
- .then((res) => {
313
- res
314
- })
315
- .catch((err) => {
316
- err
317
- })
318
- },
319
- /**
320
- * @description- Skip directly to the specify containt/region*
321
- * main content is Node with defined ID
322
- * @param {String} targetID- Css selector for the target element
323
- */
324
-
325
- skipTo(targetID) {
326
- let skipTo = document.querySelector(`#wrapper-content`) //default definition of main element
327
-
328
- if (targetID) {
329
- let targetEl = document.querySelector(`#${targetID}`) // search for node element specified as main
330
-
331
- if (targetEl) skipTo = targetEl
332
- }
333
- let targetTop = skipTo.offsetTop
334
-
335
- let scrollOpt = {
336
- top: targetTop - 100,
337
- left: 0,
338
- behavior: 'auto' //auto
339
- }
340
-
341
- //Allowing accessibility control with keyboard
342
- skipTo.setAttribute('tabIndex', -1)
343
- window.scrollTo(scrollOpt)
344
- skipTo.focus()
345
- },
346
-
347
- async setMediaElement() {
348
- const { type } = await this.getCurrentPage
349
- if (!['pg_media, pg_animation'].includes(type)) return null
350
-
351
- let mediaObject = {}
352
- switch (type) {
353
- case 'pg_media': {
354
- const {
355
- animation,
356
- type,
357
- mediaData: { mSources, mType, mSubtitle, mPoster, mTranscript },
358
- timeline,
359
- mElement
360
- } = this.getCurrentPage
361
-
362
- mediaObject = {
363
- animation,
364
- type,
365
- mediaData: { mSources, mType, mSubtitle, mPoster, mTranscript },
366
- timeline,
367
- mElement
368
- }
369
-
370
- break
371
- }
372
- case 'pg_animation': {
373
- const { animation, type, timeline } = this.getCurrentPage
374
- mediaObject = { animation, type, timeline }
375
- break
376
- }
377
- }
378
-
379
- return mediaObject
380
- }
381
- }
382
- }
383
- </script>
384
- <style lang="scss">
385
- $widthVideo: 100%;
386
- $widthAudioAnimation: 50%;
387
-
388
- %animation {
389
- position: absolute;
390
- top: 0;
391
- left: 0;
392
- width: $widthVideo;
393
- height: 100%;
394
- z-index: 1;
395
- overflow: hidden;
396
- }
397
- </style>
@@ -1,77 +0,0 @@
1
- export default function timeManager(Vue) {
2
- Vue.prototype.$timeManager = new Vue({
3
- data() {
4
- return {
5
- lessonTimeCounter: 0,
6
- //activityTimeCounter: 0,
7
- lessonDuration: 0,
8
- activityDuration: 0,
9
- activityInterval: null,
10
- lessonInterval: null,
11
- fullInterval: null
12
- }
13
- },
14
- methods: {
15
- /* Start the timer */
16
- startTimer(op) {
17
- if (op && !['activity', 'lesson'].includes(op))
18
- throw new Error('this is not a valid option for the timer')
19
-
20
- switch (op) {
21
- case 'activity':
22
- this.activityInterval = setInterval(() => {
23
- this.activityDuration += 1
24
- }, 1000)
25
-
26
- break
27
-
28
- case 'lesson':
29
- this.lessonInterval = setInterval(() => {
30
- this.lessonTimeCounter += 1
31
- }, 1000)
32
- break
33
-
34
- default:
35
- this.fullInterval = setInterval(() => {
36
- this.activityDuration += 1
37
- this.lessonDuration += 1
38
- }, 1000)
39
- }
40
- },
41
- /* Stop the timer and reset thimer to zero */
42
- stopTimer(op) {
43
- if (op && !['activity', 'lesson'].includes(op))
44
- throw new Error('this is not a valid parameter')
45
-
46
- if (op === 'activity' && this.activityDuration > 0) {
47
- clearInterval(this.activityInterval)
48
- this.activityDuration = 0
49
- } else if (op === 'lesson' && this.lessonDuration > 0) {
50
- clearInterval(this.lessonInterval)
51
- this.lessonDuration = 0
52
- } else {
53
- clearInterval(this.fullInterval)
54
- this.activityDuration = 0
55
- this.lessonDuration = 0
56
- }
57
- },
58
- /* Pause the timer but does not reset to zero*/
59
- pauseTimer(op) {
60
- if (op && !['activity', 'lesson'].includes(op))
61
- throw new Error('this is not a valid parameter')
62
-
63
- if (op === 'activity' && this.activityDuration > 0) {
64
- window.clearInterval(this.activityDuration)
65
- } else if (op === 'lesson') {
66
- window.clearInterval(this.lessonDuration)
67
- } else {
68
- clearInterval(this.fullInterval)
69
- }
70
- },
71
- formatTime(seconds) {
72
- let t = new Date(null)
73
- t.setSeconds(seconds)
74
- }
75
- }
76
- })
77
- }