scorm-again 2.6.2 → 2.6.4

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.
Files changed (70) hide show
  1. package/dist/aicc.js +41 -25
  2. package/dist/aicc.js.map +1 -1
  3. package/dist/aicc.min.js +1 -1
  4. package/dist/aicc.min.js.map +1 -1
  5. package/dist/esm/aicc.js +41 -25
  6. package/dist/esm/aicc.js.map +1 -1
  7. package/dist/esm/aicc.min.js +1 -1
  8. package/dist/esm/aicc.min.js.map +1 -1
  9. package/dist/esm/scorm-again.js +70 -45
  10. package/dist/esm/scorm-again.js.map +1 -1
  11. package/dist/esm/scorm-again.min.js +1 -1
  12. package/dist/esm/scorm-again.min.js.map +1 -1
  13. package/dist/esm/scorm12.js +38 -23
  14. package/dist/esm/scorm12.js.map +1 -1
  15. package/dist/esm/scorm12.min.js +1 -1
  16. package/dist/esm/scorm12.min.js.map +1 -1
  17. package/dist/esm/scorm2004.js +60 -39
  18. package/dist/esm/scorm2004.js.map +1 -1
  19. package/dist/esm/scorm2004.min.js +1 -1
  20. package/dist/esm/scorm2004.min.js.map +1 -1
  21. package/dist/scorm-again.js +70 -45
  22. package/dist/scorm-again.js.map +1 -1
  23. package/dist/scorm-again.min.js +1 -1
  24. package/dist/scorm-again.min.js.map +1 -1
  25. package/dist/scorm12.js +38 -23
  26. package/dist/scorm12.js.map +1 -1
  27. package/dist/scorm12.min.js +1 -1
  28. package/dist/scorm12.min.js.map +1 -1
  29. package/dist/scorm2004.js +60 -39
  30. package/dist/scorm2004.js.map +1 -1
  31. package/dist/scorm2004.min.js +1 -1
  32. package/dist/scorm2004.min.js.map +1 -1
  33. package/index.d.ts +24 -3
  34. package/package.json +1 -1
  35. package/src/BaseAPI.ts +57 -35
  36. package/src/Scorm12API.ts +3 -2
  37. package/src/Scorm2004API.ts +31 -24
  38. package/src/cmi/aicc/attempts.ts +3 -3
  39. package/src/cmi/aicc/core.ts +30 -12
  40. package/src/cmi/aicc/evaluation.ts +1 -1
  41. package/src/cmi/aicc/student_data.ts +1 -1
  42. package/src/cmi/aicc/student_demographics.ts +13 -13
  43. package/src/cmi/aicc/student_preferences.ts +1 -1
  44. package/src/cmi/aicc/tries.ts +3 -3
  45. package/src/cmi/aicc/validation.ts +1 -1
  46. package/src/cmi/common/array.ts +1 -1
  47. package/src/cmi/common/score.ts +3 -3
  48. package/src/cmi/common/validation.ts +2 -2
  49. package/src/cmi/scorm12/cmi.ts +12 -4
  50. package/src/cmi/scorm12/interactions.ts +27 -11
  51. package/src/cmi/scorm12/objectives.ts +4 -4
  52. package/src/cmi/scorm12/student_data.ts +12 -4
  53. package/src/cmi/scorm12/student_preference.ts +3 -1
  54. package/src/cmi/scorm12/validation.ts +5 -3
  55. package/src/cmi/scorm2004/adl.ts +21 -7
  56. package/src/cmi/scorm2004/cmi.ts +45 -15
  57. package/src/cmi/scorm2004/comments.ts +11 -5
  58. package/src/cmi/scorm2004/interactions.ts +25 -23
  59. package/src/cmi/scorm2004/learner_preference.ts +3 -1
  60. package/src/cmi/scorm2004/objectives.ts +5 -5
  61. package/src/cmi/scorm2004/score.ts +3 -3
  62. package/src/cmi/scorm2004/validation.ts +2 -2
  63. package/src/constants/regex.ts +8 -1
  64. package/src/exceptions/aicc_exceptions.ts +4 -4
  65. package/src/exceptions/scorm12_exceptions.ts +4 -4
  66. package/src/exceptions/scorm2004_exceptions.ts +4 -4
  67. package/src/utilities.ts +3 -3
  68. package/test/Scorm12API.spec.ts +45 -0
  69. package/test/Scorm2004API.spec.ts +43 -0
  70. package/tsconfig.json +1 -0
