scorm-again 3.0.4 → 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 +96 -5
- 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 +503 -138
- 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 +339 -73
- 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 +486 -131
- 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 +443 -119
- 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 +316 -57
- 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 +431 -112
- 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/cmi/scorm2004/interaction_delimiters.d.ts +4 -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 +46 -25
package/dist/scorm2004.js
CHANGED
|
@@ -198,6 +198,15 @@ this.Scorm2004API = (function () {
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
const appendQueryParam = (url, name, value) => {
|
|
202
|
+
const fragmentIndex = url.indexOf("#");
|
|
203
|
+
const baseUrl = fragmentIndex === -1 ? url : url.slice(0, fragmentIndex);
|
|
204
|
+
const fragment = fragmentIndex === -1 ? "" : url.slice(fragmentIndex);
|
|
205
|
+
const separator = baseUrl.includes("?") ? baseUrl.endsWith("?") || baseUrl.endsWith("&") ? "" : "&" : "?";
|
|
206
|
+
const queryParam = `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}`;
|
|
207
|
+
return `${baseUrl}${separator}${queryParam}${fragment}`;
|
|
208
|
+
};
|
|
209
|
+
|
|
201
210
|
var __defProp$15 = Object.defineProperty;
|
|
202
211
|
var __defNormalProp$15 = (obj, key, value) => key in obj ? __defProp$15(obj, key, {
|
|
203
212
|
enumerable: true,
|
|
@@ -570,7 +579,10 @@ this.Scorm2004API = (function () {
|
|
|
570
579
|
INVALID_SET_VALUE: 402,
|
|
571
580
|
READ_ONLY_ELEMENT: 403,
|
|
572
581
|
TYPE_MISMATCH: 405,
|
|
573
|
-
|
|
582
|
+
// SCORM 1.2 has no out-of-range error code; an out-of-range value is reported
|
|
583
|
+
// as 405 (incorrect data type), per the SCORM 1.2 RTE error table and the ADL
|
|
584
|
+
// 1.2 CTS (DataModelValidator.checkScoreDecimal failure -> CMICategory 405).
|
|
585
|
+
VALUE_OUT_OF_RANGE: 405};
|
|
574
586
|
const scorm2004_errors = {
|
|
575
587
|
...global_errors,
|
|
576
588
|
INITIALIZATION_FAILED: 102,
|
|
@@ -725,6 +737,7 @@ this.Scorm2004API = (function () {
|
|
|
725
737
|
xhrWithCredentials: false,
|
|
726
738
|
fetchMode: "cors",
|
|
727
739
|
asyncModeBeaconBehavior: "never",
|
|
740
|
+
includeCommitSequence: false,
|
|
728
741
|
responseHandler: async function (response) {
|
|
729
742
|
if (typeof response !== "undefined") {
|
|
730
743
|
let httpResult = null;
|
|
@@ -943,6 +956,11 @@ this.Scorm2004API = (function () {
|
|
|
943
956
|
progress_range: "0#1"
|
|
944
957
|
};
|
|
945
958
|
|
|
959
|
+
const PERFORMANCE_STEP_NAME = "^$|" + scorm2004_regex.CMIShortIdentifier;
|
|
960
|
+
const PERFORMANCE_CHARACTERSTRING = "(?![\\s\\S]*(?:\\[,\\]|\\[\\.\\]|\\[:\\]))[\\s\\S]{1,250}";
|
|
961
|
+
const PERFORMANCE_NUMERIC_RANGE = "(?:-?\\d+(?:\\.\\d+)?)?\\[:\\](?:-?\\d+(?:\\.\\d+)?)?";
|
|
962
|
+
const CR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_NUMERIC_RANGE + "|" + PERFORMANCE_CHARACTERSTRING + ")$";
|
|
963
|
+
const LR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_CHARACTERSTRING + ")$";
|
|
946
964
|
const LearnerResponses = {
|
|
947
965
|
"true-false": {
|
|
948
966
|
format: "^true$|^false$",
|
|
@@ -977,8 +995,8 @@ this.Scorm2004API = (function () {
|
|
|
977
995
|
unique: false
|
|
978
996
|
},
|
|
979
997
|
performance: {
|
|
980
|
-
format:
|
|
981
|
-
format2:
|
|
998
|
+
format: PERFORMANCE_STEP_NAME,
|
|
999
|
+
format2: LR_PERFORMANCE_STEP_ANSWER,
|
|
982
1000
|
max: 250,
|
|
983
1001
|
delimiter: "[,]",
|
|
984
1002
|
delimiter2: "[.]",
|
|
@@ -1054,10 +1072,10 @@ this.Scorm2004API = (function () {
|
|
|
1054
1072
|
delimiter2: "[.]",
|
|
1055
1073
|
unique: false,
|
|
1056
1074
|
duplicate: false,
|
|
1057
|
-
// step_name
|
|
1058
|
-
format:
|
|
1059
|
-
// step_answer
|
|
1060
|
-
format2:
|
|
1075
|
+
// step_name: optional short_identifier_type
|
|
1076
|
+
format: PERFORMANCE_STEP_NAME,
|
|
1077
|
+
// step_answer: optional characterstring (spaces allowed) or numeric range
|
|
1078
|
+
format2: CR_PERFORMANCE_STEP_ANSWER
|
|
1061
1079
|
},
|
|
1062
1080
|
sequencing: {
|
|
1063
1081
|
max: 36,
|
|
@@ -1133,7 +1151,7 @@ this.Scorm2004API = (function () {
|
|
|
1133
1151
|
wrapper() {
|
|
1134
1152
|
if (!this._cancelled) {
|
|
1135
1153
|
if (this._API.isInitialized()) {
|
|
1136
|
-
(async () => await this._API.commit(this._callback))();
|
|
1154
|
+
(async () => await this._API.commit(this._callback, false, "autocommit"))();
|
|
1137
1155
|
}
|
|
1138
1156
|
}
|
|
1139
1157
|
}
|
|
@@ -4606,6 +4624,7 @@ this.Scorm2004API = (function () {
|
|
|
4606
4624
|
* @param {ErrorCode} error_codes - The error codes object
|
|
4607
4625
|
*/
|
|
4608
4626
|
constructor(settings, error_codes) {
|
|
4627
|
+
__publicField$Q(this, "reportsRequestCompletion", true);
|
|
4609
4628
|
__publicField$Q(this, "settings");
|
|
4610
4629
|
__publicField$Q(this, "error_codes");
|
|
4611
4630
|
this.settings = settings;
|
|
@@ -4624,13 +4643,17 @@ this.Scorm2004API = (function () {
|
|
|
4624
4643
|
* @param {boolean} immediate - Whether to send the request immediately without waiting
|
|
4625
4644
|
* @param {Function} apiLog - Function to log API messages with appropriate levels
|
|
4626
4645
|
* @param {Function} processListeners - Function to trigger event listeners for commit events
|
|
4646
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
4647
|
+
* @param {Function} onRequestComplete - Callback invoked after the background request settles
|
|
4627
4648
|
* @return {ResultObject} - Immediate optimistic success result
|
|
4628
4649
|
*/
|
|
4629
4650
|
processHttpRequest(url, params) {
|
|
4630
4651
|
let immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
4631
4652
|
let apiLog = arguments.length > 3 ? arguments[3] : undefined;
|
|
4632
4653
|
let processListeners = arguments.length > 4 ? arguments[4] : undefined;
|
|
4633
|
-
|
|
4654
|
+
let metadata = arguments.length > 5 ? arguments[5] : undefined;
|
|
4655
|
+
let onRequestComplete = arguments.length > 6 ? arguments[6] : undefined;
|
|
4656
|
+
this._performAsyncRequest(url, params, immediate, apiLog, processListeners, metadata, onRequestComplete);
|
|
4634
4657
|
return {
|
|
4635
4658
|
result: global_constants.SCORM_TRUE,
|
|
4636
4659
|
errorCode: 0
|
|
@@ -4643,11 +4666,14 @@ this.Scorm2004API = (function () {
|
|
|
4643
4666
|
* @param {boolean} immediate - Whether this is an immediate request
|
|
4644
4667
|
* @param apiLog - Function to log API messages
|
|
4645
4668
|
* @param {Function} processListeners - Function to process event listeners
|
|
4669
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
4670
|
+
* @param {Function} onRequestComplete - Callback invoked after the request settles
|
|
4646
4671
|
* @private
|
|
4647
4672
|
*/
|
|
4648
|
-
async _performAsyncRequest(url, params, immediate, apiLog, processListeners) {
|
|
4673
|
+
async _performAsyncRequest(url, params, immediate, apiLog, processListeners, metadata, onRequestComplete) {
|
|
4649
4674
|
try {
|
|
4650
|
-
const
|
|
4675
|
+
const handledParams = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
4676
|
+
const processedParams = handledParams;
|
|
4651
4677
|
let response;
|
|
4652
4678
|
if (immediate && this.settings.asyncModeBeaconBehavior !== "never") {
|
|
4653
4679
|
response = await this.performBeacon(url, processedParams);
|
|
@@ -4663,7 +4689,9 @@ this.Scorm2004API = (function () {
|
|
|
4663
4689
|
} catch (e) {
|
|
4664
4690
|
const message = e instanceof Error ? e.message : String(e);
|
|
4665
4691
|
apiLog("processHttpRequest", `Async request failed: ${message}`, LogLevelEnum.ERROR);
|
|
4666
|
-
processListeners("CommitError");
|
|
4692
|
+
processListeners("CommitError", void 0, this.error_codes.GENERAL_COMMIT_FAILURE || 391);
|
|
4693
|
+
} finally {
|
|
4694
|
+
onRequestComplete?.();
|
|
4667
4695
|
}
|
|
4668
4696
|
}
|
|
4669
4697
|
/**
|
|
@@ -4825,10 +4853,13 @@ this.Scorm2004API = (function () {
|
|
|
4825
4853
|
}
|
|
4826
4854
|
/**
|
|
4827
4855
|
* Gets the appropriate error code for undefined data model elements.
|
|
4828
|
-
* SCORM 2004
|
|
4856
|
+
* Both SCORM 2004 and SCORM 1.2 use UNDEFINED_DATA_MODEL (401): an
|
|
4857
|
+
* unrecognized element is "Not implemented", not a general exception. SCORM
|
|
4858
|
+
* 1.2 previously returned GENERAL (101) here, which is non-conformant — the
|
|
4859
|
+
* ADL 1.2 CTS and the SCORM 1.2 RTE spec expect 401 for an unknown element.
|
|
4829
4860
|
*/
|
|
4830
|
-
getUndefinedDataModelErrorCode(
|
|
4831
|
-
return
|
|
4861
|
+
getUndefinedDataModelErrorCode() {
|
|
4862
|
+
return getErrorCode(this.context.errorCodes, "UNDEFINED_DATA_MODEL");
|
|
4832
4863
|
}
|
|
4833
4864
|
/**
|
|
4834
4865
|
* Sets a value on a CMI element path
|
|
@@ -4852,7 +4883,7 @@ this.Scorm2004API = (function () {
|
|
|
4852
4883
|
let returnValue = global_constants.SCORM_FALSE;
|
|
4853
4884
|
let foundFirstIndex = false;
|
|
4854
4885
|
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
|
4855
|
-
const invalidErrorCode = this.getUndefinedDataModelErrorCode(
|
|
4886
|
+
const invalidErrorCode = this.getUndefinedDataModelErrorCode();
|
|
4856
4887
|
for (let idx = 0; idx < structure.length; idx++) {
|
|
4857
4888
|
const attribute = structure[idx];
|
|
4858
4889
|
if (idx === structure.length - 1) {
|
|
@@ -4892,12 +4923,13 @@ this.Scorm2004API = (function () {
|
|
|
4892
4923
|
this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "GENERAL_GET_FAILURE"), "The _version keyword was used incorrectly");
|
|
4893
4924
|
return "";
|
|
4894
4925
|
}
|
|
4926
|
+
this.context.setLastErrorCode("0");
|
|
4895
4927
|
const structure = CMIElement.split(".");
|
|
4896
4928
|
let refObject = this.context.getDataModel();
|
|
4897
4929
|
let attribute = null;
|
|
4898
4930
|
const uninitializedErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) has not been initialized.`;
|
|
4899
4931
|
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
|
4900
|
-
const invalidErrorCode = this.getUndefinedDataModelErrorCode(
|
|
4932
|
+
const invalidErrorCode = this.getUndefinedDataModelErrorCode();
|
|
4901
4933
|
for (let idx = 0; idx < structure.length; idx++) {
|
|
4902
4934
|
attribute = structure[idx];
|
|
4903
4935
|
const validationResult = this.validateGetAttribute(refObject, attribute, CMIElement, scorm2004, invalidErrorCode, invalidErrorMessage, idx === structure.length - 1);
|
|
@@ -5082,7 +5114,13 @@ this.Scorm2004API = (function () {
|
|
|
5082
5114
|
if (!scorm2004) {
|
|
5083
5115
|
if (isFinalAttribute) {
|
|
5084
5116
|
if (typeof attribute === "undefined" || !this.context.checkObjectHasProperty(refObject, attribute)) {
|
|
5085
|
-
|
|
5117
|
+
if (attribute === "_children") {
|
|
5118
|
+
this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "CHILDREN_ERROR"));
|
|
5119
|
+
} else if (attribute === "_count") {
|
|
5120
|
+
this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "COUNT_ERROR"));
|
|
5121
|
+
} else {
|
|
5122
|
+
this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);
|
|
5123
|
+
}
|
|
5086
5124
|
return {
|
|
5087
5125
|
error: true
|
|
5088
5126
|
};
|
|
@@ -5617,8 +5655,9 @@ ${stackTrace}`);
|
|
|
5617
5655
|
* @param {string} functionName - The name of the function that triggered the event
|
|
5618
5656
|
* @param {string} CMIElement - The CMI element that was affected
|
|
5619
5657
|
* @param {any} value - The value that was set
|
|
5658
|
+
* @param {CommitEventContext} context - Optional context for commit lifecycle events
|
|
5620
5659
|
*/
|
|
5621
|
-
processListeners(functionName, CMIElement, value) {
|
|
5660
|
+
processListeners(functionName, CMIElement, value, context) {
|
|
5622
5661
|
this.apiLog(functionName, value, LogLevelEnum.INFO, CMIElement);
|
|
5623
5662
|
const listeners = this.listenerMap.get(functionName);
|
|
5624
5663
|
if (!listeners) return;
|
|
@@ -5638,9 +5677,17 @@ ${stackTrace}`);
|
|
|
5638
5677
|
if (functionName.startsWith("Sequence")) {
|
|
5639
5678
|
listener.callback(value);
|
|
5640
5679
|
} else if (functionName === "CommitError") {
|
|
5641
|
-
|
|
5680
|
+
if (context !== void 0) {
|
|
5681
|
+
listener.callback(value, context);
|
|
5682
|
+
} else {
|
|
5683
|
+
listener.callback(value);
|
|
5684
|
+
}
|
|
5642
5685
|
} else if (functionName === "CommitSuccess") {
|
|
5643
|
-
|
|
5686
|
+
if (context !== void 0) {
|
|
5687
|
+
listener.callback(context);
|
|
5688
|
+
} else {
|
|
5689
|
+
listener.callback();
|
|
5690
|
+
}
|
|
5644
5691
|
} else {
|
|
5645
5692
|
listener.callback(CMIElement, value);
|
|
5646
5693
|
}
|
|
@@ -5749,16 +5796,23 @@ ${stackTrace}`);
|
|
|
5749
5796
|
* Store commit data offline
|
|
5750
5797
|
* @param {string} courseId - Identifier for the course
|
|
5751
5798
|
* @param {CommitObject} commitData - The data to store offline
|
|
5799
|
+
* @param {OfflineCommitMetadata} metadata - Metadata captured with the original commit
|
|
5752
5800
|
* @returns {ResultObject} - Result of the storage operation
|
|
5753
5801
|
*/
|
|
5754
|
-
storeOffline(courseId, commitData) {
|
|
5802
|
+
storeOffline(courseId, commitData, metadata) {
|
|
5755
5803
|
try {
|
|
5756
5804
|
const queueItem = {
|
|
5757
5805
|
id: `${courseId}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
5758
5806
|
courseId,
|
|
5759
5807
|
timestamp: Date.now(),
|
|
5760
5808
|
data: commitData,
|
|
5761
|
-
syncAttempts: 0
|
|
5809
|
+
syncAttempts: 0,
|
|
5810
|
+
...(metadata?.isTerminateCommit !== void 0 ? {
|
|
5811
|
+
isTerminateCommit: metadata.isTerminateCommit
|
|
5812
|
+
} : {}),
|
|
5813
|
+
...(metadata?.sequence !== void 0 ? {
|
|
5814
|
+
sequence: metadata.sequence
|
|
5815
|
+
} : {})
|
|
5762
5816
|
};
|
|
5763
5817
|
const currentQueue = this.getFromStorage(this.syncQueue) || [];
|
|
5764
5818
|
currentQueue.push(queueItem);
|
|
@@ -5817,7 +5871,14 @@ ${stackTrace}`);
|
|
|
5817
5871
|
continue;
|
|
5818
5872
|
}
|
|
5819
5873
|
try {
|
|
5820
|
-
const syncResult = await this.sendDataToLMS(item.data
|
|
5874
|
+
const syncResult = await this.sendDataToLMS(item.data, {
|
|
5875
|
+
...(item.isTerminateCommit !== void 0 ? {
|
|
5876
|
+
isTerminateCommit: item.isTerminateCommit
|
|
5877
|
+
} : {}),
|
|
5878
|
+
...(item.sequence !== void 0 ? {
|
|
5879
|
+
sequence: item.sequence
|
|
5880
|
+
} : {})
|
|
5881
|
+
});
|
|
5821
5882
|
if (syncResult.result === true || syncResult.result === global_constants.SCORM_TRUE) {
|
|
5822
5883
|
this.apiLog("OfflineStorageService", `Successfully synced item ${item.id}`, LogLevelEnum.INFO);
|
|
5823
5884
|
} else {
|
|
@@ -5844,17 +5905,27 @@ ${stackTrace}`);
|
|
|
5844
5905
|
/**
|
|
5845
5906
|
* Send data to the LMS when online
|
|
5846
5907
|
* @param {CommitObject} data - The data to send to the LMS
|
|
5908
|
+
* @param {OfflineCommitMetadata} metadata - Metadata captured with the original commit
|
|
5847
5909
|
* @returns {Promise<ResultObject>} - Result of the sync operation
|
|
5848
5910
|
*/
|
|
5849
|
-
async sendDataToLMS(data) {
|
|
5850
|
-
|
|
5911
|
+
async sendDataToLMS(data, metadata) {
|
|
5912
|
+
const configuredCommitUrl = this.settings.lmsCommitUrl;
|
|
5913
|
+
if (!configuredCommitUrl) {
|
|
5851
5914
|
return {
|
|
5852
5915
|
result: global_constants.SCORM_FALSE,
|
|
5853
5916
|
errorCode: this.error_codes.GENERAL || 101
|
|
5854
5917
|
};
|
|
5855
5918
|
}
|
|
5856
5919
|
try {
|
|
5857
|
-
const
|
|
5920
|
+
const lmsCommitUrl = String(configuredCommitUrl);
|
|
5921
|
+
const processedData = this.settings.requestHandler(data, {
|
|
5922
|
+
isTerminateCommit: metadata?.isTerminateCommit ?? false,
|
|
5923
|
+
trigger: "offline-replay",
|
|
5924
|
+
...(metadata?.sequence !== void 0 ? {
|
|
5925
|
+
sequence: metadata.sequence
|
|
5926
|
+
} : {})
|
|
5927
|
+
});
|
|
5928
|
+
const requestUrl = metadata?.isTerminateCommit && this.settings.terminateCommitParam ? appendQueryParam(lmsCommitUrl, this.settings.terminateCommitParam, "true") : lmsCommitUrl;
|
|
5858
5929
|
const init = {
|
|
5859
5930
|
method: "POST",
|
|
5860
5931
|
mode: this.settings.fetchMode,
|
|
@@ -5867,7 +5938,7 @@ ${stackTrace}`);
|
|
|
5867
5938
|
if (this.settings.xhrWithCredentials) {
|
|
5868
5939
|
init.credentials = "include";
|
|
5869
5940
|
}
|
|
5870
|
-
const response = await fetch(
|
|
5941
|
+
const response = await fetch(requestUrl, init);
|
|
5871
5942
|
const result = typeof this.settings.responseHandler === "function" ? await this.settings.responseHandler(response) : await response.json();
|
|
5872
5943
|
if (response.status >= 200 && response.status <= 299 && (result.result === true || result.result === global_constants.SCORM_TRUE)) {
|
|
5873
5944
|
if (!Object.hasOwnProperty.call(result, "errorCode")) {
|
|
@@ -14764,6 +14835,8 @@ ${stackTrace}`);
|
|
|
14764
14835
|
* @param {boolean} immediate - Whether this is a termination commit (use sendBeacon)
|
|
14765
14836
|
* @param {Function} _apiLog - Function to log API messages (unused in synchronous mode - errors returned directly)
|
|
14766
14837
|
* @param {Function} _processListeners - Function to trigger event listeners (unused in synchronous mode - no async events)
|
|
14838
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
14839
|
+
* @param {Function} _onRequestComplete - Completion callback (unused because requests settle before return)
|
|
14767
14840
|
* @return {ResultObject} - The result of the request (synchronous)
|
|
14768
14841
|
*
|
|
14769
14842
|
* @remarks
|
|
@@ -14775,20 +14848,23 @@ ${stackTrace}`);
|
|
|
14775
14848
|
*/
|
|
14776
14849
|
processHttpRequest(url, params) {
|
|
14777
14850
|
let immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
14851
|
+
let metadata = arguments.length > 5 ? arguments[5] : undefined;
|
|
14778
14852
|
if (immediate) {
|
|
14779
|
-
return this._handleImmediateRequest(url, params);
|
|
14853
|
+
return this._handleImmediateRequest(url, params, metadata);
|
|
14780
14854
|
}
|
|
14781
|
-
return this._performSyncXHR(url, params);
|
|
14855
|
+
return this._performSyncXHR(url, params, metadata);
|
|
14782
14856
|
}
|
|
14783
14857
|
/**
|
|
14784
14858
|
* Handles an immediate request using sendBeacon
|
|
14785
14859
|
* @param {string} url - The URL to send the request to
|
|
14786
14860
|
* @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request
|
|
14861
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
14787
14862
|
* @return {ResultObject} - The result based on beacon success
|
|
14788
14863
|
* @private
|
|
14789
14864
|
*/
|
|
14790
|
-
_handleImmediateRequest(url, params) {
|
|
14791
|
-
const
|
|
14865
|
+
_handleImmediateRequest(url, params, metadata) {
|
|
14866
|
+
const handledPayload = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
14867
|
+
const requestPayload = handledPayload ?? params;
|
|
14792
14868
|
const {
|
|
14793
14869
|
body
|
|
14794
14870
|
} = this._prepareRequestBody(requestPayload);
|
|
@@ -14804,11 +14880,13 @@ ${stackTrace}`);
|
|
|
14804
14880
|
* Performs a synchronous XMLHttpRequest
|
|
14805
14881
|
* @param {string} url - The URL to send the request to
|
|
14806
14882
|
* @param {CommitObject|StringKeyMap|Array} params - The parameters to include in the request
|
|
14883
|
+
* @param {CommitMetadata} metadata - Metadata describing the captured commit
|
|
14807
14884
|
* @return {ResultObject} - The result of the request
|
|
14808
14885
|
* @private
|
|
14809
14886
|
*/
|
|
14810
|
-
_performSyncXHR(url, params) {
|
|
14811
|
-
const
|
|
14887
|
+
_performSyncXHR(url, params, metadata) {
|
|
14888
|
+
const handledPayload = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
14889
|
+
const requestPayload = handledPayload ?? params;
|
|
14812
14890
|
const {
|
|
14813
14891
|
body,
|
|
14814
14892
|
contentType
|
|
@@ -14881,9 +14959,16 @@ ${stackTrace}`);
|
|
|
14881
14959
|
* @param {number} invalidTypeCode - The error code for invalid type
|
|
14882
14960
|
* @param {number} invalidRangeCode - The error code for invalid range
|
|
14883
14961
|
* @param {typeof BaseScormValidationError} errorClass - The error class to use for validation errors
|
|
14962
|
+
* @param {boolean} allowEmptyString - When true, an empty string is accepted (clears the value).
|
|
14963
|
+
* SCORM 1.2 score elements may be blank per the ADL 1.2 CTS
|
|
14964
|
+
* (DataModelValidator.checkScoreDecimal treats blank as valid).
|
|
14884
14965
|
* @return {boolean} - True if validation passes, throws an error otherwise
|
|
14885
14966
|
*/
|
|
14886
14967
|
validateScore(CMIElement, value, decimalRegex, scoreRange, invalidTypeCode, invalidRangeCode, errorClass) {
|
|
14968
|
+
let allowEmptyString = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;
|
|
14969
|
+
if (allowEmptyString && value === "") {
|
|
14970
|
+
return true;
|
|
14971
|
+
}
|
|
14887
14972
|
return checkValidFormat(CMIElement, value, decimalRegex, invalidTypeCode, errorClass) && (!scoreRange || checkValidRange(CMIElement, value, scoreRange, invalidRangeCode, errorClass));
|
|
14888
14973
|
}
|
|
14889
14974
|
/**
|
|
@@ -14979,6 +15064,21 @@ ${stackTrace}`);
|
|
|
14979
15064
|
__publicField$o(this, "_offlineStorageService");
|
|
14980
15065
|
__publicField$o(this, "_cmiValueAccessService");
|
|
14981
15066
|
__publicField$o(this, "_courseId", "");
|
|
15067
|
+
__publicField$o(this, "_pendingCommitCount", 0);
|
|
15068
|
+
/**
|
|
15069
|
+
* Monotonic sequence for commits captured by this API instance. It is
|
|
15070
|
+
* intentionally not reset by reset().
|
|
15071
|
+
*/
|
|
15072
|
+
__publicField$o(this, "_commitSequence", 0);
|
|
15073
|
+
__publicField$o(this, "_commitSettleWaiters", []);
|
|
15074
|
+
/**
|
|
15075
|
+
* Canonical paths of every CMI element that has been explicitly assigned a
|
|
15076
|
+
* value via SetValue / loadFromJSON (both funnel through _commonSetCMIValue).
|
|
15077
|
+
* Used by standards that must tell "implemented but never set" apart from a
|
|
15078
|
+
* legitimately empty value when answering GetValue (SCORM 2004 error 403).
|
|
15079
|
+
* Cleared on reset so a fresh SCO attempt starts with nothing "set".
|
|
15080
|
+
*/
|
|
15081
|
+
__publicField$o(this, "_setCMIElements", /* @__PURE__ */new Set());
|
|
14982
15082
|
__publicField$o(this, "startingData");
|
|
14983
15083
|
__publicField$o(this, "currentState");
|
|
14984
15084
|
if (new.target === BaseAPI) {
|
|
@@ -15125,6 +15225,7 @@ ${stackTrace}`);
|
|
|
15125
15225
|
this.lastErrorCode = "0";
|
|
15126
15226
|
this._eventService.reset();
|
|
15127
15227
|
this.startingData = {};
|
|
15228
|
+
this._setCMIElements.clear();
|
|
15128
15229
|
if (this._offlineStorageService) {
|
|
15129
15230
|
this._offlineStorageService.updateSettings(this.settings);
|
|
15130
15231
|
if (settings?.courseId) {
|
|
@@ -15208,6 +15309,54 @@ ${stackTrace}`);
|
|
|
15208
15309
|
this._loggingService?.setLogHandler(settings.onLogMessage);
|
|
15209
15310
|
}
|
|
15210
15311
|
}
|
|
15312
|
+
/**
|
|
15313
|
+
* Gets the number of captured commit requests that have not yet settled.
|
|
15314
|
+
*
|
|
15315
|
+
* @return {number} The number of in-flight commits
|
|
15316
|
+
*/
|
|
15317
|
+
get pendingCommitCount() {
|
|
15318
|
+
return this._pendingCommitCount;
|
|
15319
|
+
}
|
|
15320
|
+
/**
|
|
15321
|
+
* Resolves when all currently in-flight commits have settled. A timeout is
|
|
15322
|
+
* best-effort: the promise resolves when it elapses even if commits remain,
|
|
15323
|
+
* and callers can inspect pendingCommitCount afterward to detect that case.
|
|
15324
|
+
*
|
|
15325
|
+
* @param {Object} [options] - Settle options
|
|
15326
|
+
* @param {number} [options.timeoutMs] - Maximum time to wait in milliseconds
|
|
15327
|
+
* @return {Promise<void>} A promise that resolves after the drain or timeout
|
|
15328
|
+
*/
|
|
15329
|
+
whenCommitsSettled(options) {
|
|
15330
|
+
if (this._pendingCommitCount === 0) {
|
|
15331
|
+
return Promise.resolve();
|
|
15332
|
+
}
|
|
15333
|
+
return new Promise(resolve => {
|
|
15334
|
+
const waiter = {
|
|
15335
|
+
resolve
|
|
15336
|
+
};
|
|
15337
|
+
if (options?.timeoutMs !== void 0) {
|
|
15338
|
+
waiter.timeoutId = setTimeout(() => {
|
|
15339
|
+
const waiterIndex = this._commitSettleWaiters.indexOf(waiter);
|
|
15340
|
+
if (waiterIndex === -1) {
|
|
15341
|
+
return;
|
|
15342
|
+
}
|
|
15343
|
+
this._commitSettleWaiters.splice(waiterIndex, 1);
|
|
15344
|
+
resolve();
|
|
15345
|
+
}, options.timeoutMs);
|
|
15346
|
+
}
|
|
15347
|
+
this._commitSettleWaiters.push(waiter);
|
|
15348
|
+
});
|
|
15349
|
+
}
|
|
15350
|
+
/** Resolve and clear every waiter after the pending count reaches zero. */
|
|
15351
|
+
_flushCommitSettleWaiters() {
|
|
15352
|
+
const waiters = this._commitSettleWaiters.splice(0);
|
|
15353
|
+
for (const waiter of waiters) {
|
|
15354
|
+
if (waiter.timeoutId !== void 0) {
|
|
15355
|
+
clearTimeout(waiter.timeoutId);
|
|
15356
|
+
}
|
|
15357
|
+
waiter.resolve();
|
|
15358
|
+
}
|
|
15359
|
+
}
|
|
15211
15360
|
/**
|
|
15212
15361
|
* Terminates the current run of the API
|
|
15213
15362
|
* @param {string} callbackName
|
|
@@ -15228,7 +15377,7 @@ ${stackTrace}`);
|
|
|
15228
15377
|
} else {
|
|
15229
15378
|
stateCheckPassed = true;
|
|
15230
15379
|
this.processListeners("BeforeTerminate");
|
|
15231
|
-
const result = this.storeData(true);
|
|
15380
|
+
const result = this.storeData(true, "terminate");
|
|
15232
15381
|
if ((result.errorCode ?? 0) > 0) {
|
|
15233
15382
|
if (result.errorMessage) {
|
|
15234
15383
|
this.apiLog("terminate", `Terminate failed with error: ${result.errorMessage}`, LogLevelEnum.ERROR);
|
|
@@ -15268,6 +15417,9 @@ ${stackTrace}`);
|
|
|
15268
15417
|
} catch (e) {
|
|
15269
15418
|
returnValue = this.handleValueAccessException(CMIElement, e, returnValue);
|
|
15270
15419
|
}
|
|
15420
|
+
if (this.lastErrorCode === "0") {
|
|
15421
|
+
this.checkUninitializedGet(CMIElement, returnValue);
|
|
15422
|
+
}
|
|
15271
15423
|
this.processListeners(callbackName, CMIElement);
|
|
15272
15424
|
}
|
|
15273
15425
|
this.apiLog(callbackName, ": returned: " + returnValue, LogLevelEnum.INFO, CMIElement);
|
|
@@ -15277,6 +15429,10 @@ ${stackTrace}`);
|
|
|
15277
15429
|
if (this.lastErrorCode === "0") {
|
|
15278
15430
|
this.clearSCORMError(returnValue);
|
|
15279
15431
|
}
|
|
15432
|
+
const rawReturn = returnValue;
|
|
15433
|
+
if (typeof rawReturn === "number" || typeof rawReturn === "boolean") {
|
|
15434
|
+
return String(rawReturn);
|
|
15435
|
+
}
|
|
15280
15436
|
return returnValue;
|
|
15281
15437
|
}
|
|
15282
15438
|
/**
|
|
@@ -15320,10 +15476,12 @@ ${stackTrace}`);
|
|
|
15320
15476
|
* Orders LMS to store all content parameters
|
|
15321
15477
|
* @param {string} callbackName
|
|
15322
15478
|
* @param {boolean} checkTerminated
|
|
15479
|
+
* @param {CommitTrigger} trigger - What initiated the commit
|
|
15323
15480
|
* @return {string}
|
|
15324
15481
|
*/
|
|
15325
15482
|
commit(callbackName) {
|
|
15326
15483
|
let checkTerminated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
15484
|
+
let trigger = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "manual";
|
|
15327
15485
|
this.clearScheduledCommit();
|
|
15328
15486
|
let returnValue = global_constants.SCORM_TRUE;
|
|
15329
15487
|
if (this.isNotInitialized()) {
|
|
@@ -15335,20 +15493,21 @@ ${stackTrace}`);
|
|
|
15335
15493
|
this.throwSCORMError("api", errorCode);
|
|
15336
15494
|
if (errorCode === 143) returnValue = global_constants.SCORM_FALSE;
|
|
15337
15495
|
} else {
|
|
15338
|
-
const result = this.storeData(false);
|
|
15339
|
-
|
|
15496
|
+
const result = this.storeData(false, trigger);
|
|
15497
|
+
const errorCode = result.errorCode ?? 0;
|
|
15498
|
+
if (errorCode > 0) {
|
|
15340
15499
|
if (result.errorMessage) {
|
|
15341
15500
|
this.apiLog("commit", `Commit failed with error: ${result.errorMessage}`, LogLevelEnum.ERROR);
|
|
15342
15501
|
}
|
|
15343
15502
|
if (result.errorDetails) {
|
|
15344
15503
|
this.apiLog("commit", `Error details: ${JSON.stringify(result.errorDetails)}`, LogLevelEnum.DEBUG);
|
|
15345
15504
|
}
|
|
15346
|
-
this.throwSCORMError("api",
|
|
15505
|
+
this.throwSCORMError("api", errorCode);
|
|
15347
15506
|
}
|
|
15348
15507
|
const resultValue = result?.result ?? global_constants.SCORM_FALSE;
|
|
15349
15508
|
returnValue = typeof resultValue === "boolean" ? String(resultValue) : resultValue;
|
|
15350
15509
|
this.apiLog(callbackName, " Result: " + returnValue, LogLevelEnum.DEBUG, "HttpRequest");
|
|
15351
|
-
if (checkTerminated) this.lastErrorCode = "0";
|
|
15510
|
+
if (checkTerminated && errorCode === 0) this.lastErrorCode = "0";
|
|
15352
15511
|
this.processListeners(callbackName);
|
|
15353
15512
|
if (this.settings.enableOfflineSupport && this._offlineStorageService && this._offlineStorageService.isDeviceOnline() && this._courseId) {
|
|
15354
15513
|
this._offlineStorageService.hasPendingOfflineData(this._courseId).then(hasPendingData => {
|
|
@@ -15555,7 +15714,11 @@ ${stackTrace}`);
|
|
|
15555
15714
|
* @return {string}
|
|
15556
15715
|
*/
|
|
15557
15716
|
_commonSetCMIValue(methodName, scorm2004, CMIElement, value) {
|
|
15558
|
-
|
|
15717
|
+
const result = this._cmiValueAccessService.setCMIValue(methodName, scorm2004, CMIElement, value);
|
|
15718
|
+
if (result === global_constants.SCORM_TRUE) {
|
|
15719
|
+
this._setCMIElements.add(CMIElement);
|
|
15720
|
+
}
|
|
15721
|
+
return result;
|
|
15559
15722
|
}
|
|
15560
15723
|
/**
|
|
15561
15724
|
* Gets a value from the CMI Object.
|
|
@@ -15569,6 +15732,17 @@ ${stackTrace}`);
|
|
|
15569
15732
|
_commonGetCMIValue(methodName, scorm2004, CMIElement) {
|
|
15570
15733
|
return this._cmiValueAccessService.getCMIValue(methodName, scorm2004, CMIElement);
|
|
15571
15734
|
}
|
|
15735
|
+
/**
|
|
15736
|
+
* Hook invoked by getValue after a successful resolution. Standards that must
|
|
15737
|
+
* distinguish "implemented but never set, no default value" from a
|
|
15738
|
+
* legitimately empty value override this to raise VALUE_NOT_INITIALIZED.
|
|
15739
|
+
* Default is a no-op, so SCORM 1.2 / AICC keep returning "" with code 0.
|
|
15740
|
+
*
|
|
15741
|
+
* @param {string} _CMIElement - the element that was read
|
|
15742
|
+
* @param {any} _returnValue - the value getCMIValue resolved
|
|
15743
|
+
* @protected
|
|
15744
|
+
*/
|
|
15745
|
+
checkUninitializedGet(_CMIElement, _returnValue) {}
|
|
15572
15746
|
/**
|
|
15573
15747
|
* Returns true if the API's current state is STATE_INITIALIZED
|
|
15574
15748
|
*
|
|
@@ -15651,9 +15825,14 @@ ${stackTrace}`);
|
|
|
15651
15825
|
* @param {string} functionName - The name of the function/event that occurred
|
|
15652
15826
|
* @param {string} CMIElement - Optional CMI element involved in the event
|
|
15653
15827
|
* @param {any} value - Optional value associated with the event
|
|
15828
|
+
* @param {CommitEventContext} context - Optional context for commit lifecycle events
|
|
15654
15829
|
*/
|
|
15655
|
-
processListeners(functionName, CMIElement, value) {
|
|
15656
|
-
|
|
15830
|
+
processListeners(functionName, CMIElement, value, context) {
|
|
15831
|
+
if (context !== void 0) {
|
|
15832
|
+
this._eventService.processListeners(functionName, CMIElement, value, context);
|
|
15833
|
+
} else {
|
|
15834
|
+
this._eventService.processListeners(functionName, CMIElement, value);
|
|
15835
|
+
}
|
|
15657
15836
|
}
|
|
15658
15837
|
/**
|
|
15659
15838
|
* Throws a SCORM error with the specified error number and optional message.
|
|
@@ -15775,23 +15954,97 @@ ${stackTrace}`);
|
|
|
15775
15954
|
* @param {string} url - The URL to send the request to
|
|
15776
15955
|
* @param {CommitObject | StringKeyMap | Array<any>} params - The parameters to send
|
|
15777
15956
|
* @param {boolean} immediate - Whether to send the request immediately without waiting
|
|
15957
|
+
* @param {CommitTrigger} [trigger] - What initiated the commit
|
|
15778
15958
|
* @returns {ResultObject} - The result of the request
|
|
15779
15959
|
*/
|
|
15780
15960
|
processHttpRequest(url, params) {
|
|
15781
15961
|
let immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
15782
|
-
|
|
15783
|
-
|
|
15784
|
-
|
|
15785
|
-
|
|
15786
|
-
|
|
15787
|
-
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
|
|
15962
|
+
let trigger = arguments.length > 3 ? arguments[3] : undefined;
|
|
15963
|
+
const sequence = ++this._commitSequence;
|
|
15964
|
+
this._pendingCommitCount += 1;
|
|
15965
|
+
let settled = false;
|
|
15966
|
+
let completionDeferred = false;
|
|
15967
|
+
const settle = () => {
|
|
15968
|
+
if (settled) {
|
|
15969
|
+
return;
|
|
15970
|
+
}
|
|
15971
|
+
settled = true;
|
|
15972
|
+
this._pendingCommitCount -= 1;
|
|
15973
|
+
if (this._pendingCommitCount === 0) {
|
|
15974
|
+
this._flushCommitSettleWaiters();
|
|
15975
|
+
}
|
|
15976
|
+
};
|
|
15977
|
+
try {
|
|
15978
|
+
const resolvedTrigger = trigger ?? (immediate ? "terminate" : "manual");
|
|
15979
|
+
let finalParams = params;
|
|
15980
|
+
if (immediate && this.settings.terminateCommitPayloadField) {
|
|
15981
|
+
const field = this.settings.terminateCommitPayloadField;
|
|
15982
|
+
if (Array.isArray(finalParams)) {
|
|
15983
|
+
finalParams = [...finalParams, `${encodeURIComponent(field)}=true`];
|
|
15984
|
+
} else if (finalParams && typeof finalParams === "object") {
|
|
15985
|
+
finalParams = {
|
|
15986
|
+
...finalParams,
|
|
15987
|
+
[field]: true
|
|
15988
|
+
};
|
|
15989
|
+
}
|
|
15990
|
+
}
|
|
15991
|
+
if (this.settings.includeCommitSequence === true) {
|
|
15992
|
+
if (Array.isArray(finalParams)) {
|
|
15993
|
+
finalParams = [...finalParams, `commitSequence=${sequence}`];
|
|
15994
|
+
} else if (finalParams && typeof finalParams === "object") {
|
|
15995
|
+
finalParams = {
|
|
15996
|
+
...finalParams,
|
|
15997
|
+
commitSequence: sequence
|
|
15998
|
+
};
|
|
15999
|
+
}
|
|
16000
|
+
}
|
|
16001
|
+
const finalUrl = immediate && this.settings.terminateCommitParam ? appendQueryParam(url, this.settings.terminateCommitParam, "true") : url;
|
|
16002
|
+
const metadata = {
|
|
16003
|
+
isTerminateCommit: immediate,
|
|
16004
|
+
trigger: resolvedTrigger,
|
|
16005
|
+
sequence
|
|
16006
|
+
};
|
|
16007
|
+
const context = {
|
|
16008
|
+
url: finalUrl,
|
|
16009
|
+
trigger: resolvedTrigger,
|
|
16010
|
+
isTerminateCommit: immediate,
|
|
16011
|
+
sequence
|
|
16012
|
+
};
|
|
16013
|
+
if (this.settings.enableOfflineSupport && this._offlineStorageService && !this._offlineStorageService.isDeviceOnline() && this._courseId) {
|
|
16014
|
+
this.apiLog("processHttpRequest", "Device is offline, storing data locally", LogLevelEnum.INFO);
|
|
16015
|
+
if (finalParams && typeof finalParams === "object" && "cmi" in finalParams) {
|
|
16016
|
+
return this._offlineStorageService.storeOffline(this._courseId, finalParams, {
|
|
16017
|
+
isTerminateCommit: immediate,
|
|
16018
|
+
sequence
|
|
16019
|
+
});
|
|
16020
|
+
} else {
|
|
16021
|
+
this.apiLog("processHttpRequest", "Invalid commit data format for offline storage", LogLevelEnum.ERROR);
|
|
16022
|
+
return {
|
|
16023
|
+
result: global_constants.SCORM_FALSE,
|
|
16024
|
+
errorCode: this._error_codes.GENERAL ?? 101
|
|
16025
|
+
};
|
|
16026
|
+
}
|
|
16027
|
+
}
|
|
16028
|
+
const apiLog = (functionName, message, level, element) => this.apiLog(functionName, message, level, element);
|
|
16029
|
+
const processListeners = (functionName, CMIElement, value) => {
|
|
16030
|
+
if (functionName === "CommitSuccess" || functionName === "CommitError") {
|
|
16031
|
+
if (functionName === "CommitError" && typeof value === "number") {
|
|
16032
|
+
context.errorCode = value;
|
|
16033
|
+
}
|
|
16034
|
+
settle();
|
|
16035
|
+
this.processListeners(functionName, CMIElement, value, context);
|
|
16036
|
+
} else {
|
|
16037
|
+
this.processListeners(functionName, CMIElement, value);
|
|
16038
|
+
}
|
|
16039
|
+
};
|
|
16040
|
+
const result = this._httpService.processHttpRequest(finalUrl, finalParams, immediate, apiLog, processListeners, metadata, settle);
|
|
16041
|
+
completionDeferred = this._httpService.reportsRequestCompletion === true;
|
|
16042
|
+
return result;
|
|
16043
|
+
} finally {
|
|
16044
|
+
if (!completionDeferred) {
|
|
16045
|
+
settle();
|
|
15792
16046
|
}
|
|
15793
16047
|
}
|
|
15794
|
-
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));
|
|
15795
16048
|
}
|
|
15796
16049
|
/**
|
|
15797
16050
|
* Schedules a commit operation to occur after a specified delay.
|
|
@@ -16065,6 +16318,49 @@ ${stackTrace}`);
|
|
|
16065
16318
|
}
|
|
16066
16319
|
}
|
|
16067
16320
|
|
|
16321
|
+
function stripBrackets(delim) {
|
|
16322
|
+
return delim.replace(/[[\]]/g, "");
|
|
16323
|
+
}
|
|
16324
|
+
function escapeRegex(s) {
|
|
16325
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16326
|
+
}
|
|
16327
|
+
function splitDelimited(value, bracketed) {
|
|
16328
|
+
if (!bracketed) {
|
|
16329
|
+
return [value];
|
|
16330
|
+
}
|
|
16331
|
+
if (value.includes(bracketed)) {
|
|
16332
|
+
return value.split(bracketed);
|
|
16333
|
+
}
|
|
16334
|
+
const bare = stripBrackets(bracketed);
|
|
16335
|
+
if (!bare) {
|
|
16336
|
+
return [value];
|
|
16337
|
+
}
|
|
16338
|
+
const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`, "g");
|
|
16339
|
+
const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
|
|
16340
|
+
return value.split(splitRe).map(part => part.replace(unescapeRe, bare));
|
|
16341
|
+
}
|
|
16342
|
+
function splitFirstDelimited(value, bracketed) {
|
|
16343
|
+
if (!bracketed) {
|
|
16344
|
+
return [value];
|
|
16345
|
+
}
|
|
16346
|
+
if (value.includes(bracketed)) {
|
|
16347
|
+
const idx = value.indexOf(bracketed);
|
|
16348
|
+
return [value.slice(0, idx), value.slice(idx + bracketed.length)];
|
|
16349
|
+
}
|
|
16350
|
+
const bare = stripBrackets(bracketed);
|
|
16351
|
+
if (!bare) {
|
|
16352
|
+
return [value];
|
|
16353
|
+
}
|
|
16354
|
+
const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`);
|
|
16355
|
+
const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
|
|
16356
|
+
const parts = value.split(splitRe);
|
|
16357
|
+
const first = (parts[0] ?? "").replace(unescapeRe, bare);
|
|
16358
|
+
if (parts.length === 1) {
|
|
16359
|
+
return [first];
|
|
16360
|
+
}
|
|
16361
|
+
return [first, parts.slice(1).join(bare).replace(unescapeRe, bare)];
|
|
16362
|
+
}
|
|
16363
|
+
|
|
16068
16364
|
var __defProp$m = Object.defineProperty;
|
|
16069
16365
|
var __defNormalProp$m = (obj, key, value) => key in obj ? __defProp$m(obj, key, {
|
|
16070
16366
|
enumerable: true,
|
|
@@ -16260,8 +16556,7 @@ ${stackTrace}`);
|
|
|
16260
16556
|
const response_type = LearnerResponses[this.type];
|
|
16261
16557
|
if (response_type) {
|
|
16262
16558
|
if (response_type?.delimiter) {
|
|
16263
|
-
|
|
16264
|
-
nodes = learner_response.split(delimiter);
|
|
16559
|
+
nodes = splitDelimited(learner_response, response_type.delimiter);
|
|
16265
16560
|
} else {
|
|
16266
16561
|
nodes[0] = learner_response;
|
|
16267
16562
|
}
|
|
@@ -16269,10 +16564,10 @@ ${stackTrace}`);
|
|
|
16269
16564
|
const formatRegex = new RegExp(response_type.format);
|
|
16270
16565
|
for (let i = 0; i < nodes.length; i++) {
|
|
16271
16566
|
if (response_type?.delimiter2) {
|
|
16272
|
-
const
|
|
16273
|
-
const values =
|
|
16567
|
+
const node = nodes[i] ?? "";
|
|
16568
|
+
const values = this.type === "performance" ? splitFirstDelimited(node, response_type.delimiter2) : splitDelimited(node, response_type.delimiter2);
|
|
16274
16569
|
if (values?.length === 2) {
|
|
16275
|
-
if (this.type === "performance" &&
|
|
16570
|
+
if (this.type === "performance" && values[0] === "" && values[1] === "") {
|
|
16276
16571
|
throw new Scorm2004ValidationError(this._cmi_element + ".learner_response", scorm2004_errors.TYPE_MISMATCH);
|
|
16277
16572
|
}
|
|
16278
16573
|
if (!values[0]?.match(formatRegex)) {
|
|
@@ -16453,37 +16748,19 @@ ${stackTrace}`);
|
|
|
16453
16748
|
return result;
|
|
16454
16749
|
}
|
|
16455
16750
|
}
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
}
|
|
16462
|
-
function splitUnescaped(text, delim) {
|
|
16463
|
-
const reDelim = escapeRegex(delim);
|
|
16464
|
-
const splitRe = new RegExp(`(?<!\\\\)${reDelim}`, "g");
|
|
16465
|
-
const unescapeRe = new RegExp(`\\\\${reDelim}`, "g");
|
|
16466
|
-
return text.split(splitRe).map(part => part.replace(unescapeRe, delim));
|
|
16467
|
-
}
|
|
16468
|
-
function splitFirstUnescaped(text, delim) {
|
|
16469
|
-
const reDelim = escapeRegex(delim);
|
|
16470
|
-
const splitRe = new RegExp(`(?<!\\\\)${reDelim}`);
|
|
16471
|
-
const unescapeRe = new RegExp(`\\\\${reDelim}`, "g");
|
|
16472
|
-
const parts = text.split(splitRe);
|
|
16473
|
-
const firstPart = parts[0] ?? "";
|
|
16474
|
-
if (parts.length === 1) {
|
|
16475
|
-
return [firstPart.replace(unescapeRe, delim)];
|
|
16751
|
+
const RESPONSE_PREFIX_RE = /^\{(?:lang|case_matters|order_matters)=[^}]+\}/;
|
|
16752
|
+
function stripResponsePrefixes(node) {
|
|
16753
|
+
let result = node;
|
|
16754
|
+
while (RESPONSE_PREFIX_RE.test(result)) {
|
|
16755
|
+
result = result.replace(RESPONSE_PREFIX_RE, "");
|
|
16476
16756
|
}
|
|
16477
|
-
|
|
16478
|
-
const part2 = parts.slice(1).join(delim).replace(unescapeRe, delim);
|
|
16479
|
-
return [part1, part2];
|
|
16757
|
+
return result;
|
|
16480
16758
|
}
|
|
16481
16759
|
function validatePattern(type, pattern, responseDef) {
|
|
16482
16760
|
if (pattern.trim() !== pattern) {
|
|
16483
16761
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16484
16762
|
}
|
|
16485
|
-
const
|
|
16486
|
-
const rawNodes = subDelim1 ? splitUnescaped(pattern, subDelim1) : [pattern];
|
|
16763
|
+
const rawNodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
|
|
16487
16764
|
for (const raw of rawNodes) {
|
|
16488
16765
|
if (raw.trim() !== raw) {
|
|
16489
16766
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
@@ -16492,17 +16769,11 @@ ${stackTrace}`);
|
|
|
16492
16769
|
if (type === "fill-in" && pattern === "") {
|
|
16493
16770
|
return;
|
|
16494
16771
|
}
|
|
16495
|
-
const
|
|
16496
|
-
let nodes;
|
|
16497
|
-
if (delim1) {
|
|
16498
|
-
nodes = splitUnescaped(pattern, delim1);
|
|
16499
|
-
} else {
|
|
16500
|
-
nodes = [pattern];
|
|
16501
|
-
}
|
|
16772
|
+
const nodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
|
|
16502
16773
|
if (!responseDef.delimiter && pattern.includes(",")) {
|
|
16503
16774
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16504
16775
|
}
|
|
16505
|
-
if (responseDef.unique || responseDef.duplicate === false) {
|
|
16776
|
+
if (type !== "numeric" && (responseDef.unique || responseDef.duplicate === false)) {
|
|
16506
16777
|
const seen = new Set(nodes);
|
|
16507
16778
|
if (seen.size !== nodes.length) {
|
|
16508
16779
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
@@ -16522,8 +16793,7 @@ ${stackTrace}`);
|
|
|
16522
16793
|
if (!delimBracketed) {
|
|
16523
16794
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16524
16795
|
}
|
|
16525
|
-
const
|
|
16526
|
-
const parts = value.split(new RegExp(`(?<!\\\\)${escapeRegex(delim)}`, "g")).map(n => n.replace(new RegExp(`\\\\${escapeRegex(delim)}`, "g"), delim));
|
|
16796
|
+
const parts = splitDelimited(value, delimBracketed);
|
|
16527
16797
|
if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
|
|
16528
16798
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16529
16799
|
}
|
|
@@ -16535,12 +16805,14 @@ ${stackTrace}`);
|
|
|
16535
16805
|
switch (type) {
|
|
16536
16806
|
case "numeric":
|
|
16537
16807
|
{
|
|
16538
|
-
|
|
16539
|
-
|
|
16540
|
-
|
|
16541
|
-
|
|
16808
|
+
if (node === "") {
|
|
16809
|
+
const bracketedRange = nodes.length >= 2 && !!responseDef.delimiter && pattern.includes(responseDef.delimiter);
|
|
16810
|
+
if (!bracketedRange) {
|
|
16811
|
+
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16812
|
+
}
|
|
16813
|
+
break;
|
|
16542
16814
|
}
|
|
16543
|
-
|
|
16815
|
+
checkSingle(node);
|
|
16544
16816
|
break;
|
|
16545
16817
|
}
|
|
16546
16818
|
case "performance":
|
|
@@ -16549,13 +16821,13 @@ ${stackTrace}`);
|
|
|
16549
16821
|
if (!delimBracketed) {
|
|
16550
16822
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16551
16823
|
}
|
|
16552
|
-
const
|
|
16553
|
-
const parts =
|
|
16824
|
+
const record = stripResponsePrefixes(node);
|
|
16825
|
+
const parts = splitFirstDelimited(record, delimBracketed);
|
|
16554
16826
|
if (parts.length !== 2) {
|
|
16555
16827
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16556
16828
|
}
|
|
16557
16829
|
const [part1, part2] = parts;
|
|
16558
|
-
if (part1 === ""
|
|
16830
|
+
if (part1 === "" && part2 === "") {
|
|
16559
16831
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
16560
16832
|
}
|
|
16561
16833
|
if (part1 === void 0 || !fmt1.test(part1)) {
|
|
@@ -16665,6 +16937,11 @@ ${stackTrace}`);
|
|
|
16665
16937
|
__publicField$l(this, "__invalid_range_code");
|
|
16666
16938
|
__publicField$l(this, "__decimal_regex");
|
|
16667
16939
|
__publicField$l(this, "__error_class");
|
|
16940
|
+
/**
|
|
16941
|
+
* When true, an empty string is a valid value (clears the element). SCORM 1.2
|
|
16942
|
+
* score elements may be blank per the ADL 1.2 CTS; SCORM 2004 leaves this off.
|
|
16943
|
+
*/
|
|
16944
|
+
__publicField$l(this, "__allow_empty_string");
|
|
16668
16945
|
__publicField$l(this, "_raw", "");
|
|
16669
16946
|
__publicField$l(this, "_min", "");
|
|
16670
16947
|
__publicField$l(this, "_max");
|
|
@@ -16676,6 +16953,7 @@ ${stackTrace}`);
|
|
|
16676
16953
|
this.__invalid_range_code = params.invalidRangeCode || scorm12_errors.VALUE_OUT_OF_RANGE;
|
|
16677
16954
|
this.__decimal_regex = params.decimalRegex || scorm12_regex.CMIDecimal;
|
|
16678
16955
|
this.__error_class = params.errorClass;
|
|
16956
|
+
this.__allow_empty_string = params.allowEmptyString ?? false;
|
|
16679
16957
|
}
|
|
16680
16958
|
/**
|
|
16681
16959
|
* Called when the API has been reset
|
|
@@ -16715,7 +16993,7 @@ ${stackTrace}`);
|
|
|
16715
16993
|
* @param {string} raw
|
|
16716
16994
|
*/
|
|
16717
16995
|
set raw(raw) {
|
|
16718
|
-
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)) {
|
|
16996
|
+
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)) {
|
|
16719
16997
|
this._raw = raw;
|
|
16720
16998
|
}
|
|
16721
16999
|
}
|
|
@@ -16731,7 +17009,7 @@ ${stackTrace}`);
|
|
|
16731
17009
|
* @param {string} min
|
|
16732
17010
|
*/
|
|
16733
17011
|
set min(min) {
|
|
16734
|
-
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)) {
|
|
17012
|
+
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)) {
|
|
16735
17013
|
this._min = min;
|
|
16736
17014
|
}
|
|
16737
17015
|
}
|
|
@@ -16747,7 +17025,7 @@ ${stackTrace}`);
|
|
|
16747
17025
|
* @param {string} max
|
|
16748
17026
|
*/
|
|
16749
17027
|
set max(max) {
|
|
16750
|
-
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)) {
|
|
17028
|
+
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)) {
|
|
16751
17029
|
this._max = max;
|
|
16752
17030
|
}
|
|
16753
17031
|
}
|
|
@@ -19510,7 +19788,7 @@ ${stackTrace}`);
|
|
|
19510
19788
|
checkValidResponseType(CMIElement, response_type, value, interaction_type) {
|
|
19511
19789
|
let nodes = [];
|
|
19512
19790
|
if (response_type?.delimiter) {
|
|
19513
|
-
nodes = String(value)
|
|
19791
|
+
nodes = splitDelimited(String(value), response_type.delimiter);
|
|
19514
19792
|
} else {
|
|
19515
19793
|
nodes[0] = value;
|
|
19516
19794
|
}
|
|
@@ -19608,26 +19886,33 @@ ${stackTrace}`);
|
|
|
19608
19886
|
nodes[i] = this.removeCorrectResponsePrefixes(CMIElement, nodes[i]);
|
|
19609
19887
|
}
|
|
19610
19888
|
if (response?.delimiter2) {
|
|
19611
|
-
const values = nodes[i].
|
|
19889
|
+
const values = interaction_type === "performance" ? splitFirstDelimited(nodes[i], response.delimiter2) : splitDelimited(nodes[i], response.delimiter2);
|
|
19612
19890
|
if (values.length === 2) {
|
|
19613
|
-
|
|
19614
|
-
if (!matches) {
|
|
19891
|
+
if (interaction_type === "performance" && values[0] === "" && values[1] === "") {
|
|
19615
19892
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
19616
19893
|
} else {
|
|
19617
|
-
|
|
19894
|
+
const matches = values[0]?.match(formatRegex);
|
|
19895
|
+
if (!matches) {
|
|
19618
19896
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
19897
|
+
} else {
|
|
19898
|
+
if (!response.format2 || !values[1]?.match(new RegExp(response.format2))) {
|
|
19899
|
+
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
19900
|
+
}
|
|
19619
19901
|
}
|
|
19620
19902
|
}
|
|
19621
19903
|
} else {
|
|
19622
19904
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
19623
19905
|
}
|
|
19624
19906
|
} else {
|
|
19907
|
+
if (interaction_type === "numeric" && nodes.length > 1 && nodes[i] === "" && !!response.delimiter && String(value).includes(response.delimiter)) {
|
|
19908
|
+
continue;
|
|
19909
|
+
}
|
|
19625
19910
|
const matches = nodes[i].match(formatRegex);
|
|
19626
19911
|
if (!matches && value !== "" || !matches && interaction_type === "true-false") {
|
|
19627
19912
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
19628
19913
|
} else {
|
|
19629
19914
|
if (interaction_type === "numeric" && nodes.length > 1) {
|
|
19630
|
-
if (Number(nodes[0]) > Number(nodes[1])) {
|
|
19915
|
+
if (nodes[0] !== "" && nodes[1] !== "" && Number(nodes[0]) > Number(nodes[1])) {
|
|
19631
19916
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
19632
19917
|
}
|
|
19633
19918
|
} else {
|
|
@@ -21306,6 +21591,10 @@ ${stackTrace}`);
|
|
|
21306
21591
|
value
|
|
21307
21592
|
}) : obj[key] = value;
|
|
21308
21593
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21594
|
+
const NO_DEFAULT_2004_ELEMENTS = /* @__PURE__ */new Set(["cmi.suspend_data", "cmi.location", "cmi.scaled_passing_score", "cmi.max_time_allowed", "cmi.completion_threshold", "cmi.progress_measure", "cmi.score.scaled", "cmi.score.raw", "cmi.score.min", "cmi.score.max", "cmi.objectives.N.score.scaled", "cmi.objectives.N.score.raw", "cmi.objectives.N.score.min", "cmi.objectives.N.score.max", "cmi.objectives.N.progress_measure", "cmi.objectives.N.description", "cmi.interactions.N.weighting", "cmi.interactions.N.type", "cmi.interactions.N.timestamp", "cmi.interactions.N.result", "cmi.interactions.N.latency", "cmi.interactions.N.learner_response", "cmi.interactions.N.description", "cmi.comments_from_learner.N.timestamp"]);
|
|
21595
|
+
function normalizeCMIIndices(CMIElement) {
|
|
21596
|
+
return CMIElement.replace(/\.\d+(?=\.|$)/g, ".N");
|
|
21597
|
+
}
|
|
21309
21598
|
class Scorm2004API extends BaseAPI {
|
|
21310
21599
|
/**
|
|
21311
21600
|
* Constructor for SCORM 2004 API
|
|
@@ -21805,6 +22094,35 @@ ${stackTrace}`);
|
|
|
21805
22094
|
getCMIValue(CMIElement) {
|
|
21806
22095
|
return this._commonGetCMIValue("GetValue", true, CMIElement);
|
|
21807
22096
|
}
|
|
22097
|
+
/**
|
|
22098
|
+
* Raises 403 (VALUE_NOT_INITIALIZED) when an implemented, no-default element is
|
|
22099
|
+
* read before it has ever been set, per IEEE 1484.11.2 / SCORM 2004 RTE.
|
|
22100
|
+
*
|
|
22101
|
+
* Invoked by BaseAPI.getValue after an otherwise-successful resolution, so it
|
|
22102
|
+
* only sees values the data model already returned cleanly. The decision is
|
|
22103
|
+
* deliberately gated to this public boundary (never the getters) to keep
|
|
22104
|
+
* serialization/commit/rollup — which read the getters directly — unaffected.
|
|
22105
|
+
*
|
|
22106
|
+
* A 403 is raised only when all hold:
|
|
22107
|
+
* - the resolved value is "" — any non-empty value is by definition set,
|
|
22108
|
+
* including values written by internal sequencing/activity-tree paths that
|
|
22109
|
+
* bypass _commonSetCMIValue (those only ever write non-empty values);
|
|
22110
|
+
* - the element was never set — _setCMIElements records every successful
|
|
22111
|
+
* SetValue, loadFromJSON, and global-objective restore (all route through
|
|
22112
|
+
* _commonSetCMIValue), so an explicit SetValue(x, "") still reads back as
|
|
22113
|
+
* "" with code 0; and
|
|
22114
|
+
* - the element has no spec-defined default ({@link NO_DEFAULT_2004_ELEMENTS}).
|
|
22115
|
+
*
|
|
22116
|
+
* @param {string} CMIElement
|
|
22117
|
+
* @param {*} returnValue - the value getCMIValue resolved
|
|
22118
|
+
* @protected
|
|
22119
|
+
*/
|
|
22120
|
+
checkUninitializedGet(CMIElement, returnValue) {
|
|
22121
|
+
if (returnValue !== "") return;
|
|
22122
|
+
if (this._setCMIElements.has(CMIElement)) return;
|
|
22123
|
+
if (!NO_DEFAULT_2004_ELEMENTS.has(normalizeCMIIndices(CMIElement))) return;
|
|
22124
|
+
this.throwSCORMError(CMIElement, this._error_codes.VALUE_NOT_INITIALIZED ?? 403, `The data model element passed to GetValue (${CMIElement}) has not been initialized.`);
|
|
22125
|
+
}
|
|
21808
22126
|
/**
|
|
21809
22127
|
* Returns the message that corresponds to errorNumber.
|
|
21810
22128
|
* @param {(string|number)} errorNumber
|
|
@@ -21853,9 +22171,10 @@ ${stackTrace}`);
|
|
|
21853
22171
|
/**
|
|
21854
22172
|
* Attempts to store the data to the LMS
|
|
21855
22173
|
* @param {boolean} terminateCommit
|
|
22174
|
+
* @param {CommitTrigger} [trigger] - What initiated the commit
|
|
21856
22175
|
* @return {ResultObject}
|
|
21857
22176
|
*/
|
|
21858
|
-
storeData(terminateCommit) {
|
|
22177
|
+
storeData(terminateCommit, trigger) {
|
|
21859
22178
|
if (terminateCommit) {
|
|
21860
22179
|
if (this.cmi.mode === "normal") {
|
|
21861
22180
|
if (this.cmi.credit === "credit") {
|
|
@@ -21896,7 +22215,7 @@ ${stackTrace}`);
|
|
|
21896
22215
|
}
|
|
21897
22216
|
this._globalObjectiveManager.syncCmiToSequencingActivity(completionStatusEnum, successStatusEnum, scoreObject);
|
|
21898
22217
|
if (typeof this.settings.lmsCommitUrl === "string") {
|
|
21899
|
-
const result = this.processHttpRequest(this.settings.lmsCommitUrl, commitObject, terminateCommit);
|
|
22218
|
+
const result = this.processHttpRequest(this.settings.lmsCommitUrl, commitObject, terminateCommit, trigger);
|
|
21900
22219
|
if (navRequest && result.navRequest !== void 0 && result.navRequest !== "" && typeof result.navRequest === "string") {
|
|
21901
22220
|
const parsed = parseNavigationRequest(result.navRequest);
|
|
21902
22221
|
if (!parsed.valid) {
|