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/dist/scorm2004.js CHANGED
@@ -943,6 +943,11 @@ this.Scorm2004API = (function () {
943
943
  progress_range: "0#1"
944
944
  };
945
945
 
946
+ const PERFORMANCE_STEP_NAME = "^$|" + scorm2004_regex.CMIShortIdentifier;
947
+ const PERFORMANCE_CHARACTERSTRING = "(?![\\s\\S]*(?:\\[,\\]|\\[\\.\\]|\\[:\\]))[\\s\\S]{1,250}";
948
+ const PERFORMANCE_NUMERIC_RANGE = "(?:-?\\d+(?:\\.\\d+)?)?\\[:\\](?:-?\\d+(?:\\.\\d+)?)?";
949
+ const CR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_NUMERIC_RANGE + "|" + PERFORMANCE_CHARACTERSTRING + ")$";
950
+ const LR_PERFORMANCE_STEP_ANSWER = "^(?:|" + PERFORMANCE_CHARACTERSTRING + ")$";
946
951
  const LearnerResponses = {
947
952
  "true-false": {
948
953
  format: "^true$|^false$",
@@ -977,8 +982,8 @@ this.Scorm2004API = (function () {
977
982
  unique: false
978
983
  },
979
984
  performance: {
980
- format: "^$|" + scorm2004_regex.CMIShortIdentifier,
981
- format2: scorm2004_regex.CMIDecimal + "|^$|" + scorm2004_regex.CMIShortIdentifier,
985
+ format: PERFORMANCE_STEP_NAME,
986
+ format2: LR_PERFORMANCE_STEP_ANSWER,
982
987
  max: 250,
983
988
  delimiter: "[,]",
984
989
  delimiter2: "[.]",
@@ -1054,10 +1059,10 @@ this.Scorm2004API = (function () {
1054
1059
  delimiter2: "[.]",
1055
1060
  unique: false,
1056
1061
  duplicate: false,
1057
- // step_name must be a non-empty short identifier
1058
- format: scorm2004_regex.CMIShortIdentifier,
1059
- // step_answer may be short identifier or numeric range (<decimal>[:<decimal>])
1060
- format2: `^(${scorm2004_regex.CMIShortIdentifier})$|^(?:\\d+(?:\\.\\d+)?(?::\\d+(?:\\.\\d+)?)?)$`
1062
+ // step_name: optional short_identifier_type
1063
+ format: PERFORMANCE_STEP_NAME,
1064
+ // step_answer: optional characterstring (spaces allowed) or numeric range
1065
+ format2: CR_PERFORMANCE_STEP_ANSWER
1061
1066
  },
1062
1067
  sequencing: {
1063
1068
  max: 36,
@@ -5082,7 +5087,13 @@ this.Scorm2004API = (function () {
5082
5087
  if (!scorm2004) {
5083
5088
  if (isFinalAttribute) {
5084
5089
  if (typeof attribute === "undefined" || !this.context.checkObjectHasProperty(refObject, attribute)) {
5085
- this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);
5090
+ if (attribute === "_children") {
5091
+ this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "CHILDREN_ERROR"));
5092
+ } else if (attribute === "_count") {
5093
+ this.context.throwSCORMError(CMIElement, getErrorCode(this.context.errorCodes, "COUNT_ERROR"));
5094
+ } else {
5095
+ this.context.throwSCORMError(CMIElement, invalidErrorCode, invalidErrorMessage);
5096
+ }
5086
5097
  return {
5087
5098
  error: true
5088
5099
  };
@@ -15336,19 +15347,20 @@ ${stackTrace}`);
15336
15347
  if (errorCode === 143) returnValue = global_constants.SCORM_FALSE;
15337
15348
  } else {
15338
15349
  const result = this.storeData(false);
15339
- if ((result.errorCode ?? 0) > 0) {
15350
+ const errorCode = result.errorCode ?? 0;
15351
+ if (errorCode > 0) {
15340
15352
  if (result.errorMessage) {
15341
15353
  this.apiLog("commit", `Commit failed with error: ${result.errorMessage}`, LogLevelEnum.ERROR);
15342
15354
  }
15343
15355
  if (result.errorDetails) {
15344
15356
  this.apiLog("commit", `Error details: ${JSON.stringify(result.errorDetails)}`, LogLevelEnum.DEBUG);
15345
15357
  }
15346
- this.throwSCORMError("api", result.errorCode);
15358
+ this.throwSCORMError("api", errorCode);
15347
15359
  }
15348
15360
  const resultValue = result?.result ?? global_constants.SCORM_FALSE;
15349
15361
  returnValue = typeof resultValue === "boolean" ? String(resultValue) : resultValue;
15350
15362
  this.apiLog(callbackName, " Result: " + returnValue, LogLevelEnum.DEBUG, "HttpRequest");
15351
- if (checkTerminated) this.lastErrorCode = "0";
15363
+ if (checkTerminated && errorCode === 0) this.lastErrorCode = "0";
15352
15364
  this.processListeners(callbackName);
15353
15365
  if (this.settings.enableOfflineSupport && this._offlineStorageService && this._offlineStorageService.isDeviceOnline() && this._courseId) {
15354
15366
  this._offlineStorageService.hasPendingOfflineData(this._courseId).then(hasPendingData => {
@@ -16065,6 +16077,49 @@ ${stackTrace}`);
16065
16077
  }
16066
16078
  }
16067
16079
 
16080
+ function stripBrackets(delim) {
16081
+ return delim.replace(/[[\]]/g, "");
16082
+ }
16083
+ function escapeRegex(s) {
16084
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16085
+ }
16086
+ function splitDelimited(value, bracketed) {
16087
+ if (!bracketed) {
16088
+ return [value];
16089
+ }
16090
+ if (value.includes(bracketed)) {
16091
+ return value.split(bracketed);
16092
+ }
16093
+ const bare = stripBrackets(bracketed);
16094
+ if (!bare) {
16095
+ return [value];
16096
+ }
16097
+ const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`, "g");
16098
+ const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
16099
+ return value.split(splitRe).map(part => part.replace(unescapeRe, bare));
16100
+ }
16101
+ function splitFirstDelimited(value, bracketed) {
16102
+ if (!bracketed) {
16103
+ return [value];
16104
+ }
16105
+ if (value.includes(bracketed)) {
16106
+ const idx = value.indexOf(bracketed);
16107
+ return [value.slice(0, idx), value.slice(idx + bracketed.length)];
16108
+ }
16109
+ const bare = stripBrackets(bracketed);
16110
+ if (!bare) {
16111
+ return [value];
16112
+ }
16113
+ const splitRe = new RegExp(`(?<!\\\\)${escapeRegex(bare)}`);
16114
+ const unescapeRe = new RegExp(`\\\\${escapeRegex(bare)}`, "g");
16115
+ const parts = value.split(splitRe);
16116
+ const first = (parts[0] ?? "").replace(unescapeRe, bare);
16117
+ if (parts.length === 1) {
16118
+ return [first];
16119
+ }
16120
+ return [first, parts.slice(1).join(bare).replace(unescapeRe, bare)];
16121
+ }
16122
+
16068
16123
  var __defProp$m = Object.defineProperty;
16069
16124
  var __defNormalProp$m = (obj, key, value) => key in obj ? __defProp$m(obj, key, {
16070
16125
  enumerable: true,
@@ -16260,8 +16315,7 @@ ${stackTrace}`);
16260
16315
  const response_type = LearnerResponses[this.type];
16261
16316
  if (response_type) {
16262
16317
  if (response_type?.delimiter) {
16263
- const delimiter = response_type.delimiter === "[,]" ? "," : response_type.delimiter;
16264
- nodes = learner_response.split(delimiter);
16318
+ nodes = splitDelimited(learner_response, response_type.delimiter);
16265
16319
  } else {
16266
16320
  nodes[0] = learner_response;
16267
16321
  }
@@ -16269,10 +16323,10 @@ ${stackTrace}`);
16269
16323
  const formatRegex = new RegExp(response_type.format);
16270
16324
  for (let i = 0; i < nodes.length; i++) {
16271
16325
  if (response_type?.delimiter2) {
16272
- const delimiter2 = response_type.delimiter2 === "[.]" ? "." : response_type.delimiter2;
16273
- const values = nodes[i]?.split(delimiter2);
16326
+ const node = nodes[i] ?? "";
16327
+ const values = this.type === "performance" ? splitFirstDelimited(node, response_type.delimiter2) : splitDelimited(node, response_type.delimiter2);
16274
16328
  if (values?.length === 2) {
16275
- if (this.type === "performance" && (values[0] === "" || values[1] === "")) {
16329
+ if (this.type === "performance" && values[0] === "" && values[1] === "") {
16276
16330
  throw new Scorm2004ValidationError(this._cmi_element + ".learner_response", scorm2004_errors.TYPE_MISMATCH);
16277
16331
  }
16278
16332
  if (!values[0]?.match(formatRegex)) {
@@ -16453,37 +16507,19 @@ ${stackTrace}`);
16453
16507
  return result;
16454
16508
  }
16455
16509
  }
16456
- function stripBrackets(delim) {
16457
- return delim.replace(/[[\]]/g, "");
16458
- }
16459
- function escapeRegex(s) {
16460
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
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)];
16510
+ const RESPONSE_PREFIX_RE = /^\{(?:lang|case_matters|order_matters)=[^}]+\}/;
16511
+ function stripResponsePrefixes(node) {
16512
+ let result = node;
16513
+ while (RESPONSE_PREFIX_RE.test(result)) {
16514
+ result = result.replace(RESPONSE_PREFIX_RE, "");
16476
16515
  }
16477
- const part1 = firstPart.replace(unescapeRe, delim);
16478
- const part2 = parts.slice(1).join(delim).replace(unescapeRe, delim);
16479
- return [part1, part2];
16516
+ return result;
16480
16517
  }
16481
16518
  function validatePattern(type, pattern, responseDef) {
16482
16519
  if (pattern.trim() !== pattern) {
16483
16520
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16484
16521
  }
16485
- const subDelim1 = responseDef.delimiter ? stripBrackets(responseDef.delimiter) : null;
16486
- const rawNodes = subDelim1 ? splitUnescaped(pattern, subDelim1) : [pattern];
16522
+ const rawNodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
16487
16523
  for (const raw of rawNodes) {
16488
16524
  if (raw.trim() !== raw) {
16489
16525
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
@@ -16492,17 +16528,11 @@ ${stackTrace}`);
16492
16528
  if (type === "fill-in" && pattern === "") {
16493
16529
  return;
16494
16530
  }
16495
- const delim1 = responseDef.delimiter ? stripBrackets(responseDef.delimiter) : null;
16496
- let nodes;
16497
- if (delim1) {
16498
- nodes = splitUnescaped(pattern, delim1);
16499
- } else {
16500
- nodes = [pattern];
16501
- }
16531
+ const nodes = responseDef.delimiter ? splitDelimited(pattern, responseDef.delimiter) : [pattern];
16502
16532
  if (!responseDef.delimiter && pattern.includes(",")) {
16503
16533
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16504
16534
  }
16505
- if (responseDef.unique || responseDef.duplicate === false) {
16535
+ if (type !== "numeric" && (responseDef.unique || responseDef.duplicate === false)) {
16506
16536
  const seen = new Set(nodes);
16507
16537
  if (seen.size !== nodes.length) {
16508
16538
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
@@ -16522,8 +16552,7 @@ ${stackTrace}`);
16522
16552
  if (!delimBracketed) {
16523
16553
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16524
16554
  }
16525
- const delim = stripBrackets(delimBracketed);
16526
- const parts = value.split(new RegExp(`(?<!\\\\)${escapeRegex(delim)}`, "g")).map(n => n.replace(new RegExp(`\\\\${escapeRegex(delim)}`, "g"), delim));
16555
+ const parts = splitDelimited(value, delimBracketed);
16527
16556
  if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
16528
16557
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16529
16558
  }
@@ -16535,12 +16564,14 @@ ${stackTrace}`);
16535
16564
  switch (type) {
16536
16565
  case "numeric":
16537
16566
  {
16538
- const numDelim = responseDef.delimiter ? stripBrackets(responseDef.delimiter) : ":";
16539
- const nums = node.split(numDelim);
16540
- if (nums.length < 1 || nums.length > 2) {
16541
- throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16567
+ if (node === "") {
16568
+ const bracketedRange = nodes.length >= 2 && !!responseDef.delimiter && pattern.includes(responseDef.delimiter);
16569
+ if (!bracketedRange) {
16570
+ throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16571
+ }
16572
+ break;
16542
16573
  }
16543
- nums.forEach(checkSingle);
16574
+ checkSingle(node);
16544
16575
  break;
16545
16576
  }
16546
16577
  case "performance":
@@ -16549,13 +16580,13 @@ ${stackTrace}`);
16549
16580
  if (!delimBracketed) {
16550
16581
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16551
16582
  }
16552
- const delim = stripBrackets(delimBracketed);
16553
- const parts = splitFirstUnescaped(node, delim);
16583
+ const record = stripResponsePrefixes(node);
16584
+ const parts = splitFirstDelimited(record, delimBracketed);
16554
16585
  if (parts.length !== 2) {
16555
16586
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16556
16587
  }
16557
16588
  const [part1, part2] = parts;
16558
- if (part1 === "" || part2 === "" || part1 === part2) {
16589
+ if (part1 === "" && part2 === "") {
16559
16590
  throw new Scorm2004ValidationError("cmi.interactions.n.correct_responses.n.pattern", scorm2004_errors.TYPE_MISMATCH);
16560
16591
  }
16561
16592
  if (part1 === void 0 || !fmt1.test(part1)) {
@@ -19510,7 +19541,7 @@ ${stackTrace}`);
19510
19541
  checkValidResponseType(CMIElement, response_type, value, interaction_type) {
19511
19542
  let nodes = [];
19512
19543
  if (response_type?.delimiter) {
19513
- nodes = String(value).split(response_type.delimiter);
19544
+ nodes = splitDelimited(String(value), response_type.delimiter);
19514
19545
  } else {
19515
19546
  nodes[0] = value;
19516
19547
  }
@@ -19608,26 +19639,33 @@ ${stackTrace}`);
19608
19639
  nodes[i] = this.removeCorrectResponsePrefixes(CMIElement, nodes[i]);
19609
19640
  }
19610
19641
  if (response?.delimiter2) {
19611
- const values = nodes[i].split(response.delimiter2);
19642
+ const values = interaction_type === "performance" ? splitFirstDelimited(nodes[i], response.delimiter2) : splitDelimited(nodes[i], response.delimiter2);
19612
19643
  if (values.length === 2) {
19613
- const matches = values[0].match(formatRegex);
19614
- if (!matches) {
19644
+ if (interaction_type === "performance" && values[0] === "" && values[1] === "") {
19615
19645
  this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
19616
19646
  } else {
19617
- if (!response.format2 || !values[1].match(new RegExp(response.format2))) {
19647
+ const matches = values[0]?.match(formatRegex);
19648
+ if (!matches) {
19618
19649
  this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
19650
+ } else {
19651
+ if (!response.format2 || !values[1]?.match(new RegExp(response.format2))) {
19652
+ this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
19653
+ }
19619
19654
  }
19620
19655
  }
19621
19656
  } else {
19622
19657
  this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
19623
19658
  }
19624
19659
  } else {
19660
+ if (interaction_type === "numeric" && nodes.length > 1 && nodes[i] === "" && !!response.delimiter && String(value).includes(response.delimiter)) {
19661
+ continue;
19662
+ }
19625
19663
  const matches = nodes[i].match(formatRegex);
19626
19664
  if (!matches && value !== "" || !matches && interaction_type === "true-false") {
19627
19665
  this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
19628
19666
  } else {
19629
19667
  if (interaction_type === "numeric" && nodes.length > 1) {
19630
- if (Number(nodes[0]) > Number(nodes[1])) {
19668
+ if (nodes[0] !== "" && nodes[1] !== "" && Number(nodes[0]) > Number(nodes[1])) {
19631
19669
  this.context.throwSCORMError(CMIElement, scorm2004_errors.TYPE_MISMATCH, `${interaction_type}: ${value}`);
19632
19670
  }
19633
19671
  } else {