qti3-item-player-vue3 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>