scorm-again 3.0.4 → 3.0.5
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 +19 -4
- package/dist/esm/scorm-again.js +115 -67
- 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 +17 -4
- 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 +115 -67
- 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 +102 -64
- 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 +11 -4
- 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 +102 -64
- 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/cmi/scorm2004/interaction_delimiters.d.ts +4 -0
- package/package.json +42 -21
package/dist/scorm-again.js
CHANGED
|
@@ -1159,6 +1159,11 @@
|
|
|
1159
1159
|
progress_range: "0#1"
|
|
1160
1160
|
};
|
|
1161
1161
|
|
|
1162
|
+
const PERFORMANCE_STEP_NAME = "^$|" + scorm2004_regex.CMIShortIdentifier;
|
|
1163
|
+
const PERFORMANCE_CHARACTERSTRING = "(?![\\s\\S]*(?:\\[,\\]|\\[\\.\\]|\\[:\\]))[\\s\\S]{1,250}";
|
|
1164
|
+
const PERFORMANCE_NUMERIC_RANGE = "(?:-?\\d+(?:\\.\\d+)?)?\\[:\\](?:-?\\d+(?:\\.\\d+)?)?";
|
|
1165
|
+
const CR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_NUMERIC_RANGE + "|" + PERFORMANCE_CHARACTERSTRING + ")$";
|
|
1166
|
+
const LR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_CHARACTERSTRING + ")$";
|
|
1162
1167
|
const LearnerResponses = {
|
|
1163
1168
|
"true-false": {
|
|
1164
1169
|
format: "^true$|^false$",
|
|
@@ -1193,8 +1198,8 @@
|
|
|
1193
1198
|
unique: false
|
|
1194
1199
|
},
|
|
1195
1200
|
performance: {
|
|
1196
|
-
format:
|
|
1197
|
-
format2:
|
|
1201
|
+
format: PERFORMANCE_STEP_NAME,
|
|
1202
|
+
format2: LR_PERFORMANCE_STEP_ANSWER,
|
|
1198
1203
|
max: 250,
|
|
1199
1204
|
delimiter: "[,]",
|
|
1200
1205
|
delimiter2: "[.]",
|
|
@@ -1270,10 +1275,10 @@
|
|
|
1270
1275
|
delimiter2: "[.]",
|
|
1271
1276
|
unique: false,
|
|
1272
1277
|
duplicate: false,
|
|
1273
|
-
// step_name
|
|
1274
|
-
format:
|
|
1275
|
-
// step_answer
|
|
1276
|
-
format2:
|
|
1278
|
+
// step_name: optional short_identifier_type
|
|
1279
|
+
format: PERFORMANCE_STEP_NAME,
|
|
1280
|
+
// step_answer: optional characterstring (spaces allowed) or numeric range
|
|
1281
|
+
format2: CR_PERFORMANCE_STEP_ANSWER
|
|
1277
1282
|
},
|
|
1278
1283
|
sequencing: {
|
|
1279
1284
|
max: 36,
|
|
@@ -5298,7 +5303,13 @@
|
|
|
5298
5303
|
if (!scorm2004) {
|
|
5299
5304
|
if (isFinalAttribute) {
|
|
5300
5305
|
if (typeof attribute === "undefined" || !this.context.checkObjectHasProperty(refObject, attribute)) {
|
|
5301
|
-
|
|
5306
|
+
if (attribute === "_children") {
|
|
5307
|
+
this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "CHILDREN_ERROR"));
|
|
5308
|
+
} else if (attribute === "_count") {
|
|
5309
|
+
this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "COUNT_ERROR"));
|
|
5310
|
+
} else {
|
|
5311
|
+
this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);
|
|
5312
|
+
}
|
|
5302
5313
|
return {
|
|
5303
5314
|
error: true
|
|
5304
5315
|
};
|
|
@@ -15552,19 +15563,20 @@ ${stackTrace}`);
|
|
|
15552
15563
|
if (errorCode === 143) returnValue = global_constants.SCORM_FALSE;
|
|
15553
15564
|
} else {
|
|
15554
15565
|
const result = this.storeData(false);
|
|
15555
|
-
|
|
15566
|
+
const errorCode = result.errorCode ?? 0;
|
|
15567
|
+
if (errorCode > 0) {
|
|
15556
15568
|
if (result.errorMessage) {
|
|
15557
15569
|
this.apiLog("commit", `Commit failed with error: ${result.errorMessage}`, LogLevelEnum.ERROR);
|
|
15558
15570
|
}
|
|
15559
15571
|
if (result.errorDetails) {
|
|
15560
15572
|
this.apiLog("commit", `Error details: ${JSON.stringify(result.errorDetails)}`, LogLevelEnum.DEBUG);
|
|
15561
15573
|
}
|
|
15562
|
-
this.throwSCORMError("api",
|
|
15574
|
+
this.throwSCORMError("api", errorCode);
|
|
15563
15575
|
}
|
|
15564
15576
|
const resultValue = result?.result ?? global_constants.SCORM_FALSE;
|
|
15565
15577
|
returnValue = typeof resultValue === "boolean" ? String(resultValue) : resultValue;
|
|
15566
15578
|
this.apiLog(callbackName, " Result: " + returnValue, LogLevelEnum.DEBUG, "HttpRequest");
|
|
15567
|
-
if (checkTerminated) this.lastErrorCode = "0";
|
|
15579
|
+
if (checkTerminated && errorCode === 0) this.lastErrorCode = "0";
|
|
15568
15580
|
this.processListeners(callbackName);
|
|
15569
15581
|
if (this.settings.enableOfflineSupport && this._offlineStorageService && this._offlineStorageService.isDeviceOnline() && this._courseId) {
|
|
15570
15582
|
this._offlineStorageService.hasPendingOfflineData(this._courseId).then(hasPendingData => {
|
|
@@ -18349,6 +18361,49 @@ ${stackTrace}`);
|
|
|
18349
18361
|
}
|
|
18350
18362
|
}
|
|
18351
18363
|
|
|
18364
|
+
function stripBrackets(delim) {
|
|
18365
|
+
return delim.replace(/[[\]]/g, "");
|
|
18366
|
+
}
|
|
18367
|
+
function escapeRegex(s) {
|
|
18368
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18369
|
+
}
|
|
18370
|
+
function splitDelimited(value, bracketed) {
|
|
18371
|
+
if (!bracketed) {
|
|
18372
|
+
return [value];
|
|
18373
|
+
}
|
|
18374
|
+
if (value.includes(bracketed)) {
|
|
18375
|
+
return value.split(bracketed);
|
|
18376
|
+
}
|
|
18377
|
+
const bare = stripBrackets(bracketed);
|
|
18378
|
+
if (!bare) {
|
|
18379
|
+
return [value];
|
|
18380
|
+
}
|
|
18381
|
+
const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`, "g");
|
|
18382
|
+
const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
|
|
18383
|
+
return value.split(splitRe).map(part => part.replace(unescapeRe, bare));
|
|
18384
|
+
}
|
|
18385
|
+
function splitFirstDelimited(value, bracketed) {
|
|
18386
|
+
if (!bracketed) {
|
|
18387
|
+
return [value];
|
|
18388
|
+
}
|
|
18389
|
+
if (value.includes(bracketed)) {
|
|
18390
|
+
const idx = value.indexOf(bracketed);
|
|
18391
|
+
return [value.slice(0, idx), value.slice(idx + bracketed.length)];
|
|
18392
|
+
}
|
|
18393
|
+
const bare = stripBrackets(bracketed);
|
|
18394
|
+
if (!bare) {
|
|
18395
|
+
return [value];
|
|
18396
|
+
}
|
|
18397
|
+
const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`);
|
|
18398
|
+
const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
|
|
18399
|
+
const parts = value.split(splitRe);
|
|
18400
|
+
const first = (parts[0] ?? "").replace(unescapeRe, bare);
|
|
18401
|
+
if (parts.length === 1) {
|
|
18402
|
+
return [first];
|
|
18403
|
+
}
|
|
18404
|
+
return [first, parts.slice(1).join(bare).replace(unescapeRe, bare)];
|
|
18405
|
+
}
|
|
18406
|
+
|
|
18352
18407
|
var __defProp$l = Object.defineProperty;
|
|
18353
18408
|
var __defNormalProp$l = (obj, key, value) => key in obj ? __defProp$l(obj, key, {
|
|
18354
18409
|
enumerable: true,
|
|
@@ -18544,8 +18599,7 @@ ${stackTrace}`);
|
|
|
18544
18599
|
const response_type = LearnerResponses[this.type];
|
|
18545
18600
|
if (response_type) {
|
|
18546
18601
|
if (response_type?.delimiter) {
|
|
18547
|
-
|
|
18548
|
-
nodes = learner_response.split(delimiter);
|
|
18602
|
+
nodes = splitDelimited(learner_response, response_type.delimiter);
|
|
18549
18603
|
} else {
|
|
18550
18604
|
nodes[0] = learner_response;
|
|
18551
18605
|
}
|
|
@@ -18553,10 +18607,10 @@ ${stackTrace}`);
|
|
|
18553
18607
|
const formatRegex = new RegExp(response_type.format);
|
|
18554
18608
|
for (let i = 0; i < nodes.length; i++) {
|
|
18555
18609
|
if (response_type?.delimiter2) {
|
|
18556
|
-
const
|
|
18557
|
-
const values =
|
|
18610
|
+
const node = nodes[i] ?? "";
|
|
18611
|
+
const values = this.type === "performance" ? splitFirstDelimited(node, response_type.delimiter2) : splitDelimited(node, response_type.delimiter2);
|
|
18558
18612
|
if (values?.length === 2) {
|
|
18559
|
-
if (this.type === "performance" &&
|
|
18613
|
+
if (this.type === "performance" && values[0] === "" && values[1] === "") {
|
|
18560
18614
|
throw new Scorm2004ValidationError(this._cmi_element + ".learner_response", scorm2004_errors.TYPE_MISMATCH);
|
|
18561
18615
|
}
|
|
18562
18616
|
if (!values[0]?.match(formatRegex)) {
|
|
@@ -18737,37 +18791,19 @@ ${stackTrace}`);
|
|
|
18737
18791
|
return result;
|
|
18738
18792
|
}
|
|
18739
18793
|
}
|
|
18740
|
-
|
|
18741
|
-
|
|
18742
|
-
|
|
18743
|
-
|
|
18744
|
-
|
|
18745
|
-
}
|
|
18746
|
-
function splitUnescaped(text, delim) {
|
|
18747
|
-
const reDelim = escapeRegex(delim);
|
|
18748
|
-
const splitRe = new RegExp(`(?<!\\\\)${reDelim}`, "g");
|
|
18749
|
-
const unescapeRe = new RegExp(`\\\\${reDelim}`, "g");
|
|
18750
|
-
return text.split(splitRe).map(part => part.replace(unescapeRe, delim));
|
|
18751
|
-
}
|
|
18752
|
-
function splitFirstUnescaped(text, delim) {
|
|
18753
|
-
const reDelim = escapeRegex(delim);
|
|
18754
|
-
const splitRe = new RegExp(`(?<!\\\\)${reDelim}`);
|
|
18755
|
-
const unescapeRe = new RegExp(`\\\\${reDelim}`, "g");
|
|
18756
|
-
const parts = text.split(splitRe);
|
|
18757
|
-
const firstPart = parts[0] ?? "";
|
|
18758
|
-
if (parts.length === 1) {
|
|
18759
|
-
return [firstPart.replace(unescapeRe, delim)];
|
|
18794
|
+
const RESPONSE_PREFIX_RE = /^\{(?:lang|case_matters|order_matters)=[^}]+\}/;
|
|
18795
|
+
function stripResponsePrefixes(node) {
|
|
18796
|
+
let result = node;
|
|
18797
|
+
while (RESPONSE_PREFIX_RE.test(result)) {
|
|
18798
|
+
result = result.replace(RESPONSE_PREFIX_RE, "");
|
|
18760
18799
|
}
|
|
18761
|
-
|
|
18762
|
-
const part2 = parts.slice(1).join(delim).replace(unescapeRe, delim);
|
|
18763
|
-
return [part1, part2];
|
|
18800
|
+
return result;
|
|
18764
18801
|
}
|
|
18765
18802
|
function validatePattern(type, pattern, responseDef) {
|
|
18766
18803
|
if (pattern.trim() !== pattern) {
|
|
18767
18804
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18768
18805
|
}
|
|
18769
|
-
const
|
|
18770
|
-
const rawNodes = subDelim1 ? splitUnescaped(pattern, subDelim1) : [pattern];
|
|
18806
|
+
const rawNodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
|
|
18771
18807
|
for (const raw of rawNodes) {
|
|
18772
18808
|
if (raw.trim() !== raw) {
|
|
18773
18809
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
@@ -18776,17 +18812,11 @@ ${stackTrace}`);
|
|
|
18776
18812
|
if (type === "fill-in" && pattern === "") {
|
|
18777
18813
|
return;
|
|
18778
18814
|
}
|
|
18779
|
-
const
|
|
18780
|
-
let nodes;
|
|
18781
|
-
if (delim1) {
|
|
18782
|
-
nodes = splitUnescaped(pattern, delim1);
|
|
18783
|
-
} else {
|
|
18784
|
-
nodes = [pattern];
|
|
18785
|
-
}
|
|
18815
|
+
const nodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
|
|
18786
18816
|
if (!responseDef.delimiter && pattern.includes(",")) {
|
|
18787
18817
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18788
18818
|
}
|
|
18789
|
-
if (responseDef.unique || responseDef.duplicate === false) {
|
|
18819
|
+
if (type !== "numeric" && (responseDef.unique || responseDef.duplicate === false)) {
|
|
18790
18820
|
const seen = new Set(nodes);
|
|
18791
18821
|
if (seen.size !== nodes.length) {
|
|
18792
18822
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
@@ -18806,8 +18836,7 @@ ${stackTrace}`);
|
|
|
18806
18836
|
if (!delimBracketed) {
|
|
18807
18837
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18808
18838
|
}
|
|
18809
|
-
const
|
|
18810
|
-
const parts = value.split(new RegExp(`(?<!\\\\)${escapeRegex(delim)}`, "g")).map(n => n.replace(new RegExp(`\\\\${escapeRegex(delim)}`, "g"), delim));
|
|
18839
|
+
const parts = splitDelimited(value, delimBracketed);
|
|
18811
18840
|
if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
|
|
18812
18841
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18813
18842
|
}
|
|
@@ -18819,12 +18848,14 @@ ${stackTrace}`);
|
|
|
18819
18848
|
switch (type) {
|
|
18820
18849
|
case "numeric":
|
|
18821
18850
|
{
|
|
18822
|
-
|
|
18823
|
-
|
|
18824
|
-
|
|
18825
|
-
|
|
18851
|
+
if (node === "") {
|
|
18852
|
+
const bracketedRange = nodes.length >= 2 && !!responseDef.delimiter && pattern.includes(responseDef.delimiter);
|
|
18853
|
+
if (!bracketedRange) {
|
|
18854
|
+
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18855
|
+
}
|
|
18856
|
+
break;
|
|
18826
18857
|
}
|
|
18827
|
-
|
|
18858
|
+
checkSingle(node);
|
|
18828
18859
|
break;
|
|
18829
18860
|
}
|
|
18830
18861
|
case "performance":
|
|
@@ -18833,13 +18864,13 @@ ${stackTrace}`);
|
|
|
18833
18864
|
if (!delimBracketed) {
|
|
18834
18865
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18835
18866
|
}
|
|
18836
|
-
const
|
|
18837
|
-
const parts =
|
|
18867
|
+
const record = stripResponsePrefixes(node);
|
|
18868
|
+
const parts = splitFirstDelimited(record, delimBracketed);
|
|
18838
18869
|
if (parts.length !== 2) {
|
|
18839
18870
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18840
18871
|
}
|
|
18841
18872
|
const [part1, part2] = parts;
|
|
18842
|
-
if (part1 === ""
|
|
18873
|
+
if (part1 === "" && part2 === "") {
|
|
18843
18874
|
throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
|
|
18844
18875
|
}
|
|
18845
18876
|
if (part1 === void 0 || !fmt1.test(part1)) {
|
|
@@ -21625,7 +21656,7 @@ ${stackTrace}`);
|
|
|
21625
21656
|
checkValidResponseType(CMIElement, response_type, value, interaction_type) {
|
|
21626
21657
|
let nodes = [];
|
|
21627
21658
|
if (response_type?.delimiter) {
|
|
21628
|
-
nodes = String(value)
|
|
21659
|
+
nodes = splitDelimited(String(value), response_type.delimiter);
|
|
21629
21660
|
} else {
|
|
21630
21661
|
nodes[0] = value;
|
|
21631
21662
|
}
|
|
@@ -21723,26 +21754,33 @@ ${stackTrace}`);
|
|
|
21723
21754
|
nodes[i] = this.removeCorrectResponsePrefixes(CMIElement, nodes[i]);
|
|
21724
21755
|
}
|
|
21725
21756
|
if (response?.delimiter2) {
|
|
21726
|
-
const values = nodes[i].
|
|
21757
|
+
const values = interaction_type === "performance" ? splitFirstDelimited(nodes[i], response.delimiter2) : splitDelimited(nodes[i], response.delimiter2);
|
|
21727
21758
|
if (values.length === 2) {
|
|
21728
|
-
|
|
21729
|
-
if (!matches) {
|
|
21759
|
+
if (interaction_type === "performance" && values[0] === "" && values[1] === "") {
|
|
21730
21760
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
21731
21761
|
} else {
|
|
21732
|
-
|
|
21762
|
+
const matches = values[0]?.match(formatRegex);
|
|
21763
|
+
if (!matches) {
|
|
21733
21764
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
21765
|
+
} else {
|
|
21766
|
+
if (!response.format2 || !values[1]?.match(new RegExp(response.format2))) {
|
|
21767
|
+
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
21768
|
+
}
|
|
21734
21769
|
}
|
|
21735
21770
|
}
|
|
21736
21771
|
} else {
|
|
21737
21772
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
21738
21773
|
}
|
|
21739
21774
|
} else {
|
|
21775
|
+
if (interaction_type === "numeric" && nodes.length > 1 && nodes[i] === "" && !!response.delimiter && String(value).includes(response.delimiter)) {
|
|
21776
|
+
continue;
|
|
21777
|
+
}
|
|
21740
21778
|
const matches = nodes[i].match(formatRegex);
|
|
21741
21779
|
if (!matches && value !== "" || !matches && interaction_type === "true-false") {
|
|
21742
21780
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
21743
21781
|
} else {
|
|
21744
21782
|
if (interaction_type === "numeric" && nodes.length > 1) {
|
|
21745
|
-
if (Number(nodes[0]) > Number(nodes[1])) {
|
|
21783
|
+
if (nodes[0] !== "" && nodes[1] !== "" && Number(nodes[0]) > Number(nodes[1])) {
|
|
21746
21784
|
this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
|
|
21747
21785
|
}
|
|
21748
21786
|
} else {
|