scorm-again 3.0.5 → 3.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.
- package/README.md +77 -1
- package/dist/cross-frame-api.js.map +1 -1
- package/dist/esm/cross-frame-api.js.map +1 -1
- package/dist/esm/scorm-again.js +388 -71
- package/dist/esm/scorm-again.js.map +1 -1
- package/dist/esm/scorm-again.min.js +1 -1
- package/dist/esm/scorm-again.min.js.map +1 -1
- package/dist/esm/scorm12.js +322 -69
- package/dist/esm/scorm12.js.map +1 -1
- package/dist/esm/scorm12.min.js +1 -1
- package/dist/esm/scorm12.min.js.map +1 -1
- package/dist/esm/scorm2004.js +371 -64
- package/dist/esm/scorm2004.js.map +1 -1
- package/dist/esm/scorm2004.min.js +1 -1
- package/dist/esm/scorm2004.min.js.map +1 -1
- package/dist/scorm-again.js +341 -55
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +1 -1
- package/dist/scorm-again.min.js.map +1 -1
- package/dist/scorm12.js +305 -53
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +1 -1
- package/dist/scorm12.min.js.map +1 -1
- package/dist/scorm2004.js +329 -48
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +1 -1
- package/dist/scorm2004.min.js.map +1 -1
- package/dist/types/BaseAPI.d.ts +15 -5
- package/dist/types/Scorm12API.d.ts +2 -2
- package/dist/types/Scorm2004API.d.ts +3 -2
- package/dist/types/cmi/common/score.d.ts +2 -0
- package/dist/types/interfaces/services.d.ts +8 -4
- package/dist/types/services/AsynchronousHttpService.d.ts +3 -2
- package/dist/types/services/EventService.d.ts +2 -2
- package/dist/types/services/OfflineStorageService.d.ts +6 -1
- package/dist/types/services/SynchronousHttpService.d.ts +2 -2
- package/dist/types/services/ValidationService.d.ts +1 -1
- package/dist/types/types/api_types.d.ts +22 -2
- package/dist/types/utilities/index.d.ts +1 -0
- package/dist/types/utilities/url.d.ts +2 -0
- package/package.json +21 -21
package/dist/scorm12.js
CHANGED
|
@@ -161,6 +161,15 @@ this.Scorm12API = (function () {
|
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
const appendQueryParam = (url, name, value) => {
|
|
165
|
+
const fragmentIndex = url.indexOf("#");
|
|
166
|
+
const baseUrl = fragmentIndex === -1 ? url : url.slice(0, fragmentIndex);
|
|
167
|
+
const fragment = fragmentIndex === -1 ? "" : url.slice(fragmentIndex);
|
|
168
|
+
const separator = baseUrl.includes("?") ? baseUrl.endsWith("?") || baseUrl.endsWith("&") ? "" : "&" : "?";
|
|
169
|
+
const queryParam = `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}`;
|
|
170
|
+
return `${baseUrl}${separator}${queryParam}${fragment}`;
|
|
171
|
+
};
|
|
172
|
+
|
|
164
173
|
var __defProp$m = Object.defineProperty;
|
|
165
174
|
var __defNormalProp$m = (obj, key, value) => key in obj ? __defProp$m(obj, key, {
|
|
166
175
|
enumerable: true,
|
|
@@ -417,7 +426,10 @@ this.Scorm12API = (function () {
|
|
|
417
426
|
READ_ONLY_ELEMENT: 403,
|
|
418
427
|
WRITE_ONLY_ELEMENT: 404,
|
|
419
428
|
TYPE_MISMATCH: 405,
|
|
420
|
-
|
|
429
|
+
// SCORM 1.2 has no out-of-range error code; an out-of-range value is reported
|
|
430
|
+
// as 405 (incorrect data type), per the SCORM 1.2 RTE error table and the ADL
|
|
431
|
+
// 1.2 CTS (DataModelValidator.checkScoreDecimal failure -> CMICategory 405).
|
|
432
|
+
VALUE_OUT_OF_RANGE: 405,
|
|
421
433
|
DEPENDENCY_NOT_ESTABLISHED: 408
|
|
422
434
|
};
|
|
423
435
|
|
|
@@ -541,6 +553,7 @@ this.Scorm12API = (function () {
|
|
|
541
553
|
xhrWithCredentials: false,
|
|
542
554
|
fetchMode: "cors",
|
|
543
555
|
asyncModeBeaconBehavior: "never",
|
|
556
|
+
includeCommitSequence: false,
|
|
544
557
|
responseHandler: async function (response) {
|
|
545
558
|
if (typeof response !== "undefined") {
|
|
546
559
|
let httpResult = null;
|
|
@@ -940,7 +953,7 @@ this.Scorm12API = (function () {
|
|
|
940
953
|
wrapper() {
|
|
941
954
|
if (!this._cancelled) {
|
|
942
955
|
if (this._API.isInitialized()) {
|
|
943
|
-
(async () => await this._API.commit(this._callback))();
|
|
956
|
+
(async () => await this._API.commit(this._callback, false, "autocommit"))();
|
|
944
957
|
}
|
|
945
958
|
}
|
|
946
959
|
}
|
|
@@ -1430,6 +1443,7 @@ this.Scorm12API = (function () {
|
|
|
1430
1443
|
* @param {ErrorCode} error_codes - The error codes object
|
|
1431
1444
|
*/
|
|
1432
1445
|
constructor(settings, error_codes) {
|
|
1446
|
+
__publicField$h(this, "reportsRequestCompletion", true);
|
|
1433
1447
|
__publicField$h(this, "settings");
|
|
1434
1448
|
__publicField$h(this, "error_codes");
|
|
1435
1449
|
this.settings = settings;
|
|
@@ -1448,13 +1462,17 @@ this.Scorm12API = (function () {
|
|
|
1448
1462
|
* @param {boolean} immediate - Whether to send the request immediately without waiting
|
|
1449
1463
|
* @param {Function} apiLog - Function to log API messages with appropriate levels
|
|
1450
1464
|
* @param {Function} processListeners - Function to trigger event listeners for commit events
|
|
1465
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
1466
|
+
* @param {Function} onRequestComplete - Callback invoked after the background request settles
|
|
1451
1467
|
* @return {ResultObject} - Immediate optimistic success result
|
|
1452
1468
|
*/
|
|
1453
1469
|
processHttpRequest(url, params) {
|
|
1454
1470
|
let immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
1455
1471
|
let apiLog = arguments.length > 3 ? arguments[3] : undefined;
|
|
1456
1472
|
let processListeners = arguments.length > 4 ? arguments[4] : undefined;
|
|
1457
|
-
|
|
1473
|
+
let metadata = arguments.length > 5 ? arguments[5] : undefined;
|
|
1474
|
+
let onRequestComplete = arguments.length > 6 ? arguments[6] : undefined;
|
|
1475
|
+
this._performAsyncRequest(url, params, immediate, apiLog, processListeners, metadata, onRequestComplete);
|
|
1458
1476
|
return {
|
|
1459
1477
|
result: global_constants.SCORM_TRUE,
|
|
1460
1478
|
errorCode: 0
|
|
@@ -1467,11 +1485,14 @@ this.Scorm12API = (function () {
|
|
|
1467
1485
|
* @param {boolean} immediate - Whether this is an immediate request
|
|
1468
1486
|
* @param apiLog - Function to log API messages
|
|
1469
1487
|
* @param {Function} processListeners - Function to process event listeners
|
|
1488
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
1489
|
+
* @param {Function} onRequestComplete - Callback invoked after the request settles
|
|
1470
1490
|
* @private
|
|
1471
1491
|
*/
|
|
1472
|
-
async _performAsyncRequest(url, params, immediate, apiLog, processListeners) {
|
|
1492
|
+
async _performAsyncRequest(url, params, immediate, apiLog, processListeners, metadata, onRequestComplete) {
|
|
1473
1493
|
try {
|
|
1474
|
-
const
|
|
1494
|
+
const handledParams = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
1495
|
+
const processedParams = handledParams;
|
|
1475
1496
|
let response;
|
|
1476
1497
|
if (immediate && this.settings.asyncModeBeaconBehavior !== "never") {
|
|
1477
1498
|
response = await this.performBeacon(url, processedParams);
|
|
@@ -1487,7 +1508,9 @@ this.Scorm12API = (function () {
|
|
|
1487
1508
|
} catch (e) {
|
|
1488
1509
|
const message = e instanceof Error ? e.message : String(e);
|
|
1489
1510
|
apiLog("processHttpRequest", `Async request failed: ${message}`, LogLevelEnum.ERROR);
|
|
1490
|
-
processListeners("CommitError");
|
|
1511
|
+
processListeners("CommitError", void 0, this.error_codes.GENERAL_COMMIT_FAILURE || 391);
|
|
1512
|
+
} finally {
|
|
1513
|
+
onRequestComplete?.();
|
|
1491
1514
|
}
|
|
1492
1515
|
}
|
|
1493
1516
|
/**
|
|
@@ -1649,10 +1672,13 @@ this.Scorm12API = (function () {
|
|
|
1649
1672
|
}
|
|
1650
1673
|
/**
|
|
1651
1674
|
* Gets the appropriate error code for undefined data model elements.
|
|
1652
|
-
* SCORM 2004
|
|
1675
|
+
* Both SCORM 2004 and SCORM 1.2 use UNDEFINED_DATA_MODEL (401): an
|
|
1676
|
+
* unrecognized element is "Not implemented", not a general exception. SCORM
|
|
1677
|
+
* 1.2 previously returned GENERAL (101) here, which is non-conformant — the
|
|
1678
|
+
* ADL 1.2 CTS and the SCORM 1.2 RTE spec expect 401 for an unknown element.
|
|
1653
1679
|
*/
|
|
1654
|
-
getUndefinedDataModelErrorCode(
|
|
1655
|
-
return
|
|
1680
|
+
getUndefinedDataModelErrorCode() {
|
|
1681
|
+
return getErrorCode(this.context.errorCodes, "UNDEFINED_DATA_MODEL");
|
|
1656
1682
|
}
|
|
1657
1683
|
/**
|
|
1658
1684
|
* Sets a value on a CMI element path
|
|
@@ -1676,7 +1702,7 @@ this.Scorm12API = (function () {
|
|
|
1676
1702
|
let returnValue = global_constants.SCORM_FALSE;
|
|
1677
1703
|
let foundFirstIndex = false;
|
|
1678
1704
|
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
|
1679
|
-
const invalidErrorCode = this.getUndefinedDataModelErrorCode(
|
|
1705
|
+
const invalidErrorCode = this.getUndefinedDataModelErrorCode();
|
|
1680
1706
|
for (let idx = 0; idx < structure.length; idx++) {
|
|
1681
1707
|
const attribute = structure[idx];
|
|
1682
1708
|
if (idx === structure.length - 1) {
|
|
@@ -1716,12 +1742,13 @@ this.Scorm12API = (function () {
|
|
|
1716
1742
|
this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "GENERAL_GET_FAILURE"), "The _version keyword was used incorrectly");
|
|
1717
1743
|
return "";
|
|
1718
1744
|
}
|
|
1745
|
+
this.context.setLastErrorCode("0");
|
|
1719
1746
|
const structure = CMIElement.split(".");
|
|
1720
1747
|
let refObject = this.context.getDataModel();
|
|
1721
1748
|
let attribute = null;
|
|
1722
1749
|
const uninitializedErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) has not been initialized.`;
|
|
1723
1750
|
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
|
1724
|
-
const invalidErrorCode = this.getUndefinedDataModelErrorCode(
|
|
1751
|
+
const invalidErrorCode = this.getUndefinedDataModelErrorCode();
|
|
1725
1752
|
for (let idx = 0; idx < structure.length; idx++) {
|
|
1726
1753
|
attribute = structure[idx];
|
|
1727
1754
|
const validationResult = this.validateGetAttribute(refObject, attribute, CMIElement, scorm2004, invalidErrorCode, invalidErrorMessage, idx === structure.length - 1);
|
|
@@ -2447,8 +2474,9 @@ ${stackTrace}`);
|
|
|
2447
2474
|
* @param {string} functionName - The name of the function that triggered the event
|
|
2448
2475
|
* @param {string} CMIElement - The CMI element that was affected
|
|
2449
2476
|
* @param {any} value - The value that was set
|
|
2477
|
+
* @param {CommitEventContext} context - Optional context for commit lifecycle events
|
|
2450
2478
|
*/
|
|
2451
|
-
processListeners(functionName, CMIElement, value) {
|
|
2479
|
+
processListeners(functionName, CMIElement, value, context) {
|
|
2452
2480
|
this.apiLog(functionName, value, LogLevelEnum.INFO, CMIElement);
|
|
2453
2481
|
const listeners = this.listenerMap.get(functionName);
|
|
2454
2482
|
if (!listeners) return;
|
|
@@ -2468,9 +2496,17 @@ ${stackTrace}`);
|
|
|
2468
2496
|
if (functionName.startsWith("Sequence")) {
|
|
2469
2497
|
listener.callback(value);
|
|
2470
2498
|
} else if (functionName === "CommitError") {
|
|
2471
|
-
|
|
2499
|
+
if (context !== void 0) {
|
|
2500
|
+
listener.callback(value, context);
|
|
2501
|
+
} else {
|
|
2502
|
+
listener.callback(value);
|
|
2503
|
+
}
|
|
2472
2504
|
} else if (functionName === "CommitSuccess") {
|
|
2473
|
-
|
|
2505
|
+
if (context !== void 0) {
|
|
2506
|
+
listener.callback(context);
|
|
2507
|
+
} else {
|
|
2508
|
+
listener.callback();
|
|
2509
|
+
}
|
|
2474
2510
|
} else {
|
|
2475
2511
|
listener.callback(CMIElement, value);
|
|
2476
2512
|
}
|
|
@@ -2579,16 +2615,23 @@ ${stackTrace}`);
|
|
|
2579
2615
|
* Store commit data offline
|
|
2580
2616
|
* @param {string} courseId - Identifier for the course
|
|
2581
2617
|
* @param {CommitObject} commitData - The data to store offline
|
|
2618
|
+
* @param {OfflineCommitMetadata} metadata - Metadata captured with the original commit
|
|
2582
2619
|
* @returns {ResultObject} - Result of the storage operation
|
|
2583
2620
|
*/
|
|
2584
|
-
storeOffline(courseId, commitData) {
|
|
2621
|
+
storeOffline(courseId, commitData, metadata) {
|
|
2585
2622
|
try {
|
|
2586
2623
|
const queueItem = {
|
|
2587
2624
|
id: `${courseId}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
2588
2625
|
courseId,
|
|
2589
2626
|
timestamp: Date.now(),
|
|
2590
2627
|
data: commitData,
|
|
2591
|
-
syncAttempts: 0
|
|
2628
|
+
syncAttempts: 0,
|
|
2629
|
+
...(metadata?.isTerminateCommit !== void 0 ? {
|
|
2630
|
+
isTerminateCommit: metadata.isTerminateCommit
|
|
2631
|
+
} : {}),
|
|
2632
|
+
...(metadata?.sequence !== void 0 ? {
|
|
2633
|
+
sequence: metadata.sequence
|
|
2634
|
+
} : {})
|
|
2592
2635
|
};
|
|
2593
2636
|
const currentQueue = this.getFromStorage(this.syncQueue) || [];
|
|
2594
2637
|
currentQueue.push(queueItem);
|
|
@@ -2647,7 +2690,14 @@ ${stackTrace}`);
|
|
|
2647
2690
|
continue;
|
|
2648
2691
|
}
|
|
2649
2692
|
try {
|
|
2650
|
-
const syncResult = await this.sendDataToLMS(item.data
|
|
2693
|
+
const syncResult = await this.sendDataToLMS(item.data, {
|
|
2694
|
+
...(item.isTerminateCommit !== void 0 ? {
|
|
2695
|
+
isTerminateCommit: item.isTerminateCommit
|
|
2696
|
+
} : {}),
|
|
2697
|
+
...(item.sequence !== void 0 ? {
|
|
2698
|
+
sequence: item.sequence
|
|
2699
|
+
} : {})
|
|
2700
|
+
});
|
|
2651
2701
|
if (syncResult.result === true || syncResult.result === global_constants.SCORM_TRUE) {
|
|
2652
2702
|
this.apiLog("OfflineStorageService", `Successfully synced item ${item.id}`, LogLevelEnum.INFO);
|
|
2653
2703
|
} else {
|
|
@@ -2674,17 +2724,27 @@ ${stackTrace}`);
|
|
|
2674
2724
|
/**
|
|
2675
2725
|
* Send data to the LMS when online
|
|
2676
2726
|
* @param {CommitObject} data - The data to send to the LMS
|
|
2727
|
+
* @param {OfflineCommitMetadata} metadata - Metadata captured with the original commit
|
|
2677
2728
|
* @returns {Promise<ResultObject>} - Result of the sync operation
|
|
2678
2729
|
*/
|
|
2679
|
-
async sendDataToLMS(data) {
|
|
2680
|
-
|
|
2730
|
+
async sendDataToLMS(data, metadata) {
|
|
2731
|
+
const configuredCommitUrl = this.settings.lmsCommitUrl;
|
|
2732
|
+
if (!configuredCommitUrl) {
|
|
2681
2733
|
return {
|
|
2682
2734
|
result: global_constants.SCORM_FALSE,
|
|
2683
2735
|
errorCode: this.error_codes.GENERAL || 101
|
|
2684
2736
|
};
|
|
2685
2737
|
}
|
|
2686
2738
|
try {
|
|
2687
|
-
const
|
|
2739
|
+
const lmsCommitUrl = String(configuredCommitUrl);
|
|
2740
|
+
const processedData = this.settings.requestHandler(data, {
|
|
2741
|
+
isTerminateCommit: metadata?.isTerminateCommit ?? false,
|
|
2742
|
+
trigger: "offline-replay",
|
|
2743
|
+
...(metadata?.sequence !== void 0 ? {
|
|
2744
|
+
sequence: metadata.sequence
|
|
2745
|
+
} : {})
|
|
2746
|
+
});
|
|
2747
|
+
const requestUrl = metadata?.isTerminateCommit && this.settings.terminateCommitParam ? appendQueryParam(lmsCommitUrl, this.settings.terminateCommitParam, "true") : lmsCommitUrl;
|
|
2688
2748
|
const init = {
|
|
2689
2749
|
method: "POST",
|
|
2690
2750
|
mode: this.settings.fetchMode,
|
|
@@ -2697,7 +2757,7 @@ ${stackTrace}`);
|
|
|
2697
2757
|
if (this.settings.xhrWithCredentials) {
|
|
2698
2758
|
init.credentials = "include";
|
|
2699
2759
|
}
|
|
2700
|
-
const response = await fetch(
|
|
2760
|
+
const response = await fetch(requestUrl, init);
|
|
2701
2761
|
const result = typeof this.settings.responseHandler === "function" ? await this.settings.responseHandler(response) : await response.json();
|
|
2702
2762
|
if (response.status >= 200 && response.status <= 299 && (result.result === true || result.result === global_constants.SCORM_TRUE)) {
|
|
2703
2763
|
if (!Object.hasOwnProperty.call(result, "errorCode")) {
|
|
@@ -3428,6 +3488,8 @@ ${stackTrace}`);
|
|
|
3428
3488
|
* @param {boolean} immediate - Whether this is a termination commit (use sendBeacon)
|
|
3429
3489
|
* @param {Function} _apiLog - Function to log API messages (unused in synchronous mode - errors returned directly)
|
|
3430
3490
|
* @param {Function} _processListeners - Function to trigger event listeners (unused in synchronous mode - no async events)
|
|
3491
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
3492
|
+
* @param {Function} _onRequestComplete - Completion callback (unused because requests settle before return)
|
|
3431
3493
|
* @return {ResultObject} - The result of the request (synchronous)
|
|
3432
3494
|
*
|
|
3433
3495
|
* @remarks
|
|
@@ -3439,20 +3501,23 @@ ${stackTrace}`);
|
|
|
3439
3501
|
*/
|
|
3440
3502
|
processHttpRequest(url, params) {
|
|
3441
3503
|
let immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
3504
|
+
let metadata = arguments.length > 5 ? arguments[5] : undefined;
|
|
3442
3505
|
if (immediate) {
|
|
3443
|
-
return this._handleImmediateRequest(url, params);
|
|
3506
|
+
return this._handleImmediateRequest(url, params, metadata);
|
|
3444
3507
|
}
|
|
3445
|
-
return this._performSyncXHR(url, params);
|
|
3508
|
+
return this._performSyncXHR(url, params, metadata);
|
|
3446
3509
|
}
|
|
3447
3510
|
/**
|
|
3448
3511
|
* Handles an immediate request using sendBeacon
|
|
3449
3512
|
* @param {string} url - The URL to send the request to
|
|
3450
3513
|
* @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request
|
|
3514
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
3451
3515
|
* @return {ResultObject} - The result based on beacon success
|
|
3452
3516
|
* @private
|
|
3453
3517
|
*/
|
|
3454
|
-
_handleImmediateRequest(url, params) {
|
|
3455
|
-
const
|
|
3518
|
+
_handleImmediateRequest(url, params, metadata) {
|
|
3519
|
+
const handledPayload = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
3520
|
+
const requestPayload = handledPayload ?? params;
|
|
3456
3521
|
const {
|
|
3457
3522
|
body
|
|
3458
3523
|
} = this._prepareRequestBody(requestPayload);
|
|
@@ -3468,11 +3533,13 @@ ${stackTrace}`);
|
|
|
3468
3533
|
* Performs a synchronous XMLHttpRequest
|
|
3469
3534
|
* @param {string} url - The URL to send the request to
|
|
3470
3535
|
* @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request
|
|
3536
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
3471
3537
|
* @return {ResultObject} - The result of the request
|
|
3472
3538
|
* @private
|
|
3473
3539
|
*/
|
|
3474
|
-
_performSyncXHR(url, params) {
|
|
3475
|
-
const
|
|
3540
|
+
_performSyncXHR(url, params, metadata) {
|
|
3541
|
+
const handledPayload = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
3542
|
+
const requestPayload = handledPayload ?? params;
|
|
3476
3543
|
const {
|
|
3477
3544
|
body,
|
|
3478
3545
|
contentType
|
|
@@ -3545,9 +3612,16 @@ ${stackTrace}`);
|
|
|
3545
3612
|
* @param {number} invalidTypeCode - The error code for invalid type
|
|
3546
3613
|
* @param {number} invalidRangeCode - The error code for invalid range
|
|
3547
3614
|
* @param {typeof BaseScormValidationError} errorClass - The error class to use for validation errors
|
|
3615
|
+
* @param {boolean} allowEmptyString - When true, an empty string is accepted (clears the value).
|
|
3616
|
+
* SCORM 1.2 score elements may be blank per the ADL 1.2 CTS
|
|
3617
|
+
* (DataModelValidator.checkScoreDecimal treats blank as valid).
|
|
3548
3618
|
* @return {boolean} - True if validation passes, throws an error otherwise
|
|
3549
3619
|
*/
|
|
3550
3620
|
validateScore(CMIElement, value, decimalRegex, scoreRange, invalidTypeCode, invalidRangeCode, errorClass) {
|
|
3621
|
+
let allowEmptyString = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;
|
|
3622
|
+
if (allowEmptyString && value === "") {
|
|
3623
|
+
return true;
|
|
3624
|
+
}
|
|
3551
3625
|
return checkValidFormat(CMIElement, value, decimalRegex, invalidTypeCode, errorClass) && (!scoreRange || checkValidRange(CMIElement, value, scoreRange, invalidRangeCode, errorClass));
|
|
3552
3626
|
}
|
|
3553
3627
|
/**
|
|
@@ -3643,6 +3717,21 @@ ${stackTrace}`);
|
|
|
3643
3717
|
__publicField$9(this, "_offlineStorageService");
|
|
3644
3718
|
__publicField$9(this, "_cmiValueAccessService");
|
|
3645
3719
|
__publicField$9(this, "_courseId", "");
|
|
3720
|
+
__publicField$9(this, "_pendingCommitCount", 0);
|
|
3721
|
+
/**
|
|
3722
|
+
* Monotonic sequence for commits captured by this API instance. It is
|
|
3723
|
+
* intentionally not reset by reset().
|
|
3724
|
+
*/
|
|
3725
|
+
__publicField$9(this, "_commitSequence", 0);
|
|
3726
|
+
__publicField$9(this, "_commitSettleWaiters", []);
|
|
3727
|
+
/**
|
|
3728
|
+
* Canonical paths of every CMI element that has been explicitly assigned a
|
|
3729
|
+
* value via SetValue / loadFromJSON (both funnel through _commonSetCMIValue).
|
|
3730
|
+
* Used by standards that must tell "implemented but never set" apart from a
|
|
3731
|
+
* legitimately empty value when answering GetValue (SCORM 2004 error 403).
|
|
3732
|
+
* Cleared on reset so a fresh SCO attempt starts with nothing "set".
|
|
3733
|
+
*/
|
|
3734
|
+
__publicField$9(this, "_setCMIElements", /* @__PURE__ */new Set());
|
|
3646
3735
|
__publicField$9(this, "startingData");
|
|
3647
3736
|
__publicField$9(this, "currentState");
|
|
3648
3737
|
if (new.target === BaseAPI) {
|
|
@@ -3789,6 +3878,7 @@ ${stackTrace}`);
|
|
|
3789
3878
|
this.lastErrorCode = "0";
|
|
3790
3879
|
this._eventService.reset();
|
|
3791
3880
|
this.startingData = {};
|
|
3881
|
+
this._setCMIElements.clear();
|
|
3792
3882
|
if (this._offlineStorageService) {
|
|
3793
3883
|
this._offlineStorageService.updateSettings(this.settings);
|
|
3794
3884
|
if (settings?.courseId) {
|
|
@@ -3872,6 +3962,54 @@ ${stackTrace}`);
|
|
|
3872
3962
|
this._loggingService?.setLogHandler(settings.onLogMessage);
|
|
3873
3963
|
}
|
|
3874
3964
|
}
|
|
3965
|
+
/**
|
|
3966
|
+
* Gets the number of captured commit requests that have not yet settled.
|
|
3967
|
+
*
|
|
3968
|
+
* @return {number} The number of in-flight commits
|
|
3969
|
+
*/
|
|
3970
|
+
get pendingCommitCount() {
|
|
3971
|
+
return this._pendingCommitCount;
|
|
3972
|
+
}
|
|
3973
|
+
/**
|
|
3974
|
+
* Resolves when all currently in-flight commits have settled. A timeout is
|
|
3975
|
+
* best-effort: the promise resolves when it elapses even if commits remain,
|
|
3976
|
+
* and callers can inspect pendingCommitCount afterward to detect that case.
|
|
3977
|
+
*
|
|
3978
|
+
* @param {Object} [options] - Settle options
|
|
3979
|
+
* @param {number} [options.timeoutMs] - Maximum time to wait in milliseconds
|
|
3980
|
+
* @return {Promise<void>} A promise that resolves after the drain or timeout
|
|
3981
|
+
*/
|
|
3982
|
+
whenCommitsSettled(options) {
|
|
3983
|
+
if (this._pendingCommitCount === 0) {
|
|
3984
|
+
return Promise.resolve();
|
|
3985
|
+
}
|
|
3986
|
+
return new Promise(resolve => {
|
|
3987
|
+
const waiter = {
|
|
3988
|
+
resolve
|
|
3989
|
+
};
|
|
3990
|
+
if (options?.timeoutMs !== void 0) {
|
|
3991
|
+
waiter.timeoutId = setTimeout(() => {
|
|
3992
|
+
const waiterIndex = this._commitSettleWaiters.indexOf(waiter);
|
|
3993
|
+
if (waiterIndex === -1) {
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3996
|
+
this._commitSettleWaiters.splice(waiterIndex, 1);
|
|
3997
|
+
resolve();
|
|
3998
|
+
}, options.timeoutMs);
|
|
3999
|
+
}
|
|
4000
|
+
this._commitSettleWaiters.push(waiter);
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
/** Resolve and clear every waiter after the pending count reaches zero. */
|
|
4004
|
+
_flushCommitSettleWaiters() {
|
|
4005
|
+
const waiters = this._commitSettleWaiters.splice(0);
|
|
4006
|
+
for (const waiter of waiters) {
|
|
4007
|
+
if (waiter.timeoutId !== void 0) {
|
|
4008
|
+
clearTimeout(waiter.timeoutId);
|
|
4009
|
+
}
|
|
4010
|
+
waiter.resolve();
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
3875
4013
|
/**
|
|
3876
4014
|
* Terminates the current run of the API
|
|
3877
4015
|
* @param {string} callbackName
|
|
@@ -3892,7 +4030,7 @@ ${stackTrace}`);
|
|
|
3892
4030
|
} else {
|
|
3893
4031
|
stateCheckPassed = true;
|
|
3894
4032
|
this.processListeners("BeforeTerminate");
|
|
3895
|
-
const result = this.storeData(true);
|
|
4033
|
+
const result = this.storeData(true, "terminate");
|
|
3896
4034
|
if ((result.errorCode ?? 0) > 0) {
|
|
3897
4035
|
if (result.errorMessage) {
|
|
3898
4036
|
this.apiLog("terminate", `Terminate failed with error: ${result.errorMessage}`, LogLevelEnum.ERROR);
|
|
@@ -3932,6 +4070,9 @@ ${stackTrace}`);
|
|
|
3932
4070
|
} catch (e) {
|
|
3933
4071
|
returnValue = this.handleValueAccessException(CMIElement, e, returnValue);
|
|
3934
4072
|
}
|
|
4073
|
+
if (this.lastErrorCode === "0") {
|
|
4074
|
+
this.checkUninitializedGet(CMIElement, returnValue);
|
|
4075
|
+
}
|
|
3935
4076
|
this.processListeners(callbackName, CMIElement);
|
|
3936
4077
|
}
|
|
3937
4078
|
this.apiLog(callbackName, ": returned: " + returnValue, LogLevelEnum.INFO, CMIElement);
|
|
@@ -3941,6 +4082,10 @@ ${stackTrace}`);
|
|
|
3941
4082
|
if (this.lastErrorCode === "0") {
|
|
3942
4083
|
this.clearSCORMError(returnValue);
|
|
3943
4084
|
}
|
|
4085
|
+
const rawReturn = returnValue;
|
|
4086
|
+
if (typeof rawReturn === "number" || typeof rawReturn === "boolean") {
|
|
4087
|
+
return String(rawReturn);
|
|
4088
|
+
}
|
|
3944
4089
|
return returnValue;
|
|
3945
4090
|
}
|
|
3946
4091
|
/**
|
|
@@ -3984,10 +4129,12 @@ ${stackTrace}`);
|
|
|
3984
4129
|
* Orders LMS to store all content parameters
|
|
3985
4130
|
* @param {string} callbackName
|
|
3986
4131
|
* @param {boolean} checkTerminated
|
|
4132
|
+
* @param {CommitTrigger} trigger - What initiated the commit
|
|
3987
4133
|
* @return {string}
|
|
3988
4134
|
*/
|
|
3989
4135
|
commit(callbackName) {
|
|
3990
4136
|
let checkTerminated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
4137
|
+
let trigger = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "manual";
|
|
3991
4138
|
this.clearScheduledCommit();
|
|
3992
4139
|
let returnValue = global_constants.SCORM_TRUE;
|
|
3993
4140
|
if (this.isNotInitialized()) {
|
|
@@ -3999,7 +4146,7 @@ ${stackTrace}`);
|
|
|
3999
4146
|
this.throwSCORMError("api", errorCode);
|
|
4000
4147
|
if (errorCode === 143) returnValue = global_constants.SCORM_FALSE;
|
|
4001
4148
|
} else {
|
|
4002
|
-
const result = this.storeData(false);
|
|
4149
|
+
const result = this.storeData(false, trigger);
|
|
4003
4150
|
const errorCode = result.errorCode ?? 0;
|
|
4004
4151
|
if (errorCode > 0) {
|
|
4005
4152
|
if (result.errorMessage) {
|
|
@@ -4220,7 +4367,11 @@ ${stackTrace}`);
|
|
|
4220
4367
|
* @return {string}
|
|
4221
4368
|
*/
|
|
4222
4369
|
_commonSetCMIValue(methodName, scorm2004, CMIElement, value) {
|
|
4223
|
-
|
|
4370
|
+
const result = this._cmiValueAccessService.setCMIValue(methodName, scorm2004, CMIElement, value);
|
|
4371
|
+
if (result === global_constants.SCORM_TRUE) {
|
|
4372
|
+
this._setCMIElements.add(CMIElement);
|
|
4373
|
+
}
|
|
4374
|
+
return result;
|
|
4224
4375
|
}
|
|
4225
4376
|
/**
|
|
4226
4377
|
* Gets a value from the CMI Object.
|
|
@@ -4234,6 +4385,17 @@ ${stackTrace}`);
|
|
|
4234
4385
|
_commonGetCMIValue(methodName, scorm2004, CMIElement) {
|
|
4235
4386
|
return this._cmiValueAccessService.getCMIValue(methodName, scorm2004, CMIElement);
|
|
4236
4387
|
}
|
|
4388
|
+
/**
|
|
4389
|
+
* Hook invoked by getValue after a successful resolution. Standards that must
|
|
4390
|
+
* distinguish "implemented but never set, no default value" from a
|
|
4391
|
+
* legitimately empty value override this to raise VALUE_NOT_INITIALIZED.
|
|
4392
|
+
* Default is a no-op, so SCORM 1.2 / AICC keep returning "" with code 0.
|
|
4393
|
+
*
|
|
4394
|
+
* @param {string} _CMIElement - the element that was read
|
|
4395
|
+
* @param {any} _returnValue - the value getCMIValue resolved
|
|
4396
|
+
* @protected
|
|
4397
|
+
*/
|
|
4398
|
+
checkUninitializedGet(_CMIElement, _returnValue) {}
|
|
4237
4399
|
/**
|
|
4238
4400
|
* Returns true if the API's current state is STATE_INITIALIZED
|
|
4239
4401
|
*
|
|
@@ -4316,9 +4478,14 @@ ${stackTrace}`);
|
|
|
4316
4478
|
* @param {string} functionName - The name of the function/event that occurred
|
|
4317
4479
|
* @param {string} CMIElement - Optional CMI element involved in the event
|
|
4318
4480
|
* @param {any} value - Optional value associated with the event
|
|
4481
|
+
* @param {CommitEventContext} context - Optional context for commit lifecycle events
|
|
4319
4482
|
*/
|
|
4320
|
-
processListeners(functionName, CMIElement, value) {
|
|
4321
|
-
|
|
4483
|
+
processListeners(functionName, CMIElement, value, context) {
|
|
4484
|
+
if (context !== void 0) {
|
|
4485
|
+
this._eventService.processListeners(functionName, CMIElement, value, context);
|
|
4486
|
+
} else {
|
|
4487
|
+
this._eventService.processListeners(functionName, CMIElement, value);
|
|
4488
|
+
}
|
|
4322
4489
|
}
|
|
4323
4490
|
/**
|
|
4324
4491
|
* Throws a SCORM error with the specified error number and optional message.
|
|
@@ -4440,23 +4607,97 @@ ${stackTrace}`);
|
|
|
4440
4607
|
* @param {string} url - The URL to send the request to
|
|
4441
4608
|
* @param {CommitObject | StringKeyMap | Array<any>} params - The parameters to send
|
|
4442
4609
|
* @param {boolean} immediate - Whether to send the request immediately without waiting
|
|
4610
|
+
* @param {CommitTrigger} [trigger] - What initiated the commit
|
|
4443
4611
|
* @returns {ResultObject} - The result of the request
|
|
4444
4612
|
*/
|
|
4445
4613
|
processHttpRequest(url, params) {
|
|
4446
4614
|
let immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4615
|
+
let trigger = arguments.length > 3 ? arguments[3] : undefined;
|
|
4616
|
+
const sequence = ++this._commitSequence;
|
|
4617
|
+
this._pendingCommitCount += 1;
|
|
4618
|
+
let settled = false;
|
|
4619
|
+
let completionDeferred = false;
|
|
4620
|
+
const settle = () => {
|
|
4621
|
+
if (settled) {
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
settled = true;
|
|
4625
|
+
this._pendingCommitCount -= 1;
|
|
4626
|
+
if (this._pendingCommitCount === 0) {
|
|
4627
|
+
this._flushCommitSettleWaiters();
|
|
4628
|
+
}
|
|
4629
|
+
};
|
|
4630
|
+
try {
|
|
4631
|
+
const resolvedTrigger = trigger ?? (immediate ? "terminate" : "manual");
|
|
4632
|
+
let finalParams = params;
|
|
4633
|
+
if (immediate && this.settings.terminateCommitPayloadField) {
|
|
4634
|
+
const field = this.settings.terminateCommitPayloadField;
|
|
4635
|
+
if (Array.isArray(finalParams)) {
|
|
4636
|
+
finalParams = [...finalParams, `${encodeURIComponent(field)}=true`];
|
|
4637
|
+
} else if (finalParams && typeof finalParams === "object") {
|
|
4638
|
+
finalParams = {
|
|
4639
|
+
...finalParams,
|
|
4640
|
+
[field]: true
|
|
4641
|
+
};
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
if (this.settings.includeCommitSequence === true) {
|
|
4645
|
+
if (Array.isArray(finalParams)) {
|
|
4646
|
+
finalParams = [...finalParams, `commitSequence=${sequence}`];
|
|
4647
|
+
} else if (finalParams && typeof finalParams === "object") {
|
|
4648
|
+
finalParams = {
|
|
4649
|
+
...finalParams,
|
|
4650
|
+
commitSequence: sequence
|
|
4651
|
+
};
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
const finalUrl = immediate && this.settings.terminateCommitParam ? appendQueryParam(url, this.settings.terminateCommitParam, "true") : url;
|
|
4655
|
+
const metadata = {
|
|
4656
|
+
isTerminateCommit: immediate,
|
|
4657
|
+
trigger: resolvedTrigger,
|
|
4658
|
+
sequence
|
|
4659
|
+
};
|
|
4660
|
+
const context = {
|
|
4661
|
+
url: finalUrl,
|
|
4662
|
+
trigger: resolvedTrigger,
|
|
4663
|
+
isTerminateCommit: immediate,
|
|
4664
|
+
sequence
|
|
4665
|
+
};
|
|
4666
|
+
if (this.settings.enableOfflineSupport && this._offlineStorageService && !this._offlineStorageService.isDeviceOnline() && this._courseId) {
|
|
4667
|
+
this.apiLog("processHttpRequest", "Device is offline, storing data locally", LogLevelEnum.INFO);
|
|
4668
|
+
if (finalParams && typeof finalParams === "object" && "cmi" in finalParams) {
|
|
4669
|
+
return this._offlineStorageService.storeOffline(this._courseId, finalParams, {
|
|
4670
|
+
isTerminateCommit: immediate,
|
|
4671
|
+
sequence
|
|
4672
|
+
});
|
|
4673
|
+
} else {
|
|
4674
|
+
this.apiLog("processHttpRequest", "Invalid commit data format for offline storage", LogLevelEnum.ERROR);
|
|
4675
|
+
return {
|
|
4676
|
+
result: global_constants.SCORM_FALSE,
|
|
4677
|
+
errorCode: this._error_codes.GENERAL ?? 101
|
|
4678
|
+
};
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
const apiLog = (functionName, message, level, element) => this.apiLog(functionName, message, level, element);
|
|
4682
|
+
const processListeners = (functionName, CMIElement, value) => {
|
|
4683
|
+
if (functionName === "CommitSuccess" || functionName === "CommitError") {
|
|
4684
|
+
if (functionName === "CommitError" && typeof value === "number") {
|
|
4685
|
+
context.errorCode = value;
|
|
4686
|
+
}
|
|
4687
|
+
settle();
|
|
4688
|
+
this.processListeners(functionName, CMIElement, value, context);
|
|
4689
|
+
} else {
|
|
4690
|
+
this.processListeners(functionName, CMIElement, value);
|
|
4691
|
+
}
|
|
4692
|
+
};
|
|
4693
|
+
const result = this._httpService.processHttpRequest(finalUrl, finalParams, immediate, apiLog, processListeners, metadata, settle);
|
|
4694
|
+
completionDeferred = this._httpService.reportsRequestCompletion === true;
|
|
4695
|
+
return result;
|
|
4696
|
+
} finally {
|
|
4697
|
+
if (!completionDeferred) {
|
|
4698
|
+
settle();
|
|
4457
4699
|
}
|
|
4458
4700
|
}
|
|
4459
|
-
return this._httpService.processHttpRequest(url, params, immediate, (functionName, message, level, element) => this.apiLog(functionName, message, level, element), (functionName, CMIElement, value) => this.processListeners(functionName, CMIElement, value));
|
|
4460
4701
|
}
|
|
4461
4702
|
/**
|
|
4462
4703
|
* Schedules a commit operation to occur after a specified delay.
|
|
@@ -4640,6 +4881,11 @@ ${stackTrace}`);
|
|
|
4640
4881
|
__publicField$8(this, "__invalid_range_code");
|
|
4641
4882
|
__publicField$8(this, "__decimal_regex");
|
|
4642
4883
|
__publicField$8(this, "__error_class");
|
|
4884
|
+
/**
|
|
4885
|
+
* When true, an empty string is a valid value (clears the element). SCORM 1.2
|
|
4886
|
+
* score elements may be blank per the ADL 1.2 CTS; SCORM 2004 leaves this off.
|
|
4887
|
+
*/
|
|
4888
|
+
__publicField$8(this, "__allow_empty_string");
|
|
4643
4889
|
__publicField$8(this, "_raw", "");
|
|
4644
4890
|
__publicField$8(this, "_min", "");
|
|
4645
4891
|
__publicField$8(this, "_max");
|
|
@@ -4651,6 +4897,7 @@ ${stackTrace}`);
|
|
|
4651
4897
|
this.__invalid_range_code = params.invalidRangeCode || scorm12_errors.VALUE_OUT_OF_RANGE;
|
|
4652
4898
|
this.__decimal_regex = params.decimalRegex || scorm12_regex.CMIDecimal;
|
|
4653
4899
|
this.__error_class = params.errorClass;
|
|
4900
|
+
this.__allow_empty_string = params.allowEmptyString ?? false;
|
|
4654
4901
|
}
|
|
4655
4902
|
/**
|
|
4656
4903
|
* Called when the API has been reset
|
|
@@ -4690,7 +4937,7 @@ ${stackTrace}`);
|
|
|
4690
4937
|
* @param {string} raw
|
|
4691
4938
|
*/
|
|
4692
4939
|
set raw(raw) {
|
|
4693
|
-
if (validationService.validateScore(this._cmi_element + ".raw", raw, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class)) {
|
|
4940
|
+
if (validationService.validateScore(this._cmi_element + ".raw", raw, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class, this.__allow_empty_string)) {
|
|
4694
4941
|
this._raw = raw;
|
|
4695
4942
|
}
|
|
4696
4943
|
}
|
|
@@ -4706,7 +4953,7 @@ ${stackTrace}`);
|
|
|
4706
4953
|
* @param {string} min
|
|
4707
4954
|
*/
|
|
4708
4955
|
set min(min) {
|
|
4709
|
-
if (validationService.validateScore(this._cmi_element + ".min", min, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class)) {
|
|
4956
|
+
if (validationService.validateScore(this._cmi_element + ".min", min, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class, this.__allow_empty_string)) {
|
|
4710
4957
|
this._min = min;
|
|
4711
4958
|
}
|
|
4712
4959
|
}
|
|
@@ -4722,7 +4969,7 @@ ${stackTrace}`);
|
|
|
4722
4969
|
* @param {string} max
|
|
4723
4970
|
*/
|
|
4724
4971
|
set max(max) {
|
|
4725
|
-
if (validationService.validateScore(this._cmi_element + ".max", max, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class)) {
|
|
4972
|
+
if (validationService.validateScore(this._cmi_element + ".max", max, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class, this.__allow_empty_string)) {
|
|
4726
4973
|
this._max = max;
|
|
4727
4974
|
}
|
|
4728
4975
|
}
|
|
@@ -4799,7 +5046,9 @@ ${stackTrace}`);
|
|
|
4799
5046
|
invalidErrorCode: scorm12_errors.INVALID_SET_VALUE,
|
|
4800
5047
|
invalidTypeCode: scorm12_errors.TYPE_MISMATCH,
|
|
4801
5048
|
invalidRangeCode: scorm12_errors.VALUE_OUT_OF_RANGE,
|
|
4802
|
-
errorClass: Scorm12ValidationError
|
|
5049
|
+
errorClass: Scorm12ValidationError,
|
|
5050
|
+
// SCORM 1.2 score elements may be set blank (clears the value), per ADL 1.2 CTS.
|
|
5051
|
+
allowEmptyString: true
|
|
4803
5052
|
});
|
|
4804
5053
|
}
|
|
4805
5054
|
/**
|
|
@@ -5167,7 +5416,9 @@ ${stackTrace}`);
|
|
|
5167
5416
|
invalidErrorCode: scorm12_errors.INVALID_SET_VALUE,
|
|
5168
5417
|
invalidTypeCode: scorm12_errors.TYPE_MISMATCH,
|
|
5169
5418
|
invalidRangeCode: scorm12_errors.VALUE_OUT_OF_RANGE,
|
|
5170
|
-
errorClass: Scorm12ValidationError
|
|
5419
|
+
errorClass: Scorm12ValidationError,
|
|
5420
|
+
// SCORM 1.2 score elements may be set blank (clears the value), per ADL 1.2 CTS.
|
|
5421
|
+
allowEmptyString: true
|
|
5171
5422
|
});
|
|
5172
5423
|
}
|
|
5173
5424
|
/**
|
|
@@ -6604,16 +6855,17 @@ ${stackTrace}`);
|
|
|
6604
6855
|
* Attempts to store the data to the LMS
|
|
6605
6856
|
*
|
|
6606
6857
|
* @param {boolean} terminateCommit
|
|
6858
|
+
* @param {CommitTrigger} [trigger] - What initiated the commit
|
|
6607
6859
|
* @return {ResultObject}
|
|
6608
6860
|
*/
|
|
6609
|
-
storeData(terminateCommit) {
|
|
6861
|
+
storeData(terminateCommit, trigger) {
|
|
6610
6862
|
if (terminateCommit) {
|
|
6611
6863
|
const originalStatus = this.cmi.core.lesson_status;
|
|
6612
6864
|
if (this.cmi.core.lesson_mode === "browse") {
|
|
6613
6865
|
const startingStatus = this.startingData?.cmi?.core?.lesson_status || "";
|
|
6614
6866
|
if (startingStatus === "" && originalStatus === "not attempted") {
|
|
6615
6867
|
this.cmi.core.lesson_status = "browsed";
|
|
6616
|
-
return this.processCommitData(terminateCommit);
|
|
6868
|
+
return this.processCommitData(terminateCommit, trigger);
|
|
6617
6869
|
}
|
|
6618
6870
|
}
|
|
6619
6871
|
if (!this.cmi.core.lesson_status || !this.statusSetByModule && this.cmi.core.lesson_status === "not attempted") {
|
|
@@ -6642,12 +6894,12 @@ ${stackTrace}`);
|
|
|
6642
6894
|
}
|
|
6643
6895
|
}
|
|
6644
6896
|
}
|
|
6645
|
-
return this.processCommitData(terminateCommit);
|
|
6897
|
+
return this.processCommitData(terminateCommit, trigger);
|
|
6646
6898
|
}
|
|
6647
|
-
processCommitData(terminateCommit) {
|
|
6899
|
+
processCommitData(terminateCommit, trigger) {
|
|
6648
6900
|
const commitObject = this.getCommitObject(terminateCommit);
|
|
6649
6901
|
if (typeof this.settings.lmsCommitUrl === "string") {
|
|
6650
|
-
return this.processHttpRequest(this.settings.lmsCommitUrl, commitObject, terminateCommit);
|
|
6902
|
+
return this.processHttpRequest(this.settings.lmsCommitUrl, commitObject, terminateCommit, trigger);
|
|
6651
6903
|
} else {
|
|
6652
6904
|
return {
|
|
6653
6905
|
result: global_constants.SCORM_TRUE,
|