fcad-core-dragon 2.0.0-beta.0 → 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/README.md +1 -1
- package/bk.scss +117 -0
- package/package.json +22 -40
- package/src/$locales/en.json +57 -19
- package/src/$locales/fr.json +66 -28
- package/src/components/AppBase.vue +790 -376
- package/src/components/AppBaseButton.vue +33 -5
- package/src/components/AppBaseErrorDisplay.vue +62 -25
- package/src/components/AppBaseModule.vue +831 -754
- package/src/components/AppBasePage.vue +60 -74
- package/src/components/AppCompAudio.vue +266 -0
- package/src/components/AppCompBranchButtons.vue +79 -89
- package/src/components/AppCompButtonProgress.vue +35 -61
- package/src/components/AppCompCarousel.vue +160 -249
- 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 +14 -3
- package/src/components/AppCompMenu.vue +284 -85
- package/src/components/AppCompMenuItem.vue +67 -92
- package/src/components/AppCompNavigation.vue +945 -0
- package/src/components/AppCompNoteCall.vue +141 -0
- package/src/components/AppCompNoteCredit.vue +267 -0
- package/src/components/AppCompPlayBar.vue +1122 -1391
- package/src/components/AppCompPlayBarProgress.vue +73 -0
- package/src/components/AppCompPopUp.vue +195 -135
- package/src/components/AppCompPopover.vue +27 -0
- package/src/components/AppCompQuiz.vue +90 -113
- package/src/components/AppCompQuizRecall.vue +277 -0
- package/src/components/AppCompSVG.vue +335 -0
- package/src/components/AppCompSettingsMenu.vue +7 -8
- package/src/components/AppCompTableOfContent.vue +264 -88
- package/src/components/AppCompTranscript.vue +19 -0
- package/src/components/AppCompVideoPlayer.vue +380 -0
- package/src/components/BaseModule.vue +37 -114
- package/src/main.js +130 -85
- package/src/mixins/$mediaMixins.js +827 -0
- package/src/mixins/$pageMixins.js +149 -115
- package/src/mixins/$quizMixins.js +12 -26
- package/src/mixins/timerMixin.js +39 -16
- package/src/module/store.js +218 -78
- 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 +4 -3
- 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 +127 -54
- package/src/module/xapi/xapiStatement.js +29 -29
- package/src/plugins/gsap.js +4 -1
- package/src/plugins/helper.js +58 -24
- package/src/plugins/i18n.js +23 -10
- package/src/plugins/idb.js +1 -0
- package/src/plugins/scorm.js +14 -14
- package/src/public/index.html +1 -1
- package/src/router/index.js +40 -0
- package/src/router/routes.js +317 -0
- package/src/shared/generalfuncs.js +91 -9
- package/src/shared/validators.js +959 -0
- 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/AppCompDragAndDrop.vue +0 -339
- package/src/components/AppCompInputAssociation.vue +0 -332
- package/src/components/AppCompMediaPlayer.vue +0 -365
- package/src/components/AppCompNavigationFull.vue +0 -1791
- package/src/components/AppCompToolTip.vue +0 -94
- package/src/plugins/timeManager.js +0 -77
- package/src/routes.js +0 -734
- package/vue.config.js +0 -83
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="input-box">
|
|
3
|
-
<div style="display:flex">
|
|
4
|
-
<div
|
|
5
|
-
v-for="singleAssociationZone in inputData.zone_depots"
|
|
6
|
-
:key="singleAssociationZone.id"
|
|
7
|
-
:class="
|
|
8
|
-
`associationlist-${singleAssociationZone.id} ` +
|
|
9
|
-
classInput(singleAssociationZone.id)
|
|
10
|
-
"
|
|
11
|
-
>
|
|
12
|
-
<label
|
|
13
|
-
:for="`${inputDataId}_${singleAssociationZone.id}`"
|
|
14
|
-
v-html="singleAssociationZone.contenu.alt"
|
|
15
|
-
></label>
|
|
16
|
-
<div
|
|
17
|
-
:class="
|
|
18
|
-
quizClickedZone == singleAssociationZone.id
|
|
19
|
-
? 'quizZoneActive'
|
|
20
|
-
: 'quizZone'
|
|
21
|
-
"
|
|
22
|
-
@click="activateChoice(singleAssociationZone.id)"
|
|
23
|
-
>
|
|
24
|
-
<!--zone is img-->
|
|
25
|
-
<div
|
|
26
|
-
v-if="singleAssociationZone.contenu.type == 'img'"
|
|
27
|
-
class="quizZoneIsImg"
|
|
28
|
-
>
|
|
29
|
-
<img
|
|
30
|
-
:src="singleAssociationZone.contenu.affichage"
|
|
31
|
-
:alt="singleAssociationZone.contenu.alt"
|
|
32
|
-
/>
|
|
33
|
-
</div>
|
|
34
|
-
<!--zone is text-->
|
|
35
|
-
<div
|
|
36
|
-
v-if="singleAssociationZone.contenu.type == 'str'"
|
|
37
|
-
class="quizZoneIsText"
|
|
38
|
-
>
|
|
39
|
-
<p v-html="singleAssociationZone.contenu.affichage"></p>
|
|
40
|
-
</div>
|
|
41
|
-
<!--selected item-->
|
|
42
|
-
<!---selected is img--->
|
|
43
|
-
<div
|
|
44
|
-
v-if="
|
|
45
|
-
quizAssociation[singleAssociationZone.id] &&
|
|
46
|
-
quizAssociation[singleAssociationZone.id] !== '' &&
|
|
47
|
-
inputData.choix_deplaceable[
|
|
48
|
-
quizAssociation[singleAssociationZone.id]
|
|
49
|
-
].contenu.type == 'img'
|
|
50
|
-
"
|
|
51
|
-
class="quizZoneWithImg"
|
|
52
|
-
>
|
|
53
|
-
<img
|
|
54
|
-
:src="
|
|
55
|
-
inputData.choix_deplaceable[
|
|
56
|
-
quizAssociation[singleAssociationZone.id]
|
|
57
|
-
].contenu.affichage
|
|
58
|
-
"
|
|
59
|
-
:alt="
|
|
60
|
-
inputData.choix_deplaceable[
|
|
61
|
-
quizAssociation[singleAssociationZone.id]
|
|
62
|
-
].contenu.alt
|
|
63
|
-
"
|
|
64
|
-
/>
|
|
65
|
-
</div>
|
|
66
|
-
<!---selected is text--->
|
|
67
|
-
<div
|
|
68
|
-
v-if="
|
|
69
|
-
quizAssociation[singleAssociationZone.id] &&
|
|
70
|
-
quizAssociation[singleAssociationZone.id] !== '' &&
|
|
71
|
-
inputData.choix_deplaceable[
|
|
72
|
-
quizAssociation[singleAssociationZone.id]
|
|
73
|
-
].contenu.type == 'str'
|
|
74
|
-
"
|
|
75
|
-
class="quizZoneWithText"
|
|
76
|
-
>
|
|
77
|
-
<p
|
|
78
|
-
v-html="
|
|
79
|
-
inputData.choix_deplaceable[
|
|
80
|
-
quizAssociation[singleAssociationZone.id]
|
|
81
|
-
].contenu.affichage
|
|
82
|
-
"
|
|
83
|
-
></p>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
<!--choices-->
|
|
89
|
-
<div
|
|
90
|
-
style="display:flex"
|
|
91
|
-
:class="quizClickedZone ? 'parentQuizChoicesActive' : 'parentQuizChoices'"
|
|
92
|
-
>
|
|
93
|
-
<div
|
|
94
|
-
v-for="singleAssociationChoice in inputData.choix_deplaceable"
|
|
95
|
-
:key="singleAssociationChoice.id"
|
|
96
|
-
:class="`associationlist-${singleAssociationChoice.id}`"
|
|
97
|
-
>
|
|
98
|
-
<label
|
|
99
|
-
:for="`${inputDataId}_${singleAssociationChoice.id}`"
|
|
100
|
-
v-html="singleAssociationChoice.contenu.alt"
|
|
101
|
-
></label>
|
|
102
|
-
<div
|
|
103
|
-
class="quizAssociationChoice"
|
|
104
|
-
@click="assignValueToZone(singleAssociationChoice.id)"
|
|
105
|
-
>
|
|
106
|
-
<!---is img--->
|
|
107
|
-
<div
|
|
108
|
-
v-if="singleAssociationChoice.contenu.type == 'img'"
|
|
109
|
-
class="quizChoiceWithImg"
|
|
110
|
-
>
|
|
111
|
-
<img
|
|
112
|
-
:src="singleAssociationChoice.contenu.affichage"
|
|
113
|
-
:alt="singleAssociationChoice.contenu.alt"
|
|
114
|
-
/>
|
|
115
|
-
</div>
|
|
116
|
-
<!---is text--->
|
|
117
|
-
<div
|
|
118
|
-
v-if="singleAssociationChoice.contenu.type == 'str'"
|
|
119
|
-
class="quizChoiceWithText"
|
|
120
|
-
>
|
|
121
|
-
<p v-html="singleAssociationChoice.contenu.affichage"></p>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
</template>
|
|
128
|
-
<script>
|
|
129
|
-
import $extendsQuiz from '../mixins/$quizMixins'
|
|
130
|
-
export default {
|
|
131
|
-
mixins: [$extendsQuiz],
|
|
132
|
-
|
|
133
|
-
props: {
|
|
134
|
-
inputType: {
|
|
135
|
-
type: String,
|
|
136
|
-
default: ''
|
|
137
|
-
},
|
|
138
|
-
inputDataId: {
|
|
139
|
-
type: String,
|
|
140
|
-
default: ''
|
|
141
|
-
},
|
|
142
|
-
inputData: {
|
|
143
|
-
type: Object,
|
|
144
|
-
default: () => {}
|
|
145
|
-
},
|
|
146
|
-
quizCompleted: {
|
|
147
|
-
type: Boolean,
|
|
148
|
-
default: false
|
|
149
|
-
},
|
|
150
|
-
solution: {
|
|
151
|
-
type: Object,
|
|
152
|
-
default: () => []
|
|
153
|
-
},
|
|
154
|
-
showSolution: {
|
|
155
|
-
type: Boolean,
|
|
156
|
-
default: false
|
|
157
|
-
},
|
|
158
|
-
shuffleAnswers: {
|
|
159
|
-
type: Boolean,
|
|
160
|
-
default: false
|
|
161
|
-
},
|
|
162
|
-
quizAssociation: {
|
|
163
|
-
type: Object,
|
|
164
|
-
default: () => {}
|
|
165
|
-
}, //use to pass the value of the input
|
|
166
|
-
quizSubmit: {
|
|
167
|
-
type: Boolean,
|
|
168
|
-
default: false
|
|
169
|
-
} //use to call a submit
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
data() {
|
|
173
|
-
return {
|
|
174
|
-
quizClickedZone: '', //for the id of the last clicked zone to assign the choice
|
|
175
|
-
quizAssociationValue: [] //not using quizAssociation because quizAssociation is a prop
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
computed: {
|
|
180
|
-
/**
|
|
181
|
-
* @description
|
|
182
|
-
*
|
|
183
|
-
quizZoneContent(zoneId) {
|
|
184
|
-
//check if zone is filled
|
|
185
|
-
if (zoneId == false) {
|
|
186
|
-
// check if zone has image
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
return zoneId
|
|
190
|
-
}*/
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
watch: {
|
|
194
|
-
/**
|
|
195
|
-
* @description to pass value to AppCompQuiz
|
|
196
|
-
* @fires input-change to AppCompQuiz.vue
|
|
197
|
-
*/
|
|
198
|
-
quizAssociationValue(newValue) {
|
|
199
|
-
this.$emit('input-change', newValue)
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* @description to pass the value from AppCompQuiz
|
|
204
|
-
*/
|
|
205
|
-
quizAssociation(newValue) {
|
|
206
|
-
this.quizAssociationValue = newValue
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
mounted() {
|
|
211
|
-
let associatedChoices = []
|
|
212
|
-
for (let i = 0; i < this.inputData.length; i++) {
|
|
213
|
-
let singleAssociation
|
|
214
|
-
if (this.shuffleAnswers) {
|
|
215
|
-
singleAssociation = this.inputData[i]
|
|
216
|
-
//@todo shuffle
|
|
217
|
-
singleAssociation.option = this.shuffleArray(singleAssociation.option)
|
|
218
|
-
} else {
|
|
219
|
-
singleAssociation = this.inputData[i]
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (this.quizAssociation.length == 0) {
|
|
223
|
-
this.quizAssociationValue = associatedChoices
|
|
224
|
-
} else {
|
|
225
|
-
this.quizAssociationValue = this.quizAssociation
|
|
226
|
-
}
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
methods: {
|
|
230
|
-
/**
|
|
231
|
-
* @description check if a values exists in a array
|
|
232
|
-
* @param {Array} array
|
|
233
|
-
* @param value
|
|
234
|
-
* @returns {Boolean}
|
|
235
|
-
*/
|
|
236
|
-
containsValue(array, value) {
|
|
237
|
-
return array.includes(value)
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* @description shuffles an array used to randomized the option order if shuffleAnswers is true
|
|
242
|
-
* @param {Array} array
|
|
243
|
-
* @returns {Array}
|
|
244
|
-
* @todo redo shuffle
|
|
245
|
-
*/
|
|
246
|
-
shuffleArray(array) {
|
|
247
|
-
let newArray = []
|
|
248
|
-
let newArray2 = []
|
|
249
|
-
|
|
250
|
-
for (let i = 0; i < array.length; i++) {
|
|
251
|
-
const element = array[i]
|
|
252
|
-
//todo remove null values
|
|
253
|
-
newArray.push(element)
|
|
254
|
-
}
|
|
255
|
-
while (newArray.length > 0) {
|
|
256
|
-
let pos = Math.floor(newArray.length * Math.random())
|
|
257
|
-
newArray2.push(newArray[pos])
|
|
258
|
-
newArray.splice(pos, 1)
|
|
259
|
-
}
|
|
260
|
-
return newArray2
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* @description activates choices to assign to that zone
|
|
265
|
-
* @param {String} zoneId
|
|
266
|
-
*/
|
|
267
|
-
activateChoice(zoneId) {
|
|
268
|
-
if (this.quizClickedZone == zoneId) {
|
|
269
|
-
this.quizClickedZone = ''
|
|
270
|
-
} else {
|
|
271
|
-
this.quizClickedZone = zoneId
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* @description remove current uses of value and assign value to quiz
|
|
277
|
-
* @param {String} choiceId
|
|
278
|
-
*/
|
|
279
|
-
assignValueToZone(choiceId) {
|
|
280
|
-
if (this.quizClickedZone !== '' && choiceId !== '') {
|
|
281
|
-
//remove current uses of value
|
|
282
|
-
for (const singleKey in this.quizAssociationValue) {
|
|
283
|
-
if (
|
|
284
|
-
Object.hasOwnProperty.call(this.quizAssociationValue, singleKey)
|
|
285
|
-
) {
|
|
286
|
-
const element = this.quizAssociationValue[singleKey]
|
|
287
|
-
if (element == choiceId) {
|
|
288
|
-
this.$set(this.quizAssociationValue, singleKey, '')
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
/*
|
|
293
|
-
let listKeys = Object.keys(this.quizAssociationValue)
|
|
294
|
-
for (let index = 0; index < listKeys.length; index++) {
|
|
295
|
-
if (this.quizAssociationValue[listKeys[index]] == choiceId) {
|
|
296
|
-
this.$set(this.quizAssociationValue, listKeys[index], '')
|
|
297
|
-
}
|
|
298
|
-
}*/
|
|
299
|
-
//assing value
|
|
300
|
-
this.$set(this.quizAssociationValue, this.quizClickedZone, choiceId)
|
|
301
|
-
//deactivate choices
|
|
302
|
-
this.quizClickedZone = ''
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
</script>
|
|
308
|
-
<style scoped>
|
|
309
|
-
.quizZone,
|
|
310
|
-
.quizZoneActive {
|
|
311
|
-
height: 300px;
|
|
312
|
-
width: 300px;
|
|
313
|
-
border: solid 1px;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
.quizZone {
|
|
317
|
-
border-color: black;
|
|
318
|
-
}
|
|
319
|
-
.quizZoneActive {
|
|
320
|
-
border-color: red;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.parentQuizChoicesActive {
|
|
324
|
-
background: yellow;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
.quizAssociationChoice {
|
|
328
|
-
height: 300px;
|
|
329
|
-
width: 300px;
|
|
330
|
-
border: solid black 1px;
|
|
331
|
-
}
|
|
332
|
-
</style>
|
|
@@ -1,365 +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="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
|
-
<source
|
|
35
|
-
v-for="(aMedia, index) in media.mSources"
|
|
36
|
-
:key="index"
|
|
37
|
-
:src="`${aMedia.src}`"
|
|
38
|
-
:type="`${media.mType}/${aMedia.type}`"
|
|
39
|
-
/>
|
|
40
|
-
<track
|
|
41
|
-
v-if="media.mSubtitle"
|
|
42
|
-
:src="media.mSubtitle.src"
|
|
43
|
-
:srclang="media.mSubtitle.srclang"
|
|
44
|
-
:label="media.mSubtitle.label"
|
|
45
|
-
/>
|
|
46
|
-
</video>
|
|
47
|
-
<!------------------ audio section --------------------------->
|
|
48
|
-
<video
|
|
49
|
-
v-if="media && media.mType === 'audio'"
|
|
50
|
-
ref="audio"
|
|
51
|
-
tabindex="0"
|
|
52
|
-
class="m-audio"
|
|
53
|
-
:poster="media.mPoster"
|
|
54
|
-
@loadedmetadata="updateMediaData($event)"
|
|
55
|
-
>
|
|
56
|
-
<source
|
|
57
|
-
v-for="(aMedia, index) in media.mSources"
|
|
58
|
-
:key="index"
|
|
59
|
-
:src="`${aMedia.src}`"
|
|
60
|
-
:type="`${media.mType}/${aMedia.type}`"
|
|
61
|
-
/>
|
|
62
|
-
<track
|
|
63
|
-
v-if="media.mSubtitle"
|
|
64
|
-
:src="media.mSubtitle.src"
|
|
65
|
-
:srclang="media.mSubtitle.srcLang"
|
|
66
|
-
:label="media.mSubtitle.label"
|
|
67
|
-
/>
|
|
68
|
-
</video>
|
|
69
|
-
<!------------------animation section --------------------------->
|
|
70
|
-
<div
|
|
71
|
-
v-if="media && hasAnimation"
|
|
72
|
-
id="anim-box"
|
|
73
|
-
class="anim-canvas"
|
|
74
|
-
:class="[animationType]"
|
|
75
|
-
>
|
|
76
|
-
<slot ref="animScene" name="drawingCanvas">
|
|
77
|
-
You have an animation to create
|
|
78
|
-
</slot>
|
|
79
|
-
</div>
|
|
80
|
-
<portal-target name="playbar-portal"></portal-target>
|
|
81
|
-
</div>
|
|
82
|
-
</template>
|
|
83
|
-
|
|
84
|
-
<script>
|
|
85
|
-
import { mapGetters } from 'vuex'
|
|
86
|
-
|
|
87
|
-
export default {
|
|
88
|
-
props: {
|
|
89
|
-
mData: {
|
|
90
|
-
type: Object,
|
|
91
|
-
required: true
|
|
92
|
-
},
|
|
93
|
-
fullScreen: {
|
|
94
|
-
type: Boolean,
|
|
95
|
-
default: true
|
|
96
|
-
},
|
|
97
|
-
custom: {
|
|
98
|
-
type: Boolean,
|
|
99
|
-
default: false
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
data() {
|
|
104
|
-
return {
|
|
105
|
-
media: null,
|
|
106
|
-
timeline: null,
|
|
107
|
-
subtitleMenuButtons: [],
|
|
108
|
-
subtitlesMenu: null,
|
|
109
|
-
randKey: `kid_${Math.floor(Math.random() * 1001)}`,
|
|
110
|
-
fsHeigh: null,
|
|
111
|
-
isMedia: false
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
computed: {
|
|
115
|
-
...mapGetters(['hasMediaElOrTimeline', 'getCurrentBrowser']),
|
|
116
|
-
setTimeout() {
|
|
117
|
-
return window.setTimeout
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
hasAnimation() {
|
|
121
|
-
if (
|
|
122
|
-
this.mData &&
|
|
123
|
-
(this.mData.animation || this.mData.type === 'pg_animation')
|
|
124
|
-
) {
|
|
125
|
-
return true
|
|
126
|
-
} else return false
|
|
127
|
-
},
|
|
128
|
-
animationType() {
|
|
129
|
-
let type = null
|
|
130
|
-
if (this.mData) {
|
|
131
|
-
if (this.mData.type === 'pg_animation') {
|
|
132
|
-
type = 'animationOnly'
|
|
133
|
-
}
|
|
134
|
-
if (this.mData.type === 'pg_media') {
|
|
135
|
-
if (this.mData.mediaData.mType === 'audio') {
|
|
136
|
-
type = 'audioAnimation'
|
|
137
|
-
} else if (this.mData.mediaData.mType === 'video') {
|
|
138
|
-
type = 'videoAnimation'
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return type
|
|
143
|
-
},
|
|
144
|
-
CCBrowser() {
|
|
145
|
-
let browser = this.getCurrentBrowser
|
|
146
|
-
|
|
147
|
-
if (browser === 'Safari') {
|
|
148
|
-
return 'safari'
|
|
149
|
-
} else {
|
|
150
|
-
return 'chrome'
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
watch: {
|
|
155
|
-
media(val, oldVal) {
|
|
156
|
-
let mElement = this.$refs[this.media.mType]
|
|
157
|
-
if (oldVal === null && val !== oldVal) {
|
|
158
|
-
//this.$store.dispatch('updateAppStatus', 'loading')
|
|
159
|
-
//this.$store.dispatch('updateCurrentMediaElement', mElement)
|
|
160
|
-
//this.fecthFromServer(this.media.mSources)
|
|
161
|
-
// for (let m of this.media.mSources) {
|
|
162
|
-
// this.checkRessource(m.src)
|
|
163
|
-
// }
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/* Handeling media subtitles
|
|
167
|
-
* if there is any subtitle, create the subtitle menu
|
|
168
|
-
*/
|
|
169
|
-
if (mElement && mElement.textTracks) {
|
|
170
|
-
this.subtitlesMenu = []
|
|
171
|
-
for (let i = 0; i < mElement.textTracks.length; i++) {
|
|
172
|
-
this.subtitlesMenu.push({
|
|
173
|
-
id: `subtitle-${mElement.textTracks[i].language}`,
|
|
174
|
-
lang: mElement.textTracks[i].language,
|
|
175
|
-
label: mElement.textTracks[i].label
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
mData: {
|
|
182
|
-
immediate: true,
|
|
183
|
-
handler() {
|
|
184
|
-
if (this.mData && this.mData.type == 'pg_media') {
|
|
185
|
-
this.media = {
|
|
186
|
-
mType: this.mData.mediaData.mType,
|
|
187
|
-
mSources: [...this.mData.mediaData.mSources],
|
|
188
|
-
mPoster: this.mData.mediaData.mPoster || '',
|
|
189
|
-
mSubtitle: this.mData.mediaData.mSubtitle || null,
|
|
190
|
-
mTranscript: this.mData.mediaData.mTranscript || null
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// Its an animation
|
|
194
|
-
else if (this.mData && this.mData.type == 'pg_animation') {
|
|
195
|
-
this.media = this.mData.animation
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
mounted() {
|
|
201
|
-
if (this.fullScreen) {
|
|
202
|
-
this.fsHeigh = ` ${window.innerHeight - 55}px`
|
|
203
|
-
} else {
|
|
204
|
-
this.fsHeigh = `100%`
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
this.$bus.$emit('videoFullScreen', this.fullScreen)
|
|
208
|
-
if (this.media) {
|
|
209
|
-
window.addEventListener('resize', this.caclWindowHeight)
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
beforeDestroy() {
|
|
214
|
-
this.$bus.$emit('videoFullScreen', false)
|
|
215
|
-
window.removeEventListener('resize', this.caclWindowHeight)
|
|
216
|
-
},
|
|
217
|
-
methods: {
|
|
218
|
-
hideSutitle() {
|
|
219
|
-
return
|
|
220
|
-
},
|
|
221
|
-
showSubtitle() {
|
|
222
|
-
return
|
|
223
|
-
},
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @description select available= subtitle for title for a media
|
|
227
|
-
* @param {htmlElement} e
|
|
228
|
-
*/
|
|
229
|
-
selectSubtitle(e) {
|
|
230
|
-
let mElement = document.querySelector(
|
|
231
|
-
`.app-media-player> ${this.media.mType}`
|
|
232
|
-
)
|
|
233
|
-
let subtitlesOption = this.$el.querySelectorAll('.subtitles-menu> a')
|
|
234
|
-
|
|
235
|
-
//set all the subitle options as inactive
|
|
236
|
-
for (let i = 0; i < subtitlesOption.length; i++) {
|
|
237
|
-
subtitlesOption[i].setAttribute('data-state', 'inactive')
|
|
238
|
-
subtitlesOption[i].className = 'sub_inactive'
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
for (let i = 0; i < mElement.textTracks.length; i++) {
|
|
242
|
-
// For the 'subtitles-off' button, the first condition will never match so all will subtitles be turned off
|
|
243
|
-
if (mElement.textTracks[i].language == e.target.lang) {
|
|
244
|
-
mElement.textTracks[i].mode = 'showing'
|
|
245
|
-
e.target.setAttribute('data-state', 'active')
|
|
246
|
-
e.target.className = 'sub_active'
|
|
247
|
-
} else {
|
|
248
|
-
mElement.textTracks[i].mode = 'hidden'
|
|
249
|
-
e.target.setAttribute('data-state', 'active')
|
|
250
|
-
e.target.className = 'sub_active'
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* @description update the information for the mediaElement in the store
|
|
257
|
-
* @param {htmlElement} e
|
|
258
|
-
* @fires update-page to AppBaseModule.vue
|
|
259
|
-
*/
|
|
260
|
-
updateMediaData(e) {
|
|
261
|
-
//dispatch loading status of for this component
|
|
262
|
-
this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'loading')
|
|
263
|
-
|
|
264
|
-
this.$store
|
|
265
|
-
.dispatch('updateCurrentMediaElement', e.target)
|
|
266
|
-
.then(() => {
|
|
267
|
-
this.$bus.$emit('update-page')
|
|
268
|
-
if (this.mData.timeline) {
|
|
269
|
-
//Set the length (total duration) of the timeline to the duration of the media
|
|
270
|
-
//Note: using totalDuration() or duration() will not work to define the duration to the length of the media.
|
|
271
|
-
this.mData.timeline.set({}, {}, e.target.duration)
|
|
272
|
-
}
|
|
273
|
-
})
|
|
274
|
-
.then(() =>
|
|
275
|
-
this.$bus.$emit('set-comp-status', 'AppCompMediaPlayer', 'ready')
|
|
276
|
-
)
|
|
277
|
-
},
|
|
278
|
-
caclWindowHeight() {
|
|
279
|
-
document.getElementById(
|
|
280
|
-
'contain-media-player'
|
|
281
|
-
).style.height = `${window.innerHeight - 55}px`
|
|
282
|
-
},
|
|
283
|
-
// TODO: Checking that the media ressource exist/is available
|
|
284
|
-
// true: create set the ressource src
|
|
285
|
-
// false: disable play button, show on screen message media not available to user
|
|
286
|
-
//
|
|
287
|
-
async checkRessource(url) {
|
|
288
|
-
this.axios
|
|
289
|
-
.get(url, { responseType: 'blob' })
|
|
290
|
-
.then((res) => {
|
|
291
|
-
res
|
|
292
|
-
})
|
|
293
|
-
.catch((err) => {
|
|
294
|
-
err
|
|
295
|
-
})
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
</script>
|
|
300
|
-
<style lang="scss">
|
|
301
|
-
$widthVideo: 100%;
|
|
302
|
-
$widthAudioAnimation: 100%;
|
|
303
|
-
|
|
304
|
-
%animation {
|
|
305
|
-
position: absolute;
|
|
306
|
-
top: 0;
|
|
307
|
-
left: 0;
|
|
308
|
-
width: $widthVideo;
|
|
309
|
-
height: 100%;
|
|
310
|
-
z-index: 1;
|
|
311
|
-
overflow: hidden;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
.app-media-player {
|
|
315
|
-
position: relative;
|
|
316
|
-
overflow: hidden;
|
|
317
|
-
|
|
318
|
-
&.AudioAnimation {
|
|
319
|
-
width: $widthAudioAnimation;
|
|
320
|
-
.anim-canvas {
|
|
321
|
-
@extend %animation;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
&.FS,
|
|
326
|
-
&.animationOnly {
|
|
327
|
-
width: 100%;
|
|
328
|
-
position: absolute;
|
|
329
|
-
top: 0;
|
|
330
|
-
left: 0;
|
|
331
|
-
|
|
332
|
-
video {
|
|
333
|
-
min-width: 100% !important;
|
|
334
|
-
min-height: 100% !important;
|
|
335
|
-
position: absolute !important;
|
|
336
|
-
top: 50% !important;
|
|
337
|
-
left: 50% !important;
|
|
338
|
-
transform: translate(-50%, -50%) !important;
|
|
339
|
-
z-index: 9;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.anim-canvas {
|
|
343
|
-
@extend %animation;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
&.FS,
|
|
348
|
-
&.AudioAnimation {
|
|
349
|
-
width: 100%;
|
|
350
|
-
position: absolute;
|
|
351
|
-
top: 0;
|
|
352
|
-
left: 0;
|
|
353
|
-
|
|
354
|
-
.anim-canvas {
|
|
355
|
-
@extend %animation;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
video {
|
|
361
|
-
&:focus-visible {
|
|
362
|
-
outline-color: transparent;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
</style>
|