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/README.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# scorm-again
|
|
2
|
+
|
|
3
|
+
A modern, fully-tested JavaScript runtime for SCORM 1.2 and SCORM 2004.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
<details>
|
|
7
|
+
<summary><b>Audits & consulting →</b></summary>
|
|
8
|
+
|
|
9
|
+
I do one-week, fixed-price code audits for B2B SaaS companies on AWS:
|
|
10
|
+
|
|
11
|
+
- Multi-Tenant SaaS on AWS: architecture, cost, and SOC 2 alignment
|
|
12
|
+
- AI-on-AWS for Regulated SaaS: Bedrock, RAG, and guardrails under SOC 2, HIPAA, or 21 CFR Part 11
|
|
13
|
+
|
|
14
|
+
Fixed price, written report, walkthrough call. Details at [audits.putney.io](https://audits.putney.io).
|
|
15
|
+
|
|
16
|
+
</details>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
1
20
|
[](https://github.com/jcputney/scorm-again/actions)
|
|
2
21
|
[](https://codecov.io/gh/jcputney/scorm-again)
|
|
3
22
|

|
|
@@ -7,10 +26,6 @@
|
|
|
7
26
|

|
|
8
27
|
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NF5MPZJAV26LE)
|
|
9
28
|
|
|
10
|
-
# scorm-again
|
|
11
|
-
|
|
12
|
-
A modern, fully-tested JavaScript runtime for SCORM 1.2 and SCORM 2004.
|
|
13
|
-
|
|
14
29
|
> **Upgrading from v2.x?** See the [Migration Guide](MIGRATION.md) for breaking changes and migration steps.
|
|
15
30
|
|
|
16
31
|
## Overview
|
package/dist/esm/scorm-again.js
CHANGED
|
@@ -1160,6 +1160,11 @@ const scorm2004_regex = {
|
|
|
1160
1160
|
progress_range: "0#1"
|
|
1161
1161
|
};
|
|
1162
1162
|
|
|
1163
|
+
const PERFORMANCE_STEP_NAME = "^$|" + scorm2004_regex.CMIShortIdentifier;
|
|
1164
|
+
const PERFORMANCE_CHARACTERSTRING = "(?![\\s\\S]*(?:\\[,\\]|\\[\\.\\]|\\[:\\]))[\\s\\S]{1,250}";
|
|
1165
|
+
const PERFORMANCE_NUMERIC_RANGE = "(?:-?\\d+(?:\\.\\d+)?)?\\[:\\](?:-?\\d+(?:\\.\\d+)?)?";
|
|
1166
|
+
const CR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_NUMERIC_RANGE + "|" + PERFORMANCE_CHARACTERSTRING + ")$";
|
|
1167
|
+
const LR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_CHARACTERSTRING + ")$";
|
|
1163
1168
|
const LearnerResponses = {
|
|
1164
1169
|
"true-false": {
|
|
1165
1170
|
format: "^true$|^false$",
|
|
@@ -1194,8 +1199,8 @@ const LearnerResponses = {
|
|
|
1194
1199
|
unique: false
|
|
1195
1200
|
},
|
|
1196
1201
|
performance: {
|
|
1197
|
-
format:
|
|
1198
|
-
format2:
|
|
1202
|
+
format: PERFORMANCE_STEP_NAME,
|
|
1203
|
+
format2: LR_PERFORMANCE_STEP_ANSWER,
|
|
1199
1204
|
max: 250,
|
|
1200
1205
|
delimiter: "[,]",
|
|
1201
1206
|
delimiter2: "[.]",
|
|
@@ -1271,10 +1276,10 @@ const CorrectResponses = {
|
|
|
1271
1276
|
delimiter2: "[.]",
|
|
1272
1277
|
unique: false,
|
|
1273
1278
|
duplicate: false,
|
|
1274
|
-
// step_name
|
|
1275
|
-
format:
|
|
1276
|
-
// step_answer
|
|
1277
|
-
format2:
|
|
1279
|
+
// step_name: optional short_identifier_type
|
|
1280
|
+
format: PERFORMANCE_STEP_NAME,
|
|
1281
|
+
// step_answer: optional characterstring (spaces allowed) or numeric range
|
|
1282
|
+
format2: CR_PERFORMANCE_STEP_ANSWER
|
|
1278
1283
|
},
|
|
1279
1284
|
sequencing: {
|
|
1280
1285
|
max: 36,
|
|
@@ -5583,7 +5588,19 @@ class CMIValueAccessService {
|
|
|
5583
5588
|
if (!scorm2004) {
|
|
5584
5589
|
if (isFinalAttribute) {
|
|
5585
5590
|
if (typeof attribute === "undefined" || !this.context.checkObjectHasProperty(refObject, attribute)) {
|
|
5586
|
-
|
|
5591
|
+
if (attribute === "_children") {
|
|
5592
|
+
this.context.throwSCORMError(
|
|
5593
|
+
CMIElement,
|
|
5594
|
+
getErrorCode(this.context.errorCodes, "CHILDREN_ERROR")
|
|
5595
|
+
);
|
|
5596
|
+
} else if (attribute === "_count") {
|
|
5597
|
+
this.context.throwSCORMError(
|
|
5598
|
+
CMIElement,
|
|
5599
|
+
getErrorCode(this.context.errorCodes, "COUNT_ERROR")
|
|
5600
|
+
);
|
|
5601
|
+
} else {
|
|
5602
|
+
this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);
|
|
5603
|
+
}
|
|
5587
5604
|
return { error: true };
|
|
5588
5605
|
}
|
|
5589
5606
|
}
|
|
@@ -15987,7 +16004,8 @@ class BaseAPI {
|
|
|
15987
16004
|
if (errorCode === 143) returnValue = global_constants.SCORM_FALSE;
|
|
15988
16005
|
} else {
|
|
15989
16006
|
const result = this.storeData(false);
|
|
15990
|
-
|
|
16007
|
+
const errorCode = result.errorCode ?? 0;
|
|
16008
|
+
if (errorCode > 0) {
|
|
15991
16009
|
if (result.errorMessage) {
|
|
15992
16010
|
this.apiLog(
|
|
15993
16011
|
"commit",
|
|
@@ -16002,12 +16020,12 @@ class BaseAPI {
|
|
|
16002
16020
|
LogLevelEnum.DEBUG
|
|
16003
16021
|
);
|
|
16004
16022
|
}
|
|
16005
|
-
this.throwSCORMError("api",
|
|
16023
|
+
this.throwSCORMError("api", errorCode);
|
|
16006
16024
|
}
|
|
16007
16025
|
const resultValue = result?.result ?? global_constants.SCORM_FALSE;
|
|
16008
16026
|
returnValue = typeof resultValue === "boolean" ? String(resultValue) : resultValue;
|
|
16009
16027
|
this.apiLog(callbackName, " Result: " + returnValue, LogLevelEnum.DEBUG, "HttpRequest");
|
|
16010
|
-
if (checkTerminated) this.lastErrorCode = "0";
|
|
16028
|
+
if (checkTerminated && errorCode === 0) this.lastErrorCode = "0";
|
|
16011
16029
|
this.processListeners(callbackName);
|
|
16012
16030
|
if (this.settings.enableOfflineSupport && this._offlineStorageService && this._offlineStorageService.isDeviceOnline() && this._courseId) {
|
|
16013
16031
|
this._offlineStorageService.hasPendingOfflineData(this._courseId).then((hasPendingData) => {
|
|
@@ -18930,6 +18948,49 @@ class CMILearnerPreference extends BaseCMI {
|
|
|
18930
18948
|
}
|
|
18931
18949
|
}
|
|
18932
18950
|
|
|
18951
|
+
function stripBrackets(delim) {
|
|
18952
|
+
return delim.replace(/[[\]]/g, "");
|
|
18953
|
+
}
|
|
18954
|
+
function escapeRegex(s) {
|
|
18955
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18956
|
+
}
|
|
18957
|
+
function splitDelimited(value, bracketed) {
|
|
18958
|
+
if (!bracketed) {
|
|
18959
|
+
return [value];
|
|
18960
|
+
}
|
|
18961
|
+
if (value.includes(bracketed)) {
|
|
18962
|
+
return value.split(bracketed);
|
|
18963
|
+
}
|
|
18964
|
+
const bare = stripBrackets(bracketed);
|
|
18965
|
+
if (!bare) {
|
|
18966
|
+
return [value];
|
|
18967
|
+
}
|
|
18968
|
+
const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`, "g");
|
|
18969
|
+
const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
|
|
18970
|
+
return value.split(splitRe).map((part) => part.replace(unescapeRe, bare));
|
|
18971
|
+
}
|
|
18972
|
+
function splitFirstDelimited(value, bracketed) {
|
|
18973
|
+
if (!bracketed) {
|
|
18974
|
+
return [value];
|
|
18975
|
+
}
|
|
18976
|
+
if (value.includes(bracketed)) {
|
|
18977
|
+
const idx = value.indexOf(bracketed);
|
|
18978
|
+
return [value.slice(0, idx), value.slice(idx + bracketed.length)];
|
|
18979
|
+
}
|
|
18980
|
+
const bare = stripBrackets(bracketed);
|
|
18981
|
+
if (!bare) {
|
|
18982
|
+
return [value];
|
|
18983
|
+
}
|
|
18984
|
+
const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`);
|
|
18985
|
+
const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
|
|
18986
|
+
const parts = value.split(splitRe);
|
|
18987
|
+
const first = (parts[0] ?? "").replace(unescapeRe, bare);
|
|
18988
|
+
if (parts.length === 1) {
|
|
18989
|
+
return [first];
|
|
18990
|
+
}
|
|
18991
|
+
return [first, parts.slice(1).join(bare).replace(unescapeRe, bare)];
|
|
18992
|
+
}
|
|
18993
|
+
|
|
18933
18994
|
class CMIInteractions extends CMIArray {
|
|
18934
18995
|
/**
|
|
18935
18996
|
* Constructor for `cmi.interactions` Array
|
|
@@ -19139,8 +19200,7 @@ class CMIInteractionsObject extends BaseCMI {
|
|
|
19139
19200
|
const response_type = LearnerResponses[this.type];
|
|
19140
19201
|
if (response_type) {
|
|
19141
19202
|
if (response_type?.delimiter) {
|
|
19142
|
-
|
|
19143
|
-
nodes = learner_response.split(delimiter);
|
|
19203
|
+
nodes = splitDelimited(learner_response, response_type.delimiter);
|
|
19144
19204
|
} else {
|
|
19145
19205
|
nodes[0] = learner_response;
|
|
19146
19206
|
}
|
|
@@ -19148,10 +19208,10 @@ class CMIInteractionsObject extends BaseCMI {
|
|
|
19148
19208
|
const formatRegex = new RegExp(response_type.format);
|
|
19149
19209
|
for (let i = 0; i < nodes.length; i++) {
|
|
19150
19210
|
if (response_type?.delimiter2) {
|
|
19151
|
-
const
|
|
19152
|
-
const values =
|
|
19211
|
+
const node = nodes[i] ?? "";
|
|
19212
|
+
const values = this.type === "performance" ? splitFirstDelimited(node, response_type.delimiter2) : splitDelimited(node, response_type.delimiter2);
|
|
19153
19213
|
if (values?.length === 2) {
|
|
19154
|
-
if (this.type === "performance" &&
|
|
19214
|
+
if (this.type === "performance" && values[0] === "" && values[1] === "") {
|
|
19155
19215
|
throw new Scorm2004ValidationError(
|
|
19156
19216
|
this._cmi_element + ".learner_response",
|
|
19157
19217
|
scorm2004_errors.TYPE_MISMATCH
|
|
@@ -19370,30 +19430,13 @@ class CMIInteractionsObjectivesObject extends BaseCMI {
|
|
|
19370
19430
|
return result;
|
|
19371
19431
|
}
|
|
19372
19432
|
}
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
|
|
19377
|
-
|
|
19378
|
-
}
|
|
19379
|
-
function splitUnescaped(text, delim) {
|
|
19380
|
-
const reDelim = escapeRegex(delim);
|
|
19381
|
-
const splitRe = new RegExp(`(?<!\\\\)${reDelim}`, "g");
|
|
19382
|
-
const unescapeRe = new RegExp(`\\\\${reDelim}`, "g");
|
|
19383
|
-
return text.split(splitRe).map((part) => part.replace(unescapeRe, delim));
|
|
19384
|
-
}
|
|
19385
|
-
function splitFirstUnescaped(text, delim) {
|
|
19386
|
-
const reDelim = escapeRegex(delim);
|
|
19387
|
-
const splitRe = new RegExp(`(?<!\\\\)${reDelim}`);
|
|
19388
|
-
const unescapeRe = new RegExp(`\\\\${reDelim}`, "g");
|
|
19389
|
-
const parts = text.split(splitRe);
|
|
19390
|
-
const firstPart = parts[0] ?? "";
|
|
19391
|
-
if (parts.length === 1) {
|
|
19392
|
-
return [firstPart.replace(unescapeRe, delim)];
|
|
19433
|
+
const RESPONSE_PREFIX_RE = /^\{(?:lang|case_matters|order_matters)=[^}]+\}/;
|
|
19434
|
+
function stripResponsePrefixes(node) {
|
|
19435
|
+
let result = node;
|
|
19436
|
+
while (RESPONSE_PREFIX_RE.test(result)) {
|
|
19437
|
+
result = result.replace(RESPONSE_PREFIX_RE, "");
|
|
19393
19438
|
}
|
|
19394
|
-
|
|
19395
|
-
const part2 = parts.slice(1).join(delim).replace(unescapeRe, delim);
|
|
19396
|
-
return [part1, part2];
|
|
19439
|
+
return result;
|
|
19397
19440
|
}
|
|
19398
19441
|
function validatePattern(type, pattern, responseDef) {
|
|
19399
19442
|
if (pattern.trim() !== pattern) {
|
|
@@ -19402,8 +19445,7 @@ function validatePattern(type, pattern, responseDef) {
|
|
|
19402
19445
|
scorm2004_errors.TYPE_MISMATCH
|
|
19403
19446
|
);
|
|
19404
19447
|
}
|
|
19405
|
-
const
|
|
19406
|
-
const rawNodes = subDelim1 ? splitUnescaped(pattern, subDelim1) : [pattern];
|
|
19448
|
+
const rawNodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
|
|
19407
19449
|
for (const raw of rawNodes) {
|
|
19408
19450
|
if (raw.trim() !== raw) {
|
|
19409
19451
|
throw new Scorm2004ValidationError(
|
|
@@ -19415,20 +19457,14 @@ function validatePattern(type, pattern, responseDef) {
|
|
|
19415
19457
|
if (type === "fill-in" && pattern === "") {
|
|
19416
19458
|
return;
|
|
19417
19459
|
}
|
|
19418
|
-
const
|
|
19419
|
-
let nodes;
|
|
19420
|
-
if (delim1) {
|
|
19421
|
-
nodes = splitUnescaped(pattern, delim1);
|
|
19422
|
-
} else {
|
|
19423
|
-
nodes = [pattern];
|
|
19424
|
-
}
|
|
19460
|
+
const nodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
|
|
19425
19461
|
if (!responseDef.delimiter && pattern.includes(",")) {
|
|
19426
19462
|
throw new Scorm2004ValidationError(
|
|
19427
19463
|
"cmi.interactions.n.correct_responses.n.pattern",
|
|
19428
19464
|
scorm2004_errors.TYPE_MISMATCH
|
|
19429
19465
|
);
|
|
19430
19466
|
}
|
|
19431
|
-
if (responseDef.unique || responseDef.duplicate === false) {
|
|
19467
|
+
if (type !== "numeric" && (responseDef.unique || responseDef.duplicate === false)) {
|
|
19432
19468
|
const seen = new Set(nodes);
|
|
19433
19469
|
if (seen.size !== nodes.length) {
|
|
19434
19470
|
throw new Scorm2004ValidationError(
|
|
@@ -19460,8 +19496,7 @@ function validatePattern(type, pattern, responseDef) {
|
|
|
19460
19496
|
scorm2004_errors.TYPE_MISMATCH
|
|
19461
19497
|
);
|
|
19462
19498
|
}
|
|
19463
|
-
const
|
|
19464
|
-
const parts = value.split(new RegExp(`(?<!\\\\)${escapeRegex(delim)}`, "g")).map((n) => n.replace(new RegExp(`\\\\${escapeRegex(delim)}`, "g"), delim));
|
|
19499
|
+
const parts = splitDelimited(value, delimBracketed);
|
|
19465
19500
|
if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
|
|
19466
19501
|
throw new Scorm2004ValidationError(
|
|
19467
19502
|
"cmi.interactions.n.correct_responses.n.pattern",
|
|
@@ -19478,15 +19513,17 @@ function validatePattern(type, pattern, responseDef) {
|
|
|
19478
19513
|
for (const node of nodes) {
|
|
19479
19514
|
switch (type) {
|
|
19480
19515
|
case "numeric": {
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19487
|
-
|
|
19516
|
+
if (node === "") {
|
|
19517
|
+
const bracketedRange = nodes.length >= 2 && !!responseDef.delimiter && pattern.includes(responseDef.delimiter);
|
|
19518
|
+
if (!bracketedRange) {
|
|
19519
|
+
throw new Scorm2004ValidationError(
|
|
19520
|
+
"cmi.interactions.n.correct_responses.n.pattern",
|
|
19521
|
+
scorm2004_errors.TYPE_MISMATCH
|
|
19522
|
+
);
|
|
19523
|
+
}
|
|
19524
|
+
break;
|
|
19488
19525
|
}
|
|
19489
|
-
|
|
19526
|
+
checkSingle(node);
|
|
19490
19527
|
break;
|
|
19491
19528
|
}
|
|
19492
19529
|
case "performance": {
|
|
@@ -19497,8 +19534,8 @@ function validatePattern(type, pattern, responseDef) {
|
|
|
19497
19534
|
scorm2004_errors.TYPE_MISMATCH
|
|
19498
19535
|
);
|
|
19499
19536
|
}
|
|
19500
|
-
const
|
|
19501
|
-
const parts =
|
|
19537
|
+
const record = stripResponsePrefixes(node);
|
|
19538
|
+
const parts = splitFirstDelimited(record, delimBracketed);
|
|
19502
19539
|
if (parts.length !== 2) {
|
|
19503
19540
|
throw new Scorm2004ValidationError(
|
|
19504
19541
|
"cmi.interactions.n.correct_responses.n.pattern",
|
|
@@ -19506,7 +19543,7 @@ function validatePattern(type, pattern, responseDef) {
|
|
|
19506
19543
|
);
|
|
19507
19544
|
}
|
|
19508
19545
|
const [part1, part2] = parts;
|
|
19509
|
-
if (part1 === ""
|
|
19546
|
+
if (part1 === "" && part2 === "") {
|
|
19510
19547
|
throw new Scorm2004ValidationError(
|
|
19511
19548
|
"cmi.interactions.n.correct_responses.n.pattern",
|
|
19512
19549
|
scorm2004_errors.TYPE_MISMATCH
|
|
@@ -22385,7 +22422,7 @@ class Scorm2004ResponseValidator {
|
|
|
22385
22422
|
checkValidResponseType(CMIElement, response_type, value, interaction_type) {
|
|
22386
22423
|
let nodes = [];
|
|
22387
22424
|
if (response_type?.delimiter) {
|
|
22388
|
-
nodes = String(value)
|
|
22425
|
+
nodes = splitDelimited(String(value), response_type.delimiter);
|
|
22389
22426
|
} else {
|
|
22390
22427
|
nodes[0] = value;
|
|
22391
22428
|
}
|
|
@@ -22507,22 +22544,30 @@ class Scorm2004ResponseValidator {
|
|
|
22507
22544
|
nodes[i] = this.removeCorrectResponsePrefixes(CMIElement, nodes[i]);
|
|
22508
22545
|
}
|
|
22509
22546
|
if (response?.delimiter2) {
|
|
22510
|
-
const values = nodes[i].
|
|
22547
|
+
const values = interaction_type === "performance" ? splitFirstDelimited(nodes[i], response.delimiter2) : splitDelimited(nodes[i], response.delimiter2);
|
|
22511
22548
|
if (values.length === 2) {
|
|
22512
|
-
|
|
22513
|
-
if (!matches) {
|
|
22549
|
+
if (interaction_type === "performance" && values[0] === "" && values[1] === "") {
|
|
22514
22550
|
this.context.throwSCORMError(
|
|
22515
22551
|
CMIElement,
|
|
22516
22552
|
scorm2004_errors.TYPE_MISMATCH,
|
|
22517
22553
|
`${interaction_type}: ${value}`
|
|
22518
22554
|
);
|
|
22519
22555
|
} else {
|
|
22520
|
-
|
|
22556
|
+
const matches = values[0]?.match(formatRegex);
|
|
22557
|
+
if (!matches) {
|
|
22521
22558
|
this.context.throwSCORMError(
|
|
22522
22559
|
CMIElement,
|
|
22523
22560
|
scorm2004_errors.TYPE_MISMATCH,
|
|
22524
22561
|
`${interaction_type}: ${value}`
|
|
22525
22562
|
);
|
|
22563
|
+
} else {
|
|
22564
|
+
if (!response.format2 || !values[1]?.match(new RegExp(response.format2))) {
|
|
22565
|
+
this.context.throwSCORMError(
|
|
22566
|
+
CMIElement,
|
|
22567
|
+
scorm2004_errors.TYPE_MISMATCH,
|
|
22568
|
+
`${interaction_type}: ${value}`
|
|
22569
|
+
);
|
|
22570
|
+
}
|
|
22526
22571
|
}
|
|
22527
22572
|
}
|
|
22528
22573
|
} else {
|
|
@@ -22533,6 +22578,9 @@ class Scorm2004ResponseValidator {
|
|
|
22533
22578
|
);
|
|
22534
22579
|
}
|
|
22535
22580
|
} else {
|
|
22581
|
+
if (interaction_type === "numeric" && nodes.length > 1 && nodes[i] === "" && !!response.delimiter && String(value).includes(response.delimiter)) {
|
|
22582
|
+
continue;
|
|
22583
|
+
}
|
|
22536
22584
|
const matches = nodes[i].match(formatRegex);
|
|
22537
22585
|
if (!matches && value !== "" || !matches && interaction_type === "true-false") {
|
|
22538
22586
|
this.context.throwSCORMError(
|
|
@@ -22542,7 +22590,7 @@ class Scorm2004ResponseValidator {
|
|
|
22542
22590
|
);
|
|
22543
22591
|
} else {
|
|
22544
22592
|
if (interaction_type === "numeric" && nodes.length > 1) {
|
|
22545
|
-
if (Number(nodes[0]) > Number(nodes[1])) {
|
|
22593
|
+
if (nodes[0] !== "" && nodes[1] !== "" && Number(nodes[0]) > Number(nodes[1])) {
|
|
22546
22594
|
this.context.throwSCORMError(
|
|
22547
22595
|
CMIElement,
|
|
22548
22596
|
scorm2004_errors.TYPE_MISMATCH,
|