package/dist/scorm2004.js CHANGED
@@ -417,6 +417,10 @@ var BaseAPI = (function () {
417
417
  : this._error_codes.GENERAL;
418
418
  for (var idx = 0; idx < structure.length; idx++) {
419
419
  var attribute = structure[idx];
420
+ if (!attribute) {
421
+ this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
422
+ return api_constants.global_constants.SCORM_FALSE;
423
+ }
420
424
  if (idx === structure.length - 1) {
421
425
  if (scorm2004 && attribute.substring(0, 8) === "{target=") {
422
426
  if (this.isInitialized()) {
@@ -447,7 +451,7 @@ var BaseAPI = (function () {
447
451
  break;
448
452
  }
449
453
  if (refObject instanceof array.CMIArray) {
450
- var index = parseInt(structure[idx + 1], 10);
454
+ var index = parseInt(structure[idx + 1] || "0", 10);
451
455
  if (!isNaN(index)) {
452
456
  var item = refObject.childArray[index];
453
457
  if (item) {
@@ -491,6 +495,10 @@ var BaseAPI = (function () {
491
495
  : this._error_codes.GENERAL;
492
496
  for (var idx = 0; idx < structure.length; idx++) {
493
497
  attribute = structure[idx];
498
+ if (!attribute) {
499
+ this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
500
+ return;
501
+ }
494
502
  if (!scorm2004) {
495
503
  if (idx === structure.length - 1) {
496
504
  if (!this._checkObjectHasProperty(refObject, attribute)) {
@@ -516,7 +524,7 @@ var BaseAPI = (function () {
516
524
  break;
517
525
  }
518
526
  if (refObject instanceof array.CMIArray) {
519
- var index = parseInt(structure[idx + 1], 10);
527
+ var index = parseInt(structure[idx + 1] || "", 10);
520
528
  if (!isNaN(index)) {
521
529
  var item = refObject.childArray[index];
522
530
  if (item) {
@@ -554,17 +562,18 @@ var BaseAPI = (function () {
554
562
  return this.currentState === api_constants.global_constants.STATE_TERMINATED;
555
563
  };
556
564
  BaseAPI.prototype.on = function (listenerName, callback) {
565
+ var _a, _c;
557
566
  if (!callback)
558
567
  return;
559
568
  var listenerFunctions = listenerName.split(" ");
560
569
  for (var i = 0; i < listenerFunctions.length; i++) {
561
- var listenerSplit = listenerFunctions[i].split(".");
562
- if (listenerSplit.length === 0)
570
+ var listenerSplit = (_a = listenerFunctions[i]) === null || _a === void 0 ? void 0 : _a.split(".");
571
+ if (!listenerSplit || listenerSplit.length === 0)
563
572
  return;
564
573
  var functionName = listenerSplit[0];
565
574
  var CMIElement = null;
566
575
  if (listenerSplit.length > 1) {
567
- CMIElement = listenerName.replace(functionName + ".", "");
576
+ CMIElement = (_c = listenerFunctions[i]) === null || _c === void 0 ? void 0 : _c.replace(functionName + ".", "");
568
577
  }
569
578
  this.listenerArray.push({
570
579
  functionName: functionName,
@@ -575,17 +584,18 @@ var BaseAPI = (function () {
575
584
  }
576
585
  };
577
586
  BaseAPI.prototype.off = function (listenerName, callback) {
587
+ var _a, _c;
578
588
  if (!callback)
579
589
  return;
580
590
  var listenerFunctions = listenerName.split(" ");
581
591
  var _loop_1 = function (i) {
582
- var listenerSplit = listenerFunctions[i].split(".");
583
- if (listenerSplit.length === 0)
592
+ var listenerSplit = (_a = listenerFunctions[i]) === null || _a === void 0 ? void 0 : _a.split(".");
593
+ if (!listenerSplit || listenerSplit.length === 0)
584
594
  return { value: void 0 };
585
595
  var functionName = listenerSplit[0];
586
596
  var CMIElement = null;
587
597
  if (listenerSplit.length > 1) {
588
- CMIElement = listenerName.replace(functionName + ".", "");
598
+ CMIElement = (_c = listenerFunctions[i]) === null || _c === void 0 ? void 0 : _c.replace(functionName + ".", "");
589
599
  }
590
600
  var removeIndex = this_1.listenerArray.findIndex(function (obj) {
591
601
  return obj.functionName === functionName &&
@@ -605,15 +615,16 @@ var BaseAPI = (function () {
605
615
  }
606
616
  };
607
617
  BaseAPI.prototype.clear = function (listenerName) {
618
+ var _a, _c;
608
619
  var listenerFunctions = listenerName.split(" ");
609
620
  var _loop_2 = function (i) {
610
- var listenerSplit = listenerFunctions[i].split(".");
611
- if (listenerSplit.length === 0)
621
+ var listenerSplit = (_a = listenerFunctions[i]) === null || _a === void 0 ? void 0 : _a.split(".");
622
+ if (!listenerSplit || (listenerSplit === null || listenerSplit === void 0 ? void 0 : listenerSplit.length) === 0)
612
623
  return { value: void 0 };
613
624
  var functionName = listenerSplit[0];
614
625
  var CMIElement = null;
615
626
  if (listenerSplit.length > 1) {
616
- CMIElement = listenerName.replace(functionName + ".", "");
627
+ CMIElement = (_c = listenerFunctions[i]) === null || _c === void 0 ? void 0 : _c.replace(functionName + ".", "");
617
628
  }
618
629
  this_2.listenerArray = this_2.listenerArray.filter(function (obj) {
619
630
  return obj.functionName !== functionName && obj.CMIElement !== CMIElement;
@@ -796,7 +807,7 @@ var BaseAPI = (function () {
796
807
  }];
797
808
  }
798
809
  process = function (url, params, settings) { return (0,tslib_es6.__awaiter)(_this, void 0, void 0, function () {
799
- var response, e_1;
810
+ var response, e_1, message;
800
811
  return (0,tslib_es6.__generator)(this, function (_a) {
801
812
  switch (_a.label) {
802
813
  case 0:
@@ -808,7 +819,8 @@ var BaseAPI = (function () {
808
819
  return [2, this.transformResponse(response)];
809
820
  case 2:
810
821
  e_1 = _a.sent();
811
- this.apiLog("processHttpRequest", e_1, enums.LogLevelEnum.ERROR);
822
+ message = e_1 instanceof Error ? e_1.message : String(e_1);
823
+ this.apiLog("processHttpRequest", message, enums.LogLevelEnum.ERROR);
812
824
  api.processListeners("CommitError");
813
825
  return [2, genericError];
814
826
  case 3: return [2];
@@ -1181,8 +1193,8 @@ function checkValidFormat(value, regexPattern, errorCode, errorClass, allowEmpty
1181
1193
  function checkValidRange(value, rangePattern, errorCode, errorClass) {
1182
1194
  var ranges = rangePattern.split("#");
1183
1195
  value = value * 1.0;
1184
- if (value >= ranges[0]) {
1185
- if (ranges[1] === "*" || value <= ranges[1]) {
1196
+ if (ranges[0] && value >= ranges[0]) {
1197
+ if (ranges[1] && (ranges[1] === "*" || value <= ranges[1])) {
1186
1198
  return true;
1187
1199
  }
1188
1200
  else {
@@ -1515,7 +1527,7 @@ var scorm12_regex = {
1515
1527
  CMISInteger: "^-?([0-9]+)$",
1516
1528
  CMIDecimal: "^-?([0-9]{0,3})(.[0-9]*)?$",
1517
1529
  CMIIdentifier: "^[\\u0021-\\u007E\\s]{0,255}$",
1518
- CMIFeedback: "^.{0,255}$",
1530
+ CMIFeedback: "^.*$",
1519
1531
  CMIIndex: "[._](\\d+).",
1520
1532
  CMIStatus: "^(passed|completed|failed|incomplete|browsed)$",
1521
1533
  CMIStatus2: "^(passed|completed|failed|incomplete|browsed|not attempted)$",
@@ -1692,7 +1704,7 @@ function getSecondsAsISODuration(seconds) {
1692
1704
  var duration = "P";
1693
1705
  var remainder = seconds;
1694
1706
  for (var designationsKey in designations) {
1695
- var current_seconds = designations[designationsKey];
1707
+ var current_seconds = designations[designationsKey] || 1;
1696
1708
  var value = Math.floor(remainder / current_seconds);
1697
1709
  remainder = remainder % current_seconds;
1698
1710
  if (countDecimals(remainder) > 2) {
@@ -1799,7 +1811,7 @@ function unflatten(data) {
1799
1811
  var m = regex.exec(p);
1800
1812
  while (m) {
1801
1813
  cur = cur[prop] || (cur[prop] = m[2] ? [] : {});
1802
- prop = m[2] || m[1];
1814
+ prop = m[2] || m[1] || "";
1803
1815
  m = regex.exec(p);
1804
1816
  }
1805
1817
  cur[prop] = data[p];
@@ -1811,7 +1823,7 @@ function countDecimals(num) {
1811
1823
  if (Math.floor(num) === num || String(num).indexOf(".") < 0)
1812
1824
  return 0;
1813
1825
  var parts = num.toString().split(".")[1];
1814
- return parts.length || 0;
1826
+ return (parts === null || parts === void 0 ? void 0 : parts.length) || 0;
1815
1827
  }
1816
1828
  function formatMessage(functionName, message, CMIElement) {
1817
1829
  var baseLength = 20;
@@ -2347,11 +2359,12 @@ var Scorm2004ValidationError = (function (_super) {
2347
2359
  (0,tslib_es6.__extends)(Scorm2004ValidationError, _super);
2348
2360
  function Scorm2004ValidationError(errorCode) {
2349
2361
  var _this = this;
2362
+ var _a, _b, _c, _d;
2350
2363
  if ({}.hasOwnProperty.call(scorm2004_errors, String(errorCode))) {
2351
- _this = _super.call(this, errorCode, scorm2004_errors[String(errorCode)].basicMessage, scorm2004_errors[String(errorCode)].detailMessage) || this;
2364
+ _this = _super.call(this, errorCode, ((_a = scorm2004_errors[String(errorCode)]) === null || _a === void 0 ? void 0 : _a.basicMessage) || "Unknown error", (_b = scorm2004_errors[String(errorCode)]) === null || _b === void 0 ? void 0 : _b.detailMessage) || this;
2352
2365
  }
2353
2366
  else {
2354
- _this = _super.call(this, 101, scorm2004_errors["101"].basicMessage, scorm2004_errors["101"].detailMessage) || this;
2367
+ _this = _super.call(this, 101, ((_c = scorm2004_errors["101"]) === null || _c === void 0 ? void 0 : _c.basicMessage) || "General error", (_d = scorm2004_errors["101"]) === null || _d === void 0 ? void 0 : _d.detailMessage) || this;
2355
2368
  }
2356
2369
  return _this;
2357
2370
  }
@@ -2767,6 +2780,7 @@ var CMIInteractionsObject = (function (_super) {
2767
2780
  return this._learner_response;
2768
2781
  },
2769
2782
  set: function (learner_response) {
2783
+ var _a, _b, _c, _d;
2770
2784
  if (this.initialized && (this._type === "" || this._id === "")) {
2771
2785
  throw new Scorm2004ValidationError(error_codes.scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED);
2772
2786
  }
@@ -2784,14 +2798,14 @@ var CMIInteractionsObject = (function (_super) {
2784
2798
  var formatRegex = new RegExp(response_type.format);
2785
2799
  for (var i = 0; i < nodes.length; i++) {
2786
2800
  if (response_type === null || response_type === void 0 ? void 0 : response_type.delimiter2) {
2787
- var values = nodes[i].split(response_type.delimiter2);
2788
- if (values.length === 2) {
2789
- if (!values[0].match(formatRegex)) {
2801
+ var values = (_a = nodes[i]) === null || _a === void 0 ? void 0 : _a.split(response_type.delimiter2);
2802
+ if ((values === null || values === void 0 ? void 0 : values.length) === 2) {
2803
+ if (!((_b = values[0]) === null || _b === void 0 ? void 0 : _b.match(formatRegex))) {
2790
2804
  throw new Scorm2004ValidationError(error_codes.scorm2004_errors.TYPE_MISMATCH);
2791
2805
  }
2792
2806
  else {
2793
2807
  if (!response_type.format2 ||
2794
- !values[1].match(new RegExp(response_type.format2))) {
2808
+ !((_c = values[1]) === null || _c === void 0 ? void 0 : _c.match(new RegExp(response_type.format2)))) {
2795
2809
  throw new Scorm2004ValidationError(error_codes.scorm2004_errors.TYPE_MISMATCH);
2796
2810
  }
2797
2811
  }
@@ -2801,7 +2815,7 @@ var CMIInteractionsObject = (function (_super) {
2801
2815
  }
2802
2816
  }
2803
2817
  else {
2804
- if (!nodes[i].match(formatRegex)) {
2818
+ if (!((_d = nodes[i]) === null || _d === void 0 ? void 0 : _d.match(formatRegex))) {
2805
2819
  throw new Scorm2004ValidationError(error_codes.scorm2004_errors.TYPE_MISMATCH);
2806
2820
  }
2807
2821
  else {
@@ -4279,7 +4293,8 @@ var ADLNavRequestValid = (function (_super) {
4279
4293
  }
4280
4294
  for (var key in choice) {
4281
4295
  if ({}.hasOwnProperty.call(choice, key)) {
4282
- if (check2004ValidFormat(choice[key], regex.scorm2004_regex.NAVBoolean) &&
4296
+ if (choice[key] !== undefined &&
4297
+ check2004ValidFormat(choice[key], regex.scorm2004_regex.NAVBoolean) &&
4283
4298
  check2004ValidFormat(key, regex.scorm2004_regex.NAVTarget)) {
4284
4299
  this._choice[key] =
4285
4300
  enums.NAVBoolean[choice[key]];
@@ -4303,7 +4318,8 @@ var ADLNavRequestValid = (function (_super) {
4303
4318
  }
4304
4319
  for (var key in jump) {
4305
4320
  if ({}.hasOwnProperty.call(jump, key)) {
4306
- if (check2004ValidFormat(jump[key], regex.scorm2004_regex.NAVBoolean) &&
4321
+ if (jump[key] !== undefined &&
4322
+ check2004ValidFormat(jump[key], regex.scorm2004_regex.NAVBoolean) &&
4307
4323
  check2004ValidFormat(key, regex.scorm2004_regex.NAVTarget)) {
4308
4324
  this._jump[key] = enums.NAVBoolean[jump[key]];
4309
4325
  }
@@ -4456,19 +4472,19 @@ var Scorm2004Impl = (function (_super) {
4456
4472
  });
4457
4473
  };
4458
4474
  Scorm2004Impl.prototype.lmsGetValue = function (CMIElement) {
4459
- var _a;
4475
+ var _a, _b;
4460
4476
  var adlNavRequestRegex = "^adl\\.nav\\.request_valid\\.(choice|jump)\\.{target=\\S{0,}([a-zA-Z0-9-_]+)}$";
4461
4477
  if ((0,utilities.stringMatches)(CMIElement, adlNavRequestRegex)) {
4462
4478
  var matches = CMIElement.match(adlNavRequestRegex);
4463
4479
  if (matches) {
4464
4480
  var request = matches[1];
4465
- var target = matches[2].replace("{target=", "").replace("}", "");
4466
- if (request === "choice" || request === "jump") {
4481
+ var target = (_a = matches[2]) === null || _a === void 0 ? void 0 : _a.replace("{target=", "").replace("}", "");
4482
+ if (target && (request === "choice" || request === "jump")) {
4467
4483
  if (this.settings.scoItemIdValidator) {
4468
4484
  return String(this.settings.scoItemIdValidator(target));
4469
4485
  }
4470
4486
  if (this.settings.scoItemIds) {
4471
- return String((_a = this.settings.scoItemIds) === null || _a === void 0 ? void 0 : _a.includes(target));
4487
+ return String((_b = this.settings.scoItemIds) === null || _b === void 0 ? void 0 : _b.includes(target));
4472
4488
  }
4473
4489
  return String(request);
4474
4490
  }
@@ -4622,8 +4638,9 @@ var Scorm2004Impl = (function (_super) {
4622
4638
  var interaction_count = interaction.correct_responses._count;
4623
4639
  this.checkDuplicateChoiceResponse(interaction, value);
4624
4640
  var response_type = CorrectResponses[interaction.type];
4625
- if (typeof response_type.limit === "undefined" ||
4626
- interaction_count <= response_type.limit) {
4641
+ if (typeof response_type !== "undefined" &&
4642
+ (typeof response_type.limit === "undefined" ||
4643
+ interaction_count <= response_type.limit)) {
4627
4644
  this.checkValidResponseType(response_type, value, interaction.type);
4628
4645
  if ((this.lastErrorCode === "0" &&
4629
4646
  (!response_type.duplicate ||
@@ -4644,14 +4661,17 @@ var Scorm2004Impl = (function (_super) {
4644
4661
  return this._commonGetCMIValue("GetValue", true, CMIElement);
4645
4662
  };
4646
4663
  Scorm2004Impl.prototype.getLmsErrorMessageDetails = function (errorNumber, detail) {
4664
+ var _a, _b;
4647
4665
  var basicMessage = "";
4648
4666
  var detailMessage = "";
4649
4667
  errorNumber = String(errorNumber);
4650
4668
  if (api_constants.scorm2004_constants.error_descriptions[errorNumber]) {
4651
4669
  basicMessage =
4652
- api_constants.scorm2004_constants.error_descriptions[errorNumber].basicMessage;
4670
+ ((_a = api_constants.scorm2004_constants.error_descriptions[errorNumber]) === null || _a === void 0 ? void 0 : _a.basicMessage) ||
4671
+ "Unknown Error";
4653
4672
  detailMessage =
4654
- api_constants.scorm2004_constants.error_descriptions[errorNumber].detailMessage;
4673
+ ((_b = api_constants.scorm2004_constants.error_descriptions[errorNumber]) === null || _b === void 0 ? void 0 : _b.detailMessage) ||
4674
+ "";
4655
4675
  }
4656
4676
  return detail ? detailMessage : basicMessage;
4657
4677
  };
@@ -4667,7 +4687,7 @@ var Scorm2004Impl = (function (_super) {
4667
4687
  };
4668
4688
  Scorm2004Impl.prototype.checkCorrectResponseValue = function (interaction_type, nodes, value) {
4669
4689
  var response = CorrectResponses[interaction_type];
4670
- var formatRegex = new RegExp(response.format);
4690
+ var formatRegex = new RegExp((response === null || response === void 0 ? void 0 : response.format) || ".*");
4671
4691
  for (var i = 0; i < nodes.length && this.lastErrorCode === "0"; i++) {
4672
4692
  if (interaction_type.match("^(fill-in|long-fill-in|matching|performance|sequencing)$")) {
4673
4693
  nodes[i] = this.removeCorrectResponsePrefixes(nodes[i]);
@@ -4703,7 +4723,7 @@ var Scorm2004Impl = (function (_super) {
4703
4723
  }
4704
4724
  }
4705
4725
  else {
4706
- if (nodes[i] !== "" && response.unique) {
4726
+ if (nodes[i] !== "" && (response === null || response === void 0 ? void 0 : response.unique)) {
4707
4727
  for (var j = 0; j < i && this.lastErrorCode === "0"; j++) {
4708
4728
  if (nodes[i] === nodes[j]) {
4709
4729
  this.throwSCORMError(error_codes.scorm2004_errors.TYPE_MISMATCH);
@@ -4716,6 +4736,7 @@ var Scorm2004Impl = (function (_super) {
4716
4736
  }
4717
4737
  };
4718
4738
  Scorm2004Impl.prototype.removeCorrectResponsePrefixes = function (node) {
4739
+ var _a;
4719
4740
  var seenOrder = false;
4720
4741
  var seenCase = false;
4721
4742
  var seenLang = false;
@@ -4753,7 +4774,7 @@ var Scorm2004Impl = (function (_super) {
4753
4774
  seenOrder = true;
4754
4775
  break;
4755
4776
  }
4756
- node = node.substring(matches[1].length);
4777
+ node = node.substring(((_a = matches[1]) === null || _a === void 0 ? void 0 : _a.length) || 0);
4757
4778
  matches = node.match(prefixRegex);
4758
4779
  }
4759
4780
  return node;