@citolab/qti-components 7.17.0 → 7.18.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.
- package/cdn/index.global.js +1 -1
- package/cdn/index.js +629 -278
- package/custom-elements.json +139 -1950
- package/dist/base.d.ts +11 -11
- package/dist/base.js +1 -1
- package/dist/{chunk-VEV4DGPH.js → chunk-3OCDS5FG.js} +2 -2
- package/dist/{chunk-C2HQFI2C.js → chunk-5D7GD3JS.js} +763 -481
- package/dist/chunk-5D7GD3JS.js.map +1 -0
- package/dist/{chunk-W4SQRNWO.js → chunk-5J5P5GJY.js} +4 -8
- package/dist/{chunk-W4SQRNWO.js.map → chunk-5J5P5GJY.js.map} +1 -1
- package/dist/{chunk-RI47B4ZT.js → chunk-DO3QY5GP.js} +2 -2
- package/dist/{chunk-DWIRLYDS.js → chunk-FOWDOLXY.js} +2 -2
- package/dist/{chunk-INKI27D5.js → chunk-GMNH42S6.js} +27 -36
- package/dist/chunk-GMNH42S6.js.map +1 -0
- package/dist/{chunk-2DOYPVF5.js → chunk-HRVIMT5Y.js} +4 -4
- package/dist/{chunk-O4XIWHTF.js → chunk-IDW2ETV2.js} +27 -12
- package/dist/chunk-IDW2ETV2.js.map +1 -0
- package/dist/{chunk-F44CI35W.js → chunk-L4TXKTUA.js} +2 -2
- package/dist/{chunk-JEUY3MYB.js → chunk-VUSGFL4B.js} +21 -17
- package/dist/chunk-VUSGFL4B.js.map +1 -0
- package/dist/{chunk-352OTVTY.js → chunk-YAPCC4BO.js} +37 -25
- package/dist/chunk-YAPCC4BO.js.map +1 -0
- package/dist/{computed-item.context-CiddHLPz.d.ts → computed-item.context-DfsW_96S.d.ts} +1 -1
- package/dist/{computed.context-CH09_LCR.d.ts → computed.context-DHbxad0N.d.ts} +2 -2
- package/dist/{config.context-DAdkDDf5.d.ts → config.context-DUz2-kxN.d.ts} +1 -1
- package/dist/elements.d.ts +5 -3
- package/dist/elements.js +4 -4
- package/dist/index.d.ts +10 -10
- package/dist/index.js +11 -11
- package/dist/{interaction-C5Up6-68.d.ts → interaction-BLQjAZqN.d.ts} +4 -4
- package/dist/interactions.d.ts +50 -119
- package/dist/interactions.js +4 -4
- package/dist/{item.context-BRKXBC3m.d.ts → item.context-CkUeDcaO.d.ts} +6 -1
- package/dist/item.css +1 -5
- package/dist/item.d.ts +3 -3
- package/dist/item.js +5 -5
- package/dist/loader.d.ts +3 -3
- package/dist/loader.js +2 -2
- package/dist/processing.d.ts +4 -4
- package/dist/processing.js +2 -2
- package/dist/qti-components-jsx.d.ts +6448 -2802
- package/dist/{qti-feedback-B4cMzOcq.d.ts → qti-feedback-BNXgxua1.d.ts} +2 -2
- package/dist/{qti-rule-base-dL4opfvi.d.ts → qti-rule-base-BZSb0WTW.d.ts} +4 -4
- package/dist/{qti-transform-test-Bz9A3hmD.d.ts → qti-transform-test-C2fL4PtU.d.ts} +1 -1
- package/dist/{test.context-Bpw1HNAZ.d.ts → test.context-Oc_8ovbV.d.ts} +3 -3
- package/dist/test.d.ts +28 -28
- package/dist/test.js +7 -7
- package/dist/transformers.d.ts +1 -1
- package/dist/transformers.js +1 -1
- package/dist/{variables-CusMRnyJ.d.ts → variables-CMYcDbyW.d.ts} +2 -1
- package/package.json +9 -9
- package/dist/chunk-352OTVTY.js.map +0 -1
- package/dist/chunk-C2HQFI2C.js.map +0 -1
- package/dist/chunk-INKI27D5.js.map +0 -1
- package/dist/chunk-JEUY3MYB.js.map +0 -1
- package/dist/chunk-O4XIWHTF.js.map +0 -1
- package/dist/vscode.css-custom-data.json +0 -11
- package/dist/vscode.html-custom-data.json +0 -960
- /package/dist/{chunk-VEV4DGPH.js.map → chunk-3OCDS5FG.js.map} +0 -0
- /package/dist/{chunk-RI47B4ZT.js.map → chunk-DO3QY5GP.js.map} +0 -0
- /package/dist/{chunk-DWIRLYDS.js.map → chunk-FOWDOLXY.js.map} +0 -0
- /package/dist/{chunk-2DOYPVF5.js.map → chunk-HRVIMT5Y.js.map} +0 -0
- /package/dist/{chunk-F44CI35W.js.map → chunk-L4TXKTUA.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
o as o2
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FOWDOLXY.js";
|
|
4
4
|
import {
|
|
5
5
|
M,
|
|
6
6
|
e as e2,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
s,
|
|
12
12
|
t as t2,
|
|
13
13
|
v
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-L4TXKTUA.js";
|
|
15
15
|
import {
|
|
16
16
|
liveQuery,
|
|
17
17
|
watch
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
t,
|
|
35
35
|
w,
|
|
36
36
|
x
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-VUSGFL4B.js";
|
|
38
38
|
import {
|
|
39
39
|
__decorateClass
|
|
40
40
|
} from "./chunk-EUXUH3YW.js";
|
|
@@ -1355,26 +1355,132 @@ QtiChoiceInteraction = __decorateClass([
|
|
|
1355
1355
|
], QtiChoiceInteraction);
|
|
1356
1356
|
|
|
1357
1357
|
// ../qti-interactions/src/components/qti-custom-interaction/qti-custom-interaction.ts
|
|
1358
|
+
var registerCES = `
|
|
1359
|
+
const postToParentWindows = (type, data) => {
|
|
1360
|
+
window.top.postMessage(data ? { type, data } : { type }, '*');
|
|
1361
|
+
let w = window.parent;
|
|
1362
|
+
while (w) {
|
|
1363
|
+
if (w !== window.top) {
|
|
1364
|
+
w.postMessage({ type, data }, '*');
|
|
1365
|
+
}
|
|
1366
|
+
if (w !== w.parent) {
|
|
1367
|
+
w = w.parent;
|
|
1368
|
+
} else {
|
|
1369
|
+
w = null;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
window.CES = {
|
|
1375
|
+
media: null,
|
|
1376
|
+
response: null,
|
|
1377
|
+
load: () => {
|
|
1378
|
+
let resolveCount = 0;
|
|
1379
|
+
|
|
1380
|
+
const handleMessage = (event) => {
|
|
1381
|
+
if (event.data.type === "mediaData") {
|
|
1382
|
+
const media = event.data.data;
|
|
1383
|
+
CES.media = media;
|
|
1384
|
+
resolveCount++;
|
|
1385
|
+
} else if (event.data.type === "responseData") {
|
|
1386
|
+
const response = event.data.data;
|
|
1387
|
+
if (response && Array.isArray(response) && response.length > 0) {
|
|
1388
|
+
// state is stored in the first element of the array
|
|
1389
|
+
CES.response = response[0];
|
|
1390
|
+
// Wait a short moment to ensure CES.response is set
|
|
1391
|
+
setTimeout(() => {
|
|
1392
|
+
// Re-create the Controller instance if it exists
|
|
1393
|
+
if (typeof Controller === 'function') {
|
|
1394
|
+
console.log("Re-creating Controller instance");
|
|
1395
|
+
ctrl = new Controller();
|
|
1396
|
+
}
|
|
1397
|
+
}, 50);
|
|
1398
|
+
} else {
|
|
1399
|
+
CES.response = response;
|
|
1400
|
+
}
|
|
1401
|
+
resolveCount++;
|
|
1402
|
+
}
|
|
1403
|
+
if (resolveCount === 2) {
|
|
1404
|
+
//window.removeEventListener("message", handleMessage);
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
window.addEventListener("message", handleMessage);
|
|
1408
|
+
postToParentWindows("getMedia");
|
|
1409
|
+
postToParentWindows("getResponse");
|
|
1410
|
+
},
|
|
1411
|
+
setResponse: (data) => {
|
|
1412
|
+
postToParentWindows("setResponse", data);
|
|
1413
|
+
},
|
|
1414
|
+
getResponse: () => {
|
|
1415
|
+
return CES.response;
|
|
1416
|
+
},
|
|
1417
|
+
getMedia: () => {
|
|
1418
|
+
return CES.media;
|
|
1419
|
+
},
|
|
1420
|
+
setStageHeight: () => {
|
|
1421
|
+
postToParentWindows("setStageHeight");
|
|
1422
|
+
},
|
|
1423
|
+
};
|
|
1424
|
+
CES.load();
|
|
1425
|
+
`;
|
|
1426
|
+
var ciBootstrap = `
|
|
1427
|
+
window.onload = function () {
|
|
1428
|
+
const handleMessage = (event) => {
|
|
1429
|
+
if (event.data.type === 'blobUrl') {
|
|
1430
|
+
const blobUrl = event.data.data;
|
|
1431
|
+
var n = document.createElement('iframe');
|
|
1432
|
+
n.frameBorder = '0';
|
|
1433
|
+
n.scrolling = 'no';
|
|
1434
|
+
n.src = blobUrl;
|
|
1435
|
+
n.style.width = '100%';
|
|
1436
|
+
n.style.height = '100%';
|
|
1437
|
+
document.body.appendChild(n);
|
|
1438
|
+
window.removeEventListener('message', handleMessage);
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
window.addEventListener('message', handleMessage);
|
|
1442
|
+
// Request the blob URL from parent
|
|
1443
|
+
let w = window.parent;
|
|
1444
|
+
while (w) {
|
|
1445
|
+
w.postMessage({ type: 'getBlobUrl' }, '*');
|
|
1446
|
+
if (w !== w.parent) {
|
|
1447
|
+
w = w.parent;
|
|
1448
|
+
} else {
|
|
1449
|
+
w = null;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
`;
|
|
1358
1454
|
var QtiCustomInteraction = class extends Interaction {
|
|
1359
1455
|
constructor() {
|
|
1360
1456
|
super();
|
|
1361
|
-
// This custom-interaction support the CES API which is use in FACET
|
|
1362
|
-
//
|
|
1363
|
-
// It works like this:
|
|
1364
|
-
// 1. The CI manifest is fetched
|
|
1365
|
-
// 2. An iframe is created and the first style and first script from the manifest are loaded
|
|
1366
|
-
// 3. The first script is bootstrap.js which also creates an iframe and loads the first media from the manifest
|
|
1367
|
-
// 4. Communication is done via the CES API but because the iframe is not allowed to access the global CES object we need to use window.postMessage
|
|
1368
|
-
// To achieve this we change the package by replacing the bootstrap.js with our own and inject a proxy CES API that communicates via postMessage
|
|
1369
|
-
// Because we also want to run this in storybook, we cannot use window.top because to send messages there, because in case of storybook that is not the top window.
|
|
1370
|
-
// So we send messages to all parent windows
|
|
1371
1457
|
this.rawResponse = "";
|
|
1458
|
+
this._manifestUrl = null;
|
|
1459
|
+
this._resourceBaseUrl = null;
|
|
1372
1460
|
this._errorMessage = null;
|
|
1461
|
+
// Pre-created blob URL for the index.html with injected CES proxy
|
|
1462
|
+
this._contentBlobUrl = null;
|
|
1373
1463
|
this.handlePostMessage = this.handlePostMessage.bind(this);
|
|
1374
1464
|
}
|
|
1375
1465
|
connectedCallback() {
|
|
1376
1466
|
super.connectedCallback();
|
|
1377
|
-
|
|
1467
|
+
let manifestPath = this.data;
|
|
1468
|
+
if (!manifestPath) {
|
|
1469
|
+
const objectEl = this.querySelector("object[data]");
|
|
1470
|
+
if (objectEl) {
|
|
1471
|
+
manifestPath = objectEl.getAttribute("data");
|
|
1472
|
+
const width = objectEl.getAttribute("width");
|
|
1473
|
+
const height = objectEl.getAttribute("height");
|
|
1474
|
+
if (width) this.setAttribute("width", width);
|
|
1475
|
+
if (height) this.setAttribute("height", height);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
if (!manifestPath) {
|
|
1479
|
+
this._errorMessage = "No manifest path found (neither data attribute nor object child)";
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
const uriToManifest = manifestPath.startsWith("http") || manifestPath.startsWith("blob") ? manifestPath : removeDoubleSlashes((this.baseItemUrl || "") + "/" + manifestPath);
|
|
1483
|
+
this._manifestUrl = new URL(uriToManifest, window.location.href).toString();
|
|
1378
1484
|
fetch(uriToManifest).then((response) => {
|
|
1379
1485
|
return response.json();
|
|
1380
1486
|
}).then((data) => {
|
|
@@ -1384,11 +1490,18 @@ var QtiCustomInteraction = class extends Interaction {
|
|
|
1384
1490
|
this._errorMessage = err;
|
|
1385
1491
|
});
|
|
1386
1492
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1493
|
+
/**
|
|
1494
|
+
* Sets up the CES custom interaction by creating the iframe structure and
|
|
1495
|
+
* handling the CES API communication via postMessage.
|
|
1496
|
+
*
|
|
1497
|
+
* For interactions that use CES, this method:
|
|
1498
|
+
* 1. Fetches the original bootstrap.js and checks if it uses CES
|
|
1499
|
+
* 2. If CES is used, fetches index.html and injects the registerCES proxy
|
|
1500
|
+
* 3. Creates a blob URL for the modified HTML (stored in _contentBlobUrl)
|
|
1501
|
+
* 4. Replaces bootstrap.js with ciBootstrap that loads the blob URL
|
|
1502
|
+
* 5. Sets up postMessage listeners for CES API calls
|
|
1503
|
+
*/
|
|
1504
|
+
async setupCES() {
|
|
1392
1505
|
const iframe = this.shadowRoot.querySelector("#pciContainer");
|
|
1393
1506
|
const iframeDoc = iframe.contentDocument;
|
|
1394
1507
|
if (!this.manifest.script || this.manifest.script.length === 0) {
|
|
@@ -1400,16 +1513,106 @@ var QtiCustomInteraction = class extends Interaction {
|
|
|
1400
1513
|
return;
|
|
1401
1514
|
}
|
|
1402
1515
|
const cssRef = this.manifest.style[0];
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1516
|
+
const baseCandidates = this.getBaseCandidates();
|
|
1517
|
+
console.debug("[qti-custom-interaction] manifest url", this._manifestUrl);
|
|
1518
|
+
console.debug("[qti-custom-interaction] base candidates", baseCandidates);
|
|
1519
|
+
console.debug("[qti-custom-interaction] refs", { cssRef, media: this.manifest.media });
|
|
1520
|
+
const cssResolved = await this.resolveResourceWithFallback(cssRef, baseCandidates);
|
|
1521
|
+
const cssUrl = cssResolved.url;
|
|
1522
|
+
if (cssResolved.baseUrl && !this._resourceBaseUrl) {
|
|
1523
|
+
this._resourceBaseUrl = cssResolved.baseUrl;
|
|
1524
|
+
}
|
|
1525
|
+
console.debug("[qti-custom-interaction] css resolved", cssResolved);
|
|
1526
|
+
const usesCES = true;
|
|
1527
|
+
console.debug("[qti-custom-interaction] using built-in ciBootstrap (skipping server bootstrap.js)");
|
|
1528
|
+
if (usesCES) {
|
|
1529
|
+
let indexUrl = "";
|
|
1530
|
+
if (this.manifest.media && this.manifest.media.length > 0) {
|
|
1531
|
+
const mediaRef = this.manifest.media[0];
|
|
1532
|
+
const indexResolved = await this.resolveResourceWithFallback(mediaRef, baseCandidates, {
|
|
1533
|
+
returnText: true
|
|
1534
|
+
});
|
|
1535
|
+
indexUrl = indexResolved.url;
|
|
1536
|
+
if (indexResolved.baseUrl && !this._resourceBaseUrl) {
|
|
1537
|
+
this._resourceBaseUrl = indexResolved.baseUrl;
|
|
1538
|
+
}
|
|
1539
|
+
console.debug("[qti-custom-interaction] index resolved (media)", {
|
|
1540
|
+
url: indexResolved.url,
|
|
1541
|
+
baseUrl: indexResolved.baseUrl,
|
|
1542
|
+
hasText: Boolean(indexResolved.text)
|
|
1543
|
+
});
|
|
1544
|
+
if (indexResolved.text) {
|
|
1545
|
+
let html = indexResolved.text;
|
|
1546
|
+
const cesScript = "<script>" + registerCES + "</script>";
|
|
1547
|
+
const headIndex = html.indexOf("<head>");
|
|
1548
|
+
if (headIndex !== -1) {
|
|
1549
|
+
html = html.slice(0, headIndex + 6) + cesScript + html.slice(headIndex + 6);
|
|
1550
|
+
} else {
|
|
1551
|
+
html = cesScript + html;
|
|
1552
|
+
}
|
|
1553
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
1554
|
+
this._contentBlobUrl = URL.createObjectURL(blob);
|
|
1555
|
+
}
|
|
1556
|
+
} else {
|
|
1557
|
+
const manifestPath = this.data || "manifest.json";
|
|
1558
|
+
const basePath = manifestPath.includes("/") ? manifestPath.substring(0, manifestPath.lastIndexOf("/")) : "";
|
|
1559
|
+
const indexPath = basePath ? `${basePath}/index.html` : "index.html";
|
|
1560
|
+
const indexResolved = await this.resolveResourceWithFallback(indexPath, baseCandidates, {
|
|
1561
|
+
returnText: true
|
|
1562
|
+
});
|
|
1563
|
+
indexUrl = indexResolved.url;
|
|
1564
|
+
if (indexResolved.baseUrl && !this._resourceBaseUrl) {
|
|
1565
|
+
this._resourceBaseUrl = indexResolved.baseUrl;
|
|
1566
|
+
}
|
|
1567
|
+
console.debug("[qti-custom-interaction] index resolved (fallback)", {
|
|
1568
|
+
url: indexResolved.url,
|
|
1569
|
+
baseUrl: indexResolved.baseUrl,
|
|
1570
|
+
hasText: Boolean(indexResolved.text)
|
|
1571
|
+
});
|
|
1572
|
+
if (indexResolved.text) {
|
|
1573
|
+
let html = indexResolved.text;
|
|
1574
|
+
const cesScript = "<script>" + registerCES + "</script>";
|
|
1575
|
+
const headIndex = html.indexOf("<head>");
|
|
1576
|
+
if (headIndex !== -1) {
|
|
1577
|
+
html = html.slice(0, headIndex + 6) + cesScript + html.slice(headIndex + 6);
|
|
1578
|
+
} else {
|
|
1579
|
+
html = cesScript + html;
|
|
1580
|
+
}
|
|
1581
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
1582
|
+
this._contentBlobUrl = URL.createObjectURL(blob);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (indexUrl && !this._contentBlobUrl) {
|
|
1586
|
+
try {
|
|
1587
|
+
const indexResponse = await fetch(indexUrl);
|
|
1588
|
+
if (indexResponse.ok) {
|
|
1589
|
+
let html = await indexResponse.text();
|
|
1590
|
+
const cesScript = "<script>" + registerCES + "</script>";
|
|
1591
|
+
const headIndex = html.indexOf("<head>");
|
|
1592
|
+
if (headIndex !== -1) {
|
|
1593
|
+
html = html.slice(0, headIndex + 6) + cesScript + html.slice(headIndex + 6);
|
|
1594
|
+
} else {
|
|
1595
|
+
html = cesScript + html;
|
|
1596
|
+
}
|
|
1597
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
1598
|
+
this._contentBlobUrl = URL.createObjectURL(blob);
|
|
1599
|
+
} else {
|
|
1600
|
+
console.error(`Failed to fetch index.html: ${indexResponse.status}`);
|
|
1601
|
+
}
|
|
1602
|
+
} catch (e5) {
|
|
1603
|
+
console.error(`Error fetching index.html: ${e5}`);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
const inlineScript = `<script>${ciBootstrap}</script>`;
|
|
1608
|
+
console.debug("[qti-custom-interaction] using ciBootstrap inline script");
|
|
1406
1609
|
window.addEventListener("message", this.handlePostMessage);
|
|
1407
1610
|
iframeDoc.open();
|
|
1408
1611
|
iframeDoc.write(`
|
|
1409
1612
|
<html>
|
|
1410
1613
|
<head>
|
|
1411
|
-
|
|
1412
|
-
|
|
1614
|
+
${cssUrl ? `<link href='${cssUrl}' rel="stylesheet" />` : ""}
|
|
1615
|
+
${inlineScript}
|
|
1413
1616
|
</head>
|
|
1414
1617
|
<body></body>
|
|
1415
1618
|
</html>
|
|
@@ -1471,6 +1674,9 @@ var QtiCustomInteraction = class extends Interaction {
|
|
|
1471
1674
|
}
|
|
1472
1675
|
handlePostMessage(event) {
|
|
1473
1676
|
const { type, data } = event.data;
|
|
1677
|
+
if (type && type !== "setResponse") {
|
|
1678
|
+
console.debug("[qti-custom-interaction] postMessage", { type, data });
|
|
1679
|
+
}
|
|
1474
1680
|
switch (type) {
|
|
1475
1681
|
case "setResponse":
|
|
1476
1682
|
if (data === null || !(Array.isArray(data) && data.length === 1 && data[0] === "")) {
|
|
@@ -1482,11 +1688,25 @@ var QtiCustomInteraction = class extends Interaction {
|
|
|
1482
1688
|
this.postToWindowAndIframes("responseData", this.rawResponse);
|
|
1483
1689
|
break;
|
|
1484
1690
|
}
|
|
1691
|
+
case "getBlobUrl": {
|
|
1692
|
+
if (this._contentBlobUrl) {
|
|
1693
|
+
this.postToWindowAndIframes("blobUrl", this._contentBlobUrl);
|
|
1694
|
+
}
|
|
1695
|
+
break;
|
|
1696
|
+
}
|
|
1485
1697
|
case "getMedia": {
|
|
1698
|
+
const baseCandidates = this.getBaseCandidates();
|
|
1486
1699
|
const mediaData = this.manifest.media.map((media) => {
|
|
1487
|
-
|
|
1488
|
-
|
|
1700
|
+
if (media.startsWith("http") || media.startsWith("blob")) {
|
|
1701
|
+
return media;
|
|
1702
|
+
}
|
|
1703
|
+
const baseUrl = this._resourceBaseUrl || baseCandidates[0];
|
|
1704
|
+
if (!baseUrl) {
|
|
1705
|
+
return media;
|
|
1706
|
+
}
|
|
1707
|
+
return new URL(media, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
1489
1708
|
});
|
|
1709
|
+
console.debug("[qti-custom-interaction] mediaData", mediaData);
|
|
1490
1710
|
this.postToWindowAndIframes("mediaData", mediaData);
|
|
1491
1711
|
break;
|
|
1492
1712
|
}
|
|
@@ -1543,6 +1763,51 @@ var QtiCustomInteraction = class extends Interaction {
|
|
|
1543
1763
|
${this._errorMessage}
|
|
1544
1764
|
</div>`}`;
|
|
1545
1765
|
}
|
|
1766
|
+
getBaseCandidates() {
|
|
1767
|
+
const candidates = [this._resourceBaseUrl, this.baseRefUrl, this.baseItemUrl, this.getManifestBaseUrl()];
|
|
1768
|
+
const resolved = candidates.filter(Boolean).map((base) => new URL(base, window.location.href).toString());
|
|
1769
|
+
const manifestParent = this.getManifestParentBaseUrl();
|
|
1770
|
+
if (manifestParent) {
|
|
1771
|
+
resolved.push(manifestParent);
|
|
1772
|
+
}
|
|
1773
|
+
return [...new Set(resolved)];
|
|
1774
|
+
}
|
|
1775
|
+
getManifestBaseUrl() {
|
|
1776
|
+
if (!this._manifestUrl) {
|
|
1777
|
+
return null;
|
|
1778
|
+
}
|
|
1779
|
+
return new URL(".", this._manifestUrl).toString();
|
|
1780
|
+
}
|
|
1781
|
+
getManifestParentBaseUrl() {
|
|
1782
|
+
if (!this._manifestUrl) {
|
|
1783
|
+
return null;
|
|
1784
|
+
}
|
|
1785
|
+
return new URL("..", this._manifestUrl).toString();
|
|
1786
|
+
}
|
|
1787
|
+
async resolveResourceWithFallback(ref, baseCandidates, options = {}) {
|
|
1788
|
+
if (!ref) {
|
|
1789
|
+
return { url: "", baseUrl: null };
|
|
1790
|
+
}
|
|
1791
|
+
if (ref.startsWith("http") || ref.startsWith("blob")) {
|
|
1792
|
+
return { url: ref, baseUrl: null };
|
|
1793
|
+
}
|
|
1794
|
+
for (const base of baseCandidates) {
|
|
1795
|
+
const baseUrl = new URL(base, window.location.href).toString();
|
|
1796
|
+
const url = new URL(ref, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
1797
|
+
try {
|
|
1798
|
+
const response = await fetch(url);
|
|
1799
|
+
if (response.ok) {
|
|
1800
|
+
if (options.returnText) {
|
|
1801
|
+
const text = await response.text();
|
|
1802
|
+
return { url, baseUrl, text };
|
|
1803
|
+
}
|
|
1804
|
+
return { url, baseUrl };
|
|
1805
|
+
}
|
|
1806
|
+
} catch (e5) {
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
return { url: "", baseUrl: null };
|
|
1810
|
+
}
|
|
1546
1811
|
};
|
|
1547
1812
|
__decorateClass([
|
|
1548
1813
|
n({ type: String, attribute: "data" })
|
|
@@ -3195,19 +3460,17 @@ var qti_portable_custom_interaction_styles_default = i`
|
|
|
3195
3460
|
var QtiPortableCustomInteraction = class extends Interaction {
|
|
3196
3461
|
constructor() {
|
|
3197
3462
|
super(...arguments);
|
|
3198
|
-
// Only used in iframe mode
|
|
3199
3463
|
this._iframeLoaded = false;
|
|
3200
3464
|
this._pendingMessages = [];
|
|
3201
|
-
this.
|
|
3465
|
+
this._iframeMessageOrigin = null;
|
|
3202
3466
|
this.requirePathsJson = "";
|
|
3203
3467
|
this.requireShimJson = "";
|
|
3204
3468
|
this.requireJsUrl = "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js";
|
|
3205
3469
|
this.baseUrl = "";
|
|
3206
|
-
this.useIframe = false;
|
|
3207
3470
|
this.useDefaultShims = false;
|
|
3208
3471
|
this.useDefaultPaths = false;
|
|
3209
3472
|
this._errorMessage = null;
|
|
3210
|
-
this.response =
|
|
3473
|
+
this.response = null;
|
|
3211
3474
|
this._parsedRequirePaths = null;
|
|
3212
3475
|
this._parsedRequireShim = null;
|
|
3213
3476
|
/**
|
|
@@ -3218,6 +3481,17 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3218
3481
|
if (!data || data.source !== "qti-pci-iframe") {
|
|
3219
3482
|
return;
|
|
3220
3483
|
}
|
|
3484
|
+
if (!this.iframe?.contentWindow || event.source !== this.iframe.contentWindow) {
|
|
3485
|
+
return;
|
|
3486
|
+
}
|
|
3487
|
+
if (data.responseIdentifier && data.responseIdentifier !== this.responseIdentifier) {
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
if (this._iframeMessageOrigin === null) {
|
|
3491
|
+
this._iframeMessageOrigin = event.origin;
|
|
3492
|
+
} else if (event.origin !== this._iframeMessageOrigin) {
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3221
3495
|
switch (data.method) {
|
|
3222
3496
|
case "iframeReady":
|
|
3223
3497
|
this.initializeInteraction();
|
|
@@ -3235,10 +3509,19 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3235
3509
|
}
|
|
3236
3510
|
break;
|
|
3237
3511
|
case "interactionChanged": {
|
|
3238
|
-
const
|
|
3239
|
-
this.
|
|
3512
|
+
const raw = data?.params?.value;
|
|
3513
|
+
const converted = raw && typeof raw === "object" ? this.convertQtiVariableJSON(raw) : null;
|
|
3514
|
+
const state = typeof data?.params?.state === "string" ? data.params.state : null;
|
|
3515
|
+
if (converted === null) {
|
|
3516
|
+
const emptyResponse = this.responseVariable?.cardinality === "single" ? "" : [];
|
|
3517
|
+
this.response = emptyResponse;
|
|
3518
|
+
this.validate();
|
|
3519
|
+
this.saveResponse(emptyResponse, state);
|
|
3520
|
+
break;
|
|
3521
|
+
}
|
|
3522
|
+
this.response = converted;
|
|
3240
3523
|
this.validate();
|
|
3241
|
-
this.saveResponse(
|
|
3524
|
+
this.saveResponse(converted, state);
|
|
3242
3525
|
break;
|
|
3243
3526
|
}
|
|
3244
3527
|
case "error":
|
|
@@ -3247,46 +3530,6 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3247
3530
|
break;
|
|
3248
3531
|
}
|
|
3249
3532
|
};
|
|
3250
|
-
this._onInteractionChanged = (event) => {
|
|
3251
|
-
event.stopPropagation();
|
|
3252
|
-
const value = this.convertQtiVariableJSON(event.detail.value);
|
|
3253
|
-
this.response = value;
|
|
3254
|
-
this.saveResponse(value);
|
|
3255
|
-
};
|
|
3256
|
-
/**
|
|
3257
|
-
* DIRECT MODE: Load config from URL
|
|
3258
|
-
*/
|
|
3259
|
-
this.loadConfig = async (url, baseUrl) => {
|
|
3260
|
-
url = this.removeDoubleSlashes(url);
|
|
3261
|
-
try {
|
|
3262
|
-
const requireConfig = await fetch(url);
|
|
3263
|
-
if (requireConfig.ok) {
|
|
3264
|
-
const config = await requireConfig.json();
|
|
3265
|
-
const moduleCong = config;
|
|
3266
|
-
for (const moduleId in moduleCong.paths) {
|
|
3267
|
-
if (baseUrl) {
|
|
3268
|
-
moduleCong.paths[moduleId] = this.getResolvablePath(moduleCong.paths[moduleId], baseUrl);
|
|
3269
|
-
}
|
|
3270
|
-
}
|
|
3271
|
-
return moduleCong;
|
|
3272
|
-
}
|
|
3273
|
-
} catch (e5) {
|
|
3274
|
-
}
|
|
3275
|
-
return null;
|
|
3276
|
-
};
|
|
3277
|
-
/**
|
|
3278
|
-
* DIRECT MODE: Helper method to get resolvable path string
|
|
3279
|
-
*/
|
|
3280
|
-
this.getResolvablePathString = (path, basePath) => {
|
|
3281
|
-
path = path.replace(/\.js$/, "");
|
|
3282
|
-
return path?.toLocaleLowerCase().startsWith("http") || !basePath ? path : this.removeDoubleSlashes(`${basePath}/${path}`);
|
|
3283
|
-
};
|
|
3284
|
-
/**
|
|
3285
|
-
* DIRECT MODE: Helper method to get resolvable path
|
|
3286
|
-
*/
|
|
3287
|
-
this.getResolvablePath = (path, basePath) => {
|
|
3288
|
-
return Array.isArray(path) ? path.map((p2) => this.getResolvablePathString(p2, basePath)) : this.getResolvablePathString(path, basePath);
|
|
3289
|
-
};
|
|
3290
3533
|
// Add this property to store the previous state
|
|
3291
3534
|
this._previousState = null;
|
|
3292
3535
|
}
|
|
@@ -3454,7 +3697,12 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3454
3697
|
}
|
|
3455
3698
|
}
|
|
3456
3699
|
validate() {
|
|
3457
|
-
return
|
|
3700
|
+
if (this.response === null || this.response === void 0) return false;
|
|
3701
|
+
if (Array.isArray(this.response)) {
|
|
3702
|
+
if (this.response.length === 0) return false;
|
|
3703
|
+
return this.response.some((v2) => v2 !== "" && v2 !== null && v2 !== void 0);
|
|
3704
|
+
}
|
|
3705
|
+
return this.response !== "";
|
|
3458
3706
|
}
|
|
3459
3707
|
set value(v2) {
|
|
3460
3708
|
if (v2 === null) {
|
|
@@ -3464,16 +3712,8 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3464
3712
|
}
|
|
3465
3713
|
}
|
|
3466
3714
|
get value() {
|
|
3467
|
-
if (this.
|
|
3468
|
-
|
|
3469
|
-
} else {
|
|
3470
|
-
const pciValue = this.pci?.getResponse();
|
|
3471
|
-
if (pciValue) {
|
|
3472
|
-
const convertedValue = this.convertQtiVariableJSON(pciValue);
|
|
3473
|
-
return Array.isArray(convertedValue) ? convertedValue.join(",") : convertedValue;
|
|
3474
|
-
}
|
|
3475
|
-
}
|
|
3476
|
-
return Array.isArray(this._value) ? this._value.join(",") : this._value?.toString() || null;
|
|
3715
|
+
if (this._value === null || this._value === void 0) return null;
|
|
3716
|
+
return Array.isArray(this._value) ? this._value.join(",") : String(this._value);
|
|
3477
3717
|
}
|
|
3478
3718
|
set boundTo(newValue) {
|
|
3479
3719
|
if (!newValue || !newValue[this.responseIdentifier]) {
|
|
@@ -3516,129 +3756,23 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3516
3756
|
}
|
|
3517
3757
|
return unescaped;
|
|
3518
3758
|
}
|
|
3519
|
-
/**
|
|
3520
|
-
* DIRECT MODE: Register PCI instance
|
|
3521
|
-
*/
|
|
3522
|
-
register(pci) {
|
|
3523
|
-
this.pci = pci;
|
|
3524
|
-
this.dom = this.querySelector("qti-interaction-markup");
|
|
3525
|
-
if (!this.dom) {
|
|
3526
|
-
this.dom = document.createElement("div");
|
|
3527
|
-
this.appendChild(this.dom);
|
|
3528
|
-
}
|
|
3529
|
-
this.dom.classList.add("qti-customInteraction");
|
|
3530
|
-
this.dom.style.width = "100%";
|
|
3531
|
-
this.dom.style.minHeight = "50px";
|
|
3532
|
-
this.dom.style.display = "block";
|
|
3533
|
-
this.dom.addEventListener("qti-interaction-changed", this._onInteractionChanged);
|
|
3534
|
-
if (this.querySelector("properties")) {
|
|
3535
|
-
this.querySelector("properties").style.display = "none";
|
|
3536
|
-
}
|
|
3537
|
-
const config = {
|
|
3538
|
-
properties: this.addHyphenatedKeys(this.unescapeDataAttributes({ ...this.dataset })),
|
|
3539
|
-
contextVariables: {},
|
|
3540
|
-
templateVariables: {},
|
|
3541
|
-
onready: (pciInstance) => {
|
|
3542
|
-
this.pci = pciInstance;
|
|
3543
|
-
try {
|
|
3544
|
-
const resizeObserver = new ResizeObserver((entries) => {
|
|
3545
|
-
for (const entry of entries) {
|
|
3546
|
-
if (entry.contentRect) {
|
|
3547
|
-
if (entry.contentRect.height > 0) {
|
|
3548
|
-
this.style.height = `${entry.contentRect.height + 20}px`;
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
}
|
|
3552
|
-
});
|
|
3553
|
-
resizeObserver.observe(this.dom);
|
|
3554
|
-
this._resizeObserver = resizeObserver;
|
|
3555
|
-
} catch (e5) {
|
|
3556
|
-
console.warn("ResizeObserver not supported, falling back to static sizing");
|
|
3557
|
-
}
|
|
3558
|
-
},
|
|
3559
|
-
ondone: (_pciInstance, response, _state, _status) => {
|
|
3560
|
-
this.response = this.convertQtiVariableJSON(response);
|
|
3561
|
-
this.saveResponse(this.response);
|
|
3562
|
-
},
|
|
3563
|
-
responseIdentifier: this.responseIdentifier,
|
|
3564
|
-
boundTo: this.boundTo
|
|
3565
|
-
};
|
|
3566
|
-
if (pci.getInstance) {
|
|
3567
|
-
pci.getInstance(this.dom, config, void 0);
|
|
3568
|
-
} else {
|
|
3569
|
-
const restoreTAOConfig = (element) => {
|
|
3570
|
-
const config2 = {};
|
|
3571
|
-
const parseDataAttributes = (element2) => {
|
|
3572
|
-
const result = {};
|
|
3573
|
-
Object.entries(element2.dataset).forEach(([key, value]) => {
|
|
3574
|
-
if (!key.includes("__")) {
|
|
3575
|
-
result[key] = value;
|
|
3576
|
-
}
|
|
3577
|
-
});
|
|
3578
|
-
const nestedData = {};
|
|
3579
|
-
Object.entries(element2.dataset).forEach(([key, value]) => {
|
|
3580
|
-
const parts = key.split("__");
|
|
3581
|
-
if (parts.length > 1) {
|
|
3582
|
-
const [group, index, prop] = parts;
|
|
3583
|
-
nestedData[group] = nestedData[group] || {};
|
|
3584
|
-
nestedData[group][index] = nestedData[group][index] || {};
|
|
3585
|
-
nestedData[group][index][prop] = value;
|
|
3586
|
-
}
|
|
3587
|
-
});
|
|
3588
|
-
Object.entries(nestedData).forEach(([key, group]) => {
|
|
3589
|
-
result[key] = Object.values(group);
|
|
3590
|
-
});
|
|
3591
|
-
return result;
|
|
3592
|
-
};
|
|
3593
|
-
const data = parseDataAttributes(element);
|
|
3594
|
-
for (const key in data) {
|
|
3595
|
-
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
3596
|
-
const value = data[key];
|
|
3597
|
-
if (key === "config") {
|
|
3598
|
-
config2[key] = JSON.parse(value);
|
|
3599
|
-
} else {
|
|
3600
|
-
config2[key] = value;
|
|
3601
|
-
}
|
|
3602
|
-
}
|
|
3603
|
-
}
|
|
3604
|
-
return config2;
|
|
3605
|
-
};
|
|
3606
|
-
const taoConfig = restoreTAOConfig(this);
|
|
3607
|
-
pci.initialize(
|
|
3608
|
-
this.customInteractionTypeIdentifier,
|
|
3609
|
-
this.dom.firstElementChild || this.dom,
|
|
3610
|
-
Object.keys(taoConfig).length ? taoConfig : null
|
|
3611
|
-
);
|
|
3612
|
-
}
|
|
3613
|
-
}
|
|
3614
|
-
/* ... rest of the code remains the same ... */
|
|
3615
3759
|
disconnectedCallback() {
|
|
3616
3760
|
super.disconnectedCallback();
|
|
3617
|
-
|
|
3618
|
-
window.removeEventListener("message", this.handleIframeMessage);
|
|
3619
|
-
} else {
|
|
3620
|
-
this.stopResponseCheck();
|
|
3621
|
-
if (this._resizeObserver) {
|
|
3622
|
-
this._resizeObserver.disconnect();
|
|
3623
|
-
this._resizeObserver = null;
|
|
3624
|
-
}
|
|
3625
|
-
requirejs.undef(this.customInteractionTypeIdentifier);
|
|
3626
|
-
const context = requirejs.s.contexts;
|
|
3627
|
-
delete context[this.customInteractionTypeIdentifier];
|
|
3628
|
-
this.removeEventListener("qti-interaction-changed", this._onInteractionChanged);
|
|
3629
|
-
}
|
|
3761
|
+
window.removeEventListener("message", this.handleIframeMessage);
|
|
3630
3762
|
}
|
|
3631
3763
|
/**
|
|
3632
3764
|
* IFRAME MODE: Send message to iframe
|
|
3633
3765
|
*/
|
|
3634
3766
|
sendMessageToIframe(method, params) {
|
|
3635
|
-
|
|
3767
|
+
const targetWindow = this.iframe?.contentWindow;
|
|
3768
|
+
if (!this._iframeLoaded || !targetWindow) {
|
|
3636
3769
|
this._pendingMessages.push({ method, params });
|
|
3637
3770
|
return;
|
|
3638
3771
|
}
|
|
3639
|
-
|
|
3772
|
+
targetWindow.postMessage(
|
|
3640
3773
|
{
|
|
3641
3774
|
source: "qti-portable-custom-interaction",
|
|
3775
|
+
responseIdentifier: this.responseIdentifier,
|
|
3642
3776
|
method,
|
|
3643
3777
|
params
|
|
3644
3778
|
},
|
|
@@ -3685,14 +3819,19 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3685
3819
|
* IFRAME MODE: Send initialization data to iframe
|
|
3686
3820
|
*/
|
|
3687
3821
|
sendIframeInitData() {
|
|
3822
|
+
const properties = this.addHyphenatedKeys(this.unescapeDataAttributes({ ...this.dataset }));
|
|
3823
|
+
const storedStateRaw = this.context?.state?.[this.responseIdentifier];
|
|
3824
|
+
const storedState = typeof storedStateRaw === "string" && storedStateRaw.length > 0 ? storedStateRaw : null;
|
|
3688
3825
|
const initData = {
|
|
3689
3826
|
module: this.module,
|
|
3690
3827
|
customInteractionTypeIdentifier: this.customInteractionTypeIdentifier,
|
|
3691
3828
|
baseUrl: !this.baseUrl ? window.location.origin : this.baseUrl.startsWith("http") || this.baseUrl.startsWith("blob") || this.baseUrl.startsWith("base64") ? this.baseUrl : removeDoubleSlashes(`${window.location.origin}${this.baseUrl}`),
|
|
3692
3829
|
responseIdentifier: this.responseIdentifier,
|
|
3830
|
+
properties,
|
|
3693
3831
|
dataAttributes: { ...this.dataset },
|
|
3694
3832
|
interactionModules: this.getInteractionModules(),
|
|
3695
|
-
boundTo: this.boundTo
|
|
3833
|
+
boundTo: storedState ? null : this.boundTo,
|
|
3834
|
+
state: storedState
|
|
3696
3835
|
};
|
|
3697
3836
|
this.sendMessageToIframe("initialize", initData);
|
|
3698
3837
|
}
|
|
@@ -3734,99 +3873,9 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3734
3873
|
}
|
|
3735
3874
|
connectedCallback() {
|
|
3736
3875
|
super.connectedCallback();
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
} else {
|
|
3741
|
-
define("qtiCustomInteractionContext", () => {
|
|
3742
|
-
return {
|
|
3743
|
-
register: (ctxA) => {
|
|
3744
|
-
this.register(ctxA);
|
|
3745
|
-
},
|
|
3746
|
-
notifyReady: () => {
|
|
3747
|
-
}
|
|
3748
|
-
};
|
|
3749
|
-
});
|
|
3750
|
-
const config = this.buildRequireConfig();
|
|
3751
|
-
if (config) {
|
|
3752
|
-
const requirePCI = requirejs.config(config);
|
|
3753
|
-
requirejs.onError = function(err) {
|
|
3754
|
-
console.error("RequireJS error:", err);
|
|
3755
|
-
if (err.requireType === "timeout") {
|
|
3756
|
-
console.error("Modules that timed out:", err.requireModules);
|
|
3757
|
-
}
|
|
3758
|
-
throw err;
|
|
3759
|
-
};
|
|
3760
|
-
requirePCI(["require"], (require2) => {
|
|
3761
|
-
try {
|
|
3762
|
-
require2([this.module], () => {
|
|
3763
|
-
}, (err) => {
|
|
3764
|
-
console.error("Error loading module:", err);
|
|
3765
|
-
});
|
|
3766
|
-
} catch (error) {
|
|
3767
|
-
console.error("Error in require call:", error);
|
|
3768
|
-
}
|
|
3769
|
-
});
|
|
3770
|
-
}
|
|
3771
|
-
}
|
|
3772
|
-
}
|
|
3773
|
-
/**
|
|
3774
|
-
* Stop checking for response changes
|
|
3775
|
-
*/
|
|
3776
|
-
stopResponseCheck() {
|
|
3777
|
-
if (this._responseCheckInterval !== null) {
|
|
3778
|
-
window.clearInterval(this._responseCheckInterval);
|
|
3779
|
-
this._responseCheckInterval = null;
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
/**
|
|
3783
|
-
* DIRECT MODE: Build RequireJS configuration
|
|
3784
|
-
*/
|
|
3785
|
-
buildRequireConfig() {
|
|
3786
|
-
const config = {
|
|
3787
|
-
context: this.customInteractionTypeIdentifier,
|
|
3788
|
-
catchError: true,
|
|
3789
|
-
paths: { ...this.getFinalRequirePaths(), ...window["requirePaths"] || {} },
|
|
3790
|
-
shim: { ...this.getFinalRequireShim(), ...window["requireShim"] || {} }
|
|
3791
|
-
};
|
|
3792
|
-
if (!globalThis.require) {
|
|
3793
|
-
this._errorMessage = `RequireJS not found. Please load it via CDN: https://cdnjs.com/libraries/require.js`;
|
|
3794
|
-
return null;
|
|
3795
|
-
}
|
|
3796
|
-
const baseUrl = this.getAttribute("data-base-url");
|
|
3797
|
-
const interactionModules = this.querySelector("qti-interaction-modules");
|
|
3798
|
-
if (interactionModules) {
|
|
3799
|
-
const modules = interactionModules.querySelectorAll("qti-interaction-module");
|
|
3800
|
-
for (const module of modules) {
|
|
3801
|
-
const moduleId = module.getAttribute("id");
|
|
3802
|
-
const primaryPath = module.getAttribute("primary-path");
|
|
3803
|
-
const fallbackPath = module.getAttribute("fallback-path");
|
|
3804
|
-
if (moduleId && primaryPath) {
|
|
3805
|
-
const paths = fallbackPath ? this.combineRequireResolvePaths(
|
|
3806
|
-
this.getResolvablePath(primaryPath, baseUrl),
|
|
3807
|
-
this.getResolvablePath(fallbackPath, baseUrl)
|
|
3808
|
-
) : this.getResolvablePath(primaryPath, baseUrl);
|
|
3809
|
-
const existingPath = config.paths[moduleId] || [];
|
|
3810
|
-
config.paths[moduleId] = this.combineRequireResolvePaths(existingPath, paths);
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
return config;
|
|
3815
|
-
}
|
|
3816
|
-
/**
|
|
3817
|
-
* DIRECT MODE: Helper method to combine require paths
|
|
3818
|
-
*/
|
|
3819
|
-
combineRequireResolvePaths(path1, path2) {
|
|
3820
|
-
const path1Array = Array.isArray(path1) ? path1 : [path1];
|
|
3821
|
-
const path2Array = Array.isArray(path2) ? path2 : [path2];
|
|
3822
|
-
return path1Array.concat(path2Array).filter((value, index, self) => self.indexOf(value) === index);
|
|
3823
|
-
}
|
|
3824
|
-
/**
|
|
3825
|
-
* DIRECT MODE: Helper method to remove double slashes
|
|
3826
|
-
*/
|
|
3827
|
-
removeDoubleSlashes(str) {
|
|
3828
|
-
const singleForwardSlashes = str.replace(/([^:]\/)\/+/g, "$1").replace(/\/\//g, "/").replace("http:/", "http://").replace("https:/", "https://");
|
|
3829
|
-
return singleForwardSlashes;
|
|
3876
|
+
this.response = this.responseVariable?.value ?? null;
|
|
3877
|
+
window.addEventListener("message", this.handleIframeMessage);
|
|
3878
|
+
this.createIframe();
|
|
3830
3879
|
}
|
|
3831
3880
|
/**
|
|
3832
3881
|
* IFRAME MODE: Generate iframe HTML content
|
|
@@ -3864,6 +3913,12 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3864
3913
|
}
|
|
3865
3914
|
#pci-container {
|
|
3866
3915
|
width: 100%;
|
|
3916
|
+
}
|
|
3917
|
+
qti-interaction-markup {
|
|
3918
|
+
display: block;
|
|
3919
|
+
width: 100%;
|
|
3920
|
+
min-height: 50px;
|
|
3921
|
+
}
|
|
3867
3922
|
</style>
|
|
3868
3923
|
<script src="${this.requireJsUrl}"></script>
|
|
3869
3924
|
<script>
|
|
@@ -3898,14 +3953,15 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3898
3953
|
console.error('Script error usually indicates a network or CORS issue with:', err.requireModules);
|
|
3899
3954
|
}
|
|
3900
3955
|
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3956
|
+
// Notify parent window about the error
|
|
3957
|
+
window.parent.postMessage({
|
|
3958
|
+
source: 'qti-pci-iframe',
|
|
3959
|
+
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
3960
|
+
method: 'error',
|
|
3961
|
+
params: {
|
|
3962
|
+
message: 'RequireJS ' + err.requireType + ' error for modules: ' + err.requireModules,
|
|
3963
|
+
details: {
|
|
3964
|
+
type: err.requireType,
|
|
3909
3965
|
modules: err.requireModules,
|
|
3910
3966
|
error: err.toString()
|
|
3911
3967
|
}
|
|
@@ -3918,13 +3974,96 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3918
3974
|
window.PCIManager = {
|
|
3919
3975
|
pciInstance: null,
|
|
3920
3976
|
container: null,
|
|
3977
|
+
markupEl: null,
|
|
3978
|
+
propertiesEl: null,
|
|
3921
3979
|
customInteractionTypeIdentifier: null,
|
|
3980
|
+
responseIdentifier: null,
|
|
3981
|
+
pendingBoundTo: null,
|
|
3982
|
+
pendingMarkup: null,
|
|
3983
|
+
pendingProperties: null,
|
|
3984
|
+
pendingState: null,
|
|
3985
|
+
interactionChangedViaEvent: false,
|
|
3986
|
+
eventBridgeAttached: false,
|
|
3987
|
+
lastResponseStr: null,
|
|
3988
|
+
hadResponse: false,
|
|
3922
3989
|
|
|
3923
3990
|
initialize: function(config) {
|
|
3924
3991
|
this.customInteractionTypeIdentifier = config.customInteractionTypeIdentifier;
|
|
3992
|
+
this.responseIdentifier = config.responseIdentifier;
|
|
3925
3993
|
this.container = document.getElementById('pci-container');
|
|
3926
3994
|
this.container.classList.add('qti-customInteraction');
|
|
3927
3995
|
|
|
3996
|
+
function qtiVariableHasValue(qtiVar) {
|
|
3997
|
+
if (!qtiVar) return false;
|
|
3998
|
+
if (qtiVar.base) {
|
|
3999
|
+
for (const k in qtiVar.base) {
|
|
4000
|
+
if (!Object.prototype.hasOwnProperty.call(qtiVar.base, k)) continue;
|
|
4001
|
+
const v = qtiVar.base[k];
|
|
4002
|
+
if (v !== null && v !== undefined && v !== '') return true;
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
if (qtiVar.list) {
|
|
4006
|
+
for (const k in qtiVar.list) {
|
|
4007
|
+
if (!Object.prototype.hasOwnProperty.call(qtiVar.list, k)) continue;
|
|
4008
|
+
const v = qtiVar.list[k];
|
|
4009
|
+
if (Array.isArray(v) && v.some(x => x !== null && x !== undefined && x !== '')) return true;
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
if (Array.isArray(qtiVar.record) && qtiVar.record.length > 0) return true;
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
const initialBoundTo = config.boundTo && config.boundTo[this.responseIdentifier];
|
|
4017
|
+
this.hadResponse = qtiVariableHasValue(initialBoundTo);
|
|
4018
|
+
this.lastResponseStr = this.hadResponse ? JSON.stringify(initialBoundTo) : null;
|
|
4019
|
+
// Ensure expected DOM structure exists (markup + properties)
|
|
4020
|
+
this.markupEl = this.container.querySelector('qti-interaction-markup');
|
|
4021
|
+
if (!this.markupEl) {
|
|
4022
|
+
this.markupEl = document.createElement('qti-interaction-markup');
|
|
4023
|
+
this.container.appendChild(this.markupEl);
|
|
4024
|
+
}
|
|
4025
|
+
this.markupEl.classList.add('qti-customInteraction');
|
|
4026
|
+
this.propertiesEl = this.container.querySelector('properties');
|
|
4027
|
+
if (!this.propertiesEl) {
|
|
4028
|
+
this.propertiesEl = document.createElement('properties');
|
|
4029
|
+
this.propertiesEl.style.display = 'none';
|
|
4030
|
+
this.container.appendChild(this.propertiesEl);
|
|
4031
|
+
} else {
|
|
4032
|
+
this.propertiesEl.style.display = 'none';
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
// Apply any markup/properties that arrived before initialization
|
|
4036
|
+
if (this.pendingMarkup !== null) {
|
|
4037
|
+
this.setMarkup(this.pendingMarkup);
|
|
4038
|
+
this.pendingMarkup = null;
|
|
4039
|
+
}
|
|
4040
|
+
if (this.pendingProperties !== null) {
|
|
4041
|
+
this.setProperties(this.pendingProperties);
|
|
4042
|
+
this.pendingProperties = null;
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
// Bridge qti-interaction-changed events (preferred over polling)
|
|
4046
|
+
if (!this.eventBridgeAttached) {
|
|
4047
|
+
this.eventBridgeAttached = true;
|
|
4048
|
+
const self = this;
|
|
4049
|
+
this.container.addEventListener(
|
|
4050
|
+
'qti-interaction-changed',
|
|
4051
|
+
function(evt) {
|
|
4052
|
+
try {
|
|
4053
|
+
self.interactionChangedViaEvent = true;
|
|
4054
|
+
const value = evt && evt.detail ? evt.detail.value : undefined;
|
|
4055
|
+
if (value !== undefined) {
|
|
4056
|
+
const state = self.pciInstance && typeof self.pciInstance.getState === 'function' ? self.pciInstance.getState() : null;
|
|
4057
|
+
self.notifyInteractionChanged(value, state);
|
|
4058
|
+
}
|
|
4059
|
+
} catch (e) {
|
|
4060
|
+
// ignore bridge errors, polling fallback may still work
|
|
4061
|
+
}
|
|
4062
|
+
},
|
|
4063
|
+
true
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
4066
|
+
|
|
3928
4067
|
function getResolvablePath(path, basePath) {
|
|
3929
4068
|
if (Array.isArray(path)) {
|
|
3930
4069
|
return path.map(p => getResolvablePathString(p, basePath));
|
|
@@ -3987,23 +4126,33 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
3987
4126
|
this.pciInstance = pciInstance;
|
|
3988
4127
|
// Configure PCI instance
|
|
3989
4128
|
const pciConfig = {
|
|
3990
|
-
properties:
|
|
4129
|
+
properties: config.properties || {},
|
|
3991
4130
|
contextVariables: config.contextVariables || {},
|
|
3992
4131
|
templateVariables: config.templateVariables || {},
|
|
3993
4132
|
onready: pciInstance => {
|
|
3994
4133
|
this.pciInstance = pciInstance;
|
|
4134
|
+
// Apply any pending updates that arrived before onready
|
|
4135
|
+
if (this.pendingBoundTo) {
|
|
4136
|
+
this.applyBoundTo(this.pendingBoundTo);
|
|
4137
|
+
this.pendingBoundTo = null;
|
|
4138
|
+
}
|
|
4139
|
+
if (this.pendingState && typeof this.pciInstance.setState === 'function') {
|
|
4140
|
+
this.pciInstance.setState(this.pendingState);
|
|
4141
|
+
this.pendingState = null;
|
|
4142
|
+
}
|
|
3995
4143
|
this.notifyReady();
|
|
3996
4144
|
},
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4145
|
+
ondone: (pciInstance, response, state, status) => {
|
|
4146
|
+
this.notifyInteractionChanged(response, typeof state === 'string' ? state : null);
|
|
4147
|
+
},
|
|
4148
|
+
responseIdentifier: config.responseIdentifier,
|
|
4149
|
+
boundTo: config.boundTo,
|
|
4150
|
+
};
|
|
4151
|
+
|
|
4152
|
+
if (pciInstance.getInstance) {
|
|
4153
|
+
const dom = this.markupEl || this.container;
|
|
4154
|
+
pciInstance.getInstance(dom, pciConfig, config.state || undefined);
|
|
4155
|
+
} else {
|
|
4007
4156
|
// TAO custom interaction initialization
|
|
4008
4157
|
const restoreTAOConfig = (dataset) => {
|
|
4009
4158
|
const config = {};
|
|
@@ -4011,7 +4160,7 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4011
4160
|
const result = {};
|
|
4012
4161
|
|
|
4013
4162
|
// Separate direct attributes from nested ones
|
|
4014
|
-
Object.entries(dataset ||
|
|
4163
|
+
Object.entries(dataset || {}).forEach(([key, value]) => {
|
|
4015
4164
|
if (!key.includes('__')) {
|
|
4016
4165
|
// Direct attributes (like version)
|
|
4017
4166
|
result[key] = value;
|
|
@@ -4021,7 +4170,7 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4021
4170
|
// Parse nested attributes
|
|
4022
4171
|
const nestedData = {};
|
|
4023
4172
|
|
|
4024
|
-
Object.entries(dataset ||
|
|
4173
|
+
Object.entries(dataset || {}).forEach(([key, value]) => {
|
|
4025
4174
|
const parts = key.split('__');
|
|
4026
4175
|
if (parts.length > 1) {
|
|
4027
4176
|
const [group, index, prop] = parts;
|
|
@@ -4054,17 +4203,13 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4054
4203
|
|
|
4055
4204
|
this.pciInstance.initialize(
|
|
4056
4205
|
this.customInteractionTypeIdentifier,
|
|
4057
|
-
this.container.firstElementChild || this.container,
|
|
4206
|
+
(this.markupEl || this.container).firstElementChild || (this.markupEl || this.container),
|
|
4058
4207
|
Object.keys(taoConfig).length ? taoConfig : null
|
|
4059
4208
|
);
|
|
4060
4209
|
}
|
|
4061
4210
|
},
|
|
4062
4211
|
notifyReady: () => {
|
|
4063
|
-
|
|
4064
|
-
window.parent.postMessage({
|
|
4065
|
-
source: 'qti-pci-iframe',
|
|
4066
|
-
method: 'pciReady'
|
|
4067
|
-
}, '*');
|
|
4212
|
+
PCIManager.notifyReady();
|
|
4068
4213
|
}
|
|
4069
4214
|
};
|
|
4070
4215
|
});
|
|
@@ -4094,73 +4239,121 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4094
4239
|
}
|
|
4095
4240
|
},
|
|
4096
4241
|
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4242
|
+
notifyReady: function() {
|
|
4243
|
+
window.parent.postMessage({
|
|
4244
|
+
source: 'qti-pci-iframe',
|
|
4245
|
+
responseIdentifier: this.responseIdentifier,
|
|
4246
|
+
method: 'iframeReady'
|
|
4247
|
+
}, '*');
|
|
4248
|
+
},
|
|
4249
|
+
|
|
4250
|
+
notifyInteractionChanged: function(response, state) {
|
|
4251
|
+
window.parent.postMessage({
|
|
4252
|
+
source: 'qti-pci-iframe',
|
|
4253
|
+
responseIdentifier: this.responseIdentifier,
|
|
4254
|
+
method: 'interactionChanged',
|
|
4255
|
+
params: { value: response, state: state }
|
|
4256
|
+
}, '*');
|
|
4257
|
+
},
|
|
4258
|
+
|
|
4259
|
+
notifyError: function(message) {
|
|
4260
|
+
console.error('PCI Error:', message);
|
|
4261
|
+
window.parent.postMessage({
|
|
4262
|
+
source: 'qti-pci-iframe',
|
|
4263
|
+
responseIdentifier: this.responseIdentifier,
|
|
4264
|
+
method: 'error',
|
|
4265
|
+
params: { message: message }
|
|
4266
|
+
}, '*');
|
|
4267
|
+
},
|
|
4103
4268
|
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4269
|
+
setMarkup: function(markupHtml) {
|
|
4270
|
+
if (!this.container) {
|
|
4271
|
+
this.container = document.getElementById('pci-container');
|
|
4272
|
+
}
|
|
4273
|
+
if (!this.container) {
|
|
4274
|
+
this.pendingMarkup = markupHtml;
|
|
4275
|
+
return;
|
|
4276
|
+
}
|
|
4277
|
+
this.markupEl = this.container.querySelector('qti-interaction-markup');
|
|
4278
|
+
if (!this.markupEl) {
|
|
4279
|
+
this.markupEl = document.createElement('qti-interaction-markup');
|
|
4280
|
+
this.container.appendChild(this.markupEl);
|
|
4281
|
+
}
|
|
4282
|
+
this.markupEl.classList.add('qti-customInteraction');
|
|
4283
|
+
this.markupEl.innerHTML = markupHtml || '';
|
|
4110
4284
|
},
|
|
4111
4285
|
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4286
|
+
setProperties: function(propertiesHtml) {
|
|
4287
|
+
if (!this.container) {
|
|
4288
|
+
this.container = document.getElementById('pci-container');
|
|
4289
|
+
}
|
|
4290
|
+
if (!this.container) {
|
|
4291
|
+
this.pendingProperties = propertiesHtml;
|
|
4292
|
+
return;
|
|
4293
|
+
}
|
|
4294
|
+
this.propertiesEl = this.container.querySelector('properties');
|
|
4295
|
+
if (!this.propertiesEl) {
|
|
4296
|
+
this.propertiesEl = document.createElement('properties');
|
|
4297
|
+
this.container.appendChild(this.propertiesEl);
|
|
4298
|
+
}
|
|
4299
|
+
this.propertiesEl.style.display = 'none';
|
|
4300
|
+
this.propertiesEl.innerHTML = propertiesHtml || '';
|
|
4119
4301
|
},
|
|
4120
4302
|
|
|
4121
|
-
|
|
4122
|
-
this.
|
|
4123
|
-
this.
|
|
4303
|
+
applyBoundTo: function(boundTo) {
|
|
4304
|
+
if (!this.pciInstance || typeof this.pciInstance.setResponse !== 'function') return;
|
|
4305
|
+
const value = boundTo && (boundTo[this.responseIdentifier] || boundTo[Object.keys(boundTo)[0]]);
|
|
4306
|
+
if (value) this.pciInstance.setResponse(value);
|
|
4124
4307
|
},
|
|
4308
|
+
};
|
|
4125
4309
|
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4310
|
+
// Set up message listener for communication with parent
|
|
4311
|
+
let expectedParentOrigin = null;
|
|
4312
|
+
window.addEventListener('message', function(event) {
|
|
4313
|
+
const { data } = event;
|
|
4314
|
+
|
|
4315
|
+
// Ensure the message is from our parent
|
|
4316
|
+
if (event.source !== window.parent || !data || data.source !== 'qti-portable-custom-interaction') {
|
|
4317
|
+
return;
|
|
4318
|
+
}
|
|
4319
|
+
if (expectedParentOrigin === null) {
|
|
4320
|
+
expectedParentOrigin = event.origin;
|
|
4321
|
+
} else if (event.origin !== expectedParentOrigin) {
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4325
|
+
function deepQuerySelector(root, selector) {
|
|
4326
|
+
if (!root) return null;
|
|
4327
|
+
try {
|
|
4328
|
+
const direct = root.querySelector ? root.querySelector(selector) : null;
|
|
4329
|
+
if (direct) return direct;
|
|
4330
|
+
} catch (e) {
|
|
4331
|
+
// ignore invalid selector for this root
|
|
4133
4332
|
}
|
|
4134
|
-
return
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
unescaped[key] = value
|
|
4141
|
-
.replace(/&/g, '&')
|
|
4142
|
-
.replace(/</g, '<')
|
|
4143
|
-
.replace(/>/g, '>')
|
|
4144
|
-
.replace(/"/g, '"')
|
|
4145
|
-
.replace(/'/g, "'")
|
|
4146
|
-
.replace(///g, '/')
|
|
4147
|
-
.replace(/`/g, '\`')
|
|
4148
|
-
.replace(/=/g, '=');
|
|
4149
|
-
} else {
|
|
4150
|
-
unescaped[key] = value;
|
|
4333
|
+
if (!root.querySelectorAll) return null;
|
|
4334
|
+
const nodes = root.querySelectorAll('*');
|
|
4335
|
+
for (const node of nodes) {
|
|
4336
|
+
if (node && node.shadowRoot) {
|
|
4337
|
+
const found = deepQuerySelector(node.shadowRoot, selector);
|
|
4338
|
+
if (found) return found;
|
|
4151
4339
|
}
|
|
4152
4340
|
}
|
|
4153
|
-
return
|
|
4154
|
-
}
|
|
4155
|
-
};
|
|
4156
|
-
|
|
4157
|
-
// Set up message listener for communication with parent
|
|
4158
|
-
window.addEventListener('message', function(event) {
|
|
4159
|
-
const { data } = event;
|
|
4341
|
+
return null;
|
|
4342
|
+
}
|
|
4160
4343
|
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4344
|
+
function deepFindElementByExactText(root, text) {
|
|
4345
|
+
if (!root || !text) return null;
|
|
4346
|
+
if (root.querySelectorAll) {
|
|
4347
|
+
const nodes = root.querySelectorAll('*');
|
|
4348
|
+
for (const node of nodes) {
|
|
4349
|
+
if ((node.textContent || '').trim() === text) return node;
|
|
4350
|
+
if (node.shadowRoot) {
|
|
4351
|
+
const found = deepFindElementByExactText(node.shadowRoot, text);
|
|
4352
|
+
if (found) return found;
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
return null;
|
|
4164
4357
|
}
|
|
4165
4358
|
|
|
4166
4359
|
switch(data.method) {
|
|
@@ -4173,22 +4366,140 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4173
4366
|
break;
|
|
4174
4367
|
|
|
4175
4368
|
case 'setBoundTo':
|
|
4176
|
-
|
|
4369
|
+
if (PCIManager.pciInstance) {
|
|
4370
|
+
PCIManager.applyBoundTo(data.params);
|
|
4371
|
+
} else {
|
|
4372
|
+
PCIManager.pendingBoundTo = data.params;
|
|
4373
|
+
}
|
|
4177
4374
|
break;
|
|
4178
4375
|
|
|
4179
4376
|
case 'setProperties':
|
|
4180
|
-
|
|
4377
|
+
PCIManager.setProperties(data.params);
|
|
4181
4378
|
break;
|
|
4379
|
+
|
|
4380
|
+
case 'setState':
|
|
4381
|
+
if (PCIManager.pciInstance && typeof PCIManager.pciInstance.setState === 'function') {
|
|
4382
|
+
PCIManager.pciInstance.setState((data.params && data.params.state) || data.params);
|
|
4383
|
+
} else {
|
|
4384
|
+
PCIManager.pendingState = (data.params && data.params.state) || data.params;
|
|
4385
|
+
}
|
|
4386
|
+
break;
|
|
4387
|
+
|
|
4388
|
+
case 'getContent': {
|
|
4389
|
+
const messageId = data.params && data.params.messageId;
|
|
4390
|
+
const collectShadowHtml = root => {
|
|
4391
|
+
const parts = [];
|
|
4392
|
+
if (!root || !root.querySelectorAll) return parts;
|
|
4393
|
+
const nodes = root.querySelectorAll('*');
|
|
4394
|
+
for (const node of nodes) {
|
|
4395
|
+
if (node && node.shadowRoot) {
|
|
4396
|
+
parts.push(node.shadowRoot.innerHTML || '');
|
|
4397
|
+
parts.push(...collectShadowHtml(node.shadowRoot));
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
return parts;
|
|
4401
|
+
};
|
|
4402
|
+
const shadowHtml = collectShadowHtml(document).join('\\n');
|
|
4403
|
+
window.parent.postMessage(
|
|
4404
|
+
{
|
|
4405
|
+
source: 'qti-pci-iframe',
|
|
4406
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4407
|
+
method: 'getContentResponse',
|
|
4408
|
+
messageId: messageId,
|
|
4409
|
+
content: (document.documentElement ? document.documentElement.outerHTML : '') + '\\n' + shadowHtml
|
|
4410
|
+
},
|
|
4411
|
+
'*'
|
|
4412
|
+
);
|
|
4413
|
+
break;
|
|
4414
|
+
}
|
|
4415
|
+
|
|
4416
|
+
case 'simulateClick': {
|
|
4417
|
+
const messageId = data.params && data.params.messageId;
|
|
4418
|
+
const x = data.params && data.params.x;
|
|
4419
|
+
const y = data.params && data.params.y;
|
|
4420
|
+
const el = typeof x === 'number' && typeof y === 'number' ? document.elementFromPoint(x, y) : null;
|
|
4421
|
+
if (el && typeof el.click === 'function') el.click();
|
|
4422
|
+
window.parent.postMessage(
|
|
4423
|
+
{
|
|
4424
|
+
source: 'qti-pci-iframe',
|
|
4425
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4426
|
+
method: 'clickResponse',
|
|
4427
|
+
messageId: messageId
|
|
4428
|
+
},
|
|
4429
|
+
'*'
|
|
4430
|
+
);
|
|
4431
|
+
break;
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
case 'clickOnSelector': {
|
|
4435
|
+
const messageId = data.params && data.params.messageId;
|
|
4436
|
+
const selector = data.params && data.params.selector;
|
|
4437
|
+
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
4438
|
+
const success = !!el;
|
|
4439
|
+
if (el && typeof el.click === 'function') el.click();
|
|
4440
|
+
window.parent.postMessage(
|
|
4441
|
+
{
|
|
4442
|
+
source: 'qti-pci-iframe',
|
|
4443
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4444
|
+
method: 'clickSelectorResponse',
|
|
4445
|
+
messageId: messageId,
|
|
4446
|
+
success: success
|
|
4447
|
+
},
|
|
4448
|
+
'*'
|
|
4449
|
+
);
|
|
4450
|
+
break;
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4453
|
+
case 'clickOnElementByText': {
|
|
4454
|
+
const messageId = data.params && data.params.messageId;
|
|
4455
|
+
const text = data.params && data.params.text;
|
|
4456
|
+
const target = text ? deepFindElementByExactText(document, text) : null;
|
|
4457
|
+
const success = !!target;
|
|
4458
|
+
if (target && typeof target.click === 'function') target.click();
|
|
4459
|
+
window.parent.postMessage(
|
|
4460
|
+
{
|
|
4461
|
+
source: 'qti-pci-iframe',
|
|
4462
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4463
|
+
method: 'clickTextResponse',
|
|
4464
|
+
messageId: messageId,
|
|
4465
|
+
success: success
|
|
4466
|
+
},
|
|
4467
|
+
'*'
|
|
4468
|
+
);
|
|
4469
|
+
break;
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
case 'setValueElement': {
|
|
4473
|
+
const messageId = data.params && data.params.messageId;
|
|
4474
|
+
const selector = data.params && data.params.selector;
|
|
4475
|
+
const value = data.params && data.params.value;
|
|
4476
|
+
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
4477
|
+
let success = false;
|
|
4478
|
+
if (el && 'value' in el) {
|
|
4479
|
+
try {
|
|
4480
|
+
el.value = value;
|
|
4481
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
4482
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
4483
|
+
success = true;
|
|
4484
|
+
} catch (e) {
|
|
4485
|
+
success = false;
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
window.parent.postMessage(
|
|
4489
|
+
{
|
|
4490
|
+
source: 'qti-pci-iframe',
|
|
4491
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4492
|
+
method: 'setValueResponse',
|
|
4493
|
+
messageId: messageId,
|
|
4494
|
+
success: success
|
|
4495
|
+
},
|
|
4496
|
+
'*'
|
|
4497
|
+
);
|
|
4498
|
+
break;
|
|
4499
|
+
}
|
|
4182
4500
|
}
|
|
4183
4501
|
});
|
|
4184
4502
|
|
|
4185
|
-
// Notify parent that iframe has loaded
|
|
4186
|
-
window.addEventListener('load', function() {
|
|
4187
|
-
window.parent.postMessage({
|
|
4188
|
-
source: 'qti-pci-iframe',
|
|
4189
|
-
method: 'iframeLoaded'
|
|
4190
|
-
}, '*');
|
|
4191
|
-
});
|
|
4192
4503
|
let resizeTimeout;
|
|
4193
4504
|
let previousHeight = 0;
|
|
4194
4505
|
const notifyResize = () => {
|
|
@@ -4197,14 +4508,15 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4197
4508
|
if (newHeight !== previousHeight) {
|
|
4198
4509
|
previousHeight = newHeight;
|
|
4199
4510
|
clearTimeout(resizeTimeout);
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4511
|
+
resizeTimeout = setTimeout(() => {
|
|
4512
|
+
window.parent.postMessage({
|
|
4513
|
+
source: 'qti-pci-iframe',
|
|
4514
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4515
|
+
method: 'resize',
|
|
4516
|
+
height: newHeight,
|
|
4517
|
+
width: container.scrollWidth
|
|
4518
|
+
}, '*');
|
|
4519
|
+
}, 100); // Adjust debounce time as needed
|
|
4208
4520
|
}
|
|
4209
4521
|
};
|
|
4210
4522
|
|
|
@@ -4238,18 +4550,43 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4238
4550
|
});
|
|
4239
4551
|
let lastResponseStr = '';
|
|
4240
4552
|
setInterval(() => {
|
|
4553
|
+
if (PCIManager.interactionChangedViaEvent) return;
|
|
4241
4554
|
if (PCIManager.pciInstance && PCIManager.pciInstance.getResponse) {
|
|
4242
4555
|
const response = PCIManager.pciInstance.getResponse();
|
|
4556
|
+
if (response === undefined) {
|
|
4557
|
+
// Don't emit an initial empty on load; only emit a clear if we previously had a value
|
|
4558
|
+
if (!PCIManager.hadResponse) return;
|
|
4559
|
+
PCIManager.hadResponse = false;
|
|
4560
|
+
PCIManager.lastResponseStr = null;
|
|
4561
|
+
const state = PCIManager.pciInstance && typeof PCIManager.pciInstance.getState === 'function' ? PCIManager.pciInstance.getState() : null;
|
|
4562
|
+
window.parent.postMessage(
|
|
4563
|
+
{
|
|
4564
|
+
source: 'qti-pci-iframe',
|
|
4565
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4566
|
+
method: 'interactionChanged',
|
|
4567
|
+
params: { value: null, state: state }
|
|
4568
|
+
},
|
|
4569
|
+
'*'
|
|
4570
|
+
);
|
|
4571
|
+
return;
|
|
4572
|
+
}
|
|
4573
|
+
|
|
4243
4574
|
const responseStr = JSON.stringify(response);
|
|
4244
4575
|
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4576
|
+
if (responseStr !== PCIManager.lastResponseStr) {
|
|
4577
|
+
PCIManager.lastResponseStr = responseStr;
|
|
4578
|
+
PCIManager.hadResponse = true;
|
|
4579
|
+
const state = PCIManager.pciInstance && typeof PCIManager.pciInstance.getState === 'function' ? PCIManager.pciInstance.getState() : null;
|
|
4580
|
+
window.parent.postMessage(
|
|
4581
|
+
{
|
|
4582
|
+
source: 'qti-pci-iframe',
|
|
4583
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
4584
|
+
method: 'interactionChanged',
|
|
4585
|
+
params: { value: response, state: state }
|
|
4586
|
+
},
|
|
4587
|
+
'*'
|
|
4588
|
+
);
|
|
4589
|
+
}
|
|
4253
4590
|
}
|
|
4254
4591
|
}, 500); // Check every 500ms
|
|
4255
4592
|
</script>
|
|
@@ -4313,73 +4650,33 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4313
4650
|
correctResponseViewer.appendChild(clonedChild);
|
|
4314
4651
|
});
|
|
4315
4652
|
const correctResponseValue = responseVariable.correctResponse;
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
if (this._iframeLoaded) {
|
|
4322
|
-
setTimeout(() => {
|
|
4323
|
-
const qtiVariableJSON = this.responseVariablesToQtiVariableJSON(
|
|
4324
|
-
correctResponseValue,
|
|
4325
|
-
responseVariable.cardinality,
|
|
4326
|
-
responseVariable.baseType
|
|
4327
|
-
);
|
|
4328
|
-
this.sendMessageToIframe("setBoundTo", {
|
|
4329
|
-
[originalResponseId]: qtiVariableJSON
|
|
4330
|
-
});
|
|
4331
|
-
this.sendMessageToIframe("setState", { state: "review" });
|
|
4332
|
-
}, 1e3);
|
|
4333
|
-
return true;
|
|
4334
|
-
}
|
|
4335
|
-
return false;
|
|
4336
|
-
};
|
|
4337
|
-
if (!checkIframeLoaded()) {
|
|
4338
|
-
const intervalId = setInterval(() => {
|
|
4339
|
-
if (checkIframeLoaded()) {
|
|
4340
|
-
clearInterval(intervalId);
|
|
4341
|
-
}
|
|
4342
|
-
}, 100);
|
|
4343
|
-
setTimeout(() => {
|
|
4344
|
-
clearInterval(intervalId);
|
|
4345
|
-
}, 1e4);
|
|
4346
|
-
}
|
|
4347
|
-
};
|
|
4348
|
-
} else {
|
|
4349
|
-
const originalRegister = correctResponseViewer.register;
|
|
4350
|
-
correctResponseViewer.register = function(pci) {
|
|
4351
|
-
originalRegister.call(this, pci);
|
|
4352
|
-
const setCorrectResponse = () => {
|
|
4353
|
-
if (this.pci) {
|
|
4354
|
-
if (typeof this.pci.setResponse === "function") {
|
|
4355
|
-
const pciResponse = this.responseVariablesToQtiVariableJSON(
|
|
4356
|
-
correctResponseValue,
|
|
4357
|
-
responseVariable.cardinality,
|
|
4358
|
-
responseVariable.baseType
|
|
4359
|
-
);
|
|
4360
|
-
this.pci.setResponse(pciResponse);
|
|
4361
|
-
if (typeof this.pci.setState === "function") {
|
|
4362
|
-
this.pci.setState("review");
|
|
4363
|
-
}
|
|
4364
|
-
return true;
|
|
4365
|
-
}
|
|
4366
|
-
}
|
|
4367
|
-
return false;
|
|
4368
|
-
};
|
|
4653
|
+
const originalConnectedCallback = correctResponseViewer.connectedCallback;
|
|
4654
|
+
correctResponseViewer.connectedCallback = function() {
|
|
4655
|
+
originalConnectedCallback.call(this);
|
|
4656
|
+
const checkIframeLoaded = () => {
|
|
4657
|
+
if (!this._iframeLoaded) return false;
|
|
4369
4658
|
setTimeout(() => {
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4659
|
+
const qtiVariableJSON = this.responseVariablesToQtiVariableJSON(
|
|
4660
|
+
correctResponseValue,
|
|
4661
|
+
responseVariable.cardinality,
|
|
4662
|
+
responseVariable.baseType
|
|
4663
|
+
);
|
|
4664
|
+
this.sendMessageToIframe("setBoundTo", {
|
|
4665
|
+
[originalResponseId]: qtiVariableJSON
|
|
4666
|
+
});
|
|
4667
|
+
this.sendMessageToIframe("setState", { state: "review" });
|
|
4668
|
+
}, 1e3);
|
|
4669
|
+
return true;
|
|
4381
4670
|
};
|
|
4382
|
-
|
|
4671
|
+
if (!checkIframeLoaded()) {
|
|
4672
|
+
const intervalId = setInterval(() => {
|
|
4673
|
+
if (checkIframeLoaded()) clearInterval(intervalId);
|
|
4674
|
+
}, 100);
|
|
4675
|
+
setTimeout(() => {
|
|
4676
|
+
clearInterval(intervalId);
|
|
4677
|
+
}, 1e4);
|
|
4678
|
+
}
|
|
4679
|
+
};
|
|
4383
4680
|
correctResponseViewer.style.pointerEvents = "none";
|
|
4384
4681
|
correctResponseContainer.appendChild(correctResponseViewer);
|
|
4385
4682
|
this.after(correctResponseContainer);
|
|
@@ -4393,10 +4690,7 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4393
4690
|
pointerEvents: this.style.pointerEvents,
|
|
4394
4691
|
position: this.style.position
|
|
4395
4692
|
};
|
|
4396
|
-
|
|
4397
|
-
this.sendMessageToIframe("setState", { state: "disabled" });
|
|
4398
|
-
} else {
|
|
4399
|
-
}
|
|
4693
|
+
this.sendMessageToIframe("setState", { state: "disabled" });
|
|
4400
4694
|
const existingOverlay = this.querySelector(".pci-interaction-overlay");
|
|
4401
4695
|
if (!existingOverlay) {
|
|
4402
4696
|
const overlay = document.createElement("div");
|
|
@@ -4433,10 +4727,7 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4433
4727
|
}
|
|
4434
4728
|
this._previousState = null;
|
|
4435
4729
|
}
|
|
4436
|
-
|
|
4437
|
-
this.sendMessageToIframe("setState", { state: "interacting" });
|
|
4438
|
-
} else {
|
|
4439
|
-
}
|
|
4730
|
+
this.sendMessageToIframe("setState", { state: "interacting" });
|
|
4440
4731
|
}
|
|
4441
4732
|
render() {
|
|
4442
4733
|
return x`
|
|
@@ -4448,21 +4739,15 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4448
4739
|
`;
|
|
4449
4740
|
}
|
|
4450
4741
|
};
|
|
4451
|
-
//
|
|
4742
|
+
// This implementation always renders inside an iframe.
|
|
4452
4743
|
QtiPortableCustomInteraction.styles = [
|
|
4453
4744
|
qti_portable_custom_interaction_styles_default,
|
|
4454
|
-
// Add default width/height for direct mode
|
|
4455
4745
|
i`
|
|
4456
4746
|
:host {
|
|
4457
4747
|
display: block;
|
|
4458
4748
|
width: 100%;
|
|
4459
4749
|
min-height: 50px;
|
|
4460
4750
|
}
|
|
4461
|
-
.qti-customInteraction {
|
|
4462
|
-
display: block;
|
|
4463
|
-
width: 100%;
|
|
4464
|
-
min-height: 50px;
|
|
4465
|
-
}
|
|
4466
4751
|
`
|
|
4467
4752
|
];
|
|
4468
4753
|
__decorateClass([
|
|
@@ -4483,9 +4768,6 @@ __decorateClass([
|
|
|
4483
4768
|
__decorateClass([
|
|
4484
4769
|
n({ type: String, attribute: "data-base-url" })
|
|
4485
4770
|
], QtiPortableCustomInteraction.prototype, "baseUrl", 2);
|
|
4486
|
-
__decorateClass([
|
|
4487
|
-
n({ type: Boolean, attribute: "data-use-iframe" })
|
|
4488
|
-
], QtiPortableCustomInteraction.prototype, "useIframe", 2);
|
|
4489
4771
|
__decorateClass([
|
|
4490
4772
|
n({ type: Boolean, attribute: "data-use-default-shims" })
|
|
4491
4773
|
], QtiPortableCustomInteraction.prototype, "useDefaultShims", 2);
|
|
@@ -5924,4 +6206,4 @@ lit-html/node/directives/ref.js:
|
|
|
5924
6206
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
5925
6207
|
*)
|
|
5926
6208
|
*/
|
|
5927
|
-
//# sourceMappingURL=chunk-
|
|
6209
|
+
//# sourceMappingURL=chunk-5D7GD3JS.js.map
|