qti3-item-player-vue3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,528 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
7
+ <link rel="stylesheet" href="./css/styles.css">
8
+ <script
9
+ src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"
10
+ integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg=="
11
+ crossorigin="anonymous"
12
+ referrerpolicy="no-referrer"
13
+ ></script>
14
+ <script>
15
+ let qtiCustomInteractionContext = {
16
+ // A field holding the interactions registered during initialization.
17
+ customInteractions: {},
18
+ interactions:[],
19
+
20
+ /**
21
+ * - Communication Bridge API: getInstance
22
+ *
23
+ * This method can be called by rendering engines to create a new
24
+ * instance of a PCI of the specified type.
25
+ * For composite items this method could be called multiple times
26
+ * on the same web page for the same typeIdentifier.
27
+ * @param {string} typeIdentifier the interaction type to create. Must be
28
+ * the same value as the custom-interaction-type-identifier attribute of the
29
+ * qti-portable-interaction
30
+ * @param {HTMLElement} dom The DOM element where the markup from the
31
+ * qti-portable-interaction element has been rendered.
32
+ * @param {Object.<string, Object>} configuration
33
+ * @param {string} state A representation of the state of the interaction which
34
+ * the customInteraction will later accept in a
35
+ * getInstance call to recreate an equivalent interaction state.
36
+ */
37
+ getInstance: function(typeIdentifier, dom, configuration, state) {
38
+ console.log('[PCI Context] Interaction GetInstance: ' + typeIdentifier);
39
+ const customInteraction = this.customInteractions[typeIdentifier];
40
+ //
41
+ // It is more reliable to get the instance in the onready callback.
42
+ // See notifyReady.
43
+ //
44
+ let instance = customInteraction.getInstance(dom, configuration, state);
45
+ //return instance;
46
+ },
47
+
48
+ /**
49
+ * - Communication Bridge API: register
50
+ *
51
+ * This method is called by Custom Interaction Hooks to register
52
+ * with the Rendering Engine for later cloning via a call to getInstance.
53
+ * This method is called by Custom Interaction Hooks during the loading of
54
+ * the JavaScript implementation.
55
+ *
56
+ * @param {Object} customInteraction A Custom Interaction object.
57
+ */
58
+ register: function(customInteraction) {
59
+ console.log('[PCI Context] Interaction Registered: ' + customInteraction.typeIdentifier);
60
+ this.customInteractions[customInteraction.typeIdentifier] = customInteraction;
61
+ },
62
+
63
+ /**
64
+ * - Communication Bridge API: notifyReady
65
+ *
66
+ * This method must be called by a Custom Interaction Instance
67
+ * to inform it is ready to be used.
68
+ *
69
+ * @callback notifyReady
70
+ * @param {Object} customInteraction The Custom Interaction Instance.
71
+ * @param {string} state A representation of the state of the interaction which
72
+ * the customInteraction will later accept in a
73
+ * getInstance call to recreate an equivalent interaction state.
74
+ */
75
+ notifyReady: function(customInteraction, state) {
76
+ console.log('[PCI Context] Interaction Ready: ' + customInteraction.typeIdentifier);
77
+ // IMPORTANT: Pass the instance back to the QTI_PCI_API
78
+ QTI_PCI_API.NotifyPciReady(customInteraction);
79
+ },
80
+
81
+ /**
82
+ * - Communication Bridge API: notifyDone
83
+ *
84
+ * The notifyDone method is optionally called by a Custom Interaction
85
+ * instance to notify its end. The method exists in the event a
86
+ * custom interaction has a determinate end.
87
+ *
88
+ * @callback notifyDone
89
+ * @param {Object} customInteraction The Custom Interaction Instance.
90
+ * @param {Object} response The final response value of the Custom Interaction Instance.
91
+ * @param {Object} state The final state of the Custom Interaction Instance.
92
+ * @param {Object} status The status of the Custom Interaction Instance.
93
+ */
94
+ notifyDone: function(customInteraction, response, state, status) {
95
+ console.log('[PCI Context] Interaction Done: ' + customInteraction.typeIdentifier);
96
+ QTI_PCI_API.NotifyPciDone(customInteraction, response, state, status);
97
+ },
98
+
99
+ /**
100
+ * Allow the delivery engine to notify the PCI instance that it is being
101
+ * destroyed via the Communication Bridge.
102
+ * If the PCI is interested in being notified it will provide a function
103
+ * to implement oncompleted.
104
+ */
105
+ oncompleted: function (instance) {
106
+ // Fire oncompleted if available
107
+ if ((typeof instance.oncompleted !== 'undefined') && (typeof instance.oncompleted == 'function')) {
108
+ console.log('[PCI Context] Firing Interaction oncompleted: ' + instance.typeIdentifier);
109
+ instance.oncompleted();
110
+ }
111
+ }
112
+ };
113
+
114
+ define('qtiCustomInteractionContext',[],function(){
115
+ return qtiCustomInteractionContext;
116
+ });
117
+
118
+ /**
119
+ * Provides an interface between the parent window and this child page
120
+ */
121
+ const QTI_PCI_API = {
122
+
123
+ typeIdentifier: '',
124
+ responseIdentifier: '',
125
+ pci: null,
126
+ dom: null,
127
+ properties: null,
128
+ contextVariables: null,
129
+ templateVariables: null,
130
+ moduleResolution: null,
131
+ instance: null,
132
+ response: null,
133
+ state: null,
134
+ boundTo: null,
135
+ status: 'interacting',
136
+ width: 0,
137
+ height: 0,
138
+ onQtiInteractionChanged: null,
139
+
140
+ getInstance: function () {
141
+ return this.instance;
142
+ },
143
+
144
+ setInstance: function (instance) {
145
+ this.instance = instance;
146
+ },
147
+
148
+ getTypeIdentifier: function () {
149
+ return this.typeIdentifier;
150
+ },
151
+
152
+ setTypeIdentifier: function (typeIdentifier) {
153
+ this.typeIdentifier = typeIdentifier;
154
+ },
155
+
156
+ getResponseIdentifier: function () {
157
+ return this.responseIdentifier;
158
+ },
159
+
160
+ setResponseIdentifier: function (responseIdentifier) {
161
+ this.responseIdentifier = responseIdentifier;
162
+ },
163
+
164
+ getPci: function () {
165
+ return this.pci;
166
+ },
167
+
168
+ setPci: function (pci) {
169
+ this.pci = pci;
170
+ },
171
+
172
+ getDom: function () {
173
+ return this.dom;
174
+ },
175
+
176
+ setDom: function (dom) {
177
+ this.dom = dom;
178
+ },
179
+
180
+ getProperties: function () {
181
+ return this.properties;
182
+ },
183
+
184
+ setProperties: function (properties) {
185
+ this.properties = properties;
186
+ },
187
+
188
+ getContextVariables: function () {
189
+ return this.contextVariables;
190
+ },
191
+
192
+ setContextVariables: function (contextVariables) {
193
+ this.contextVariables = contextVariables;
194
+ },
195
+
196
+ getTemplateVariables: function () {
197
+ return this.templateVariables;
198
+ },
199
+
200
+ setTemplateVariables: function (templateVariables) {
201
+ this.templateVariables = templateVariables;
202
+ },
203
+
204
+ getBoundTo: function () {
205
+ return this.boundTo;
206
+ },
207
+
208
+ setBoundTo: function (boundTo) {
209
+ this.boundTo = boundTo;
210
+ },
211
+
212
+ getStatus: function () {
213
+ return this.status;
214
+ },
215
+
216
+ setStatus: function (status) {
217
+ this.status = status;
218
+ },
219
+
220
+ getModuleResolution: function () {
221
+ return this.moduleResolution;
222
+ },
223
+
224
+ setModuleResolution: function (moduleResolution) {
225
+ this.moduleResolution = moduleResolution;
226
+ },
227
+
228
+ getState: function () {
229
+ return this.state;
230
+ },
231
+
232
+ setState: function (state) {
233
+ this.state = (typeof state === 'undefined') ? null : JSON.parse(state);
234
+ },
235
+
236
+ getStateFromState: function () {
237
+ return (this.state === null) ? null : this.state.state;
238
+ },
239
+
240
+ getResponseFromState: function () {
241
+ return (this.state === null) ? undefined : this.state.response;
242
+ },
243
+
244
+ /**
245
+ * @description Get the state and response from the PCI.
246
+ * Upon completion, call NotifyPciStateReady, passing the
247
+ * stringified response and state as parameters.
248
+ */
249
+ getInteractionState: function () {
250
+ const obj = {
251
+ response: null,
252
+ state: null
253
+ }
254
+
255
+ const instance = this.getInstance();
256
+
257
+ if (instance !== null) {
258
+ let state = instance.getState();
259
+ let response = instance.getResponse();
260
+ // Convert undefined response to null
261
+ obj.response = (typeof response === 'undefined') ? null : response;
262
+ obj.state = state;
263
+ }
264
+
265
+ this.NotifyPciStateReady(JSON.stringify(obj));
266
+ },
267
+
268
+ initialize: function (pci) {
269
+ this.setDom(document.getElementById('qti3-player-pci-element'));
270
+ this.setPci(pci);
271
+ this.setTypeIdentifier(pci.typeIdentifier);
272
+ this.setProperties(pci.properties);
273
+ this.setModuleResolution(pci.moduleResolution);
274
+ this.setContextVariables(pci.contextVariables);
275
+ this.setTemplateVariables(pci.templateVariables);
276
+ this.setBoundTo(pci.boundTo);
277
+ this.setStatus(pci.status)
278
+
279
+ this.trackResize(this.getDom());
280
+ this.trackQtiInteractionChanged(this.getDom());
281
+ this.injectClassAttribute();
282
+ this.injectMarkup();
283
+ this.launchPci(
284
+ this.getTypeIdentifier(),
285
+ this.getDom(),
286
+ this.getResponseIdentifier(),
287
+ this.getProperties(),
288
+ this.getContextVariables(),
289
+ this.getTemplateVariables(),
290
+ this.getStateFromState(),
291
+ this.getBoundTo(),
292
+ this.getStatus()
293
+ );
294
+ },
295
+
296
+ injectClassAttribute: function () {
297
+ if (this.getPci().classAttribute.length == 0) return
298
+ let wrapperEl = document.getElementById('qti3-player-pci-wrapper');
299
+ wrapperEl.classList.add(this.getPci().classAttribute);
300
+ },
301
+
302
+ injectMarkup: function () {
303
+ this.getDom().innerHTML =
304
+ `<div
305
+ id="qti3-player-pci-markup"
306
+ class="qti-interaction-markup"
307
+ >${this.getPci().markup}</div>`;
308
+ },
309
+
310
+ getModuleDependencies: function () {
311
+ // Init qtiCustomInteractionContext as the first dependency
312
+ let dependencies = ['qtiCustomInteractionContext'];
313
+
314
+ const paths = this.getModuleResolution().paths;
315
+ for (let property in paths) {
316
+ dependencies.push(property);
317
+ };
318
+
319
+ return dependencies;
320
+ },
321
+
322
+ /**
323
+ * @description This is the final step of the launch sequence. Require
324
+ * all moduleDependencies. This will result in the main module of the PCI to
325
+ * register itself in qtiCustomInteractionContext.
326
+ * @param {String} typeIdentifier the interaction type to create.
327
+ * @param {HTMLElement} dom
328
+ * @param {String} responseIdentifier the interaction's responseIdentifier
329
+ * @param {Object} properties
330
+ * @param {Object} contextVariables
331
+ * @param {Object} templateVariables
332
+ * @param {String} state A prior state
333
+ * @param {Object} boundTo The response variable this PCI is bound to and its value
334
+ * @param {String} status Item lifecycle status; e.g., 'interacting'
335
+ */
336
+ launchPci: function (
337
+ typeIdentifier,
338
+ dom,
339
+ responseIdentifier,
340
+ properties,
341
+ contextVariables,
342
+ templateVariables,
343
+ state,
344
+ boundTo,
345
+ status) {
346
+
347
+ console.log('[PCI Context] Interaction Dependencies: ', this.getModuleDependencies());
348
+
349
+ // Load module resolution
350
+ require.config(this.getModuleResolution());
351
+
352
+ // Launch it!
353
+ require(this.getModuleDependencies(), function(ctx) {
354
+
355
+ const configuration = {
356
+ properties: properties,
357
+ templateVariables: templateVariables,
358
+ contextVariables: contextVariables,
359
+ boundTo: boundTo,
360
+ responseIdentifier: responseIdentifier,
361
+ onready: ctx.notifyReady,
362
+ ondone: ctx.notifyDone,
363
+ status: status
364
+ };
365
+
366
+ console.log('[PCI Context] Configuration: ', configuration);
367
+ console.log('[PCI Context] State: ', (state === null ? undefined : state))
368
+
369
+ if (state === null)
370
+ ctx.getInstance(typeIdentifier, dom, configuration)
371
+ else
372
+ ctx.getInstance(typeIdentifier, dom, configuration, state);
373
+
374
+ });
375
+ },
376
+
377
+ NotifyPciChildLoaded: function () {
378
+ if (self == top) return;
379
+
380
+ // Extract the identifier qs param
381
+ const responseIdentifier = this.getQueryParameterByName('identifier');
382
+ this.setResponseIdentifier(responseIdentifier);
383
+
384
+ window.parent.postMessage({ message: 'PciChildLoaded', identifier: this.getResponseIdentifier(), success: true },'*');
385
+ },
386
+
387
+ NotifyPciReady: function (instance, state) {
388
+ this.setInstance(instance);
389
+
390
+ if (self == top) return;
391
+
392
+ const height = this.getDom().clientHeight;;
393
+ const computedHeight = (height) ? height : 0;
394
+ const width = this.getDom().clientWidth;
395
+ const computedWidth = (width) ? width : 0;
396
+
397
+ window.parent.postMessage({ message: 'PciReady', identifier: this.getResponseIdentifier(), width: computedWidth, height: computedHeight, success: true },'*');
398
+ },
399
+
400
+ NotifyPciDone: function (instance, response, state, status) {
401
+ if (self == top) return;
402
+
403
+ const stringifiedState = JSON.stringify({
404
+ response: (typeof response === 'undefined' ? null : response),
405
+ state: (typeof state === 'undefined' ? null : state)
406
+ });
407
+
408
+ window.parent.postMessage({ message: 'PciDone', identifier: this.getResponseIdentifier(), state: stringifiedStateObject, status: status, success: true },'*');
409
+ },
410
+
411
+ NotifyPciStateReady: function(stringifiedStateObject) {
412
+ if (self == top) return;
413
+ window.parent.postMessage({ message: 'PciGetState_Reply', identifier: this.getResponseIdentifier(), state: stringifiedStateObject, success: true },'*');
414
+ },
415
+
416
+ NotifyPciInteractionChanged: function(valid, value) {
417
+ if (self == top) return;
418
+
419
+ const stringifiedState = JSON.stringify({
420
+ valid: (typeof valid === 'undefined' ? null : valid),
421
+ value: (typeof value === 'undefined' ? null : value)
422
+ });
423
+ window.parent.postMessage({ message: 'PciInteractionChanged', identifier: this.getResponseIdentifier(), state: stringifiedStateObject, success: true },'*');
424
+ },
425
+
426
+ /**
427
+ * @description Generic function for parsing URL params.
428
+ * Via http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
429
+ *
430
+ * @method getQueryParameterByName
431
+ * @param {String} name The name of the paramter to get from the URL.
432
+ */
433
+ getQueryParameterByName: function (name) {
434
+ let regex = new RegExp("[\\?&]" + name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]') + '=([^&#]*)');
435
+ let results = regex.exec(window.location.search);
436
+
437
+ if (results === null) return '';
438
+
439
+ return decodeURIComponent(results[1].replace(/\+/g, " "));
440
+ },
441
+
442
+ bindWindowEventListener: function (element, eventName, eventHandler) {
443
+ if (element.addEventListener) {
444
+ element.addEventListener(eventName, eventHandler, false);
445
+ } else if (element.attachEvent) {
446
+ // For IE8 and older IE
447
+ element.attachEvent('on' + eventName, eventHandler);
448
+ }
449
+ },
450
+
451
+ trackResize: function (element) {
452
+ // create a mutation observer instance
453
+ let observer = new MutationObserver(function(mutations) {
454
+ mutations.forEach(function(mutation) {
455
+ let bounds = element.getBoundingClientRect();
456
+ let width = Math.ceil(bounds.right);
457
+ let height = Math.ceil(bounds.bottom);
458
+ if (Math.abs(width - this.width) > 15 || Math.abs(height - this.height) > 15) {
459
+ this.width = width;
460
+ this.height = height;
461
+ const msg = {
462
+ width: width,
463
+ height: height
464
+ };
465
+ window.parent.postMessage({ message: 'PciResize', identifier: this.getResponseIdentifier(), height: msg.height, width: msg.width, success: true },'*');
466
+ }
467
+ }.bind(this));
468
+ }.bind(this));
469
+
470
+ observer.observe(element, {
471
+ attributes: true,
472
+ childList: true,
473
+ characterData: true,
474
+ subtree: true
475
+ });
476
+ },
477
+
478
+ trackQtiInteractionChanged: function (domElement) {
479
+ this.onQtiInteractionChanged = this.onQtiInteractionChangedHandler.bind(this);
480
+ domElement.addEventListener('qti-interaction-changed', this.onQtiInteractionChanged);
481
+ },
482
+
483
+ onQtiInteractionChangedHandler: function(event) {
484
+ event.preventDefault();
485
+
486
+ if (typeof event.detail === 'undefined') {
487
+ console.log('[QtiInteractionChanged][Engine][Module Error]: event is missing required "detail" property', event);
488
+ return;
489
+ }
490
+
491
+ console.log('[QtiInteractionChanged][Engine]', event.detail);
492
+ this.NotifyPciInteractionChanged(event.detail.valid, event.detail.value);
493
+ }
494
+ };
495
+
496
+ window.onload = (event) => {
497
+
498
+ QTI_PCI_API.bindWindowEventListener(window, 'message', function(e) {
499
+ switch (e.data.message) {
500
+ case 'PciLoadInteraction':
501
+ QTI_PCI_API.setState(e.data.state);
502
+ QTI_PCI_API.initialize(JSON.parse(e.data.pci));
503
+ break;
504
+
505
+ case 'PciGetState_Request':
506
+ QTI_PCI_API.getInteractionState()
507
+ break;
508
+
509
+ case 'PciResizeContainerWidth':
510
+ break;
511
+
512
+ default:
513
+ }
514
+ });
515
+
516
+ // Notify the parent window that this Page is loaded.
517
+ // Upon receipt of this message, the parent window should
518
+ // send a PciLoadInteraction message to this page, which
519
+ // will launch the PCI with a configuration and state.
520
+ QTI_PCI_API.NotifyPciChildLoaded();
521
+ };
522
+ </script>
523
+ <body>
524
+ <div id="qti3-player-pci-wrapper" class="">
525
+ <div id="qti3-player-pci-element"></div>
526
+ </div>
527
+ </body>
528
+ </html>
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 280 280" style="enable-background:new 0 0 280 280;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#573C81;}
7
+ .st1{fill:#FFFFFF;}
8
+ </style>
9
+ <g id="Layer_1" transform="translate(-167 -214)">
10
+ <g>
11
+ <path class="st0" d="M380.47,378.2c-3.4,40.19-32.18,72.58-72.29,72.58c-40.01,0-68.6-32.49-72.1-72.58H192.1
12
+ c3.5,62.44,54.23,112.14,116.08,112.14c61.86,0,112.58-49.7,116.08-112.14H380.47z"/>
13
+ <polygon class="st1" points="289.52,246.63 308.12,222.79 326.98,247.06 308.26,271"/>
14
+ <g>
15
+ <polygon class="st0" points="236.4,368.2 308.18,275.52 379.96,368.2 424.44,368.2 308.18,218.09 191.93,368.2"/>
16
+ </g>
17
+ <polygon class="st1" points="289.52,246.63 308.12,222.79 326.98,247.06 308.26,271"/>
18
+ </g>
19
+ </g>
20
+ </svg>