scorm-again 3.1.4 → 3.1.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/esm/scorm-again.js +1080 -187
- 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 +14 -3
- 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 +1080 -187
- 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 +1406 -410
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +1 -1
- package/dist/scorm12.js +54 -14
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +1 -1
- package/dist/scorm2004.js +1401 -405
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +1 -1
- package/dist/types/Scorm2004API.d.ts +11 -0
- package/dist/types/cmi/scorm2004/sequencing/activity.d.ts +47 -5
- package/dist/types/cmi/scorm2004/sequencing/handlers/rte_data_transfer.d.ts +1 -0
- package/dist/types/cmi/scorm2004/sequencing/objectives/global_objective_synchronizer.d.ts +17 -1
- package/dist/types/cmi/scorm2004/sequencing/sequencing_rules.d.ts +6 -1
- package/dist/types/cmi/scorm2004/sequencing/traversal/flow_traversal_service.d.ts +5 -2
- package/dist/types/constants/regex.d.ts +1 -0
- package/dist/types/types/api_types.d.ts +12 -0
- package/dist/types/types/sequencing_types.d.ts +7 -0
- package/dist/types/utilities/core.d.ts +4 -1
- package/package.json +1 -1
package/dist/esm/scorm2004.js
CHANGED
|
@@ -139,12 +139,18 @@ function stringMatches(str, tester) {
|
|
|
139
139
|
}
|
|
140
140
|
return new RegExp(tester).test(str);
|
|
141
141
|
}
|
|
142
|
-
function memoize(fn, keyFn) {
|
|
142
|
+
function memoize(fn, keyFn, options) {
|
|
143
143
|
const cache = /* @__PURE__ */ new Map();
|
|
144
144
|
return ((...args) => {
|
|
145
145
|
const key = keyFn ? keyFn(...args) : JSON.stringify(args);
|
|
146
|
+
if (options?.maxKeyLength !== void 0 && key.length > options.maxKeyLength) {
|
|
147
|
+
return fn(...args);
|
|
148
|
+
}
|
|
146
149
|
return cache.has(key) ? cache.get(key) : (() => {
|
|
147
150
|
const result = fn(...args);
|
|
151
|
+
if (options?.maxEntries !== void 0 && cache.size >= options.maxEntries) {
|
|
152
|
+
cache.delete(cache.keys().next().value);
|
|
153
|
+
}
|
|
148
154
|
cache.set(key, result);
|
|
149
155
|
return result;
|
|
150
156
|
})();
|
|
@@ -896,6 +902,8 @@ const scorm2004_regex = {
|
|
|
896
902
|
CMILang: "^([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?$|^$",
|
|
897
903
|
/** CMILangString250 - String with optional language tag, max 250 chars (RTE C.1.3) */
|
|
898
904
|
CMILangString250: "^({lang=([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?})?((?!{.*$).{0,250}$)?$",
|
|
905
|
+
/** CMILangString - Optional language tag, no length cap; RTE C.1.3 SPM is a floor and is deliberately not enforced */
|
|
906
|
+
CMILangString: "^({lang=([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?})?((?!{.*$).*$)?$",
|
|
899
907
|
/** CMILangcr - Language tag pattern with content */
|
|
900
908
|
CMILangcr: "^(({lang=([a-zA-Z]{1,8}|i|x)?(-[a-zA-Z0-9-]{2,8})?}))(.*?)$",
|
|
901
909
|
/** CMILangString250cr - String with optional language tag (carriage return variant) */
|
|
@@ -1548,12 +1556,6 @@ const HIDE_LMS_UI_TOKENS = [
|
|
|
1548
1556
|
"suspendAll"
|
|
1549
1557
|
];
|
|
1550
1558
|
|
|
1551
|
-
var RuleConditionOperator = /* @__PURE__ */ ((RuleConditionOperator2) => {
|
|
1552
|
-
RuleConditionOperator2["NOT"] = "not";
|
|
1553
|
-
RuleConditionOperator2["AND"] = "and";
|
|
1554
|
-
RuleConditionOperator2["OR"] = "or";
|
|
1555
|
-
return RuleConditionOperator2;
|
|
1556
|
-
})(RuleConditionOperator || {});
|
|
1557
1559
|
var RuleActionType = /* @__PURE__ */ ((RuleActionType2) => {
|
|
1558
1560
|
RuleActionType2["SKIP"] = "skip";
|
|
1559
1561
|
RuleActionType2["DISABLED"] = "disabled";
|
|
@@ -1568,6 +1570,48 @@ var RuleActionType = /* @__PURE__ */ ((RuleActionType2) => {
|
|
|
1568
1570
|
RuleActionType2["EXIT"] = "exit";
|
|
1569
1571
|
return RuleActionType2;
|
|
1570
1572
|
})(RuleActionType || {});
|
|
1573
|
+
function kleeneNot(value) {
|
|
1574
|
+
if (value === "unknown") {
|
|
1575
|
+
return "unknown";
|
|
1576
|
+
}
|
|
1577
|
+
return !value;
|
|
1578
|
+
}
|
|
1579
|
+
function kleeneAnd(values) {
|
|
1580
|
+
let hasUnknown = false;
|
|
1581
|
+
for (const value of values) {
|
|
1582
|
+
if (value === false) {
|
|
1583
|
+
return false;
|
|
1584
|
+
}
|
|
1585
|
+
if (value === "unknown") {
|
|
1586
|
+
hasUnknown = true;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return hasUnknown ? "unknown" : true;
|
|
1590
|
+
}
|
|
1591
|
+
function kleeneOr(values) {
|
|
1592
|
+
let hasUnknown = false;
|
|
1593
|
+
for (const value of values) {
|
|
1594
|
+
if (value === true) {
|
|
1595
|
+
return true;
|
|
1596
|
+
}
|
|
1597
|
+
if (value === "unknown") {
|
|
1598
|
+
hasUnknown = true;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return hasUnknown ? "unknown" : false;
|
|
1602
|
+
}
|
|
1603
|
+
function combineRuleConditionResults(values, conditionCombination) {
|
|
1604
|
+
if (values.length === 0) {
|
|
1605
|
+
return false;
|
|
1606
|
+
}
|
|
1607
|
+
if (conditionCombination === "all" || conditionCombination === "and" /* AND */) {
|
|
1608
|
+
return kleeneAnd(values);
|
|
1609
|
+
}
|
|
1610
|
+
if (conditionCombination === "any" || conditionCombination === "or" /* OR */) {
|
|
1611
|
+
return kleeneOr(values);
|
|
1612
|
+
}
|
|
1613
|
+
return false;
|
|
1614
|
+
}
|
|
1571
1615
|
class RuleCondition extends BaseCMI {
|
|
1572
1616
|
_condition = "always" /* ALWAYS */;
|
|
1573
1617
|
_operator = null;
|
|
@@ -1673,51 +1717,72 @@ class RuleCondition extends BaseCMI {
|
|
|
1673
1717
|
/**
|
|
1674
1718
|
* Evaluate the condition for an activity
|
|
1675
1719
|
* @param {Activity} activity - The activity to evaluate the condition for
|
|
1676
|
-
* @return {
|
|
1720
|
+
* @return {RuleConditionEvaluation} - True, false, or unknown per SCORM 2004 4th Ed.
|
|
1677
1721
|
*/
|
|
1678
1722
|
evaluate(activity) {
|
|
1679
1723
|
let result;
|
|
1724
|
+
const hasReferencedObjective = this._referencedObjective !== null;
|
|
1680
1725
|
const referencedObjective = this.resolveReferencedObjective(activity);
|
|
1681
1726
|
switch (this._condition) {
|
|
1682
1727
|
case "satisfied" /* SATISFIED */:
|
|
1683
1728
|
case "objectiveSatisfied" /* OBJECTIVE_SATISFIED */:
|
|
1684
|
-
if (referencedObjective) {
|
|
1685
|
-
result =
|
|
1729
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1730
|
+
result = false;
|
|
1731
|
+
} else if (referencedObjective) {
|
|
1732
|
+
result = referencedObjective.satisfiedStatusKnown || referencedObjective.progressStatus ? referencedObjective.satisfiedStatus === true : "unknown";
|
|
1733
|
+
} else if (activity.objectiveSatisfiedStatusKnown) {
|
|
1734
|
+
result = activity.objectiveSatisfiedStatus === true;
|
|
1735
|
+
} else if (activity.successStatus !== SuccessStatus.UNKNOWN) {
|
|
1736
|
+
result = activity.successStatus === SuccessStatus.PASSED;
|
|
1686
1737
|
} else {
|
|
1687
|
-
result =
|
|
1738
|
+
result = "unknown";
|
|
1688
1739
|
}
|
|
1689
1740
|
break;
|
|
1690
1741
|
case "objectiveStatusKnown" /* OBJECTIVE_STATUS_KNOWN */:
|
|
1691
|
-
result = referencedObjective ? !!referencedObjective.
|
|
1742
|
+
result = hasReferencedObjective && !referencedObjective ? false : referencedObjective ? !!referencedObjective.satisfiedStatusKnown : !!activity.objectiveSatisfiedStatusKnown;
|
|
1692
1743
|
break;
|
|
1693
1744
|
case "objectiveMeasureKnown" /* OBJECTIVE_MEASURE_KNOWN */:
|
|
1694
|
-
result = referencedObjective ? !!referencedObjective.measureStatus : !!activity.objectiveMeasureStatus;
|
|
1745
|
+
result = hasReferencedObjective && !referencedObjective ? false : referencedObjective ? !!referencedObjective.measureStatus : !!activity.objectiveMeasureStatus;
|
|
1695
1746
|
break;
|
|
1696
1747
|
case "objectiveMeasureGreaterThan" /* OBJECTIVE_MEASURE_GREATER_THAN */: {
|
|
1748
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1749
|
+
result = false;
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1697
1752
|
const greaterThanValue = this._parameters.get("threshold") || 0;
|
|
1698
1753
|
const measureStatus = referencedObjective ? referencedObjective.measureStatus : activity.objectiveMeasureStatus;
|
|
1699
1754
|
const measureValue = referencedObjective ? referencedObjective.normalizedMeasure : activity.objectiveNormalizedMeasure;
|
|
1700
|
-
result =
|
|
1755
|
+
result = measureStatus ? measureValue > greaterThanValue : "unknown";
|
|
1701
1756
|
break;
|
|
1702
1757
|
}
|
|
1703
1758
|
case "objectiveMeasureLessThan" /* OBJECTIVE_MEASURE_LESS_THAN */: {
|
|
1759
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1760
|
+
result = false;
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1704
1763
|
const lessThanValue = this._parameters.get("threshold") || 0;
|
|
1705
1764
|
const measureStatus = referencedObjective ? referencedObjective.measureStatus : activity.objectiveMeasureStatus;
|
|
1706
1765
|
const measureValue = referencedObjective ? referencedObjective.normalizedMeasure : activity.objectiveNormalizedMeasure;
|
|
1707
|
-
result =
|
|
1766
|
+
result = measureStatus ? measureValue < lessThanValue : "unknown";
|
|
1708
1767
|
break;
|
|
1709
1768
|
}
|
|
1710
1769
|
case "completed" /* COMPLETED */:
|
|
1711
1770
|
case "activityCompleted" /* ACTIVITY_COMPLETED */:
|
|
1712
|
-
if (referencedObjective) {
|
|
1713
|
-
result =
|
|
1771
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1772
|
+
result = false;
|
|
1773
|
+
} else if (referencedObjective) {
|
|
1774
|
+
result = referencedObjective.completionStatus === CompletionStatus.UNKNOWN ? "unknown" : referencedObjective.completionStatus === CompletionStatus.COMPLETED;
|
|
1775
|
+
} else if (activity.completionStatus === CompletionStatus.UNKNOWN) {
|
|
1776
|
+
result = "unknown";
|
|
1714
1777
|
} else {
|
|
1715
|
-
result = activity.
|
|
1778
|
+
result = activity.completionStatus === CompletionStatus.COMPLETED;
|
|
1716
1779
|
}
|
|
1717
1780
|
break;
|
|
1718
1781
|
case "progressKnown" /* PROGRESS_KNOWN */:
|
|
1719
1782
|
case "activityProgressKnown" /* ACTIVITY_PROGRESS_KNOWN */:
|
|
1720
|
-
if (referencedObjective) {
|
|
1783
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1784
|
+
result = false;
|
|
1785
|
+
} else if (referencedObjective) {
|
|
1721
1786
|
result = referencedObjective.completionStatus !== CompletionStatus.UNKNOWN;
|
|
1722
1787
|
} else {
|
|
1723
1788
|
result = activity.completionStatus !== "unknown";
|
|
@@ -1746,7 +1811,7 @@ class RuleCondition extends BaseCMI {
|
|
|
1746
1811
|
break;
|
|
1747
1812
|
}
|
|
1748
1813
|
if (this._operator === "not" /* NOT */) {
|
|
1749
|
-
result =
|
|
1814
|
+
result = kleeneNot(result);
|
|
1750
1815
|
}
|
|
1751
1816
|
return result;
|
|
1752
1817
|
}
|
|
@@ -1953,15 +2018,10 @@ class SequencingRule extends BaseCMI {
|
|
|
1953
2018
|
* @return {boolean} - True if the rule conditions are met, false otherwise
|
|
1954
2019
|
*/
|
|
1955
2020
|
evaluate(activity) {
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
return this._conditions.every((condition) => condition.evaluate(activity));
|
|
1961
|
-
} else if (this._conditionCombination === "any" || this._conditionCombination === "or" /* OR */) {
|
|
1962
|
-
return this._conditions.some((condition) => condition.evaluate(activity));
|
|
1963
|
-
}
|
|
1964
|
-
return false;
|
|
2021
|
+
return combineRuleConditionResults(
|
|
2022
|
+
this._conditions.map((condition) => condition.evaluate(activity)),
|
|
2023
|
+
this._conditionCombination
|
|
2024
|
+
) === true;
|
|
1965
2025
|
}
|
|
1966
2026
|
/**
|
|
1967
2027
|
* toJSON for SequencingRule
|
|
@@ -2794,19 +2854,10 @@ class RuleEvaluationEngine {
|
|
|
2794
2854
|
* Evaluates individual sequencing rule conditions
|
|
2795
2855
|
* @param {Activity} activity - The activity to evaluate the rule for
|
|
2796
2856
|
* @param {SequencingRule} rule - The rule to evaluate
|
|
2797
|
-
* @return {boolean} - True
|
|
2857
|
+
* @return {boolean} - True only when the rule evaluates to definite true
|
|
2798
2858
|
*/
|
|
2799
2859
|
checkRuleSubprocess(activity, rule) {
|
|
2800
|
-
|
|
2801
|
-
return true;
|
|
2802
|
-
}
|
|
2803
|
-
const conditionCombination = rule.conditionCombination;
|
|
2804
|
-
if (conditionCombination === "all" || conditionCombination === RuleConditionOperator.AND) {
|
|
2805
|
-
return rule.conditions.every((condition) => condition.evaluate(activity));
|
|
2806
|
-
} else if (conditionCombination === "any" || conditionCombination === RuleConditionOperator.OR) {
|
|
2807
|
-
return rule.conditions.some((condition) => condition.evaluate(activity));
|
|
2808
|
-
}
|
|
2809
|
-
return false;
|
|
2860
|
+
return rule.evaluate(activity);
|
|
2810
2861
|
}
|
|
2811
2862
|
/**
|
|
2812
2863
|
* Exit Action Rules Subprocess (TB.2.1)
|
|
@@ -3678,16 +3729,21 @@ class FlowTraversalService {
|
|
|
3678
3729
|
* @param {Activity} fromActivity - The activity to flow from
|
|
3679
3730
|
* @param {FlowSubprocessMode} direction - The flow direction
|
|
3680
3731
|
* @return {FlowSubprocessResult} - Result containing the deliverable activity
|
|
3732
|
+
* @spec SN Book: SB.2.3 (Flow Subprocess) - preserves the SB.2.1 effective traversal direction for SB.2.2.
|
|
3733
|
+
* @spec SN Book: SB.2.2 (Flow Activity Traversal Subprocess) - evaluates candidates using the effective direction returned by SB.2.1.
|
|
3681
3734
|
*/
|
|
3682
3735
|
flowSubprocess(fromActivity, direction) {
|
|
3683
3736
|
let candidateActivity = fromActivity;
|
|
3684
3737
|
let firstIteration = true;
|
|
3685
3738
|
let lastCandidateHadNoChildren = false;
|
|
3739
|
+
let currentDirection = direction;
|
|
3740
|
+
let forwardOnlyCluster = null;
|
|
3686
3741
|
while (candidateActivity) {
|
|
3687
3742
|
const traversalResult = this.flowTreeTraversalSubprocess(
|
|
3688
3743
|
candidateActivity,
|
|
3689
|
-
|
|
3690
|
-
firstIteration
|
|
3744
|
+
currentDirection,
|
|
3745
|
+
firstIteration,
|
|
3746
|
+
forwardOnlyCluster
|
|
3691
3747
|
);
|
|
3692
3748
|
if (!traversalResult.activity) {
|
|
3693
3749
|
let exceptionCode = null;
|
|
@@ -3705,17 +3761,22 @@ class FlowTraversalService {
|
|
|
3705
3761
|
traversalResult.endSequencingSession
|
|
3706
3762
|
);
|
|
3707
3763
|
}
|
|
3764
|
+
const effectiveDirection = traversalResult.direction || currentDirection;
|
|
3765
|
+
if (traversalResult.forwardOnlyCluster) {
|
|
3766
|
+
forwardOnlyCluster = traversalResult.forwardOnlyCluster;
|
|
3767
|
+
}
|
|
3708
3768
|
lastCandidateHadNoChildren = traversalResult.activity.children.length > 0 && traversalResult.activity.getAvailableChildren().length === 0;
|
|
3709
3769
|
const deliverable = this.flowActivityTraversalSubprocess(
|
|
3710
3770
|
traversalResult.activity,
|
|
3711
|
-
|
|
3771
|
+
effectiveDirection === FlowSubprocessMode.FORWARD,
|
|
3712
3772
|
true,
|
|
3713
|
-
|
|
3773
|
+
effectiveDirection
|
|
3714
3774
|
);
|
|
3715
3775
|
if (deliverable) {
|
|
3716
3776
|
return new FlowSubprocessResult(deliverable, true, null, false);
|
|
3717
3777
|
}
|
|
3718
3778
|
candidateActivity = traversalResult.activity;
|
|
3779
|
+
currentDirection = effectiveDirection;
|
|
3719
3780
|
firstIteration = false;
|
|
3720
3781
|
}
|
|
3721
3782
|
return new FlowSubprocessResult(null, false, null, false);
|
|
@@ -3726,11 +3787,13 @@ class FlowTraversalService {
|
|
|
3726
3787
|
* @param {Activity} fromActivity - The activity to traverse from
|
|
3727
3788
|
* @param {FlowSubprocessMode} direction - The traversal direction
|
|
3728
3789
|
* @param {boolean} skipChildren - Whether to skip checking children
|
|
3790
|
+
* @param {Activity | null} forwardTraversalBoundary - Cluster boundary for an SB.2.1 forwardOnly direction reversal
|
|
3729
3791
|
* @return {FlowTreeTraversalResult} - The next activity and flags
|
|
3792
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - backward traversal into a forwardOnly cluster selects the first available child and reverses traversal direction to Forward.
|
|
3730
3793
|
*/
|
|
3731
|
-
flowTreeTraversalSubprocess(fromActivity, direction, skipChildren = false) {
|
|
3794
|
+
flowTreeTraversalSubprocess(fromActivity, direction, skipChildren = false, forwardTraversalBoundary = null) {
|
|
3732
3795
|
if (direction === FlowSubprocessMode.FORWARD) {
|
|
3733
|
-
return this.traverseForward(fromActivity, skipChildren);
|
|
3796
|
+
return this.traverseForward(fromActivity, skipChildren, forwardTraversalBoundary);
|
|
3734
3797
|
} else {
|
|
3735
3798
|
return this.traverseBackward(fromActivity);
|
|
3736
3799
|
}
|
|
@@ -3739,10 +3802,15 @@ class FlowTraversalService {
|
|
|
3739
3802
|
* Traverse forward in the activity tree
|
|
3740
3803
|
* @param {Activity} fromActivity - Starting activity
|
|
3741
3804
|
* @param {boolean} skipChildren - Whether to skip children
|
|
3805
|
+
* @param {Activity | null} forwardTraversalBoundary - Cluster boundary for an SB.2.1 forwardOnly direction reversal
|
|
3742
3806
|
* @return {FlowTreeTraversalResult}
|
|
3807
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - a reversed Forward traversal from a forwardOnly cluster remains within that cluster.
|
|
3743
3808
|
*/
|
|
3744
|
-
traverseForward(fromActivity, skipChildren) {
|
|
3745
|
-
if (
|
|
3809
|
+
traverseForward(fromActivity, skipChildren, forwardTraversalBoundary = null) {
|
|
3810
|
+
if (forwardTraversalBoundary && !this.isDescendantOfOrSelf(fromActivity, forwardTraversalBoundary)) {
|
|
3811
|
+
return { activity: null, endSequencingSession: false };
|
|
3812
|
+
}
|
|
3813
|
+
if (!forwardTraversalBoundary && skipChildren && this.isActivityLastOverall(fromActivity)) {
|
|
3746
3814
|
if (this.activityTree.root) {
|
|
3747
3815
|
this.terminateDescendentAttempts(this.activityTree.root);
|
|
3748
3816
|
}
|
|
@@ -3761,6 +3829,9 @@ class FlowTraversalService {
|
|
|
3761
3829
|
if (nextSibling) {
|
|
3762
3830
|
return { activity: nextSibling, endSequencingSession: false };
|
|
3763
3831
|
}
|
|
3832
|
+
if (forwardTraversalBoundary && (current === forwardTraversalBoundary || current.parent === forwardTraversalBoundary)) {
|
|
3833
|
+
return { activity: null, endSequencingSession: false };
|
|
3834
|
+
}
|
|
3764
3835
|
current = current.parent;
|
|
3765
3836
|
}
|
|
3766
3837
|
if (this.activityTree.root) {
|
|
@@ -3772,6 +3843,7 @@ class FlowTraversalService {
|
|
|
3772
3843
|
* Traverse backward in the activity tree
|
|
3773
3844
|
* @param {Activity} fromActivity - Starting activity
|
|
3774
3845
|
* @return {FlowTreeTraversalResult}
|
|
3846
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - backward traversal into a forwardOnly cluster selects the first available child and reverses traversal direction to Forward.
|
|
3775
3847
|
*/
|
|
3776
3848
|
traverseBackward(fromActivity) {
|
|
3777
3849
|
if (fromActivity.parent && fromActivity.parent.sequencingControls.forwardOnly) {
|
|
@@ -3779,10 +3851,7 @@ class FlowTraversalService {
|
|
|
3779
3851
|
}
|
|
3780
3852
|
const previousSibling = this.activityTree.getPreviousSibling(fromActivity);
|
|
3781
3853
|
if (previousSibling) {
|
|
3782
|
-
return
|
|
3783
|
-
activity: this.getLastDescendant(previousSibling),
|
|
3784
|
-
endSequencingSession: false
|
|
3785
|
-
};
|
|
3854
|
+
return this.getBackwardTraversalEntry(previousSibling);
|
|
3786
3855
|
}
|
|
3787
3856
|
let current = fromActivity;
|
|
3788
3857
|
let ancestorIterations = 0;
|
|
@@ -3793,38 +3862,64 @@ class FlowTraversalService {
|
|
|
3793
3862
|
}
|
|
3794
3863
|
const parentPreviousSibling = this.activityTree.getPreviousSibling(current.parent);
|
|
3795
3864
|
if (parentPreviousSibling) {
|
|
3796
|
-
return
|
|
3797
|
-
activity: this.getLastDescendant(parentPreviousSibling),
|
|
3798
|
-
endSequencingSession: false
|
|
3799
|
-
};
|
|
3865
|
+
return this.getBackwardTraversalEntry(parentPreviousSibling);
|
|
3800
3866
|
}
|
|
3801
3867
|
current = current.parent;
|
|
3802
3868
|
}
|
|
3803
3869
|
return { activity: null, endSequencingSession: false };
|
|
3804
3870
|
}
|
|
3805
3871
|
/**
|
|
3806
|
-
* Get the
|
|
3872
|
+
* Get the activity entered by backward traversal.
|
|
3807
3873
|
* @param {Activity} activity - The activity
|
|
3808
|
-
* @return {
|
|
3874
|
+
* @return {FlowTreeTraversalResult} - The entered activity and effective direction
|
|
3875
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - entering a forwardOnly cluster while moving Backward uses the first available child and changes direction to Forward.
|
|
3809
3876
|
*/
|
|
3810
|
-
|
|
3811
|
-
let
|
|
3877
|
+
getBackwardTraversalEntry(activity) {
|
|
3878
|
+
let enteredActivity = activity;
|
|
3812
3879
|
let iterations = 0;
|
|
3813
3880
|
const maxIterations = 1e4;
|
|
3814
3881
|
while (true) {
|
|
3815
3882
|
if (++iterations > maxIterations) {
|
|
3816
|
-
throw new Error("Infinite loop detected while getting
|
|
3883
|
+
throw new Error("Infinite loop detected while getting backward traversal entry");
|
|
3817
3884
|
}
|
|
3818
|
-
this.ensureSelectionAndRandomization(
|
|
3819
|
-
const children =
|
|
3885
|
+
this.ensureSelectionAndRandomization(enteredActivity);
|
|
3886
|
+
const children = enteredActivity.getAvailableChildren();
|
|
3820
3887
|
if (children.length === 0) {
|
|
3821
3888
|
break;
|
|
3822
3889
|
}
|
|
3890
|
+
if (enteredActivity.sequencingControls.forwardOnly) {
|
|
3891
|
+
return {
|
|
3892
|
+
activity: children[0] || null,
|
|
3893
|
+
endSequencingSession: false,
|
|
3894
|
+
direction: FlowSubprocessMode.FORWARD,
|
|
3895
|
+
forwardOnlyCluster: enteredActivity
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3823
3898
|
const lastChild = children[children.length - 1];
|
|
3824
3899
|
if (!lastChild) break;
|
|
3825
|
-
|
|
3900
|
+
enteredActivity = lastChild;
|
|
3826
3901
|
}
|
|
3827
|
-
return
|
|
3902
|
+
return {
|
|
3903
|
+
activity: enteredActivity,
|
|
3904
|
+
endSequencingSession: false
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3907
|
+
/**
|
|
3908
|
+
* Check whether an activity is the same as or beneath an ancestor.
|
|
3909
|
+
* @param {Activity} activity - The activity to check
|
|
3910
|
+
* @param {Activity} ancestor - The expected ancestor
|
|
3911
|
+
* @return {boolean} - True when activity is within ancestor
|
|
3912
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - bounds Forward traversal after a forwardOnly direction reversal to the entered cluster.
|
|
3913
|
+
*/
|
|
3914
|
+
isDescendantOfOrSelf(activity, ancestor) {
|
|
3915
|
+
let current = activity;
|
|
3916
|
+
while (current) {
|
|
3917
|
+
if (current === ancestor) {
|
|
3918
|
+
return true;
|
|
3919
|
+
}
|
|
3920
|
+
current = current.parent;
|
|
3921
|
+
}
|
|
3922
|
+
return false;
|
|
3828
3923
|
}
|
|
3829
3924
|
/**
|
|
3830
3925
|
* Flow Activity Traversal Subprocess (SB.2.2)
|
|
@@ -6374,7 +6469,9 @@ const checkValidFormat = memoize(
|
|
|
6374
6469
|
(CMIElement, value, regexPattern, errorCode, _errorClass, allowEmptyString) => {
|
|
6375
6470
|
const valueKey = typeof value === "string" ? value : `[${typeof value}]`;
|
|
6376
6471
|
return `${CMIElement}:${valueKey}:${regexPattern}:${errorCode}:${allowEmptyString || false}`;
|
|
6377
|
-
}
|
|
6472
|
+
},
|
|
6473
|
+
// Normal capped CMI values and regexes fit within 2000 characters; large uncapped values bypass caching.
|
|
6474
|
+
{ maxEntries: 1e3, maxKeyLength: 2e3 }
|
|
6378
6475
|
);
|
|
6379
6476
|
const checkValidRange = memoize(
|
|
6380
6477
|
(CMIElement, value, rangePattern, errorCode, errorClass) => {
|
|
@@ -6397,7 +6494,8 @@ const checkValidRange = memoize(
|
|
|
6397
6494
|
},
|
|
6398
6495
|
// Custom key function that excludes the error class from the cache key
|
|
6399
6496
|
// since it can't be stringified and doesn't affect the validation result
|
|
6400
|
-
(CMIElement, value, rangePattern, errorCode, _errorClass) => `${CMIElement}:${value}:${rangePattern}:${errorCode}
|
|
6497
|
+
(CMIElement, value, rangePattern, errorCode, _errorClass) => `${CMIElement}:${value}:${rangePattern}:${errorCode}`,
|
|
6498
|
+
{ maxEntries: 1e3 }
|
|
6401
6499
|
);
|
|
6402
6500
|
|
|
6403
6501
|
function check2004ValidFormat(CMIElement, value, regexPattern, allowEmptyString) {
|
|
@@ -6906,6 +7004,12 @@ class ActivityObjective {
|
|
|
6906
7004
|
// objectives. It serves as a validity gate for other synced properties.
|
|
6907
7005
|
_measureStatus = false;
|
|
6908
7006
|
_normalizedMeasure = 0;
|
|
7007
|
+
_rawScore = "";
|
|
7008
|
+
_rawScoreKnown = false;
|
|
7009
|
+
_minScore = "";
|
|
7010
|
+
_minScoreKnown = false;
|
|
7011
|
+
_maxScore = "";
|
|
7012
|
+
_maxScoreKnown = false;
|
|
6909
7013
|
_progressMeasure = 0;
|
|
6910
7014
|
_progressMeasureStatus = false;
|
|
6911
7015
|
_completionStatus = CompletionStatus.UNKNOWN;
|
|
@@ -6915,6 +7019,9 @@ class ActivityObjective {
|
|
|
6915
7019
|
_normalizedMeasureDirty = false;
|
|
6916
7020
|
_completionStatusDirty = false;
|
|
6917
7021
|
_progressMeasureDirty = false;
|
|
7022
|
+
_rawScoreDirty = false;
|
|
7023
|
+
_minScoreDirty = false;
|
|
7024
|
+
_maxScoreDirty = false;
|
|
6918
7025
|
constructor(id, options = {}) {
|
|
6919
7026
|
this._id = id;
|
|
6920
7027
|
this._description = options.description ?? null;
|
|
@@ -6960,6 +7067,8 @@ class ActivityObjective {
|
|
|
6960
7067
|
if (this._satisfiedStatus !== value) {
|
|
6961
7068
|
this._satisfiedStatus = value;
|
|
6962
7069
|
this._satisfiedStatusDirty = true;
|
|
7070
|
+
this._satisfiedStatusKnown = true;
|
|
7071
|
+
this._progressStatus = true;
|
|
6963
7072
|
}
|
|
6964
7073
|
}
|
|
6965
7074
|
get satisfiedStatusKnown() {
|
|
@@ -6983,6 +7092,114 @@ class ActivityObjective {
|
|
|
6983
7092
|
this._normalizedMeasureDirty = true;
|
|
6984
7093
|
}
|
|
6985
7094
|
}
|
|
7095
|
+
/**
|
|
7096
|
+
* Return the known raw score value held for ADLSEQ objective score mapping.
|
|
7097
|
+
*
|
|
7098
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw score mapInfo
|
|
7099
|
+
*/
|
|
7100
|
+
get rawScore() {
|
|
7101
|
+
return this._rawScore;
|
|
7102
|
+
}
|
|
7103
|
+
/**
|
|
7104
|
+
* Store the RTE raw score associated with this objective.
|
|
7105
|
+
*
|
|
7106
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - raw score mapInfo
|
|
7107
|
+
*/
|
|
7108
|
+
set rawScore(value) {
|
|
7109
|
+
if (this._rawScore !== value) {
|
|
7110
|
+
this._rawScore = value;
|
|
7111
|
+
this._rawScoreDirty = true;
|
|
7112
|
+
}
|
|
7113
|
+
this._rawScoreKnown = value !== "";
|
|
7114
|
+
}
|
|
7115
|
+
/**
|
|
7116
|
+
* Return whether this objective's raw score is known.
|
|
7117
|
+
*
|
|
7118
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw score known state
|
|
7119
|
+
*/
|
|
7120
|
+
get rawScoreKnown() {
|
|
7121
|
+
return this._rawScoreKnown;
|
|
7122
|
+
}
|
|
7123
|
+
/**
|
|
7124
|
+
* Set whether this objective's raw score is known.
|
|
7125
|
+
*
|
|
7126
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw score known state
|
|
7127
|
+
*/
|
|
7128
|
+
set rawScoreKnown(value) {
|
|
7129
|
+
this._rawScoreKnown = value;
|
|
7130
|
+
}
|
|
7131
|
+
/**
|
|
7132
|
+
* Return the known minimum score value held for ADLSEQ objective score mapping.
|
|
7133
|
+
*
|
|
7134
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - min score mapInfo
|
|
7135
|
+
*/
|
|
7136
|
+
get minScore() {
|
|
7137
|
+
return this._minScore;
|
|
7138
|
+
}
|
|
7139
|
+
/**
|
|
7140
|
+
* Store the RTE minimum score associated with this objective.
|
|
7141
|
+
*
|
|
7142
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - min score mapInfo
|
|
7143
|
+
*/
|
|
7144
|
+
set minScore(value) {
|
|
7145
|
+
if (this._minScore !== value) {
|
|
7146
|
+
this._minScore = value;
|
|
7147
|
+
this._minScoreDirty = true;
|
|
7148
|
+
}
|
|
7149
|
+
this._minScoreKnown = value !== "";
|
|
7150
|
+
}
|
|
7151
|
+
/**
|
|
7152
|
+
* Return whether this objective's minimum score is known.
|
|
7153
|
+
*
|
|
7154
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - min score known state
|
|
7155
|
+
*/
|
|
7156
|
+
get minScoreKnown() {
|
|
7157
|
+
return this._minScoreKnown;
|
|
7158
|
+
}
|
|
7159
|
+
/**
|
|
7160
|
+
* Set whether this objective's minimum score is known.
|
|
7161
|
+
*
|
|
7162
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - min score known state
|
|
7163
|
+
*/
|
|
7164
|
+
set minScoreKnown(value) {
|
|
7165
|
+
this._minScoreKnown = value;
|
|
7166
|
+
}
|
|
7167
|
+
/**
|
|
7168
|
+
* Return the known maximum score value held for ADLSEQ objective score mapping.
|
|
7169
|
+
*
|
|
7170
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - max score mapInfo
|
|
7171
|
+
*/
|
|
7172
|
+
get maxScore() {
|
|
7173
|
+
return this._maxScore;
|
|
7174
|
+
}
|
|
7175
|
+
/**
|
|
7176
|
+
* Store the RTE maximum score associated with this objective.
|
|
7177
|
+
*
|
|
7178
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - max score mapInfo
|
|
7179
|
+
*/
|
|
7180
|
+
set maxScore(value) {
|
|
7181
|
+
if (this._maxScore !== value) {
|
|
7182
|
+
this._maxScore = value;
|
|
7183
|
+
this._maxScoreDirty = true;
|
|
7184
|
+
}
|
|
7185
|
+
this._maxScoreKnown = value !== "";
|
|
7186
|
+
}
|
|
7187
|
+
/**
|
|
7188
|
+
* Return whether this objective's maximum score is known.
|
|
7189
|
+
*
|
|
7190
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - max score known state
|
|
7191
|
+
*/
|
|
7192
|
+
get maxScoreKnown() {
|
|
7193
|
+
return this._maxScoreKnown;
|
|
7194
|
+
}
|
|
7195
|
+
/**
|
|
7196
|
+
* Set whether this objective's maximum score is known.
|
|
7197
|
+
*
|
|
7198
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - max score known state
|
|
7199
|
+
*/
|
|
7200
|
+
set maxScoreKnown(value) {
|
|
7201
|
+
this._maxScoreKnown = value;
|
|
7202
|
+
}
|
|
6986
7203
|
get progressMeasure() {
|
|
6987
7204
|
return this._progressMeasure;
|
|
6988
7205
|
}
|
|
@@ -7013,6 +7230,11 @@ class ActivityObjective {
|
|
|
7013
7230
|
set progressStatus(value) {
|
|
7014
7231
|
this._progressStatus = value;
|
|
7015
7232
|
}
|
|
7233
|
+
/**
|
|
7234
|
+
* Report whether a local objective field has changed since the last global write.
|
|
7235
|
+
*
|
|
7236
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - write maps use known local objective data
|
|
7237
|
+
*/
|
|
7016
7238
|
isDirty(property) {
|
|
7017
7239
|
switch (property) {
|
|
7018
7240
|
case "satisfiedStatus":
|
|
@@ -7023,8 +7245,19 @@ class ActivityObjective {
|
|
|
7023
7245
|
return this._completionStatusDirty;
|
|
7024
7246
|
case "progressMeasure":
|
|
7025
7247
|
return this._progressMeasureDirty;
|
|
7248
|
+
case "rawScore":
|
|
7249
|
+
return this._rawScoreDirty;
|
|
7250
|
+
case "minScore":
|
|
7251
|
+
return this._minScoreDirty;
|
|
7252
|
+
case "maxScore":
|
|
7253
|
+
return this._maxScoreDirty;
|
|
7026
7254
|
}
|
|
7027
7255
|
}
|
|
7256
|
+
/**
|
|
7257
|
+
* Clear a local objective dirty flag after a successful global write.
|
|
7258
|
+
*
|
|
7259
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - write maps update global objective state
|
|
7260
|
+
*/
|
|
7028
7261
|
clearDirty(property) {
|
|
7029
7262
|
switch (property) {
|
|
7030
7263
|
case "satisfiedStatus":
|
|
@@ -7039,13 +7272,31 @@ class ActivityObjective {
|
|
|
7039
7272
|
case "progressMeasure":
|
|
7040
7273
|
this._progressMeasureDirty = false;
|
|
7041
7274
|
break;
|
|
7275
|
+
case "rawScore":
|
|
7276
|
+
this._rawScoreDirty = false;
|
|
7277
|
+
break;
|
|
7278
|
+
case "minScore":
|
|
7279
|
+
this._minScoreDirty = false;
|
|
7280
|
+
break;
|
|
7281
|
+
case "maxScore":
|
|
7282
|
+
this._maxScoreDirty = false;
|
|
7283
|
+
break;
|
|
7042
7284
|
}
|
|
7043
7285
|
}
|
|
7286
|
+
/**
|
|
7287
|
+
* Clear all write-map dirty flags for this objective.
|
|
7288
|
+
*
|
|
7289
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - objective mapInfo writes are field-specific
|
|
7290
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score mapInfo writes are field-specific
|
|
7291
|
+
*/
|
|
7044
7292
|
clearAllDirty() {
|
|
7045
7293
|
this._satisfiedStatusDirty = false;
|
|
7046
7294
|
this._normalizedMeasureDirty = false;
|
|
7047
7295
|
this._completionStatusDirty = false;
|
|
7048
7296
|
this._progressMeasureDirty = false;
|
|
7297
|
+
this._rawScoreDirty = false;
|
|
7298
|
+
this._minScoreDirty = false;
|
|
7299
|
+
this._maxScoreDirty = false;
|
|
7049
7300
|
}
|
|
7050
7301
|
/**
|
|
7051
7302
|
* Initialize objective values from CMI data transfer
|
|
@@ -7055,6 +7306,8 @@ class ActivityObjective {
|
|
|
7055
7306
|
* @param satisfiedStatus - The satisfied status from CMI
|
|
7056
7307
|
* @param normalizedMeasure - The normalized measure from CMI
|
|
7057
7308
|
* @param measureStatus - Whether measure is valid
|
|
7309
|
+
*
|
|
7310
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - objective satisfaction and measure transfer
|
|
7058
7311
|
*/
|
|
7059
7312
|
initializeFromCMI(satisfiedStatus, normalizedMeasure, measureStatus) {
|
|
7060
7313
|
this._satisfiedStatus = satisfiedStatus;
|
|
@@ -7063,17 +7316,93 @@ class ActivityObjective {
|
|
|
7063
7316
|
this._normalizedMeasureDirty = true;
|
|
7064
7317
|
this._measureStatus = measureStatus;
|
|
7065
7318
|
}
|
|
7319
|
+
/**
|
|
7320
|
+
* Initialize raw/min/max objective score values from RTE data transfer.
|
|
7321
|
+
*
|
|
7322
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - objective score transfer
|
|
7323
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score mapInfo writes
|
|
7324
|
+
*/
|
|
7325
|
+
initializeScoreFromCMI(score) {
|
|
7326
|
+
if (score.rawScore !== void 0 && score.rawScore !== "") {
|
|
7327
|
+
this._rawScore = score.rawScore;
|
|
7328
|
+
this._rawScoreKnown = true;
|
|
7329
|
+
this._rawScoreDirty = true;
|
|
7330
|
+
}
|
|
7331
|
+
if (score.minScore !== void 0 && score.minScore !== "") {
|
|
7332
|
+
this._minScore = score.minScore;
|
|
7333
|
+
this._minScoreKnown = true;
|
|
7334
|
+
this._minScoreDirty = true;
|
|
7335
|
+
}
|
|
7336
|
+
if (score.maxScore !== void 0 && score.maxScore !== "") {
|
|
7337
|
+
this._maxScore = score.maxScore;
|
|
7338
|
+
this._maxScoreKnown = true;
|
|
7339
|
+
this._maxScoreDirty = true;
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
/**
|
|
7343
|
+
* Apply read-mapped global objective state without marking the values dirty.
|
|
7344
|
+
*
|
|
7345
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps provide access to global objective state
|
|
7346
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score read maps are access-only
|
|
7347
|
+
*/
|
|
7348
|
+
applyReadMappedState(state) {
|
|
7349
|
+
if (state.satisfiedStatus !== void 0) {
|
|
7350
|
+
this._satisfiedStatus = state.satisfiedStatus;
|
|
7351
|
+
this._satisfiedStatusKnown = true;
|
|
7352
|
+
this._progressStatus = true;
|
|
7353
|
+
}
|
|
7354
|
+
if (state.normalizedMeasure !== void 0) {
|
|
7355
|
+
this._normalizedMeasure = state.normalizedMeasure;
|
|
7356
|
+
this._measureStatus = true;
|
|
7357
|
+
}
|
|
7358
|
+
if (state.completionStatus !== void 0) {
|
|
7359
|
+
this._completionStatus = state.completionStatus;
|
|
7360
|
+
}
|
|
7361
|
+
if (state.progressMeasure !== void 0) {
|
|
7362
|
+
this._progressMeasure = state.progressMeasure;
|
|
7363
|
+
this._progressMeasureStatus = true;
|
|
7364
|
+
}
|
|
7365
|
+
if (state.rawScore !== void 0) {
|
|
7366
|
+
this._rawScore = state.rawScore;
|
|
7367
|
+
this._rawScoreKnown = true;
|
|
7368
|
+
}
|
|
7369
|
+
if (state.minScore !== void 0) {
|
|
7370
|
+
this._minScore = state.minScore;
|
|
7371
|
+
this._minScoreKnown = true;
|
|
7372
|
+
}
|
|
7373
|
+
if (state.maxScore !== void 0) {
|
|
7374
|
+
this._maxScore = state.maxScore;
|
|
7375
|
+
this._maxScoreKnown = true;
|
|
7376
|
+
}
|
|
7377
|
+
}
|
|
7378
|
+
/**
|
|
7379
|
+
* Reset local objective state for a fresh activity attempt.
|
|
7380
|
+
*
|
|
7381
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - unknown objective state before tracking data exists
|
|
7382
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score map fields are unknown until transferred or read
|
|
7383
|
+
*/
|
|
7066
7384
|
resetState() {
|
|
7067
7385
|
this._satisfiedStatus = false;
|
|
7068
7386
|
this._satisfiedStatusKnown = false;
|
|
7069
7387
|
this._measureStatus = false;
|
|
7070
7388
|
this._normalizedMeasure = 0;
|
|
7389
|
+
this._rawScore = "";
|
|
7390
|
+
this._rawScoreKnown = false;
|
|
7391
|
+
this._minScore = "";
|
|
7392
|
+
this._minScoreKnown = false;
|
|
7393
|
+
this._maxScore = "";
|
|
7394
|
+
this._maxScoreKnown = false;
|
|
7071
7395
|
this._progressMeasure = 0;
|
|
7072
7396
|
this._progressMeasureStatus = false;
|
|
7073
7397
|
this._completionStatus = CompletionStatus.UNKNOWN;
|
|
7074
7398
|
this._progressStatus = false;
|
|
7075
7399
|
this.clearAllDirty();
|
|
7076
7400
|
}
|
|
7401
|
+
/**
|
|
7402
|
+
* Copy primary activity objective state back into the primary objective model.
|
|
7403
|
+
*
|
|
7404
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - primary objective state is available to sequencing
|
|
7405
|
+
*/
|
|
7077
7406
|
updateFromActivity(activity) {
|
|
7078
7407
|
if (this._satisfiedStatus !== activity.objectiveSatisfiedStatus) {
|
|
7079
7408
|
this._satisfiedStatus = activity.objectiveSatisfiedStatus;
|
|
@@ -7095,6 +7424,11 @@ class ActivityObjective {
|
|
|
7095
7424
|
this._completionStatusDirty = true;
|
|
7096
7425
|
}
|
|
7097
7426
|
}
|
|
7427
|
+
/**
|
|
7428
|
+
* Apply primary objective state to the owning activity for sequencing rules and rollup.
|
|
7429
|
+
*
|
|
7430
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - primary objective contributes activity state
|
|
7431
|
+
*/
|
|
7098
7432
|
applyToActivity(activity) {
|
|
7099
7433
|
if (!this._isPrimary) {
|
|
7100
7434
|
return;
|
|
@@ -7105,7 +7439,8 @@ class ActivityObjective {
|
|
|
7105
7439
|
this._normalizedMeasure,
|
|
7106
7440
|
this._progressMeasure,
|
|
7107
7441
|
this._progressMeasureStatus,
|
|
7108
|
-
this._completionStatus
|
|
7442
|
+
this._completionStatus,
|
|
7443
|
+
this._progressStatus || this._satisfiedStatusKnown
|
|
7109
7444
|
);
|
|
7110
7445
|
}
|
|
7111
7446
|
}
|
|
@@ -8175,7 +8510,7 @@ class Activity extends BaseCMI {
|
|
|
8175
8510
|
/**
|
|
8176
8511
|
* Setter for primary objective
|
|
8177
8512
|
* @param {ActivityObjective | null} objective
|
|
8178
|
-
|
|
8513
|
+
*/
|
|
8179
8514
|
set primaryObjective(objective) {
|
|
8180
8515
|
this._primaryObjective = objective;
|
|
8181
8516
|
if (this._primaryObjective) {
|
|
@@ -8197,7 +8532,7 @@ class Activity extends BaseCMI {
|
|
|
8197
8532
|
/**
|
|
8198
8533
|
* Replace objectives collection
|
|
8199
8534
|
* @param {ActivityObjective[]} objectives
|
|
8200
|
-
|
|
8535
|
+
*/
|
|
8201
8536
|
set objectives(objectives) {
|
|
8202
8537
|
this._objectives = [...objectives];
|
|
8203
8538
|
this.syncPrimaryObjectiveCollection();
|
|
@@ -8290,12 +8625,12 @@ class Activity extends BaseCMI {
|
|
|
8290
8625
|
this._objectiveNormalizedMeasureDirty = false;
|
|
8291
8626
|
this._objectiveMeasureStatusDirty = false;
|
|
8292
8627
|
}
|
|
8293
|
-
setPrimaryObjectiveState(satisfiedStatus, measureStatus, normalizedMeasure, progressMeasure, progressMeasureStatus, completionStatus) {
|
|
8628
|
+
setPrimaryObjectiveState(satisfiedStatus, measureStatus, normalizedMeasure, progressMeasure, progressMeasureStatus, completionStatus, objectiveProgressStatus = true) {
|
|
8294
8629
|
if (this._objectiveSatisfiedStatus !== satisfiedStatus) {
|
|
8295
8630
|
this._objectiveSatisfiedStatus = satisfiedStatus;
|
|
8296
8631
|
this._objectiveSatisfiedStatusDirty = true;
|
|
8297
8632
|
}
|
|
8298
|
-
this._objectiveSatisfiedStatusKnown =
|
|
8633
|
+
this._objectiveSatisfiedStatusKnown = objectiveProgressStatus;
|
|
8299
8634
|
if (this._objectiveMeasureStatus !== measureStatus) {
|
|
8300
8635
|
this._objectiveMeasureStatus = measureStatus;
|
|
8301
8636
|
this._objectiveMeasureStatusDirty = true;
|
|
@@ -8314,14 +8649,28 @@ class Activity extends BaseCMI {
|
|
|
8314
8649
|
this._primaryObjective.progressMeasure = progressMeasure;
|
|
8315
8650
|
this._primaryObjective.progressMeasureStatus = progressMeasureStatus;
|
|
8316
8651
|
this._primaryObjective.completionStatus = completionStatus;
|
|
8652
|
+
this._primaryObjective.satisfiedStatusKnown = objectiveProgressStatus;
|
|
8653
|
+
this._primaryObjective.progressStatus = objectiveProgressStatus;
|
|
8317
8654
|
}
|
|
8318
8655
|
}
|
|
8656
|
+
/**
|
|
8657
|
+
* Snapshot objective state for sequencing persistence.
|
|
8658
|
+
*
|
|
8659
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - objective state persists across attempts
|
|
8660
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score-map state persists with objective state
|
|
8661
|
+
*/
|
|
8319
8662
|
getObjectiveStateSnapshot() {
|
|
8320
8663
|
const primarySnapshot = this._primaryObjective ? {
|
|
8321
8664
|
id: this._primaryObjective.id,
|
|
8322
8665
|
satisfiedStatus: this.objectiveSatisfiedStatus,
|
|
8323
8666
|
measureStatus: this.objectiveMeasureStatus,
|
|
8324
8667
|
normalizedMeasure: this.objectiveNormalizedMeasure,
|
|
8668
|
+
rawScore: this._primaryObjective.rawScore,
|
|
8669
|
+
rawScoreKnown: this._primaryObjective.rawScoreKnown,
|
|
8670
|
+
minScore: this._primaryObjective.minScore,
|
|
8671
|
+
minScoreKnown: this._primaryObjective.minScoreKnown,
|
|
8672
|
+
maxScore: this._primaryObjective.maxScore,
|
|
8673
|
+
maxScoreKnown: this._primaryObjective.maxScoreKnown,
|
|
8325
8674
|
progressMeasure: this.progressMeasure ?? 0,
|
|
8326
8675
|
progressMeasureStatus: this.progressMeasureStatus,
|
|
8327
8676
|
progressStatus: this._primaryObjective.progressStatus,
|
|
@@ -8334,6 +8683,12 @@ class Activity extends BaseCMI {
|
|
|
8334
8683
|
satisfiedStatus: objective.satisfiedStatus,
|
|
8335
8684
|
measureStatus: objective.measureStatus,
|
|
8336
8685
|
normalizedMeasure: objective.normalizedMeasure,
|
|
8686
|
+
rawScore: objective.rawScore,
|
|
8687
|
+
rawScoreKnown: objective.rawScoreKnown,
|
|
8688
|
+
minScore: objective.minScore,
|
|
8689
|
+
minScoreKnown: objective.minScoreKnown,
|
|
8690
|
+
maxScore: objective.maxScore,
|
|
8691
|
+
maxScoreKnown: objective.maxScoreKnown,
|
|
8337
8692
|
progressMeasure: objective.progressMeasure,
|
|
8338
8693
|
progressMeasureStatus: objective.progressMeasureStatus,
|
|
8339
8694
|
progressStatus: objective.progressStatus,
|
|
@@ -8346,6 +8701,12 @@ class Activity extends BaseCMI {
|
|
|
8346
8701
|
objectives: additionalSnapshots
|
|
8347
8702
|
};
|
|
8348
8703
|
}
|
|
8704
|
+
/**
|
|
8705
|
+
* Restore objective state from a sequencing persistence snapshot.
|
|
8706
|
+
*
|
|
8707
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - persisted objective state restores sequencing state
|
|
8708
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - persisted score-map state restores objective score fields
|
|
8709
|
+
*/
|
|
8349
8710
|
applyObjectiveStateSnapshot(snapshot) {
|
|
8350
8711
|
if (snapshot.primary) {
|
|
8351
8712
|
const primary = this.getObjectiveById(snapshot.primary.id);
|
|
@@ -8359,8 +8720,10 @@ class Activity extends BaseCMI {
|
|
|
8359
8720
|
state.normalizedMeasure,
|
|
8360
8721
|
state.progressMeasure,
|
|
8361
8722
|
state.progressMeasureStatus,
|
|
8362
|
-
state.completionStatus
|
|
8723
|
+
state.completionStatus,
|
|
8724
|
+
state.progressStatus
|
|
8363
8725
|
);
|
|
8726
|
+
this.applyObjectiveScoreSnapshot(primary.objective, state);
|
|
8364
8727
|
}
|
|
8365
8728
|
}
|
|
8366
8729
|
for (const state of snapshot.objectives) {
|
|
@@ -8373,11 +8736,30 @@ class Activity extends BaseCMI {
|
|
|
8373
8736
|
objective.progressMeasure = state.progressMeasure;
|
|
8374
8737
|
objective.progressMeasureStatus = state.progressMeasureStatus;
|
|
8375
8738
|
objective.completionStatus = state.completionStatus;
|
|
8739
|
+
this.applyObjectiveScoreSnapshot(objective, state);
|
|
8376
8740
|
objective.satisfiedByMeasure = state.satisfiedByMeasure ?? objective.satisfiedByMeasure;
|
|
8377
8741
|
objective.minNormalizedMeasure = state.minNormalizedMeasure !== void 0 ? state.minNormalizedMeasure : objective.minNormalizedMeasure;
|
|
8378
8742
|
}
|
|
8379
8743
|
}
|
|
8380
8744
|
}
|
|
8745
|
+
/**
|
|
8746
|
+
* Restore known raw/min/max score fields from an objective state snapshot.
|
|
8747
|
+
*
|
|
8748
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score known flags restore independently
|
|
8749
|
+
*/
|
|
8750
|
+
applyObjectiveScoreSnapshot(objective, state) {
|
|
8751
|
+
const scoreState = {};
|
|
8752
|
+
if (state.rawScoreKnown) {
|
|
8753
|
+
scoreState.rawScore = state.rawScore;
|
|
8754
|
+
}
|
|
8755
|
+
if (state.minScoreKnown) {
|
|
8756
|
+
scoreState.minScore = state.minScore;
|
|
8757
|
+
}
|
|
8758
|
+
if (state.maxScoreKnown) {
|
|
8759
|
+
scoreState.maxScore = state.maxScore;
|
|
8760
|
+
}
|
|
8761
|
+
objective.applyReadMappedState(scoreState);
|
|
8762
|
+
}
|
|
8381
8763
|
/**
|
|
8382
8764
|
* Get available children with selection and randomization applied
|
|
8383
8765
|
* @return {Activity[]}
|
|
@@ -9768,11 +10150,18 @@ class GlobalObjectiveSynchronizer {
|
|
|
9768
10150
|
*
|
|
9769
10151
|
* @param activity - The activity to process
|
|
9770
10152
|
* @param globalObjectives - Global objective map
|
|
10153
|
+
*
|
|
10154
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - write mapInfo transfers local objective state to global objectives
|
|
10155
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score write maps
|
|
9771
10156
|
*/
|
|
9772
10157
|
syncGlobalObjectivesWritePhase(activity, globalObjectives) {
|
|
10158
|
+
if (!this.canWriteGlobalObjectives(activity)) {
|
|
10159
|
+
return;
|
|
10160
|
+
}
|
|
9773
10161
|
const objectives = activity.getAllObjectives();
|
|
9774
10162
|
for (const objective of objectives) {
|
|
9775
10163
|
const mapInfos = objective.mapInfo.length > 0 ? objective.mapInfo : [this.createDefaultMapInfo(objective)];
|
|
10164
|
+
const dirtyFieldsToClear = /* @__PURE__ */ new Set();
|
|
9776
10165
|
for (const mapInfo of mapInfos) {
|
|
9777
10166
|
const targetId = mapInfo.targetObjectiveID || objective.id;
|
|
9778
10167
|
const globalObjective = this.ensureGlobalObjectiveEntry(
|
|
@@ -9780,36 +10169,54 @@ class GlobalObjectiveSynchronizer {
|
|
|
9780
10169
|
targetId,
|
|
9781
10170
|
objective
|
|
9782
10171
|
);
|
|
9783
|
-
if (mapInfo.writeSatisfiedStatus && objective
|
|
10172
|
+
if (mapInfo.writeSatisfiedStatus && this.hasKnownSatisfiedStatus(objective) && objective.isDirty("satisfiedStatus")) {
|
|
9784
10173
|
globalObjective.satisfiedStatus = objective.satisfiedStatus;
|
|
9785
10174
|
globalObjective.satisfiedStatusKnown = true;
|
|
9786
|
-
|
|
10175
|
+
dirtyFieldsToClear.add("satisfiedStatus");
|
|
9787
10176
|
}
|
|
9788
10177
|
if (mapInfo.writeNormalizedMeasure && objective.measureStatus && objective.isDirty("normalizedMeasure")) {
|
|
9789
10178
|
globalObjective.normalizedMeasure = objective.normalizedMeasure;
|
|
9790
10179
|
globalObjective.normalizedMeasureKnown = true;
|
|
9791
|
-
|
|
9792
|
-
if (
|
|
10180
|
+
dirtyFieldsToClear.add("normalizedMeasure");
|
|
10181
|
+
if (mapInfo.writeSatisfiedStatus && objective.satisfiedByMeasure) {
|
|
9793
10182
|
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
9794
10183
|
globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;
|
|
9795
10184
|
globalObjective.satisfiedStatusKnown = true;
|
|
9796
|
-
|
|
10185
|
+
dirtyFieldsToClear.add("satisfiedStatus");
|
|
9797
10186
|
}
|
|
9798
10187
|
}
|
|
9799
10188
|
if (mapInfo.writeCompletionStatus && objective.completionStatus !== CompletionStatus.UNKNOWN && objective.isDirty("completionStatus")) {
|
|
9800
10189
|
globalObjective.completionStatus = objective.completionStatus;
|
|
9801
10190
|
globalObjective.completionStatusKnown = true;
|
|
9802
|
-
|
|
10191
|
+
dirtyFieldsToClear.add("completionStatus");
|
|
10192
|
+
}
|
|
10193
|
+
if (mapInfo.writeRawScore && objective.rawScoreKnown && objective.isDirty("rawScore")) {
|
|
10194
|
+
globalObjective.rawScore = objective.rawScore;
|
|
10195
|
+
globalObjective.rawScoreKnown = true;
|
|
10196
|
+
dirtyFieldsToClear.add("rawScore");
|
|
10197
|
+
}
|
|
10198
|
+
if (mapInfo.writeMinScore && objective.minScoreKnown && objective.isDirty("minScore")) {
|
|
10199
|
+
globalObjective.minScore = objective.minScore;
|
|
10200
|
+
globalObjective.minScoreKnown = true;
|
|
10201
|
+
dirtyFieldsToClear.add("minScore");
|
|
10202
|
+
}
|
|
10203
|
+
if (mapInfo.writeMaxScore && objective.maxScoreKnown && objective.isDirty("maxScore")) {
|
|
10204
|
+
globalObjective.maxScore = objective.maxScore;
|
|
10205
|
+
globalObjective.maxScoreKnown = true;
|
|
10206
|
+
dirtyFieldsToClear.add("maxScore");
|
|
9803
10207
|
}
|
|
9804
10208
|
if (mapInfo.writeProgressMeasure && objective.progressMeasureStatus && objective.isDirty("progressMeasure")) {
|
|
9805
10209
|
globalObjective.progressMeasure = objective.progressMeasure;
|
|
9806
10210
|
globalObjective.progressMeasureKnown = true;
|
|
9807
|
-
|
|
10211
|
+
dirtyFieldsToClear.add("progressMeasure");
|
|
9808
10212
|
}
|
|
9809
10213
|
if (mapInfo.updateAttemptData) {
|
|
9810
10214
|
this.updateActivityAttemptData(activity, globalObjective, objective);
|
|
9811
10215
|
}
|
|
9812
10216
|
}
|
|
10217
|
+
for (const property of dirtyFieldsToClear) {
|
|
10218
|
+
objective.clearDirty(property);
|
|
10219
|
+
}
|
|
9813
10220
|
}
|
|
9814
10221
|
}
|
|
9815
10222
|
/**
|
|
@@ -9817,6 +10224,9 @@ class GlobalObjectiveSynchronizer {
|
|
|
9817
10224
|
*
|
|
9818
10225
|
* @param activity - The activity to process
|
|
9819
10226
|
* @param globalObjectives - Global objective map
|
|
10227
|
+
*
|
|
10228
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read mapInfo transfers global objective state into the local view
|
|
10229
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps
|
|
9820
10230
|
*/
|
|
9821
10231
|
syncGlobalObjectivesReadPhase(activity, globalObjectives) {
|
|
9822
10232
|
const objectives = activity.getAllObjectives();
|
|
@@ -9827,25 +10237,13 @@ class GlobalObjectiveSynchronizer {
|
|
|
9827
10237
|
const globalObjective = globalObjectives.get(targetId);
|
|
9828
10238
|
if (!globalObjective) continue;
|
|
9829
10239
|
const isPrimary = objective.isPrimary;
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
objective
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {
|
|
9838
|
-
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
9839
|
-
objective.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;
|
|
9840
|
-
}
|
|
9841
|
-
}
|
|
9842
|
-
if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {
|
|
9843
|
-
objective.progressMeasure = globalObjective.progressMeasure;
|
|
9844
|
-
objective.progressMeasureStatus = true;
|
|
9845
|
-
}
|
|
9846
|
-
if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {
|
|
9847
|
-
objective.completionStatus = globalObjective.completionStatus;
|
|
9848
|
-
}
|
|
10240
|
+
const readState = GlobalObjectiveSynchronizer.getGlobalObjectiveReadState(
|
|
10241
|
+
activity,
|
|
10242
|
+
objective,
|
|
10243
|
+
mapInfo,
|
|
10244
|
+
globalObjective
|
|
10245
|
+
);
|
|
10246
|
+
this.applyGlobalObjectiveReadState(objective, readState);
|
|
9849
10247
|
if (isPrimary) {
|
|
9850
10248
|
objective.applyToActivity(activity);
|
|
9851
10249
|
}
|
|
@@ -9888,56 +10286,61 @@ class GlobalObjectiveSynchronizer {
|
|
|
9888
10286
|
* @param objective - The objective to sync
|
|
9889
10287
|
* @param mapInfo - Map info for this objective
|
|
9890
10288
|
* @param globalObjective - The global objective
|
|
10289
|
+
*
|
|
10290
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - objective mapInfo read/write synchronization
|
|
10291
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score mapInfo synchronization
|
|
9891
10292
|
*/
|
|
9892
10293
|
syncObjectiveState(activity, objective, mapInfo, globalObjective) {
|
|
9893
10294
|
try {
|
|
9894
10295
|
const isPrimary = objective.isPrimary;
|
|
9895
10296
|
const localObjective = this.getLocalObjectiveState(activity, objective, isPrimary);
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
objective
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {
|
|
9904
|
-
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
9905
|
-
objective.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;
|
|
9906
|
-
}
|
|
9907
|
-
}
|
|
9908
|
-
if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {
|
|
9909
|
-
objective.progressMeasure = globalObjective.progressMeasure;
|
|
9910
|
-
objective.progressMeasureStatus = true;
|
|
9911
|
-
}
|
|
9912
|
-
if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {
|
|
9913
|
-
objective.completionStatus = globalObjective.completionStatus;
|
|
9914
|
-
}
|
|
10297
|
+
const readState = GlobalObjectiveSynchronizer.getGlobalObjectiveReadState(
|
|
10298
|
+
activity,
|
|
10299
|
+
objective,
|
|
10300
|
+
mapInfo,
|
|
10301
|
+
globalObjective
|
|
10302
|
+
);
|
|
10303
|
+
this.applyGlobalObjectiveReadState(objective, readState);
|
|
9915
10304
|
if (objective.isPrimary) {
|
|
9916
10305
|
objective.applyToActivity(activity);
|
|
9917
10306
|
}
|
|
9918
|
-
if (
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
}
|
|
9922
|
-
if (mapInfo.writeNormalizedMeasure && objective.measureStatus) {
|
|
9923
|
-
globalObjective.normalizedMeasure = objective.normalizedMeasure;
|
|
9924
|
-
globalObjective.normalizedMeasureKnown = true;
|
|
9925
|
-
if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {
|
|
9926
|
-
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
9927
|
-
globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;
|
|
10307
|
+
if (this.canWriteGlobalObjectives(activity)) {
|
|
10308
|
+
if (mapInfo.writeSatisfiedStatus && this.hasKnownSatisfiedStatus(objective)) {
|
|
10309
|
+
globalObjective.satisfiedStatus = objective.satisfiedStatus;
|
|
9928
10310
|
globalObjective.satisfiedStatusKnown = true;
|
|
9929
10311
|
}
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
10312
|
+
if (mapInfo.writeNormalizedMeasure && objective.measureStatus) {
|
|
10313
|
+
globalObjective.normalizedMeasure = objective.normalizedMeasure;
|
|
10314
|
+
globalObjective.normalizedMeasureKnown = true;
|
|
10315
|
+
if (mapInfo.writeSatisfiedStatus && objective.satisfiedByMeasure) {
|
|
10316
|
+
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10317
|
+
globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;
|
|
10318
|
+
globalObjective.satisfiedStatusKnown = true;
|
|
10319
|
+
}
|
|
10320
|
+
}
|
|
10321
|
+
if (mapInfo.writeCompletionStatus && objective.completionStatus !== CompletionStatus.UNKNOWN) {
|
|
10322
|
+
globalObjective.completionStatus = objective.completionStatus;
|
|
10323
|
+
globalObjective.completionStatusKnown = true;
|
|
10324
|
+
}
|
|
10325
|
+
if (mapInfo.writeRawScore && objective.rawScoreKnown) {
|
|
10326
|
+
globalObjective.rawScore = objective.rawScore;
|
|
10327
|
+
globalObjective.rawScoreKnown = true;
|
|
10328
|
+
}
|
|
10329
|
+
if (mapInfo.writeMinScore && objective.minScoreKnown) {
|
|
10330
|
+
globalObjective.minScore = objective.minScore;
|
|
10331
|
+
globalObjective.minScoreKnown = true;
|
|
10332
|
+
}
|
|
10333
|
+
if (mapInfo.writeMaxScore && objective.maxScoreKnown) {
|
|
10334
|
+
globalObjective.maxScore = objective.maxScore;
|
|
10335
|
+
globalObjective.maxScoreKnown = true;
|
|
10336
|
+
}
|
|
10337
|
+
if (mapInfo.writeProgressMeasure && objective.progressMeasureStatus) {
|
|
10338
|
+
globalObjective.progressMeasure = objective.progressMeasure;
|
|
10339
|
+
globalObjective.progressMeasureKnown = true;
|
|
10340
|
+
}
|
|
10341
|
+
if (mapInfo.updateAttemptData) {
|
|
10342
|
+
this.updateActivityAttemptData(activity, globalObjective, objective);
|
|
10343
|
+
}
|
|
9941
10344
|
}
|
|
9942
10345
|
this.eventCallback?.("objective_synchronized", {
|
|
9943
10346
|
activityId: activity.id,
|
|
@@ -9955,6 +10358,50 @@ class GlobalObjectiveSynchronizer {
|
|
|
9955
10358
|
});
|
|
9956
10359
|
}
|
|
9957
10360
|
}
|
|
10361
|
+
/**
|
|
10362
|
+
* Project a global objective through one local objective's read mapInfo.
|
|
10363
|
+
*
|
|
10364
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps provide access to mapped global objective fields
|
|
10365
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps are independent fields
|
|
10366
|
+
*/
|
|
10367
|
+
static getGlobalObjectiveReadState(activity, objective, mapInfo, globalObjective) {
|
|
10368
|
+
const readState = {};
|
|
10369
|
+
if (mapInfo.readSatisfiedStatus && globalObjective.satisfiedStatusKnown) {
|
|
10370
|
+
readState.satisfiedStatus = globalObjective.satisfiedStatus;
|
|
10371
|
+
}
|
|
10372
|
+
if (mapInfo.readNormalizedMeasure && globalObjective.normalizedMeasureKnown) {
|
|
10373
|
+
readState.normalizedMeasure = globalObjective.normalizedMeasure;
|
|
10374
|
+
if (objective.satisfiedByMeasure) {
|
|
10375
|
+
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10376
|
+
readState.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {
|
|
10380
|
+
readState.completionStatus = globalObjective.completionStatus;
|
|
10381
|
+
}
|
|
10382
|
+
if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {
|
|
10383
|
+
readState.progressMeasure = globalObjective.progressMeasure;
|
|
10384
|
+
}
|
|
10385
|
+
if (mapInfo.readRawScore && globalObjective.rawScoreKnown) {
|
|
10386
|
+
readState.rawScore = globalObjective.rawScore;
|
|
10387
|
+
}
|
|
10388
|
+
if (mapInfo.readMinScore && globalObjective.minScoreKnown) {
|
|
10389
|
+
readState.minScore = globalObjective.minScore;
|
|
10390
|
+
}
|
|
10391
|
+
if (mapInfo.readMaxScore && globalObjective.maxScoreKnown) {
|
|
10392
|
+
readState.maxScore = globalObjective.maxScore;
|
|
10393
|
+
}
|
|
10394
|
+
return readState;
|
|
10395
|
+
}
|
|
10396
|
+
/**
|
|
10397
|
+
* Apply read-mapped state to an objective without marking those fields dirty.
|
|
10398
|
+
*
|
|
10399
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps are access to global state, not local writes
|
|
10400
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score read maps do not imply score writes
|
|
10401
|
+
*/
|
|
10402
|
+
applyGlobalObjectiveReadState(objective, readState) {
|
|
10403
|
+
objective.applyReadMappedState(readState);
|
|
10404
|
+
}
|
|
9958
10405
|
/**
|
|
9959
10406
|
* Ensure global objective entry exists
|
|
9960
10407
|
*
|
|
@@ -9962,19 +10409,30 @@ class GlobalObjectiveSynchronizer {
|
|
|
9962
10409
|
* @param targetId - Target objective ID
|
|
9963
10410
|
* @param objective - Source objective
|
|
9964
10411
|
* @returns The global objective entry
|
|
10412
|
+
*
|
|
10413
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective entries hold mapped objective state
|
|
10414
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - global entries hold score-map state
|
|
9965
10415
|
*/
|
|
9966
10416
|
ensureGlobalObjectiveEntry(globalObjectives, targetId, objective) {
|
|
9967
10417
|
if (!globalObjectives.has(targetId)) {
|
|
9968
10418
|
globalObjectives.set(targetId, {
|
|
9969
10419
|
id: targetId,
|
|
9970
|
-
satisfiedStatus:
|
|
9971
|
-
satisfiedStatusKnown:
|
|
9972
|
-
normalizedMeasure:
|
|
9973
|
-
normalizedMeasureKnown:
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
10420
|
+
satisfiedStatus: false,
|
|
10421
|
+
satisfiedStatusKnown: false,
|
|
10422
|
+
normalizedMeasure: 0,
|
|
10423
|
+
normalizedMeasureKnown: false,
|
|
10424
|
+
// @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score fields
|
|
10425
|
+
// remain unknown until their corresponding write map explicitly writes them.
|
|
10426
|
+
rawScore: "",
|
|
10427
|
+
rawScoreKnown: false,
|
|
10428
|
+
minScore: "",
|
|
10429
|
+
minScoreKnown: false,
|
|
10430
|
+
maxScore: "",
|
|
10431
|
+
maxScoreKnown: false,
|
|
10432
|
+
progressMeasure: 0,
|
|
10433
|
+
progressMeasureKnown: false,
|
|
10434
|
+
completionStatus: CompletionStatus.UNKNOWN,
|
|
10435
|
+
completionStatusKnown: false,
|
|
9978
10436
|
satisfiedByMeasure: objective.satisfiedByMeasure,
|
|
9979
10437
|
minNormalizedMeasure: objective.minNormalizedMeasure
|
|
9980
10438
|
});
|
|
@@ -9989,6 +10447,9 @@ class GlobalObjectiveSynchronizer {
|
|
|
9989
10447
|
*
|
|
9990
10448
|
* @param objective - The objective to create default map info for
|
|
9991
10449
|
* @returns Default map info
|
|
10450
|
+
*
|
|
10451
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - mapInfo defaults are applied before objective synchronization
|
|
10452
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score maps require explicit read/write flags
|
|
9992
10453
|
*/
|
|
9993
10454
|
createDefaultMapInfo(objective) {
|
|
9994
10455
|
return {
|
|
@@ -10001,9 +10462,34 @@ class GlobalObjectiveSynchronizer {
|
|
|
10001
10462
|
writeCompletionStatus: true,
|
|
10002
10463
|
readProgressMeasure: false,
|
|
10003
10464
|
writeProgressMeasure: true,
|
|
10465
|
+
readRawScore: false,
|
|
10466
|
+
writeRawScore: false,
|
|
10467
|
+
readMinScore: false,
|
|
10468
|
+
writeMinScore: false,
|
|
10469
|
+
readMaxScore: false,
|
|
10470
|
+
writeMaxScore: false,
|
|
10004
10471
|
updateAttemptData: objective.isPrimary
|
|
10005
10472
|
};
|
|
10006
10473
|
}
|
|
10474
|
+
/**
|
|
10475
|
+
* Return whether the local objective has known satisfaction state to write.
|
|
10476
|
+
*
|
|
10477
|
+
* @spec SCORM 2004 4th Ed. SN 4.2.1 Tracking Model - Objective Progress
|
|
10478
|
+
* Status identifies whether Objective Satisfied Status is known; Objective
|
|
10479
|
+
* Measure Status is independent measure knowledge.
|
|
10480
|
+
*/
|
|
10481
|
+
hasKnownSatisfiedStatus(objective) {
|
|
10482
|
+
return objective.progressStatus || objective.satisfiedStatusKnown;
|
|
10483
|
+
}
|
|
10484
|
+
/**
|
|
10485
|
+
* Return whether this activity is allowed to write tracked state to globals.
|
|
10486
|
+
*
|
|
10487
|
+
* @spec SCORM 2004 4th Ed. SN 3.13.1 Tracked - when False, the LMS
|
|
10488
|
+
* "does not initialize, manage or access any tracking status information".
|
|
10489
|
+
*/
|
|
10490
|
+
canWriteGlobalObjectives(activity) {
|
|
10491
|
+
return activity.sequencingControls.tracked !== false;
|
|
10492
|
+
}
|
|
10007
10493
|
/**
|
|
10008
10494
|
* Get local objective state
|
|
10009
10495
|
*
|
|
@@ -10011,6 +10497,9 @@ class GlobalObjectiveSynchronizer {
|
|
|
10011
10497
|
* @param objective - The objective
|
|
10012
10498
|
* @param isPrimary - Whether this is the primary objective
|
|
10013
10499
|
* @returns Local objective state
|
|
10500
|
+
*
|
|
10501
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - local objective state used for synchronization
|
|
10502
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - local score fields are part of mapped objective state
|
|
10014
10503
|
*/
|
|
10015
10504
|
getLocalObjectiveState(activity, objective, isPrimary) {
|
|
10016
10505
|
if (isPrimary) {
|
|
@@ -10019,6 +10508,12 @@ class GlobalObjectiveSynchronizer {
|
|
|
10019
10508
|
satisfiedStatus: activity.objectiveSatisfiedStatus,
|
|
10020
10509
|
measureStatus: activity.objectiveMeasureStatus,
|
|
10021
10510
|
normalizedMeasure: activity.objectiveNormalizedMeasure,
|
|
10511
|
+
rawScore: objective.rawScore,
|
|
10512
|
+
rawScoreKnown: objective.rawScoreKnown,
|
|
10513
|
+
minScore: objective.minScore,
|
|
10514
|
+
minScoreKnown: objective.minScoreKnown,
|
|
10515
|
+
maxScore: objective.maxScore,
|
|
10516
|
+
maxScoreKnown: objective.maxScoreKnown,
|
|
10022
10517
|
progressMeasure: activity.progressMeasure,
|
|
10023
10518
|
progressMeasureStatus: activity.progressMeasureStatus,
|
|
10024
10519
|
completionStatus: activity.completionStatus,
|
|
@@ -10030,6 +10525,12 @@ class GlobalObjectiveSynchronizer {
|
|
|
10030
10525
|
satisfiedStatus: objective.satisfiedStatus,
|
|
10031
10526
|
measureStatus: objective.measureStatus,
|
|
10032
10527
|
normalizedMeasure: objective.normalizedMeasure,
|
|
10528
|
+
rawScore: objective.rawScore,
|
|
10529
|
+
rawScoreKnown: objective.rawScoreKnown,
|
|
10530
|
+
minScore: objective.minScore,
|
|
10531
|
+
minScoreKnown: objective.minScoreKnown,
|
|
10532
|
+
maxScore: objective.maxScore,
|
|
10533
|
+
maxScoreKnown: objective.maxScoreKnown,
|
|
10033
10534
|
progressMeasure: objective.progressMeasure,
|
|
10034
10535
|
progressMeasureStatus: objective.progressMeasureStatus,
|
|
10035
10536
|
completionStatus: objective.completionStatus,
|
|
@@ -10052,7 +10553,7 @@ class GlobalObjectiveSynchronizer {
|
|
|
10052
10553
|
(rule) => rule.action === "completed" || rule.action === "incomplete"
|
|
10053
10554
|
);
|
|
10054
10555
|
if (globalObjective.satisfiedStatusKnown && globalObjective.satisfiedStatus) {
|
|
10055
|
-
if (!hasCompletionRollupRules && (activity.completionStatus === CompletionStatus.UNKNOWN || activity.completionStatus === CompletionStatus.INCOMPLETE)) {
|
|
10556
|
+
if (!hasCompletionRollupRules && !activity.sequencingControls.completionSetByContent && !activity.attemptProgressStatus && (activity.completionStatus === CompletionStatus.UNKNOWN || activity.completionStatus === CompletionStatus.INCOMPLETE)) {
|
|
10056
10557
|
activity.completionStatus = CompletionStatus.COMPLETED;
|
|
10057
10558
|
}
|
|
10058
10559
|
if (activity.successStatus === "unknown") {
|
|
@@ -10498,6 +10999,9 @@ class RteDataTransferService {
|
|
|
10498
10999
|
* Transfer primary objective data from CMI to activity
|
|
10499
11000
|
* @param {Activity} activity - The activity to transfer data to
|
|
10500
11001
|
* @param {CMIDataForTransfer} cmiData - CMI data from runtime
|
|
11002
|
+
*
|
|
11003
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - primary objective status and score data
|
|
11004
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score write-map source data
|
|
10501
11005
|
*/
|
|
10502
11006
|
transferPrimaryObjective(activity, cmiData) {
|
|
10503
11007
|
let hasProgressMeasure = false;
|
|
@@ -10544,9 +11048,9 @@ class RteDataTransferService {
|
|
|
10544
11048
|
activity.objectiveSatisfiedStatus = successStatus;
|
|
10545
11049
|
activity.objectiveSatisfiedStatusKnown = true;
|
|
10546
11050
|
activity.successStatus = validatedSuccessStatus;
|
|
10547
|
-
activity.objectiveMeasureStatus = true;
|
|
10548
11051
|
}
|
|
10549
11052
|
if (cmiData.score) {
|
|
11053
|
+
activity.primaryObjective?.initializeScoreFromCMI(this.getObjectiveScoreState(cmiData.score));
|
|
10550
11054
|
const normalized = this.normalizeScore(cmiData.score);
|
|
10551
11055
|
if (normalized !== null) {
|
|
10552
11056
|
normalizedScore = normalized;
|
|
@@ -10558,7 +11062,7 @@ class RteDataTransferService {
|
|
|
10558
11062
|
if (activity.primaryObjective && (hasSuccessStatus || hasNormalizedMeasure)) {
|
|
10559
11063
|
const finalStatus = hasSuccessStatus ? successStatus : activity.primaryObjective.satisfiedStatus;
|
|
10560
11064
|
const finalMeasure = hasNormalizedMeasure ? normalizedScore : activity.primaryObjective.normalizedMeasure;
|
|
10561
|
-
const measureStatus =
|
|
11065
|
+
const measureStatus = hasNormalizedMeasure;
|
|
10562
11066
|
activity.primaryObjective.initializeFromCMI(finalStatus, finalMeasure, measureStatus);
|
|
10563
11067
|
if (hasSuccessStatus) {
|
|
10564
11068
|
activity.primaryObjective.satisfiedStatusKnown = true;
|
|
@@ -10567,10 +11071,16 @@ class RteDataTransferService {
|
|
|
10567
11071
|
}
|
|
10568
11072
|
}
|
|
10569
11073
|
/**
|
|
10570
|
-
* Transfer
|
|
10571
|
-
* Only transfers changed values to protect global objectives
|
|
11074
|
+
* Transfer objective-array data from CMI to matching activity objectives.
|
|
11075
|
+
* Only transfers changed values to protect global objectives.
|
|
10572
11076
|
* @param {Activity} activity - The activity to transfer data to
|
|
10573
11077
|
* @param {CMIDataForTransfer} cmiData - CMI data from runtime
|
|
11078
|
+
*
|
|
11079
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17 - cmi.objectives.n data, including
|
|
11080
|
+
* the primary objective entry, is part of the RTE objective data model.
|
|
11081
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 and ADLSEQ objectives extension -
|
|
11082
|
+
* mapped objective status and raw/min/max score data transfer through
|
|
11083
|
+
* objective maps to sequencing state.
|
|
10574
11084
|
*/
|
|
10575
11085
|
transferNonPrimaryObjectives(activity, cmiData) {
|
|
10576
11086
|
if (!cmiData.objectives || cmiData.objectives.length === 0) {
|
|
@@ -10581,14 +11091,17 @@ class RteDataTransferService {
|
|
|
10581
11091
|
continue;
|
|
10582
11092
|
}
|
|
10583
11093
|
const activityObjectiveMatch = activity.getObjectiveById(cmiObjective.id);
|
|
10584
|
-
if (!activityObjectiveMatch
|
|
11094
|
+
if (!activityObjectiveMatch) {
|
|
10585
11095
|
continue;
|
|
10586
11096
|
}
|
|
10587
11097
|
const activityObjective = activityObjectiveMatch.objective;
|
|
11098
|
+
const isPrimaryObjective = activityObjectiveMatch.isPrimary;
|
|
10588
11099
|
let hasSuccessStatus = false;
|
|
10589
11100
|
let successStatus = false;
|
|
10590
11101
|
let hasNormalizedMeasure = false;
|
|
10591
11102
|
let normalizedScore = 0;
|
|
11103
|
+
let hasCompletionStatus = false;
|
|
11104
|
+
let hasProgressMeasure = false;
|
|
10592
11105
|
const validatedObjSuccessStatus = validateSuccessStatus(cmiObjective.success_status);
|
|
10593
11106
|
if (validatedObjSuccessStatus && validatedObjSuccessStatus !== SuccessStatus.UNKNOWN) {
|
|
10594
11107
|
successStatus = validatedObjSuccessStatus === SuccessStatus.PASSED;
|
|
@@ -10598,8 +11111,10 @@ class RteDataTransferService {
|
|
|
10598
11111
|
const validatedObjCompletionStatus = validateCompletionStatus(cmiObjective.completion_status);
|
|
10599
11112
|
if (validatedObjCompletionStatus && validatedObjCompletionStatus !== CompletionStatus.UNKNOWN) {
|
|
10600
11113
|
activityObjective.completionStatus = validatedObjCompletionStatus;
|
|
11114
|
+
hasCompletionStatus = true;
|
|
10601
11115
|
}
|
|
10602
11116
|
if (cmiObjective.score) {
|
|
11117
|
+
activityObjective.initializeScoreFromCMI(this.getObjectiveScoreState(cmiObjective.score));
|
|
10603
11118
|
const normalized = this.normalizeScore(cmiObjective.score);
|
|
10604
11119
|
if (normalized !== null) {
|
|
10605
11120
|
normalizedScore = normalized;
|
|
@@ -10611,12 +11126,29 @@ class RteDataTransferService {
|
|
|
10611
11126
|
const finalMeasure = hasNormalizedMeasure ? normalizedScore : activityObjective.normalizedMeasure;
|
|
10612
11127
|
const measureStatus = hasNormalizedMeasure;
|
|
10613
11128
|
activityObjective.initializeFromCMI(finalStatus, finalMeasure, measureStatus);
|
|
11129
|
+
if (hasSuccessStatus) {
|
|
11130
|
+
activityObjective.satisfiedStatusKnown = true;
|
|
11131
|
+
}
|
|
10614
11132
|
}
|
|
10615
11133
|
if (cmiObjective.progress_measure && cmiObjective.progress_measure !== "") {
|
|
10616
11134
|
const progressMeasure = parseFloat(cmiObjective.progress_measure);
|
|
10617
11135
|
if (!isNaN(progressMeasure)) {
|
|
10618
11136
|
activityObjective.progressMeasure = progressMeasure;
|
|
10619
11137
|
activityObjective.progressMeasureStatus = true;
|
|
11138
|
+
hasProgressMeasure = true;
|
|
11139
|
+
}
|
|
11140
|
+
}
|
|
11141
|
+
if (isPrimaryObjective && (hasSuccessStatus || hasNormalizedMeasure || hasCompletionStatus || hasProgressMeasure)) {
|
|
11142
|
+
activityObjective.applyToActivity(activity);
|
|
11143
|
+
if (validatedObjSuccessStatus && validatedObjSuccessStatus !== SuccessStatus.UNKNOWN) {
|
|
11144
|
+
activity.successStatus = validatedObjSuccessStatus;
|
|
11145
|
+
}
|
|
11146
|
+
if (hasCompletionStatus) {
|
|
11147
|
+
activity.attemptProgressStatus = true;
|
|
11148
|
+
}
|
|
11149
|
+
if (hasProgressMeasure) {
|
|
11150
|
+
activity.attemptCompletionAmount = activityObjective.progressMeasure;
|
|
11151
|
+
activity.attemptCompletionAmountStatus = true;
|
|
10620
11152
|
}
|
|
10621
11153
|
}
|
|
10622
11154
|
}
|
|
@@ -10645,6 +11177,25 @@ class RteDataTransferService {
|
|
|
10645
11177
|
}
|
|
10646
11178
|
return null;
|
|
10647
11179
|
}
|
|
11180
|
+
/**
|
|
11181
|
+
* Convert RTE score data into objective score-map state without numeric reformatting.
|
|
11182
|
+
*
|
|
11183
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - score values transfer from RTE to sequencing state
|
|
11184
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score map fields are independent
|
|
11185
|
+
*/
|
|
11186
|
+
getObjectiveScoreState(score) {
|
|
11187
|
+
const scoreState = {};
|
|
11188
|
+
if (score.raw !== void 0) {
|
|
11189
|
+
scoreState.rawScore = score.raw;
|
|
11190
|
+
}
|
|
11191
|
+
if (score.min !== void 0) {
|
|
11192
|
+
scoreState.minScore = score.min;
|
|
11193
|
+
}
|
|
11194
|
+
if (score.max !== void 0) {
|
|
11195
|
+
scoreState.maxScore = score.max;
|
|
11196
|
+
}
|
|
11197
|
+
return scoreState;
|
|
11198
|
+
}
|
|
10648
11199
|
}
|
|
10649
11200
|
|
|
10650
11201
|
class TerminationHandler {
|
|
@@ -11006,13 +11557,7 @@ class TerminationHandler {
|
|
|
11006
11557
|
recursionDepth++;
|
|
11007
11558
|
const exitRules = activity.sequencingRules.exitConditionRules;
|
|
11008
11559
|
for (const rule of exitRules) {
|
|
11009
|
-
|
|
11010
|
-
if (rule.conditionCombination === "all") {
|
|
11011
|
-
conditionsMet = rule.conditions.every((condition) => condition.evaluate(activity));
|
|
11012
|
-
} else {
|
|
11013
|
-
conditionsMet = rule.conditions.some((condition) => condition.evaluate(activity));
|
|
11014
|
-
}
|
|
11015
|
-
if (conditionsMet) {
|
|
11560
|
+
if (rule.evaluate(activity)) {
|
|
11016
11561
|
if (rule.action === RuleActionType.EXIT) {
|
|
11017
11562
|
return { action: "EXIT", recursionDepth };
|
|
11018
11563
|
} else if (rule.action === RuleActionType.EXIT_PARENT) {
|
|
@@ -11178,13 +11723,7 @@ class TerminationHandler {
|
|
|
11178
11723
|
exitActionRulesSubprocess(activity) {
|
|
11179
11724
|
const exitRules = activity.sequencingRules.exitConditionRules;
|
|
11180
11725
|
for (const rule of exitRules) {
|
|
11181
|
-
|
|
11182
|
-
if (rule.conditionCombination === "all") {
|
|
11183
|
-
conditionsMet = rule.conditions.every((condition) => condition.evaluate(activity));
|
|
11184
|
-
} else {
|
|
11185
|
-
conditionsMet = rule.conditions.some((condition) => condition.evaluate(activity));
|
|
11186
|
-
}
|
|
11187
|
-
if (conditionsMet) {
|
|
11726
|
+
if (rule.evaluate(activity)) {
|
|
11188
11727
|
if (rule.action === RuleActionType.EXIT) {
|
|
11189
11728
|
return "EXIT";
|
|
11190
11729
|
} else if (rule.action === RuleActionType.EXIT_PARENT) {
|
|
@@ -12703,6 +13242,9 @@ class GlobalObjectiveService {
|
|
|
12703
13242
|
* Collect Global Objectives
|
|
12704
13243
|
* Recursively collects global objectives from the activity tree
|
|
12705
13244
|
* @param {Activity} activity - Activity to collect objectives from
|
|
13245
|
+
*
|
|
13246
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective map contains mapped objective state
|
|
13247
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score fields are unknown until written
|
|
12706
13248
|
*/
|
|
12707
13249
|
collectObjectives(activity) {
|
|
12708
13250
|
const objectives = activity.getAllObjectives();
|
|
@@ -12712,9 +13254,15 @@ class GlobalObjectiveService {
|
|
|
12712
13254
|
this.globalObjectiveMap.set(defaultId, {
|
|
12713
13255
|
id: defaultId,
|
|
12714
13256
|
satisfiedStatus: activity.objectiveSatisfiedStatus,
|
|
12715
|
-
satisfiedStatusKnown: activity.
|
|
13257
|
+
satisfiedStatusKnown: activity.objectiveSatisfiedStatusKnown,
|
|
12716
13258
|
normalizedMeasure: activity.objectiveNormalizedMeasure,
|
|
12717
13259
|
normalizedMeasureKnown: activity.objectiveMeasureStatus,
|
|
13260
|
+
rawScore: "",
|
|
13261
|
+
rawScoreKnown: false,
|
|
13262
|
+
minScore: "",
|
|
13263
|
+
minScoreKnown: false,
|
|
13264
|
+
maxScore: "",
|
|
13265
|
+
maxScoreKnown: false,
|
|
12718
13266
|
progressMeasure: activity.progressMeasure,
|
|
12719
13267
|
progressMeasureKnown: activity.progressMeasureStatus,
|
|
12720
13268
|
completionStatus: activity.completionStatus,
|
|
@@ -12727,6 +13275,12 @@ class GlobalObjectiveService {
|
|
|
12727
13275
|
writeCompletionStatus: true,
|
|
12728
13276
|
readProgressMeasure: true,
|
|
12729
13277
|
writeProgressMeasure: true,
|
|
13278
|
+
readRawScore: false,
|
|
13279
|
+
writeRawScore: false,
|
|
13280
|
+
readMinScore: false,
|
|
13281
|
+
writeMinScore: false,
|
|
13282
|
+
readMaxScore: false,
|
|
13283
|
+
writeMaxScore: false,
|
|
12730
13284
|
satisfiedByMeasure: activity.scaledPassingScore !== null,
|
|
12731
13285
|
minNormalizedMeasure: activity.scaledPassingScore,
|
|
12732
13286
|
updateAttemptData: true
|
|
@@ -12754,9 +13308,15 @@ class GlobalObjectiveService {
|
|
|
12754
13308
|
this.globalObjectiveMap.set(targetId, {
|
|
12755
13309
|
id: targetId,
|
|
12756
13310
|
satisfiedStatus: objective.satisfiedStatus,
|
|
12757
|
-
satisfiedStatusKnown: objective.
|
|
13311
|
+
satisfiedStatusKnown: objective.satisfiedStatusKnown || objective.progressStatus,
|
|
12758
13312
|
normalizedMeasure: objective.normalizedMeasure,
|
|
12759
13313
|
normalizedMeasureKnown: objective.measureStatus,
|
|
13314
|
+
rawScore: "",
|
|
13315
|
+
rawScoreKnown: false,
|
|
13316
|
+
minScore: "",
|
|
13317
|
+
minScoreKnown: false,
|
|
13318
|
+
maxScore: "",
|
|
13319
|
+
maxScoreKnown: false,
|
|
12760
13320
|
progressMeasure: objective.progressMeasure,
|
|
12761
13321
|
progressMeasureKnown: objective.progressMeasureStatus,
|
|
12762
13322
|
completionStatus: objective.completionStatus,
|
|
@@ -14739,6 +15299,9 @@ class SequencingService {
|
|
|
14739
15299
|
}
|
|
14740
15300
|
/**
|
|
14741
15301
|
* Update activity properties from current CMI values
|
|
15302
|
+
*
|
|
15303
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - current CMI values update activity objective state
|
|
15304
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max scores are available to objective write maps
|
|
14742
15305
|
*/
|
|
14743
15306
|
updateActivityFromCMI(activity) {
|
|
14744
15307
|
let hasProgressMeasure = false;
|
|
@@ -14770,7 +15333,6 @@ class SequencingService {
|
|
|
14770
15333
|
if (this.cmi.success_status !== "unknown") {
|
|
14771
15334
|
activity.successStatus = this.cmi.success_status;
|
|
14772
15335
|
activity.objectiveSatisfiedStatus = this.cmi.success_status === "passed";
|
|
14773
|
-
activity.objectiveMeasureStatus = true;
|
|
14774
15336
|
if (activity.primaryObjective) {
|
|
14775
15337
|
activity.primaryObjective.progressStatus = true;
|
|
14776
15338
|
}
|
|
@@ -14785,6 +15347,17 @@ class SequencingService {
|
|
|
14785
15347
|
}
|
|
14786
15348
|
}
|
|
14787
15349
|
}
|
|
15350
|
+
if (activity.primaryObjective && this.cmi.score) {
|
|
15351
|
+
if (this.cmi.score.raw !== "") {
|
|
15352
|
+
activity.primaryObjective.rawScore = this.cmi.score.raw;
|
|
15353
|
+
}
|
|
15354
|
+
if (this.cmi.score.min !== "") {
|
|
15355
|
+
activity.primaryObjective.minScore = this.cmi.score.min;
|
|
15356
|
+
}
|
|
15357
|
+
if (this.cmi.score.max !== "") {
|
|
15358
|
+
activity.primaryObjective.maxScore = this.cmi.score.max;
|
|
15359
|
+
}
|
|
15360
|
+
}
|
|
14788
15361
|
if (activity.primaryObjective) {
|
|
14789
15362
|
activity.primaryObjective.updateFromActivity(activity);
|
|
14790
15363
|
}
|
|
@@ -17297,7 +17870,7 @@ class CMIInteractionsObject extends BaseCMI {
|
|
|
17297
17870
|
if (check2004ValidFormat(
|
|
17298
17871
|
this._cmi_element + ".description",
|
|
17299
17872
|
description,
|
|
17300
|
-
scorm2004_regex.
|
|
17873
|
+
scorm2004_regex.CMILangString,
|
|
17301
17874
|
true
|
|
17302
17875
|
)) {
|
|
17303
17876
|
this._description = description;
|
|
@@ -18201,7 +18774,7 @@ class CMIObjectivesObject extends BaseCMI {
|
|
|
18201
18774
|
if (check2004ValidFormat(
|
|
18202
18775
|
this._cmi_element + ".description",
|
|
18203
18776
|
description,
|
|
18204
|
-
scorm2004_regex.
|
|
18777
|
+
scorm2004_regex.CMILangString,
|
|
18205
18778
|
true
|
|
18206
18779
|
)) {
|
|
18207
18780
|
this._description = description;
|
|
@@ -21044,6 +21617,9 @@ class SequencingConfigurationBuilder {
|
|
|
21044
21617
|
if (settings.objectiveSetByContent !== void 0) {
|
|
21045
21618
|
target.objectiveSetByContent = settings.objectiveSetByContent;
|
|
21046
21619
|
}
|
|
21620
|
+
if (settings.tracked !== void 0) {
|
|
21621
|
+
target.tracked = settings.tracked;
|
|
21622
|
+
}
|
|
21047
21623
|
}
|
|
21048
21624
|
/**
|
|
21049
21625
|
* Applies the sequencing rules settings to the specified target object.
|
|
@@ -21581,6 +22157,12 @@ class ActivityTreeBuilder {
|
|
|
21581
22157
|
activitySettings.sequencingControls
|
|
21582
22158
|
);
|
|
21583
22159
|
}
|
|
22160
|
+
if (activitySettings.deliveryControls) {
|
|
22161
|
+
this.sequencingConfigBuilder.applySequencingControlsSettings(
|
|
22162
|
+
activity.sequencingControls,
|
|
22163
|
+
activitySettings.deliveryControls
|
|
22164
|
+
);
|
|
22165
|
+
}
|
|
21584
22166
|
if (activitySettings.sequencingRules) {
|
|
21585
22167
|
this.sequencingConfigBuilder.applySequencingRulesSettings(
|
|
21586
22168
|
activity.sequencingRules,
|
|
@@ -21670,19 +22252,19 @@ class ActivityTreeBuilder {
|
|
|
21670
22252
|
createActivityObjectiveFromSettings(objectiveSettings, isPrimary) {
|
|
21671
22253
|
const mapInfo = (objectiveSettings.mapInfo || []).map((info) => ({
|
|
21672
22254
|
targetObjectiveID: info.targetObjectiveID,
|
|
21673
|
-
readSatisfiedStatus: info.readSatisfiedStatus ??
|
|
21674
|
-
readNormalizedMeasure: info.readNormalizedMeasure ??
|
|
22255
|
+
readSatisfiedStatus: info.readSatisfiedStatus ?? true,
|
|
22256
|
+
readNormalizedMeasure: info.readNormalizedMeasure ?? true,
|
|
21675
22257
|
writeSatisfiedStatus: info.writeSatisfiedStatus ?? false,
|
|
21676
22258
|
writeNormalizedMeasure: info.writeNormalizedMeasure ?? false,
|
|
21677
|
-
readCompletionStatus: info.readCompletionStatus ??
|
|
22259
|
+
readCompletionStatus: info.readCompletionStatus ?? true,
|
|
21678
22260
|
writeCompletionStatus: info.writeCompletionStatus ?? false,
|
|
21679
|
-
readProgressMeasure: info.readProgressMeasure ??
|
|
22261
|
+
readProgressMeasure: info.readProgressMeasure ?? true,
|
|
21680
22262
|
writeProgressMeasure: info.writeProgressMeasure ?? false,
|
|
21681
|
-
readRawScore: info.readRawScore ??
|
|
22263
|
+
readRawScore: info.readRawScore ?? true,
|
|
21682
22264
|
writeRawScore: info.writeRawScore ?? false,
|
|
21683
|
-
readMinScore: info.readMinScore ??
|
|
22265
|
+
readMinScore: info.readMinScore ?? true,
|
|
21684
22266
|
writeMinScore: info.writeMinScore ?? false,
|
|
21685
|
-
readMaxScore: info.readMaxScore ??
|
|
22267
|
+
readMaxScore: info.readMaxScore ?? true,
|
|
21686
22268
|
writeMaxScore: info.writeMaxScore ?? false,
|
|
21687
22269
|
updateAttemptData: info.updateAttemptData ?? false
|
|
21688
22270
|
}));
|
|
@@ -21864,6 +22446,9 @@ class GlobalObjectiveManager {
|
|
|
21864
22446
|
*
|
|
21865
22447
|
* @param {string} objectiveId - The global objective ID
|
|
21866
22448
|
* @param {CMIObjectivesObject} objective - The CMI objective object with updated values
|
|
22449
|
+
*
|
|
22450
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objectives synchronize CMI objective data
|
|
22451
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score fields synchronize independently
|
|
21867
22452
|
*/
|
|
21868
22453
|
updateGlobalObjectiveFromCMI(objectiveId, objective) {
|
|
21869
22454
|
if (!objectiveId || !this.context.sequencingService) {
|
|
@@ -21889,6 +22474,18 @@ class GlobalObjectiveManager {
|
|
|
21889
22474
|
updatePayload.normalizedMeasure = normalizedMeasure;
|
|
21890
22475
|
updatePayload.normalizedMeasureKnown = true;
|
|
21891
22476
|
}
|
|
22477
|
+
if (objective.score?.raw !== void 0 && objective.score.raw !== "") {
|
|
22478
|
+
updatePayload.rawScore = objective.score.raw;
|
|
22479
|
+
updatePayload.rawScoreKnown = true;
|
|
22480
|
+
}
|
|
22481
|
+
if (objective.score?.min !== void 0 && objective.score.min !== "") {
|
|
22482
|
+
updatePayload.minScore = objective.score.min;
|
|
22483
|
+
updatePayload.minScoreKnown = true;
|
|
22484
|
+
}
|
|
22485
|
+
if (objective.score?.max !== void 0 && objective.score.max !== "") {
|
|
22486
|
+
updatePayload.maxScore = objective.score.max;
|
|
22487
|
+
updatePayload.maxScoreKnown = true;
|
|
22488
|
+
}
|
|
21892
22489
|
const progressMeasure = this.parseObjectiveNumber(objective.progress_measure);
|
|
21893
22490
|
if (progressMeasure !== null) {
|
|
21894
22491
|
updatePayload.progressMeasure = progressMeasure;
|
|
@@ -21908,12 +22505,21 @@ class GlobalObjectiveManager {
|
|
|
21908
22505
|
*
|
|
21909
22506
|
* @param {CMIObjectivesObject} objective - The CMI objectives object containing data about a specific learning objective.
|
|
21910
22507
|
* @return {GlobalObjectiveMapEntry} An object containing mapped properties and their values based on the provided objective.
|
|
22508
|
+
*
|
|
22509
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective map entries capture objective state
|
|
22510
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score entries carry per-field known flags
|
|
21911
22511
|
*/
|
|
21912
22512
|
buildObjectiveMapEntryFromCMI(objective) {
|
|
21913
22513
|
const entry = {
|
|
21914
22514
|
id: objective.id,
|
|
21915
22515
|
satisfiedStatusKnown: false,
|
|
21916
22516
|
normalizedMeasureKnown: false,
|
|
22517
|
+
rawScore: "",
|
|
22518
|
+
rawScoreKnown: false,
|
|
22519
|
+
minScore: "",
|
|
22520
|
+
minScoreKnown: false,
|
|
22521
|
+
maxScore: "",
|
|
22522
|
+
maxScoreKnown: false,
|
|
21917
22523
|
progressMeasureKnown: false,
|
|
21918
22524
|
completionStatusKnown: false,
|
|
21919
22525
|
readSatisfiedStatus: true,
|
|
@@ -21923,7 +22529,13 @@ class GlobalObjectiveManager {
|
|
|
21923
22529
|
readCompletionStatus: true,
|
|
21924
22530
|
writeCompletionStatus: true,
|
|
21925
22531
|
readProgressMeasure: true,
|
|
21926
|
-
writeProgressMeasure: true
|
|
22532
|
+
writeProgressMeasure: true,
|
|
22533
|
+
readRawScore: true,
|
|
22534
|
+
writeRawScore: true,
|
|
22535
|
+
readMinScore: true,
|
|
22536
|
+
writeMinScore: true,
|
|
22537
|
+
readMaxScore: true,
|
|
22538
|
+
writeMaxScore: true
|
|
21927
22539
|
};
|
|
21928
22540
|
if (objective.success_status && objective.success_status !== SuccessStatus.UNKNOWN) {
|
|
21929
22541
|
entry.satisfiedStatus = objective.success_status === SuccessStatus.PASSED;
|
|
@@ -21934,6 +22546,18 @@ class GlobalObjectiveManager {
|
|
|
21934
22546
|
entry.normalizedMeasure = normalizedMeasure;
|
|
21935
22547
|
entry.normalizedMeasureKnown = true;
|
|
21936
22548
|
}
|
|
22549
|
+
if (objective.score?.raw !== void 0 && objective.score.raw !== "") {
|
|
22550
|
+
entry.rawScore = objective.score.raw;
|
|
22551
|
+
entry.rawScoreKnown = true;
|
|
22552
|
+
}
|
|
22553
|
+
if (objective.score?.min !== void 0 && objective.score.min !== "") {
|
|
22554
|
+
entry.minScore = objective.score.min;
|
|
22555
|
+
entry.minScoreKnown = true;
|
|
22556
|
+
}
|
|
22557
|
+
if (objective.score?.max !== void 0 && objective.score.max !== "") {
|
|
22558
|
+
entry.maxScore = objective.score.max;
|
|
22559
|
+
entry.maxScoreKnown = true;
|
|
22560
|
+
}
|
|
21937
22561
|
const progressMeasure = this.parseObjectiveNumber(objective.progress_measure);
|
|
21938
22562
|
if (progressMeasure !== null) {
|
|
21939
22563
|
entry.progressMeasure = progressMeasure;
|
|
@@ -21954,6 +22578,9 @@ class GlobalObjectiveManager {
|
|
|
21954
22578
|
* @return {CMIObjectivesObject[]} An array of `CMIObjectivesObject` instances built
|
|
21955
22579
|
* from the provided snapshot map. Returns an empty array
|
|
21956
22580
|
* if the snapshot is invalid or no valid objectives can be created.
|
|
22581
|
+
*
|
|
22582
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective snapshots restore CMI objective state
|
|
22583
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score known fields restore independently
|
|
21957
22584
|
*/
|
|
21958
22585
|
buildCMIObjectivesFromMap(snapshot) {
|
|
21959
22586
|
const objectives = [];
|
|
@@ -21973,6 +22600,15 @@ class GlobalObjectiveManager {
|
|
|
21973
22600
|
if (entry.normalizedMeasureKnown === true && normalizedMeasure !== null) {
|
|
21974
22601
|
objective.score.scaled = String(normalizedMeasure);
|
|
21975
22602
|
}
|
|
22603
|
+
if (entry.rawScoreKnown === true) {
|
|
22604
|
+
objective.score.raw = String(entry.rawScore ?? "");
|
|
22605
|
+
}
|
|
22606
|
+
if (entry.minScoreKnown === true) {
|
|
22607
|
+
objective.score.min = String(entry.minScore ?? "");
|
|
22608
|
+
}
|
|
22609
|
+
if (entry.maxScoreKnown === true) {
|
|
22610
|
+
objective.score.max = String(entry.maxScore ?? "");
|
|
22611
|
+
}
|
|
21976
22612
|
const progressMeasure = this.parseObjectiveNumber(entry.progressMeasure);
|
|
21977
22613
|
if (entry.progressMeasureKnown === true && progressMeasure !== null) {
|
|
21978
22614
|
objective.progress_measure = String(progressMeasure);
|
|
@@ -22083,6 +22719,9 @@ class GlobalObjectiveManager {
|
|
|
22083
22719
|
* @param {CompletionStatus} completionStatus
|
|
22084
22720
|
* @param {SuccessStatus} successStatus
|
|
22085
22721
|
* @param {ScoreObject} scoreObject
|
|
22722
|
+
*
|
|
22723
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - CMI score data updates the current primary objective
|
|
22724
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score data is available for write maps
|
|
22086
22725
|
*/
|
|
22087
22726
|
syncCmiToSequencingActivity(completionStatus, successStatus, scoreObject) {
|
|
22088
22727
|
if (!this.context.sequencing) {
|
|
@@ -22108,6 +22747,15 @@ class GlobalObjectiveManager {
|
|
|
22108
22747
|
primaryObjective.normalizedMeasure = scoreObject.scaled;
|
|
22109
22748
|
primaryObjective.measureStatus = true;
|
|
22110
22749
|
}
|
|
22750
|
+
if (scoreObject?.raw !== void 0 && scoreObject.raw !== null) {
|
|
22751
|
+
primaryObjective.rawScore = String(scoreObject.raw);
|
|
22752
|
+
}
|
|
22753
|
+
if (scoreObject?.min !== void 0 && scoreObject.min !== null) {
|
|
22754
|
+
primaryObjective.minScore = String(scoreObject.min);
|
|
22755
|
+
}
|
|
22756
|
+
if (scoreObject?.max !== void 0 && scoreObject.max !== null) {
|
|
22757
|
+
primaryObjective.maxScore = String(scoreObject.max);
|
|
22758
|
+
}
|
|
22111
22759
|
}
|
|
22112
22760
|
/**
|
|
22113
22761
|
* Find or create a global objective by ID
|
|
@@ -22660,16 +23308,232 @@ class Scorm2004API extends BaseAPI {
|
|
|
22660
23308
|
/**
|
|
22661
23309
|
* Apply launch-static activity data to CMI while the new SCO is pre-initialize.
|
|
22662
23310
|
*
|
|
22663
|
-
* @spec SCORM 2004 4th Ed. RTE 4.2.
|
|
23311
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold
|
|
23312
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
22664
23313
|
*/
|
|
22665
23314
|
applyCurrentActivityLaunchData() {
|
|
22666
23315
|
const currentActivity = this._sequencing?.getCurrentActivity();
|
|
22667
|
-
if (!
|
|
23316
|
+
if (!currentActivity) {
|
|
23317
|
+
return;
|
|
23318
|
+
}
|
|
23319
|
+
this.applyActivityLaunchData(currentActivity);
|
|
23320
|
+
}
|
|
23321
|
+
/**
|
|
23322
|
+
* Apply launch-static CMI data when sequencing delivers a new activity before SCO Initialize.
|
|
23323
|
+
*
|
|
23324
|
+
* @spec SCORM 2004 4th Ed. SN DB.2 - Content Delivery Environment Process establishes the delivered activity.
|
|
23325
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold is initialized before SCO access.
|
|
23326
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives is initialized before SCO access.
|
|
23327
|
+
*/
|
|
23328
|
+
applyDeliveredActivityLaunchData(activity) {
|
|
23329
|
+
if (!this.isNotInitialized()) {
|
|
23330
|
+
return;
|
|
23331
|
+
}
|
|
23332
|
+
this.applyActivityLaunchData(activity);
|
|
23333
|
+
}
|
|
23334
|
+
/**
|
|
23335
|
+
* Copy the delivered activity's static launch data into the fresh CMI model.
|
|
23336
|
+
*
|
|
23337
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold
|
|
23338
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
23339
|
+
*/
|
|
23340
|
+
applyActivityLaunchData(currentActivity) {
|
|
23341
|
+
if (!this.cmi) {
|
|
22668
23342
|
return;
|
|
22669
23343
|
}
|
|
22670
23344
|
const contentActivityData = this._sequencing.overallSequencingProcess?.getContentActivityData(currentActivity);
|
|
22671
23345
|
const completionThreshold = contentActivityData?.completionThreshold ?? currentActivity.completionThreshold?.toString() ?? "";
|
|
22672
23346
|
this.cmi.completion_threshold = completionThreshold;
|
|
23347
|
+
this.seedCurrentActivityObjectives(currentActivity);
|
|
23348
|
+
}
|
|
23349
|
+
/**
|
|
23350
|
+
* Seed CMI objective ids after Initialize when automatic sequencing starts during Initialize.
|
|
23351
|
+
*
|
|
23352
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
23353
|
+
*/
|
|
23354
|
+
applyCurrentActivityObjectiveData() {
|
|
23355
|
+
const currentActivity = this._sequencing?.getCurrentActivity();
|
|
23356
|
+
if (!this.cmi || !currentActivity) {
|
|
23357
|
+
return;
|
|
23358
|
+
}
|
|
23359
|
+
this.seedCurrentActivityObjectives(currentActivity);
|
|
23360
|
+
}
|
|
23361
|
+
/**
|
|
23362
|
+
* Seed CMI objectives from primary and secondary activity objectives.
|
|
23363
|
+
*
|
|
23364
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17 - Initialization of Run-Time Objectives from Sequencing Information
|
|
23365
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.id and success_status
|
|
23366
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - objective read maps seed the RTE view
|
|
23367
|
+
*/
|
|
23368
|
+
seedCurrentActivityObjectives(currentActivity) {
|
|
23369
|
+
const activityObjectives = [];
|
|
23370
|
+
if (currentActivity.primaryObjective) {
|
|
23371
|
+
activityObjectives.push(currentActivity.primaryObjective);
|
|
23372
|
+
}
|
|
23373
|
+
activityObjectives.push(...currentActivity.objectives);
|
|
23374
|
+
const seededObjectiveIds = /* @__PURE__ */ new Set();
|
|
23375
|
+
for (const activityObjective of activityObjectives) {
|
|
23376
|
+
const objectiveId = this.getSeedableObjectiveId(activityObjective);
|
|
23377
|
+
if (!objectiveId || seededObjectiveIds.has(objectiveId)) {
|
|
23378
|
+
continue;
|
|
23379
|
+
}
|
|
23380
|
+
seededObjectiveIds.add(objectiveId);
|
|
23381
|
+
const index = this.findOrSeedCMIObjective(objectiveId);
|
|
23382
|
+
if (index === null) {
|
|
23383
|
+
continue;
|
|
23384
|
+
}
|
|
23385
|
+
const cmiObjective = this.cmi.objectives.findObjectiveByIndex(index);
|
|
23386
|
+
if (cmiObjective && this.cmi.objectives.initialized && !cmiObjective.initialized) {
|
|
23387
|
+
cmiObjective.initialize();
|
|
23388
|
+
}
|
|
23389
|
+
const successStatus = this.getActivityObjectiveSuccessStatus(activityObjective);
|
|
23390
|
+
if (successStatus) {
|
|
23391
|
+
this._commonSetCMIValue(
|
|
23392
|
+
"SeedActivityObjective",
|
|
23393
|
+
true,
|
|
23394
|
+
`cmi.objectives.${index}.success_status`,
|
|
23395
|
+
successStatus
|
|
23396
|
+
);
|
|
23397
|
+
}
|
|
23398
|
+
this.seedObjectiveReadMapValues(currentActivity, activityObjective, index);
|
|
23399
|
+
}
|
|
23400
|
+
}
|
|
23401
|
+
/**
|
|
23402
|
+
* Seed CMI objective fields from this objective's read-mapped global objectives.
|
|
23403
|
+
*
|
|
23404
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17 - Run-Time Objectives are initialized from sequencing information
|
|
23405
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read mapInfo grants access to mapped global objective state
|
|
23406
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps seed RTE score fields
|
|
23407
|
+
*/
|
|
23408
|
+
seedObjectiveReadMapValues(currentActivity, activityObjective, objectiveIndex) {
|
|
23409
|
+
const globalObjectiveMap = this._sequencing.overallSequencingProcess?.getGlobalObjectiveMap();
|
|
23410
|
+
if (!globalObjectiveMap || activityObjective.mapInfo.length === 0) {
|
|
23411
|
+
return;
|
|
23412
|
+
}
|
|
23413
|
+
for (const mapInfo of activityObjective.mapInfo) {
|
|
23414
|
+
const targetObjectiveId = mapInfo.targetObjectiveID || activityObjective.id;
|
|
23415
|
+
const globalObjective = globalObjectiveMap.get(targetObjectiveId);
|
|
23416
|
+
if (!globalObjective) {
|
|
23417
|
+
continue;
|
|
23418
|
+
}
|
|
23419
|
+
const readState = GlobalObjectiveSynchronizer.getGlobalObjectiveReadState(
|
|
23420
|
+
currentActivity,
|
|
23421
|
+
activityObjective,
|
|
23422
|
+
mapInfo,
|
|
23423
|
+
globalObjective
|
|
23424
|
+
);
|
|
23425
|
+
this.applyObjectiveReadStateToCMI(objectiveIndex, readState);
|
|
23426
|
+
}
|
|
23427
|
+
}
|
|
23428
|
+
/**
|
|
23429
|
+
* Copy mapped global objective state into the seeded CMI objective entry.
|
|
23430
|
+
*
|
|
23431
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives launch-time initialization
|
|
23432
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps populate the RTE view without creating local writes
|
|
23433
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps populate CMI objective scores
|
|
23434
|
+
*/
|
|
23435
|
+
applyObjectiveReadStateToCMI(objectiveIndex, readState) {
|
|
23436
|
+
if (readState.satisfiedStatus !== void 0) {
|
|
23437
|
+
this._commonSetCMIValue(
|
|
23438
|
+
"SeedActivityObjectiveReadMap",
|
|
23439
|
+
true,
|
|
23440
|
+
`cmi.objectives.${objectiveIndex}.success_status`,
|
|
23441
|
+
readState.satisfiedStatus ? SuccessStatus.PASSED : SuccessStatus.FAILED
|
|
23442
|
+
);
|
|
23443
|
+
}
|
|
23444
|
+
if (readState.normalizedMeasure !== void 0) {
|
|
23445
|
+
this._commonSetCMIValue(
|
|
23446
|
+
"SeedActivityObjectiveReadMap",
|
|
23447
|
+
true,
|
|
23448
|
+
`cmi.objectives.${objectiveIndex}.score.scaled`,
|
|
23449
|
+
String(readState.normalizedMeasure)
|
|
23450
|
+
);
|
|
23451
|
+
}
|
|
23452
|
+
if (readState.completionStatus !== void 0) {
|
|
23453
|
+
this._commonSetCMIValue(
|
|
23454
|
+
"SeedActivityObjectiveReadMap",
|
|
23455
|
+
true,
|
|
23456
|
+
`cmi.objectives.${objectiveIndex}.completion_status`,
|
|
23457
|
+
readState.completionStatus
|
|
23458
|
+
);
|
|
23459
|
+
}
|
|
23460
|
+
if (readState.progressMeasure !== void 0) {
|
|
23461
|
+
this._commonSetCMIValue(
|
|
23462
|
+
"SeedActivityObjectiveReadMap",
|
|
23463
|
+
true,
|
|
23464
|
+
`cmi.objectives.${objectiveIndex}.progress_measure`,
|
|
23465
|
+
String(readState.progressMeasure)
|
|
23466
|
+
);
|
|
23467
|
+
}
|
|
23468
|
+
if (readState.rawScore !== void 0) {
|
|
23469
|
+
this._commonSetCMIValue(
|
|
23470
|
+
"SeedActivityObjectiveReadMap",
|
|
23471
|
+
true,
|
|
23472
|
+
`cmi.objectives.${objectiveIndex}.score.raw`,
|
|
23473
|
+
readState.rawScore
|
|
23474
|
+
);
|
|
23475
|
+
}
|
|
23476
|
+
if (readState.minScore !== void 0) {
|
|
23477
|
+
this._commonSetCMIValue(
|
|
23478
|
+
"SeedActivityObjectiveReadMap",
|
|
23479
|
+
true,
|
|
23480
|
+
`cmi.objectives.${objectiveIndex}.score.min`,
|
|
23481
|
+
readState.minScore
|
|
23482
|
+
);
|
|
23483
|
+
}
|
|
23484
|
+
if (readState.maxScore !== void 0) {
|
|
23485
|
+
this._commonSetCMIValue(
|
|
23486
|
+
"SeedActivityObjectiveReadMap",
|
|
23487
|
+
true,
|
|
23488
|
+
`cmi.objectives.${objectiveIndex}.score.max`,
|
|
23489
|
+
readState.maxScore
|
|
23490
|
+
);
|
|
23491
|
+
}
|
|
23492
|
+
}
|
|
23493
|
+
/**
|
|
23494
|
+
* Return a manifest-defined objective id that can initialize cmi.objectives.n.id.
|
|
23495
|
+
*
|
|
23496
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.id
|
|
23497
|
+
*/
|
|
23498
|
+
getSeedableObjectiveId(activityObjective) {
|
|
23499
|
+
const objectiveId = activityObjective.id;
|
|
23500
|
+
if (typeof objectiveId !== "string" || objectiveId.trim() === "") {
|
|
23501
|
+
return null;
|
|
23502
|
+
}
|
|
23503
|
+
return objectiveId;
|
|
23504
|
+
}
|
|
23505
|
+
/**
|
|
23506
|
+
* Find an existing CMI objective id or create the next CMI objective array entry.
|
|
23507
|
+
*
|
|
23508
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives._count
|
|
23509
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.id
|
|
23510
|
+
*/
|
|
23511
|
+
findOrSeedCMIObjective(objectiveId) {
|
|
23512
|
+
const existingIndex = this.cmi.objectives.childArray.findIndex((objective) => {
|
|
23513
|
+
return objective.id === objectiveId;
|
|
23514
|
+
});
|
|
23515
|
+
if (existingIndex >= 0) {
|
|
23516
|
+
return existingIndex;
|
|
23517
|
+
}
|
|
23518
|
+
const index = this.cmi.objectives.childArray.length;
|
|
23519
|
+
const result = this._commonSetCMIValue(
|
|
23520
|
+
"SeedActivityObjective",
|
|
23521
|
+
true,
|
|
23522
|
+
`cmi.objectives.${index}.id`,
|
|
23523
|
+
objectiveId
|
|
23524
|
+
);
|
|
23525
|
+
return result === global_constants.SCORM_TRUE ? index : null;
|
|
23526
|
+
}
|
|
23527
|
+
/**
|
|
23528
|
+
* Translate a known activity objective satisfied status to cmi.objectives.n.success_status.
|
|
23529
|
+
*
|
|
23530
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.success_status
|
|
23531
|
+
*/
|
|
23532
|
+
getActivityObjectiveSuccessStatus(activityObjective) {
|
|
23533
|
+
if (activityObjective.progressStatus || activityObjective.satisfiedStatusKnown) {
|
|
23534
|
+
return activityObjective.satisfiedStatus ? SuccessStatus.PASSED : SuccessStatus.FAILED;
|
|
23535
|
+
}
|
|
23536
|
+
return null;
|
|
22673
23537
|
}
|
|
22674
23538
|
/**
|
|
22675
23539
|
* Getter for _version
|
|
@@ -22750,6 +23614,7 @@ class Scorm2004API extends BaseAPI {
|
|
|
22750
23614
|
);
|
|
22751
23615
|
if (result === global_constants.SCORM_TRUE && this._sequencingService) {
|
|
22752
23616
|
this._sequencingService.initialize();
|
|
23617
|
+
this.applyCurrentActivityObjectiveData();
|
|
22753
23618
|
}
|
|
22754
23619
|
if (result === global_constants.SCORM_TRUE) {
|
|
22755
23620
|
this._globalObjectiveManager.restoreGlobalObjectivesToCMI();
|
|
@@ -23004,7 +23869,7 @@ class Scorm2004API extends BaseAPI {
|
|
|
23004
23869
|
objective_id = objective ? objective.id : void 0;
|
|
23005
23870
|
}
|
|
23006
23871
|
const is_global = objective_id && this.settings.globalObjectiveIds?.includes(objective_id);
|
|
23007
|
-
if (is_global) {
|
|
23872
|
+
if (is_global && this.currentActivityAllowsGlobalObjectiveWrites()) {
|
|
23008
23873
|
const { index: global_index } = this._globalObjectiveManager.findOrCreateGlobalObjective(objective_id);
|
|
23009
23874
|
const global_element = CMIElement.replace(
|
|
23010
23875
|
element_base,
|
|
@@ -23019,6 +23884,15 @@ class Scorm2004API extends BaseAPI {
|
|
|
23019
23884
|
}
|
|
23020
23885
|
return this._commonSetCMIValue("SetValue", true, CMIElement, value);
|
|
23021
23886
|
}
|
|
23887
|
+
/**
|
|
23888
|
+
* Return whether the current activity can update shared global objectives.
|
|
23889
|
+
*
|
|
23890
|
+
* @spec SCORM 2004 4th Ed. SN 3.13.1 Tracked - when False, the LMS
|
|
23891
|
+
* "does not initialize, manage or access any tracking status information".
|
|
23892
|
+
*/
|
|
23893
|
+
currentActivityAllowsGlobalObjectiveWrites() {
|
|
23894
|
+
return this._sequencing?.getCurrentActivity()?.sequencingControls.tracked !== false;
|
|
23895
|
+
}
|
|
23022
23896
|
/**
|
|
23023
23897
|
* Gets or builds a new child element to add to the array
|
|
23024
23898
|
* @param {string} CMIElement
|
|
@@ -23363,9 +24237,9 @@ class Scorm2004API extends BaseAPI {
|
|
|
23363
24237
|
this.loggingService,
|
|
23364
24238
|
sequencingConfig
|
|
23365
24239
|
);
|
|
23366
|
-
|
|
23367
|
-
this.
|
|
23368
|
-
|
|
24240
|
+
this._sequencingService.setEventListeners(
|
|
24241
|
+
this.buildSequencingEventListeners(settings?.sequencing?.eventListeners)
|
|
24242
|
+
);
|
|
23369
24243
|
this._globalObjectiveManager.updateSequencingService(this._sequencingService);
|
|
23370
24244
|
this._dataSerializer.updateSequencingService(this._sequencingService);
|
|
23371
24245
|
if (settings?.sequencingStatePersistence) {
|
|
@@ -23388,6 +24262,22 @@ class Scorm2004API extends BaseAPI {
|
|
|
23388
24262
|
this._sequencingService = null;
|
|
23389
24263
|
}
|
|
23390
24264
|
}
|
|
24265
|
+
/**
|
|
24266
|
+
* Wrap LMS-provided sequencing listeners with API-owned delivery bookkeeping.
|
|
24267
|
+
*
|
|
24268
|
+
* @spec SCORM 2004 4th Ed. SN DB.2 - Content Delivery Environment Process
|
|
24269
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold
|
|
24270
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
24271
|
+
*/
|
|
24272
|
+
buildSequencingEventListeners(listeners) {
|
|
24273
|
+
return {
|
|
24274
|
+
...listeners,
|
|
24275
|
+
onActivityDelivery: (activity) => {
|
|
24276
|
+
this.applyDeliveredActivityLaunchData(activity);
|
|
24277
|
+
listeners?.onActivityDelivery?.(activity);
|
|
24278
|
+
}
|
|
24279
|
+
};
|
|
24280
|
+
}
|
|
23391
24281
|
/**
|
|
23392
24282
|
* Get the sequencing service
|
|
23393
24283
|
* @return {SequencingService | null}
|
|
@@ -23398,10 +24288,13 @@ class Scorm2004API extends BaseAPI {
|
|
|
23398
24288
|
/**
|
|
23399
24289
|
* Set sequencing event listeners
|
|
23400
24290
|
* @param {SequencingEventListeners} listeners
|
|
24291
|
+
*
|
|
24292
|
+
* @spec SCORM 2004 4th Ed. SN DB.2 - Content Delivery Environment Process
|
|
24293
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5 / 4.2.17 - launch-static CMI data
|
|
23401
24294
|
*/
|
|
23402
24295
|
setSequencingEventListeners(listeners) {
|
|
23403
24296
|
if (this._sequencingService) {
|
|
23404
|
-
this._sequencingService.setEventListeners(listeners);
|
|
24297
|
+
this._sequencingService.setEventListeners(this.buildSequencingEventListeners(listeners));
|
|
23405
24298
|
}
|
|
23406
24299
|
}
|
|
23407
24300
|
/**
|