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/scorm-again.js
CHANGED
|
@@ -197,12 +197,18 @@ function stringMatches(str, tester) {
|
|
|
197
197
|
}
|
|
198
198
|
return new RegExp(tester).test(str);
|
|
199
199
|
}
|
|
200
|
-
function memoize(fn, keyFn) {
|
|
200
|
+
function memoize(fn, keyFn, options) {
|
|
201
201
|
const cache = /* @__PURE__ */ new Map();
|
|
202
202
|
return ((...args) => {
|
|
203
203
|
const key = keyFn ? keyFn(...args) : JSON.stringify(args);
|
|
204
|
+
if (options?.maxKeyLength !== void 0 && key.length > options.maxKeyLength) {
|
|
205
|
+
return fn(...args);
|
|
206
|
+
}
|
|
204
207
|
return cache.has(key) ? cache.get(key) : (() => {
|
|
205
208
|
const result = fn(...args);
|
|
209
|
+
if (options?.maxEntries !== void 0 && cache.size >= options.maxEntries) {
|
|
210
|
+
cache.delete(cache.keys().next().value);
|
|
211
|
+
}
|
|
206
212
|
cache.set(key, result);
|
|
207
213
|
return result;
|
|
208
214
|
})();
|
|
@@ -1103,6 +1109,8 @@ const scorm2004_regex = {
|
|
|
1103
1109
|
CMILang: "^([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?$|^$",
|
|
1104
1110
|
/** CMILangString250 - String with optional language tag, max 250 chars (RTE C.1.3) */
|
|
1105
1111
|
CMILangString250: "^({lang=([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?})?((?!{.*$).{0,250}$)?$",
|
|
1112
|
+
/** CMILangString - Optional language tag, no length cap; RTE C.1.3 SPM is a floor and is deliberately not enforced */
|
|
1113
|
+
CMILangString: "^({lang=([a-zA-Z]{1,8}|i|x)(-[a-zA-Z0-9-]{2,8})?})?((?!{.*$).*$)?$",
|
|
1106
1114
|
/** CMILangcr - Language tag pattern with content */
|
|
1107
1115
|
CMILangcr: "^(({lang=([a-zA-Z]{1,8}|i|x)?(-[a-zA-Z0-9-]{2,8})?}))(.*?)$",
|
|
1108
1116
|
/** CMILangString250cr - String with optional language tag (carriage return variant) */
|
|
@@ -1768,12 +1776,6 @@ const HIDE_LMS_UI_TOKENS = [
|
|
|
1768
1776
|
"suspendAll"
|
|
1769
1777
|
];
|
|
1770
1778
|
|
|
1771
|
-
var RuleConditionOperator = /* @__PURE__ */ ((RuleConditionOperator2) => {
|
|
1772
|
-
RuleConditionOperator2["NOT"] = "not";
|
|
1773
|
-
RuleConditionOperator2["AND"] = "and";
|
|
1774
|
-
RuleConditionOperator2["OR"] = "or";
|
|
1775
|
-
return RuleConditionOperator2;
|
|
1776
|
-
})(RuleConditionOperator || {});
|
|
1777
1779
|
var RuleActionType = /* @__PURE__ */ ((RuleActionType2) => {
|
|
1778
1780
|
RuleActionType2["SKIP"] = "skip";
|
|
1779
1781
|
RuleActionType2["DISABLED"] = "disabled";
|
|
@@ -1788,6 +1790,48 @@ var RuleActionType = /* @__PURE__ */ ((RuleActionType2) => {
|
|
|
1788
1790
|
RuleActionType2["EXIT"] = "exit";
|
|
1789
1791
|
return RuleActionType2;
|
|
1790
1792
|
})(RuleActionType || {});
|
|
1793
|
+
function kleeneNot(value) {
|
|
1794
|
+
if (value === "unknown") {
|
|
1795
|
+
return "unknown";
|
|
1796
|
+
}
|
|
1797
|
+
return !value;
|
|
1798
|
+
}
|
|
1799
|
+
function kleeneAnd(values) {
|
|
1800
|
+
let hasUnknown = false;
|
|
1801
|
+
for (const value of values) {
|
|
1802
|
+
if (value === false) {
|
|
1803
|
+
return false;
|
|
1804
|
+
}
|
|
1805
|
+
if (value === "unknown") {
|
|
1806
|
+
hasUnknown = true;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
return hasUnknown ? "unknown" : true;
|
|
1810
|
+
}
|
|
1811
|
+
function kleeneOr(values) {
|
|
1812
|
+
let hasUnknown = false;
|
|
1813
|
+
for (const value of values) {
|
|
1814
|
+
if (value === true) {
|
|
1815
|
+
return true;
|
|
1816
|
+
}
|
|
1817
|
+
if (value === "unknown") {
|
|
1818
|
+
hasUnknown = true;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
return hasUnknown ? "unknown" : false;
|
|
1822
|
+
}
|
|
1823
|
+
function combineRuleConditionResults(values, conditionCombination) {
|
|
1824
|
+
if (values.length === 0) {
|
|
1825
|
+
return false;
|
|
1826
|
+
}
|
|
1827
|
+
if (conditionCombination === "all" || conditionCombination === "and" /* AND */) {
|
|
1828
|
+
return kleeneAnd(values);
|
|
1829
|
+
}
|
|
1830
|
+
if (conditionCombination === "any" || conditionCombination === "or" /* OR */) {
|
|
1831
|
+
return kleeneOr(values);
|
|
1832
|
+
}
|
|
1833
|
+
return false;
|
|
1834
|
+
}
|
|
1791
1835
|
class RuleCondition extends BaseCMI {
|
|
1792
1836
|
_condition = "always" /* ALWAYS */;
|
|
1793
1837
|
_operator = null;
|
|
@@ -1893,51 +1937,72 @@ class RuleCondition extends BaseCMI {
|
|
|
1893
1937
|
/**
|
|
1894
1938
|
* Evaluate the condition for an activity
|
|
1895
1939
|
* @param {Activity} activity - The activity to evaluate the condition for
|
|
1896
|
-
* @return {
|
|
1940
|
+
* @return {RuleConditionEvaluation} - True, false, or unknown per SCORM 2004 4th Ed.
|
|
1897
1941
|
*/
|
|
1898
1942
|
evaluate(activity) {
|
|
1899
1943
|
let result;
|
|
1944
|
+
const hasReferencedObjective = this._referencedObjective !== null;
|
|
1900
1945
|
const referencedObjective = this.resolveReferencedObjective(activity);
|
|
1901
1946
|
switch (this._condition) {
|
|
1902
1947
|
case "satisfied" /* SATISFIED */:
|
|
1903
1948
|
case "objectiveSatisfied" /* OBJECTIVE_SATISFIED */:
|
|
1904
|
-
if (referencedObjective) {
|
|
1905
|
-
result =
|
|
1949
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1950
|
+
result = false;
|
|
1951
|
+
} else if (referencedObjective) {
|
|
1952
|
+
result = referencedObjective.satisfiedStatusKnown || referencedObjective.progressStatus ? referencedObjective.satisfiedStatus === true : "unknown";
|
|
1953
|
+
} else if (activity.objectiveSatisfiedStatusKnown) {
|
|
1954
|
+
result = activity.objectiveSatisfiedStatus === true;
|
|
1955
|
+
} else if (activity.successStatus !== SuccessStatus.UNKNOWN) {
|
|
1956
|
+
result = activity.successStatus === SuccessStatus.PASSED;
|
|
1906
1957
|
} else {
|
|
1907
|
-
result =
|
|
1958
|
+
result = "unknown";
|
|
1908
1959
|
}
|
|
1909
1960
|
break;
|
|
1910
1961
|
case "objectiveStatusKnown" /* OBJECTIVE_STATUS_KNOWN */:
|
|
1911
|
-
result = referencedObjective ? !!referencedObjective.
|
|
1962
|
+
result = hasReferencedObjective && !referencedObjective ? false : referencedObjective ? !!referencedObjective.satisfiedStatusKnown : !!activity.objectiveSatisfiedStatusKnown;
|
|
1912
1963
|
break;
|
|
1913
1964
|
case "objectiveMeasureKnown" /* OBJECTIVE_MEASURE_KNOWN */:
|
|
1914
|
-
result = referencedObjective ? !!referencedObjective.measureStatus : !!activity.objectiveMeasureStatus;
|
|
1965
|
+
result = hasReferencedObjective && !referencedObjective ? false : referencedObjective ? !!referencedObjective.measureStatus : !!activity.objectiveMeasureStatus;
|
|
1915
1966
|
break;
|
|
1916
1967
|
case "objectiveMeasureGreaterThan" /* OBJECTIVE_MEASURE_GREATER_THAN */: {
|
|
1968
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1969
|
+
result = false;
|
|
1970
|
+
break;
|
|
1971
|
+
}
|
|
1917
1972
|
const greaterThanValue = this._parameters.get("threshold") || 0;
|
|
1918
1973
|
const measureStatus = referencedObjective ? referencedObjective.measureStatus : activity.objectiveMeasureStatus;
|
|
1919
1974
|
const measureValue = referencedObjective ? referencedObjective.normalizedMeasure : activity.objectiveNormalizedMeasure;
|
|
1920
|
-
result =
|
|
1975
|
+
result = measureStatus ? measureValue > greaterThanValue : "unknown";
|
|
1921
1976
|
break;
|
|
1922
1977
|
}
|
|
1923
1978
|
case "objectiveMeasureLessThan" /* OBJECTIVE_MEASURE_LESS_THAN */: {
|
|
1979
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1980
|
+
result = false;
|
|
1981
|
+
break;
|
|
1982
|
+
}
|
|
1924
1983
|
const lessThanValue = this._parameters.get("threshold") || 0;
|
|
1925
1984
|
const measureStatus = referencedObjective ? referencedObjective.measureStatus : activity.objectiveMeasureStatus;
|
|
1926
1985
|
const measureValue = referencedObjective ? referencedObjective.normalizedMeasure : activity.objectiveNormalizedMeasure;
|
|
1927
|
-
result =
|
|
1986
|
+
result = measureStatus ? measureValue < lessThanValue : "unknown";
|
|
1928
1987
|
break;
|
|
1929
1988
|
}
|
|
1930
1989
|
case "completed" /* COMPLETED */:
|
|
1931
1990
|
case "activityCompleted" /* ACTIVITY_COMPLETED */:
|
|
1932
|
-
if (referencedObjective) {
|
|
1933
|
-
result =
|
|
1991
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
1992
|
+
result = false;
|
|
1993
|
+
} else if (referencedObjective) {
|
|
1994
|
+
result = referencedObjective.completionStatus === CompletionStatus.UNKNOWN ? "unknown" : referencedObjective.completionStatus === CompletionStatus.COMPLETED;
|
|
1995
|
+
} else if (activity.completionStatus === CompletionStatus.UNKNOWN) {
|
|
1996
|
+
result = "unknown";
|
|
1934
1997
|
} else {
|
|
1935
|
-
result = activity.
|
|
1998
|
+
result = activity.completionStatus === CompletionStatus.COMPLETED;
|
|
1936
1999
|
}
|
|
1937
2000
|
break;
|
|
1938
2001
|
case "progressKnown" /* PROGRESS_KNOWN */:
|
|
1939
2002
|
case "activityProgressKnown" /* ACTIVITY_PROGRESS_KNOWN */:
|
|
1940
|
-
if (referencedObjective) {
|
|
2003
|
+
if (hasReferencedObjective && !referencedObjective) {
|
|
2004
|
+
result = false;
|
|
2005
|
+
} else if (referencedObjective) {
|
|
1941
2006
|
result = referencedObjective.completionStatus !== CompletionStatus.UNKNOWN;
|
|
1942
2007
|
} else {
|
|
1943
2008
|
result = activity.completionStatus !== "unknown";
|
|
@@ -1966,7 +2031,7 @@ class RuleCondition extends BaseCMI {
|
|
|
1966
2031
|
break;
|
|
1967
2032
|
}
|
|
1968
2033
|
if (this._operator === "not" /* NOT */) {
|
|
1969
|
-
result =
|
|
2034
|
+
result = kleeneNot(result);
|
|
1970
2035
|
}
|
|
1971
2036
|
return result;
|
|
1972
2037
|
}
|
|
@@ -2173,15 +2238,10 @@ class SequencingRule extends BaseCMI {
|
|
|
2173
2238
|
* @return {boolean} - True if the rule conditions are met, false otherwise
|
|
2174
2239
|
*/
|
|
2175
2240
|
evaluate(activity) {
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
return this._conditions.every((condition) => condition.evaluate(activity));
|
|
2181
|
-
} else if (this._conditionCombination === "any" || this._conditionCombination === "or" /* OR */) {
|
|
2182
|
-
return this._conditions.some((condition) => condition.evaluate(activity));
|
|
2183
|
-
}
|
|
2184
|
-
return false;
|
|
2241
|
+
return combineRuleConditionResults(
|
|
2242
|
+
this._conditions.map((condition) => condition.evaluate(activity)),
|
|
2243
|
+
this._conditionCombination
|
|
2244
|
+
) === true;
|
|
2185
2245
|
}
|
|
2186
2246
|
/**
|
|
2187
2247
|
* toJSON for SequencingRule
|
|
@@ -3014,19 +3074,10 @@ class RuleEvaluationEngine {
|
|
|
3014
3074
|
* Evaluates individual sequencing rule conditions
|
|
3015
3075
|
* @param {Activity} activity - The activity to evaluate the rule for
|
|
3016
3076
|
* @param {SequencingRule} rule - The rule to evaluate
|
|
3017
|
-
* @return {boolean} - True
|
|
3077
|
+
* @return {boolean} - True only when the rule evaluates to definite true
|
|
3018
3078
|
*/
|
|
3019
3079
|
checkRuleSubprocess(activity, rule) {
|
|
3020
|
-
|
|
3021
|
-
return true;
|
|
3022
|
-
}
|
|
3023
|
-
const conditionCombination = rule.conditionCombination;
|
|
3024
|
-
if (conditionCombination === "all" || conditionCombination === RuleConditionOperator.AND) {
|
|
3025
|
-
return rule.conditions.every((condition) => condition.evaluate(activity));
|
|
3026
|
-
} else if (conditionCombination === "any" || conditionCombination === RuleConditionOperator.OR) {
|
|
3027
|
-
return rule.conditions.some((condition) => condition.evaluate(activity));
|
|
3028
|
-
}
|
|
3029
|
-
return false;
|
|
3080
|
+
return rule.evaluate(activity);
|
|
3030
3081
|
}
|
|
3031
3082
|
/**
|
|
3032
3083
|
* Exit Action Rules Subprocess (TB.2.1)
|
|
@@ -3898,16 +3949,21 @@ class FlowTraversalService {
|
|
|
3898
3949
|
* @param {Activity} fromActivity - The activity to flow from
|
|
3899
3950
|
* @param {FlowSubprocessMode} direction - The flow direction
|
|
3900
3951
|
* @return {FlowSubprocessResult} - Result containing the deliverable activity
|
|
3952
|
+
* @spec SN Book: SB.2.3 (Flow Subprocess) - preserves the SB.2.1 effective traversal direction for SB.2.2.
|
|
3953
|
+
* @spec SN Book: SB.2.2 (Flow Activity Traversal Subprocess) - evaluates candidates using the effective direction returned by SB.2.1.
|
|
3901
3954
|
*/
|
|
3902
3955
|
flowSubprocess(fromActivity, direction) {
|
|
3903
3956
|
let candidateActivity = fromActivity;
|
|
3904
3957
|
let firstIteration = true;
|
|
3905
3958
|
let lastCandidateHadNoChildren = false;
|
|
3959
|
+
let currentDirection = direction;
|
|
3960
|
+
let forwardOnlyCluster = null;
|
|
3906
3961
|
while (candidateActivity) {
|
|
3907
3962
|
const traversalResult = this.flowTreeTraversalSubprocess(
|
|
3908
3963
|
candidateActivity,
|
|
3909
|
-
|
|
3910
|
-
firstIteration
|
|
3964
|
+
currentDirection,
|
|
3965
|
+
firstIteration,
|
|
3966
|
+
forwardOnlyCluster
|
|
3911
3967
|
);
|
|
3912
3968
|
if (!traversalResult.activity) {
|
|
3913
3969
|
let exceptionCode = null;
|
|
@@ -3925,17 +3981,22 @@ class FlowTraversalService {
|
|
|
3925
3981
|
traversalResult.endSequencingSession
|
|
3926
3982
|
);
|
|
3927
3983
|
}
|
|
3984
|
+
const effectiveDirection = traversalResult.direction || currentDirection;
|
|
3985
|
+
if (traversalResult.forwardOnlyCluster) {
|
|
3986
|
+
forwardOnlyCluster = traversalResult.forwardOnlyCluster;
|
|
3987
|
+
}
|
|
3928
3988
|
lastCandidateHadNoChildren = traversalResult.activity.children.length > 0 && traversalResult.activity.getAvailableChildren().length === 0;
|
|
3929
3989
|
const deliverable = this.flowActivityTraversalSubprocess(
|
|
3930
3990
|
traversalResult.activity,
|
|
3931
|
-
|
|
3991
|
+
effectiveDirection === FlowSubprocessMode.FORWARD,
|
|
3932
3992
|
true,
|
|
3933
|
-
|
|
3993
|
+
effectiveDirection
|
|
3934
3994
|
);
|
|
3935
3995
|
if (deliverable) {
|
|
3936
3996
|
return new FlowSubprocessResult(deliverable, true, null, false);
|
|
3937
3997
|
}
|
|
3938
3998
|
candidateActivity = traversalResult.activity;
|
|
3999
|
+
currentDirection = effectiveDirection;
|
|
3939
4000
|
firstIteration = false;
|
|
3940
4001
|
}
|
|
3941
4002
|
return new FlowSubprocessResult(null, false, null, false);
|
|
@@ -3946,11 +4007,13 @@ class FlowTraversalService {
|
|
|
3946
4007
|
* @param {Activity} fromActivity - The activity to traverse from
|
|
3947
4008
|
* @param {FlowSubprocessMode} direction - The traversal direction
|
|
3948
4009
|
* @param {boolean} skipChildren - Whether to skip checking children
|
|
4010
|
+
* @param {Activity | null} forwardTraversalBoundary - Cluster boundary for an SB.2.1 forwardOnly direction reversal
|
|
3949
4011
|
* @return {FlowTreeTraversalResult} - The next activity and flags
|
|
4012
|
+
* @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.
|
|
3950
4013
|
*/
|
|
3951
|
-
flowTreeTraversalSubprocess(fromActivity, direction, skipChildren = false) {
|
|
4014
|
+
flowTreeTraversalSubprocess(fromActivity, direction, skipChildren = false, forwardTraversalBoundary = null) {
|
|
3952
4015
|
if (direction === FlowSubprocessMode.FORWARD) {
|
|
3953
|
-
return this.traverseForward(fromActivity, skipChildren);
|
|
4016
|
+
return this.traverseForward(fromActivity, skipChildren, forwardTraversalBoundary);
|
|
3954
4017
|
} else {
|
|
3955
4018
|
return this.traverseBackward(fromActivity);
|
|
3956
4019
|
}
|
|
@@ -3959,10 +4022,15 @@ class FlowTraversalService {
|
|
|
3959
4022
|
* Traverse forward in the activity tree
|
|
3960
4023
|
* @param {Activity} fromActivity - Starting activity
|
|
3961
4024
|
* @param {boolean} skipChildren - Whether to skip children
|
|
4025
|
+
* @param {Activity | null} forwardTraversalBoundary - Cluster boundary for an SB.2.1 forwardOnly direction reversal
|
|
3962
4026
|
* @return {FlowTreeTraversalResult}
|
|
4027
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - a reversed Forward traversal from a forwardOnly cluster remains within that cluster.
|
|
3963
4028
|
*/
|
|
3964
|
-
traverseForward(fromActivity, skipChildren) {
|
|
3965
|
-
if (
|
|
4029
|
+
traverseForward(fromActivity, skipChildren, forwardTraversalBoundary = null) {
|
|
4030
|
+
if (forwardTraversalBoundary && !this.isDescendantOfOrSelf(fromActivity, forwardTraversalBoundary)) {
|
|
4031
|
+
return { activity: null, endSequencingSession: false };
|
|
4032
|
+
}
|
|
4033
|
+
if (!forwardTraversalBoundary && skipChildren && this.isActivityLastOverall(fromActivity)) {
|
|
3966
4034
|
if (this.activityTree.root) {
|
|
3967
4035
|
this.terminateDescendentAttempts(this.activityTree.root);
|
|
3968
4036
|
}
|
|
@@ -3981,6 +4049,9 @@ class FlowTraversalService {
|
|
|
3981
4049
|
if (nextSibling) {
|
|
3982
4050
|
return { activity: nextSibling, endSequencingSession: false };
|
|
3983
4051
|
}
|
|
4052
|
+
if (forwardTraversalBoundary && (current === forwardTraversalBoundary || current.parent === forwardTraversalBoundary)) {
|
|
4053
|
+
return { activity: null, endSequencingSession: false };
|
|
4054
|
+
}
|
|
3984
4055
|
current = current.parent;
|
|
3985
4056
|
}
|
|
3986
4057
|
if (this.activityTree.root) {
|
|
@@ -3992,6 +4063,7 @@ class FlowTraversalService {
|
|
|
3992
4063
|
* Traverse backward in the activity tree
|
|
3993
4064
|
* @param {Activity} fromActivity - Starting activity
|
|
3994
4065
|
* @return {FlowTreeTraversalResult}
|
|
4066
|
+
* @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.
|
|
3995
4067
|
*/
|
|
3996
4068
|
traverseBackward(fromActivity) {
|
|
3997
4069
|
if (fromActivity.parent && fromActivity.parent.sequencingControls.forwardOnly) {
|
|
@@ -3999,10 +4071,7 @@ class FlowTraversalService {
|
|
|
3999
4071
|
}
|
|
4000
4072
|
const previousSibling = this.activityTree.getPreviousSibling(fromActivity);
|
|
4001
4073
|
if (previousSibling) {
|
|
4002
|
-
return
|
|
4003
|
-
activity: this.getLastDescendant(previousSibling),
|
|
4004
|
-
endSequencingSession: false
|
|
4005
|
-
};
|
|
4074
|
+
return this.getBackwardTraversalEntry(previousSibling);
|
|
4006
4075
|
}
|
|
4007
4076
|
let current = fromActivity;
|
|
4008
4077
|
let ancestorIterations = 0;
|
|
@@ -4013,38 +4082,64 @@ class FlowTraversalService {
|
|
|
4013
4082
|
}
|
|
4014
4083
|
const parentPreviousSibling = this.activityTree.getPreviousSibling(current.parent);
|
|
4015
4084
|
if (parentPreviousSibling) {
|
|
4016
|
-
return
|
|
4017
|
-
activity: this.getLastDescendant(parentPreviousSibling),
|
|
4018
|
-
endSequencingSession: false
|
|
4019
|
-
};
|
|
4085
|
+
return this.getBackwardTraversalEntry(parentPreviousSibling);
|
|
4020
4086
|
}
|
|
4021
4087
|
current = current.parent;
|
|
4022
4088
|
}
|
|
4023
4089
|
return { activity: null, endSequencingSession: false };
|
|
4024
4090
|
}
|
|
4025
4091
|
/**
|
|
4026
|
-
* Get the
|
|
4092
|
+
* Get the activity entered by backward traversal.
|
|
4027
4093
|
* @param {Activity} activity - The activity
|
|
4028
|
-
* @return {
|
|
4094
|
+
* @return {FlowTreeTraversalResult} - The entered activity and effective direction
|
|
4095
|
+
* @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.
|
|
4029
4096
|
*/
|
|
4030
|
-
|
|
4031
|
-
let
|
|
4097
|
+
getBackwardTraversalEntry(activity) {
|
|
4098
|
+
let enteredActivity = activity;
|
|
4032
4099
|
let iterations = 0;
|
|
4033
4100
|
const maxIterations = 1e4;
|
|
4034
4101
|
while (true) {
|
|
4035
4102
|
if (++iterations > maxIterations) {
|
|
4036
|
-
throw new Error("Infinite loop detected while getting
|
|
4103
|
+
throw new Error("Infinite loop detected while getting backward traversal entry");
|
|
4037
4104
|
}
|
|
4038
|
-
this.ensureSelectionAndRandomization(
|
|
4039
|
-
const children =
|
|
4105
|
+
this.ensureSelectionAndRandomization(enteredActivity);
|
|
4106
|
+
const children = enteredActivity.getAvailableChildren();
|
|
4040
4107
|
if (children.length === 0) {
|
|
4041
4108
|
break;
|
|
4042
4109
|
}
|
|
4110
|
+
if (enteredActivity.sequencingControls.forwardOnly) {
|
|
4111
|
+
return {
|
|
4112
|
+
activity: children[0] || null,
|
|
4113
|
+
endSequencingSession: false,
|
|
4114
|
+
direction: FlowSubprocessMode.FORWARD,
|
|
4115
|
+
forwardOnlyCluster: enteredActivity
|
|
4116
|
+
};
|
|
4117
|
+
}
|
|
4043
4118
|
const lastChild = children[children.length - 1];
|
|
4044
4119
|
if (!lastChild) break;
|
|
4045
|
-
|
|
4120
|
+
enteredActivity = lastChild;
|
|
4046
4121
|
}
|
|
4047
|
-
return
|
|
4122
|
+
return {
|
|
4123
|
+
activity: enteredActivity,
|
|
4124
|
+
endSequencingSession: false
|
|
4125
|
+
};
|
|
4126
|
+
}
|
|
4127
|
+
/**
|
|
4128
|
+
* Check whether an activity is the same as or beneath an ancestor.
|
|
4129
|
+
* @param {Activity} activity - The activity to check
|
|
4130
|
+
* @param {Activity} ancestor - The expected ancestor
|
|
4131
|
+
* @return {boolean} - True when activity is within ancestor
|
|
4132
|
+
* @spec SN Book: SB.2.1 (Flow Tree Traversal Subprocess) - bounds Forward traversal after a forwardOnly direction reversal to the entered cluster.
|
|
4133
|
+
*/
|
|
4134
|
+
isDescendantOfOrSelf(activity, ancestor) {
|
|
4135
|
+
let current = activity;
|
|
4136
|
+
while (current) {
|
|
4137
|
+
if (current === ancestor) {
|
|
4138
|
+
return true;
|
|
4139
|
+
}
|
|
4140
|
+
current = current.parent;
|
|
4141
|
+
}
|
|
4142
|
+
return false;
|
|
4048
4143
|
}
|
|
4049
4144
|
/**
|
|
4050
4145
|
* Flow Activity Traversal Subprocess (SB.2.2)
|
|
@@ -6594,7 +6689,9 @@ const checkValidFormat = memoize(
|
|
|
6594
6689
|
(CMIElement, value, regexPattern, errorCode, _errorClass, allowEmptyString) => {
|
|
6595
6690
|
const valueKey = typeof value === "string" ? value : `[${typeof value}]`;
|
|
6596
6691
|
return `${CMIElement}:${valueKey}:${regexPattern}:${errorCode}:${allowEmptyString || false}`;
|
|
6597
|
-
}
|
|
6692
|
+
},
|
|
6693
|
+
// Normal capped CMI values and regexes fit within 2000 characters; large uncapped values bypass caching.
|
|
6694
|
+
{ maxEntries: 1e3, maxKeyLength: 2e3 }
|
|
6598
6695
|
);
|
|
6599
6696
|
const checkValidRange = memoize(
|
|
6600
6697
|
(CMIElement, value, rangePattern, errorCode, errorClass) => {
|
|
@@ -6617,7 +6714,8 @@ const checkValidRange = memoize(
|
|
|
6617
6714
|
},
|
|
6618
6715
|
// Custom key function that excludes the error class from the cache key
|
|
6619
6716
|
// since it can't be stringified and doesn't affect the validation result
|
|
6620
|
-
(CMIElement, value, rangePattern, errorCode, _errorClass) => `${CMIElement}:${value}:${rangePattern}:${errorCode}
|
|
6717
|
+
(CMIElement, value, rangePattern, errorCode, _errorClass) => `${CMIElement}:${value}:${rangePattern}:${errorCode}`,
|
|
6718
|
+
{ maxEntries: 1e3 }
|
|
6621
6719
|
);
|
|
6622
6720
|
|
|
6623
6721
|
function check2004ValidFormat(CMIElement, value, regexPattern, allowEmptyString) {
|
|
@@ -7126,6 +7224,12 @@ class ActivityObjective {
|
|
|
7126
7224
|
// objectives. It serves as a validity gate for other synced properties.
|
|
7127
7225
|
_measureStatus = false;
|
|
7128
7226
|
_normalizedMeasure = 0;
|
|
7227
|
+
_rawScore = "";
|
|
7228
|
+
_rawScoreKnown = false;
|
|
7229
|
+
_minScore = "";
|
|
7230
|
+
_minScoreKnown = false;
|
|
7231
|
+
_maxScore = "";
|
|
7232
|
+
_maxScoreKnown = false;
|
|
7129
7233
|
_progressMeasure = 0;
|
|
7130
7234
|
_progressMeasureStatus = false;
|
|
7131
7235
|
_completionStatus = CompletionStatus.UNKNOWN;
|
|
@@ -7135,6 +7239,9 @@ class ActivityObjective {
|
|
|
7135
7239
|
_normalizedMeasureDirty = false;
|
|
7136
7240
|
_completionStatusDirty = false;
|
|
7137
7241
|
_progressMeasureDirty = false;
|
|
7242
|
+
_rawScoreDirty = false;
|
|
7243
|
+
_minScoreDirty = false;
|
|
7244
|
+
_maxScoreDirty = false;
|
|
7138
7245
|
constructor(id, options = {}) {
|
|
7139
7246
|
this._id = id;
|
|
7140
7247
|
this._description = options.description ?? null;
|
|
@@ -7180,6 +7287,8 @@ class ActivityObjective {
|
|
|
7180
7287
|
if (this._satisfiedStatus !== value) {
|
|
7181
7288
|
this._satisfiedStatus = value;
|
|
7182
7289
|
this._satisfiedStatusDirty = true;
|
|
7290
|
+
this._satisfiedStatusKnown = true;
|
|
7291
|
+
this._progressStatus = true;
|
|
7183
7292
|
}
|
|
7184
7293
|
}
|
|
7185
7294
|
get satisfiedStatusKnown() {
|
|
@@ -7203,6 +7312,114 @@ class ActivityObjective {
|
|
|
7203
7312
|
this._normalizedMeasureDirty = true;
|
|
7204
7313
|
}
|
|
7205
7314
|
}
|
|
7315
|
+
/**
|
|
7316
|
+
* Return the known raw score value held for ADLSEQ objective score mapping.
|
|
7317
|
+
*
|
|
7318
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw score mapInfo
|
|
7319
|
+
*/
|
|
7320
|
+
get rawScore() {
|
|
7321
|
+
return this._rawScore;
|
|
7322
|
+
}
|
|
7323
|
+
/**
|
|
7324
|
+
* Store the RTE raw score associated with this objective.
|
|
7325
|
+
*
|
|
7326
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - raw score mapInfo
|
|
7327
|
+
*/
|
|
7328
|
+
set rawScore(value) {
|
|
7329
|
+
if (this._rawScore !== value) {
|
|
7330
|
+
this._rawScore = value;
|
|
7331
|
+
this._rawScoreDirty = true;
|
|
7332
|
+
}
|
|
7333
|
+
this._rawScoreKnown = value !== "";
|
|
7334
|
+
}
|
|
7335
|
+
/**
|
|
7336
|
+
* Return whether this objective's raw score is known.
|
|
7337
|
+
*
|
|
7338
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw score known state
|
|
7339
|
+
*/
|
|
7340
|
+
get rawScoreKnown() {
|
|
7341
|
+
return this._rawScoreKnown;
|
|
7342
|
+
}
|
|
7343
|
+
/**
|
|
7344
|
+
* Set whether this objective's raw score is known.
|
|
7345
|
+
*
|
|
7346
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw score known state
|
|
7347
|
+
*/
|
|
7348
|
+
set rawScoreKnown(value) {
|
|
7349
|
+
this._rawScoreKnown = value;
|
|
7350
|
+
}
|
|
7351
|
+
/**
|
|
7352
|
+
* Return the known minimum score value held for ADLSEQ objective score mapping.
|
|
7353
|
+
*
|
|
7354
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - min score mapInfo
|
|
7355
|
+
*/
|
|
7356
|
+
get minScore() {
|
|
7357
|
+
return this._minScore;
|
|
7358
|
+
}
|
|
7359
|
+
/**
|
|
7360
|
+
* Store the RTE minimum score associated with this objective.
|
|
7361
|
+
*
|
|
7362
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - min score mapInfo
|
|
7363
|
+
*/
|
|
7364
|
+
set minScore(value) {
|
|
7365
|
+
if (this._minScore !== value) {
|
|
7366
|
+
this._minScore = value;
|
|
7367
|
+
this._minScoreDirty = true;
|
|
7368
|
+
}
|
|
7369
|
+
this._minScoreKnown = value !== "";
|
|
7370
|
+
}
|
|
7371
|
+
/**
|
|
7372
|
+
* Return whether this objective's minimum score is known.
|
|
7373
|
+
*
|
|
7374
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - min score known state
|
|
7375
|
+
*/
|
|
7376
|
+
get minScoreKnown() {
|
|
7377
|
+
return this._minScoreKnown;
|
|
7378
|
+
}
|
|
7379
|
+
/**
|
|
7380
|
+
* Set whether this objective's minimum score is known.
|
|
7381
|
+
*
|
|
7382
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - min score known state
|
|
7383
|
+
*/
|
|
7384
|
+
set minScoreKnown(value) {
|
|
7385
|
+
this._minScoreKnown = value;
|
|
7386
|
+
}
|
|
7387
|
+
/**
|
|
7388
|
+
* Return the known maximum score value held for ADLSEQ objective score mapping.
|
|
7389
|
+
*
|
|
7390
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - max score mapInfo
|
|
7391
|
+
*/
|
|
7392
|
+
get maxScore() {
|
|
7393
|
+
return this._maxScore;
|
|
7394
|
+
}
|
|
7395
|
+
/**
|
|
7396
|
+
* Store the RTE maximum score associated with this objective.
|
|
7397
|
+
*
|
|
7398
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - max score mapInfo
|
|
7399
|
+
*/
|
|
7400
|
+
set maxScore(value) {
|
|
7401
|
+
if (this._maxScore !== value) {
|
|
7402
|
+
this._maxScore = value;
|
|
7403
|
+
this._maxScoreDirty = true;
|
|
7404
|
+
}
|
|
7405
|
+
this._maxScoreKnown = value !== "";
|
|
7406
|
+
}
|
|
7407
|
+
/**
|
|
7408
|
+
* Return whether this objective's maximum score is known.
|
|
7409
|
+
*
|
|
7410
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - max score known state
|
|
7411
|
+
*/
|
|
7412
|
+
get maxScoreKnown() {
|
|
7413
|
+
return this._maxScoreKnown;
|
|
7414
|
+
}
|
|
7415
|
+
/**
|
|
7416
|
+
* Set whether this objective's maximum score is known.
|
|
7417
|
+
*
|
|
7418
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - max score known state
|
|
7419
|
+
*/
|
|
7420
|
+
set maxScoreKnown(value) {
|
|
7421
|
+
this._maxScoreKnown = value;
|
|
7422
|
+
}
|
|
7206
7423
|
get progressMeasure() {
|
|
7207
7424
|
return this._progressMeasure;
|
|
7208
7425
|
}
|
|
@@ -7233,6 +7450,11 @@ class ActivityObjective {
|
|
|
7233
7450
|
set progressStatus(value) {
|
|
7234
7451
|
this._progressStatus = value;
|
|
7235
7452
|
}
|
|
7453
|
+
/**
|
|
7454
|
+
* Report whether a local objective field has changed since the last global write.
|
|
7455
|
+
*
|
|
7456
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - write maps use known local objective data
|
|
7457
|
+
*/
|
|
7236
7458
|
isDirty(property) {
|
|
7237
7459
|
switch (property) {
|
|
7238
7460
|
case "satisfiedStatus":
|
|
@@ -7243,8 +7465,19 @@ class ActivityObjective {
|
|
|
7243
7465
|
return this._completionStatusDirty;
|
|
7244
7466
|
case "progressMeasure":
|
|
7245
7467
|
return this._progressMeasureDirty;
|
|
7468
|
+
case "rawScore":
|
|
7469
|
+
return this._rawScoreDirty;
|
|
7470
|
+
case "minScore":
|
|
7471
|
+
return this._minScoreDirty;
|
|
7472
|
+
case "maxScore":
|
|
7473
|
+
return this._maxScoreDirty;
|
|
7246
7474
|
}
|
|
7247
7475
|
}
|
|
7476
|
+
/**
|
|
7477
|
+
* Clear a local objective dirty flag after a successful global write.
|
|
7478
|
+
*
|
|
7479
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - write maps update global objective state
|
|
7480
|
+
*/
|
|
7248
7481
|
clearDirty(property) {
|
|
7249
7482
|
switch (property) {
|
|
7250
7483
|
case "satisfiedStatus":
|
|
@@ -7259,13 +7492,31 @@ class ActivityObjective {
|
|
|
7259
7492
|
case "progressMeasure":
|
|
7260
7493
|
this._progressMeasureDirty = false;
|
|
7261
7494
|
break;
|
|
7495
|
+
case "rawScore":
|
|
7496
|
+
this._rawScoreDirty = false;
|
|
7497
|
+
break;
|
|
7498
|
+
case "minScore":
|
|
7499
|
+
this._minScoreDirty = false;
|
|
7500
|
+
break;
|
|
7501
|
+
case "maxScore":
|
|
7502
|
+
this._maxScoreDirty = false;
|
|
7503
|
+
break;
|
|
7262
7504
|
}
|
|
7263
7505
|
}
|
|
7506
|
+
/**
|
|
7507
|
+
* Clear all write-map dirty flags for this objective.
|
|
7508
|
+
*
|
|
7509
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - objective mapInfo writes are field-specific
|
|
7510
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score mapInfo writes are field-specific
|
|
7511
|
+
*/
|
|
7264
7512
|
clearAllDirty() {
|
|
7265
7513
|
this._satisfiedStatusDirty = false;
|
|
7266
7514
|
this._normalizedMeasureDirty = false;
|
|
7267
7515
|
this._completionStatusDirty = false;
|
|
7268
7516
|
this._progressMeasureDirty = false;
|
|
7517
|
+
this._rawScoreDirty = false;
|
|
7518
|
+
this._minScoreDirty = false;
|
|
7519
|
+
this._maxScoreDirty = false;
|
|
7269
7520
|
}
|
|
7270
7521
|
/**
|
|
7271
7522
|
* Initialize objective values from CMI data transfer
|
|
@@ -7275,6 +7526,8 @@ class ActivityObjective {
|
|
|
7275
7526
|
* @param satisfiedStatus - The satisfied status from CMI
|
|
7276
7527
|
* @param normalizedMeasure - The normalized measure from CMI
|
|
7277
7528
|
* @param measureStatus - Whether measure is valid
|
|
7529
|
+
*
|
|
7530
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - objective satisfaction and measure transfer
|
|
7278
7531
|
*/
|
|
7279
7532
|
initializeFromCMI(satisfiedStatus, normalizedMeasure, measureStatus) {
|
|
7280
7533
|
this._satisfiedStatus = satisfiedStatus;
|
|
@@ -7283,17 +7536,93 @@ class ActivityObjective {
|
|
|
7283
7536
|
this._normalizedMeasureDirty = true;
|
|
7284
7537
|
this._measureStatus = measureStatus;
|
|
7285
7538
|
}
|
|
7539
|
+
/**
|
|
7540
|
+
* Initialize raw/min/max objective score values from RTE data transfer.
|
|
7541
|
+
*
|
|
7542
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - objective score transfer
|
|
7543
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score mapInfo writes
|
|
7544
|
+
*/
|
|
7545
|
+
initializeScoreFromCMI(score) {
|
|
7546
|
+
if (score.rawScore !== void 0 && score.rawScore !== "") {
|
|
7547
|
+
this._rawScore = score.rawScore;
|
|
7548
|
+
this._rawScoreKnown = true;
|
|
7549
|
+
this._rawScoreDirty = true;
|
|
7550
|
+
}
|
|
7551
|
+
if (score.minScore !== void 0 && score.minScore !== "") {
|
|
7552
|
+
this._minScore = score.minScore;
|
|
7553
|
+
this._minScoreKnown = true;
|
|
7554
|
+
this._minScoreDirty = true;
|
|
7555
|
+
}
|
|
7556
|
+
if (score.maxScore !== void 0 && score.maxScore !== "") {
|
|
7557
|
+
this._maxScore = score.maxScore;
|
|
7558
|
+
this._maxScoreKnown = true;
|
|
7559
|
+
this._maxScoreDirty = true;
|
|
7560
|
+
}
|
|
7561
|
+
}
|
|
7562
|
+
/**
|
|
7563
|
+
* Apply read-mapped global objective state without marking the values dirty.
|
|
7564
|
+
*
|
|
7565
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps provide access to global objective state
|
|
7566
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score read maps are access-only
|
|
7567
|
+
*/
|
|
7568
|
+
applyReadMappedState(state) {
|
|
7569
|
+
if (state.satisfiedStatus !== void 0) {
|
|
7570
|
+
this._satisfiedStatus = state.satisfiedStatus;
|
|
7571
|
+
this._satisfiedStatusKnown = true;
|
|
7572
|
+
this._progressStatus = true;
|
|
7573
|
+
}
|
|
7574
|
+
if (state.normalizedMeasure !== void 0) {
|
|
7575
|
+
this._normalizedMeasure = state.normalizedMeasure;
|
|
7576
|
+
this._measureStatus = true;
|
|
7577
|
+
}
|
|
7578
|
+
if (state.completionStatus !== void 0) {
|
|
7579
|
+
this._completionStatus = state.completionStatus;
|
|
7580
|
+
}
|
|
7581
|
+
if (state.progressMeasure !== void 0) {
|
|
7582
|
+
this._progressMeasure = state.progressMeasure;
|
|
7583
|
+
this._progressMeasureStatus = true;
|
|
7584
|
+
}
|
|
7585
|
+
if (state.rawScore !== void 0) {
|
|
7586
|
+
this._rawScore = state.rawScore;
|
|
7587
|
+
this._rawScoreKnown = true;
|
|
7588
|
+
}
|
|
7589
|
+
if (state.minScore !== void 0) {
|
|
7590
|
+
this._minScore = state.minScore;
|
|
7591
|
+
this._minScoreKnown = true;
|
|
7592
|
+
}
|
|
7593
|
+
if (state.maxScore !== void 0) {
|
|
7594
|
+
this._maxScore = state.maxScore;
|
|
7595
|
+
this._maxScoreKnown = true;
|
|
7596
|
+
}
|
|
7597
|
+
}
|
|
7598
|
+
/**
|
|
7599
|
+
* Reset local objective state for a fresh activity attempt.
|
|
7600
|
+
*
|
|
7601
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - unknown objective state before tracking data exists
|
|
7602
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score map fields are unknown until transferred or read
|
|
7603
|
+
*/
|
|
7286
7604
|
resetState() {
|
|
7287
7605
|
this._satisfiedStatus = false;
|
|
7288
7606
|
this._satisfiedStatusKnown = false;
|
|
7289
7607
|
this._measureStatus = false;
|
|
7290
7608
|
this._normalizedMeasure = 0;
|
|
7609
|
+
this._rawScore = "";
|
|
7610
|
+
this._rawScoreKnown = false;
|
|
7611
|
+
this._minScore = "";
|
|
7612
|
+
this._minScoreKnown = false;
|
|
7613
|
+
this._maxScore = "";
|
|
7614
|
+
this._maxScoreKnown = false;
|
|
7291
7615
|
this._progressMeasure = 0;
|
|
7292
7616
|
this._progressMeasureStatus = false;
|
|
7293
7617
|
this._completionStatus = CompletionStatus.UNKNOWN;
|
|
7294
7618
|
this._progressStatus = false;
|
|
7295
7619
|
this.clearAllDirty();
|
|
7296
7620
|
}
|
|
7621
|
+
/**
|
|
7622
|
+
* Copy primary activity objective state back into the primary objective model.
|
|
7623
|
+
*
|
|
7624
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - primary objective state is available to sequencing
|
|
7625
|
+
*/
|
|
7297
7626
|
updateFromActivity(activity) {
|
|
7298
7627
|
if (this._satisfiedStatus !== activity.objectiveSatisfiedStatus) {
|
|
7299
7628
|
this._satisfiedStatus = activity.objectiveSatisfiedStatus;
|
|
@@ -7315,6 +7644,11 @@ class ActivityObjective {
|
|
|
7315
7644
|
this._completionStatusDirty = true;
|
|
7316
7645
|
}
|
|
7317
7646
|
}
|
|
7647
|
+
/**
|
|
7648
|
+
* Apply primary objective state to the owning activity for sequencing rules and rollup.
|
|
7649
|
+
*
|
|
7650
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - primary objective contributes activity state
|
|
7651
|
+
*/
|
|
7318
7652
|
applyToActivity(activity) {
|
|
7319
7653
|
if (!this._isPrimary) {
|
|
7320
7654
|
return;
|
|
@@ -7325,7 +7659,8 @@ class ActivityObjective {
|
|
|
7325
7659
|
this._normalizedMeasure,
|
|
7326
7660
|
this._progressMeasure,
|
|
7327
7661
|
this._progressMeasureStatus,
|
|
7328
|
-
this._completionStatus
|
|
7662
|
+
this._completionStatus,
|
|
7663
|
+
this._progressStatus || this._satisfiedStatusKnown
|
|
7329
7664
|
);
|
|
7330
7665
|
}
|
|
7331
7666
|
}
|
|
@@ -8395,7 +8730,7 @@ class Activity extends BaseCMI {
|
|
|
8395
8730
|
/**
|
|
8396
8731
|
* Setter for primary objective
|
|
8397
8732
|
* @param {ActivityObjective | null} objective
|
|
8398
|
-
|
|
8733
|
+
*/
|
|
8399
8734
|
set primaryObjective(objective) {
|
|
8400
8735
|
this._primaryObjective = objective;
|
|
8401
8736
|
if (this._primaryObjective) {
|
|
@@ -8417,7 +8752,7 @@ class Activity extends BaseCMI {
|
|
|
8417
8752
|
/**
|
|
8418
8753
|
* Replace objectives collection
|
|
8419
8754
|
* @param {ActivityObjective[]} objectives
|
|
8420
|
-
|
|
8755
|
+
*/
|
|
8421
8756
|
set objectives(objectives) {
|
|
8422
8757
|
this._objectives = [...objectives];
|
|
8423
8758
|
this.syncPrimaryObjectiveCollection();
|
|
@@ -8510,12 +8845,12 @@ class Activity extends BaseCMI {
|
|
|
8510
8845
|
this._objectiveNormalizedMeasureDirty = false;
|
|
8511
8846
|
this._objectiveMeasureStatusDirty = false;
|
|
8512
8847
|
}
|
|
8513
|
-
setPrimaryObjectiveState(satisfiedStatus, measureStatus, normalizedMeasure, progressMeasure, progressMeasureStatus, completionStatus) {
|
|
8848
|
+
setPrimaryObjectiveState(satisfiedStatus, measureStatus, normalizedMeasure, progressMeasure, progressMeasureStatus, completionStatus, objectiveProgressStatus = true) {
|
|
8514
8849
|
if (this._objectiveSatisfiedStatus !== satisfiedStatus) {
|
|
8515
8850
|
this._objectiveSatisfiedStatus = satisfiedStatus;
|
|
8516
8851
|
this._objectiveSatisfiedStatusDirty = true;
|
|
8517
8852
|
}
|
|
8518
|
-
this._objectiveSatisfiedStatusKnown =
|
|
8853
|
+
this._objectiveSatisfiedStatusKnown = objectiveProgressStatus;
|
|
8519
8854
|
if (this._objectiveMeasureStatus !== measureStatus) {
|
|
8520
8855
|
this._objectiveMeasureStatus = measureStatus;
|
|
8521
8856
|
this._objectiveMeasureStatusDirty = true;
|
|
@@ -8534,14 +8869,28 @@ class Activity extends BaseCMI {
|
|
|
8534
8869
|
this._primaryObjective.progressMeasure = progressMeasure;
|
|
8535
8870
|
this._primaryObjective.progressMeasureStatus = progressMeasureStatus;
|
|
8536
8871
|
this._primaryObjective.completionStatus = completionStatus;
|
|
8872
|
+
this._primaryObjective.satisfiedStatusKnown = objectiveProgressStatus;
|
|
8873
|
+
this._primaryObjective.progressStatus = objectiveProgressStatus;
|
|
8537
8874
|
}
|
|
8538
8875
|
}
|
|
8876
|
+
/**
|
|
8877
|
+
* Snapshot objective state for sequencing persistence.
|
|
8878
|
+
*
|
|
8879
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - objective state persists across attempts
|
|
8880
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score-map state persists with objective state
|
|
8881
|
+
*/
|
|
8539
8882
|
getObjectiveStateSnapshot() {
|
|
8540
8883
|
const primarySnapshot = this._primaryObjective ? {
|
|
8541
8884
|
id: this._primaryObjective.id,
|
|
8542
8885
|
satisfiedStatus: this.objectiveSatisfiedStatus,
|
|
8543
8886
|
measureStatus: this.objectiveMeasureStatus,
|
|
8544
8887
|
normalizedMeasure: this.objectiveNormalizedMeasure,
|
|
8888
|
+
rawScore: this._primaryObjective.rawScore,
|
|
8889
|
+
rawScoreKnown: this._primaryObjective.rawScoreKnown,
|
|
8890
|
+
minScore: this._primaryObjective.minScore,
|
|
8891
|
+
minScoreKnown: this._primaryObjective.minScoreKnown,
|
|
8892
|
+
maxScore: this._primaryObjective.maxScore,
|
|
8893
|
+
maxScoreKnown: this._primaryObjective.maxScoreKnown,
|
|
8545
8894
|
progressMeasure: this.progressMeasure ?? 0,
|
|
8546
8895
|
progressMeasureStatus: this.progressMeasureStatus,
|
|
8547
8896
|
progressStatus: this._primaryObjective.progressStatus,
|
|
@@ -8554,6 +8903,12 @@ class Activity extends BaseCMI {
|
|
|
8554
8903
|
satisfiedStatus: objective.satisfiedStatus,
|
|
8555
8904
|
measureStatus: objective.measureStatus,
|
|
8556
8905
|
normalizedMeasure: objective.normalizedMeasure,
|
|
8906
|
+
rawScore: objective.rawScore,
|
|
8907
|
+
rawScoreKnown: objective.rawScoreKnown,
|
|
8908
|
+
minScore: objective.minScore,
|
|
8909
|
+
minScoreKnown: objective.minScoreKnown,
|
|
8910
|
+
maxScore: objective.maxScore,
|
|
8911
|
+
maxScoreKnown: objective.maxScoreKnown,
|
|
8557
8912
|
progressMeasure: objective.progressMeasure,
|
|
8558
8913
|
progressMeasureStatus: objective.progressMeasureStatus,
|
|
8559
8914
|
progressStatus: objective.progressStatus,
|
|
@@ -8566,6 +8921,12 @@ class Activity extends BaseCMI {
|
|
|
8566
8921
|
objectives: additionalSnapshots
|
|
8567
8922
|
};
|
|
8568
8923
|
}
|
|
8924
|
+
/**
|
|
8925
|
+
* Restore objective state from a sequencing persistence snapshot.
|
|
8926
|
+
*
|
|
8927
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - persisted objective state restores sequencing state
|
|
8928
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - persisted score-map state restores objective score fields
|
|
8929
|
+
*/
|
|
8569
8930
|
applyObjectiveStateSnapshot(snapshot) {
|
|
8570
8931
|
if (snapshot.primary) {
|
|
8571
8932
|
const primary = this.getObjectiveById(snapshot.primary.id);
|
|
@@ -8579,8 +8940,10 @@ class Activity extends BaseCMI {
|
|
|
8579
8940
|
state.normalizedMeasure,
|
|
8580
8941
|
state.progressMeasure,
|
|
8581
8942
|
state.progressMeasureStatus,
|
|
8582
|
-
state.completionStatus
|
|
8943
|
+
state.completionStatus,
|
|
8944
|
+
state.progressStatus
|
|
8583
8945
|
);
|
|
8946
|
+
this.applyObjectiveScoreSnapshot(primary.objective, state);
|
|
8584
8947
|
}
|
|
8585
8948
|
}
|
|
8586
8949
|
for (const state of snapshot.objectives) {
|
|
@@ -8593,11 +8956,30 @@ class Activity extends BaseCMI {
|
|
|
8593
8956
|
objective.progressMeasure = state.progressMeasure;
|
|
8594
8957
|
objective.progressMeasureStatus = state.progressMeasureStatus;
|
|
8595
8958
|
objective.completionStatus = state.completionStatus;
|
|
8959
|
+
this.applyObjectiveScoreSnapshot(objective, state);
|
|
8596
8960
|
objective.satisfiedByMeasure = state.satisfiedByMeasure ?? objective.satisfiedByMeasure;
|
|
8597
8961
|
objective.minNormalizedMeasure = state.minNormalizedMeasure !== void 0 ? state.minNormalizedMeasure : objective.minNormalizedMeasure;
|
|
8598
8962
|
}
|
|
8599
8963
|
}
|
|
8600
8964
|
}
|
|
8965
|
+
/**
|
|
8966
|
+
* Restore known raw/min/max score fields from an objective state snapshot.
|
|
8967
|
+
*
|
|
8968
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score known flags restore independently
|
|
8969
|
+
*/
|
|
8970
|
+
applyObjectiveScoreSnapshot(objective, state) {
|
|
8971
|
+
const scoreState = {};
|
|
8972
|
+
if (state.rawScoreKnown) {
|
|
8973
|
+
scoreState.rawScore = state.rawScore;
|
|
8974
|
+
}
|
|
8975
|
+
if (state.minScoreKnown) {
|
|
8976
|
+
scoreState.minScore = state.minScore;
|
|
8977
|
+
}
|
|
8978
|
+
if (state.maxScoreKnown) {
|
|
8979
|
+
scoreState.maxScore = state.maxScore;
|
|
8980
|
+
}
|
|
8981
|
+
objective.applyReadMappedState(scoreState);
|
|
8982
|
+
}
|
|
8601
8983
|
/**
|
|
8602
8984
|
* Get available children with selection and randomization applied
|
|
8603
8985
|
* @return {Activity[]}
|
|
@@ -9988,11 +10370,18 @@ class GlobalObjectiveSynchronizer {
|
|
|
9988
10370
|
*
|
|
9989
10371
|
* @param activity - The activity to process
|
|
9990
10372
|
* @param globalObjectives - Global objective map
|
|
10373
|
+
*
|
|
10374
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - write mapInfo transfers local objective state to global objectives
|
|
10375
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score write maps
|
|
9991
10376
|
*/
|
|
9992
10377
|
syncGlobalObjectivesWritePhase(activity, globalObjectives) {
|
|
10378
|
+
if (!this.canWriteGlobalObjectives(activity)) {
|
|
10379
|
+
return;
|
|
10380
|
+
}
|
|
9993
10381
|
const objectives = activity.getAllObjectives();
|
|
9994
10382
|
for (const objective of objectives) {
|
|
9995
10383
|
const mapInfos = objective.mapInfo.length > 0 ? objective.mapInfo : [this.createDefaultMapInfo(objective)];
|
|
10384
|
+
const dirtyFieldsToClear = /* @__PURE__ */ new Set();
|
|
9996
10385
|
for (const mapInfo of mapInfos) {
|
|
9997
10386
|
const targetId = mapInfo.targetObjectiveID || objective.id;
|
|
9998
10387
|
const globalObjective = this.ensureGlobalObjectiveEntry(
|
|
@@ -10000,36 +10389,54 @@ class GlobalObjectiveSynchronizer {
|
|
|
10000
10389
|
targetId,
|
|
10001
10390
|
objective
|
|
10002
10391
|
);
|
|
10003
|
-
if (mapInfo.writeSatisfiedStatus && objective
|
|
10392
|
+
if (mapInfo.writeSatisfiedStatus && this.hasKnownSatisfiedStatus(objective) && objective.isDirty("satisfiedStatus")) {
|
|
10004
10393
|
globalObjective.satisfiedStatus = objective.satisfiedStatus;
|
|
10005
10394
|
globalObjective.satisfiedStatusKnown = true;
|
|
10006
|
-
|
|
10395
|
+
dirtyFieldsToClear.add("satisfiedStatus");
|
|
10007
10396
|
}
|
|
10008
10397
|
if (mapInfo.writeNormalizedMeasure && objective.measureStatus && objective.isDirty("normalizedMeasure")) {
|
|
10009
10398
|
globalObjective.normalizedMeasure = objective.normalizedMeasure;
|
|
10010
10399
|
globalObjective.normalizedMeasureKnown = true;
|
|
10011
|
-
|
|
10012
|
-
if (
|
|
10400
|
+
dirtyFieldsToClear.add("normalizedMeasure");
|
|
10401
|
+
if (mapInfo.writeSatisfiedStatus && objective.satisfiedByMeasure) {
|
|
10013
10402
|
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10014
10403
|
globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;
|
|
10015
10404
|
globalObjective.satisfiedStatusKnown = true;
|
|
10016
|
-
|
|
10405
|
+
dirtyFieldsToClear.add("satisfiedStatus");
|
|
10017
10406
|
}
|
|
10018
10407
|
}
|
|
10019
10408
|
if (mapInfo.writeCompletionStatus && objective.completionStatus !== CompletionStatus.UNKNOWN && objective.isDirty("completionStatus")) {
|
|
10020
10409
|
globalObjective.completionStatus = objective.completionStatus;
|
|
10021
10410
|
globalObjective.completionStatusKnown = true;
|
|
10022
|
-
|
|
10411
|
+
dirtyFieldsToClear.add("completionStatus");
|
|
10412
|
+
}
|
|
10413
|
+
if (mapInfo.writeRawScore && objective.rawScoreKnown && objective.isDirty("rawScore")) {
|
|
10414
|
+
globalObjective.rawScore = objective.rawScore;
|
|
10415
|
+
globalObjective.rawScoreKnown = true;
|
|
10416
|
+
dirtyFieldsToClear.add("rawScore");
|
|
10417
|
+
}
|
|
10418
|
+
if (mapInfo.writeMinScore && objective.minScoreKnown && objective.isDirty("minScore")) {
|
|
10419
|
+
globalObjective.minScore = objective.minScore;
|
|
10420
|
+
globalObjective.minScoreKnown = true;
|
|
10421
|
+
dirtyFieldsToClear.add("minScore");
|
|
10422
|
+
}
|
|
10423
|
+
if (mapInfo.writeMaxScore && objective.maxScoreKnown && objective.isDirty("maxScore")) {
|
|
10424
|
+
globalObjective.maxScore = objective.maxScore;
|
|
10425
|
+
globalObjective.maxScoreKnown = true;
|
|
10426
|
+
dirtyFieldsToClear.add("maxScore");
|
|
10023
10427
|
}
|
|
10024
10428
|
if (mapInfo.writeProgressMeasure && objective.progressMeasureStatus && objective.isDirty("progressMeasure")) {
|
|
10025
10429
|
globalObjective.progressMeasure = objective.progressMeasure;
|
|
10026
10430
|
globalObjective.progressMeasureKnown = true;
|
|
10027
|
-
|
|
10431
|
+
dirtyFieldsToClear.add("progressMeasure");
|
|
10028
10432
|
}
|
|
10029
10433
|
if (mapInfo.updateAttemptData) {
|
|
10030
10434
|
this.updateActivityAttemptData(activity, globalObjective, objective);
|
|
10031
10435
|
}
|
|
10032
10436
|
}
|
|
10437
|
+
for (const property of dirtyFieldsToClear) {
|
|
10438
|
+
objective.clearDirty(property);
|
|
10439
|
+
}
|
|
10033
10440
|
}
|
|
10034
10441
|
}
|
|
10035
10442
|
/**
|
|
@@ -10037,6 +10444,9 @@ class GlobalObjectiveSynchronizer {
|
|
|
10037
10444
|
*
|
|
10038
10445
|
* @param activity - The activity to process
|
|
10039
10446
|
* @param globalObjectives - Global objective map
|
|
10447
|
+
*
|
|
10448
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read mapInfo transfers global objective state into the local view
|
|
10449
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps
|
|
10040
10450
|
*/
|
|
10041
10451
|
syncGlobalObjectivesReadPhase(activity, globalObjectives) {
|
|
10042
10452
|
const objectives = activity.getAllObjectives();
|
|
@@ -10047,25 +10457,13 @@ class GlobalObjectiveSynchronizer {
|
|
|
10047
10457
|
const globalObjective = globalObjectives.get(targetId);
|
|
10048
10458
|
if (!globalObjective) continue;
|
|
10049
10459
|
const isPrimary = objective.isPrimary;
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
objective
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {
|
|
10058
|
-
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10059
|
-
objective.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;
|
|
10060
|
-
}
|
|
10061
|
-
}
|
|
10062
|
-
if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {
|
|
10063
|
-
objective.progressMeasure = globalObjective.progressMeasure;
|
|
10064
|
-
objective.progressMeasureStatus = true;
|
|
10065
|
-
}
|
|
10066
|
-
if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {
|
|
10067
|
-
objective.completionStatus = globalObjective.completionStatus;
|
|
10068
|
-
}
|
|
10460
|
+
const readState = GlobalObjectiveSynchronizer.getGlobalObjectiveReadState(
|
|
10461
|
+
activity,
|
|
10462
|
+
objective,
|
|
10463
|
+
mapInfo,
|
|
10464
|
+
globalObjective
|
|
10465
|
+
);
|
|
10466
|
+
this.applyGlobalObjectiveReadState(objective, readState);
|
|
10069
10467
|
if (isPrimary) {
|
|
10070
10468
|
objective.applyToActivity(activity);
|
|
10071
10469
|
}
|
|
@@ -10108,56 +10506,61 @@ class GlobalObjectiveSynchronizer {
|
|
|
10108
10506
|
* @param objective - The objective to sync
|
|
10109
10507
|
* @param mapInfo - Map info for this objective
|
|
10110
10508
|
* @param globalObjective - The global objective
|
|
10509
|
+
*
|
|
10510
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - objective mapInfo read/write synchronization
|
|
10511
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score mapInfo synchronization
|
|
10111
10512
|
*/
|
|
10112
10513
|
syncObjectiveState(activity, objective, mapInfo, globalObjective) {
|
|
10113
10514
|
try {
|
|
10114
10515
|
const isPrimary = objective.isPrimary;
|
|
10115
10516
|
const localObjective = this.getLocalObjectiveState(activity, objective, isPrimary);
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
objective
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {
|
|
10124
|
-
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10125
|
-
objective.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;
|
|
10126
|
-
}
|
|
10127
|
-
}
|
|
10128
|
-
if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {
|
|
10129
|
-
objective.progressMeasure = globalObjective.progressMeasure;
|
|
10130
|
-
objective.progressMeasureStatus = true;
|
|
10131
|
-
}
|
|
10132
|
-
if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {
|
|
10133
|
-
objective.completionStatus = globalObjective.completionStatus;
|
|
10134
|
-
}
|
|
10517
|
+
const readState = GlobalObjectiveSynchronizer.getGlobalObjectiveReadState(
|
|
10518
|
+
activity,
|
|
10519
|
+
objective,
|
|
10520
|
+
mapInfo,
|
|
10521
|
+
globalObjective
|
|
10522
|
+
);
|
|
10523
|
+
this.applyGlobalObjectiveReadState(objective, readState);
|
|
10135
10524
|
if (objective.isPrimary) {
|
|
10136
10525
|
objective.applyToActivity(activity);
|
|
10137
10526
|
}
|
|
10138
|
-
if (
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
}
|
|
10142
|
-
if (mapInfo.writeNormalizedMeasure && objective.measureStatus) {
|
|
10143
|
-
globalObjective.normalizedMeasure = objective.normalizedMeasure;
|
|
10144
|
-
globalObjective.normalizedMeasureKnown = true;
|
|
10145
|
-
if (globalObjective.satisfiedByMeasure || objective.satisfiedByMeasure) {
|
|
10146
|
-
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10147
|
-
globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;
|
|
10527
|
+
if (this.canWriteGlobalObjectives(activity)) {
|
|
10528
|
+
if (mapInfo.writeSatisfiedStatus && this.hasKnownSatisfiedStatus(objective)) {
|
|
10529
|
+
globalObjective.satisfiedStatus = objective.satisfiedStatus;
|
|
10148
10530
|
globalObjective.satisfiedStatusKnown = true;
|
|
10149
10531
|
}
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10532
|
+
if (mapInfo.writeNormalizedMeasure && objective.measureStatus) {
|
|
10533
|
+
globalObjective.normalizedMeasure = objective.normalizedMeasure;
|
|
10534
|
+
globalObjective.normalizedMeasureKnown = true;
|
|
10535
|
+
if (mapInfo.writeSatisfiedStatus && objective.satisfiedByMeasure) {
|
|
10536
|
+
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10537
|
+
globalObjective.satisfiedStatus = objective.normalizedMeasure >= threshold;
|
|
10538
|
+
globalObjective.satisfiedStatusKnown = true;
|
|
10539
|
+
}
|
|
10540
|
+
}
|
|
10541
|
+
if (mapInfo.writeCompletionStatus && objective.completionStatus !== CompletionStatus.UNKNOWN) {
|
|
10542
|
+
globalObjective.completionStatus = objective.completionStatus;
|
|
10543
|
+
globalObjective.completionStatusKnown = true;
|
|
10544
|
+
}
|
|
10545
|
+
if (mapInfo.writeRawScore && objective.rawScoreKnown) {
|
|
10546
|
+
globalObjective.rawScore = objective.rawScore;
|
|
10547
|
+
globalObjective.rawScoreKnown = true;
|
|
10548
|
+
}
|
|
10549
|
+
if (mapInfo.writeMinScore && objective.minScoreKnown) {
|
|
10550
|
+
globalObjective.minScore = objective.minScore;
|
|
10551
|
+
globalObjective.minScoreKnown = true;
|
|
10552
|
+
}
|
|
10553
|
+
if (mapInfo.writeMaxScore && objective.maxScoreKnown) {
|
|
10554
|
+
globalObjective.maxScore = objective.maxScore;
|
|
10555
|
+
globalObjective.maxScoreKnown = true;
|
|
10556
|
+
}
|
|
10557
|
+
if (mapInfo.writeProgressMeasure && objective.progressMeasureStatus) {
|
|
10558
|
+
globalObjective.progressMeasure = objective.progressMeasure;
|
|
10559
|
+
globalObjective.progressMeasureKnown = true;
|
|
10560
|
+
}
|
|
10561
|
+
if (mapInfo.updateAttemptData) {
|
|
10562
|
+
this.updateActivityAttemptData(activity, globalObjective, objective);
|
|
10563
|
+
}
|
|
10161
10564
|
}
|
|
10162
10565
|
this.eventCallback?.("objective_synchronized", {
|
|
10163
10566
|
activityId: activity.id,
|
|
@@ -10175,6 +10578,50 @@ class GlobalObjectiveSynchronizer {
|
|
|
10175
10578
|
});
|
|
10176
10579
|
}
|
|
10177
10580
|
}
|
|
10581
|
+
/**
|
|
10582
|
+
* Project a global objective through one local objective's read mapInfo.
|
|
10583
|
+
*
|
|
10584
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps provide access to mapped global objective fields
|
|
10585
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps are independent fields
|
|
10586
|
+
*/
|
|
10587
|
+
static getGlobalObjectiveReadState(activity, objective, mapInfo, globalObjective) {
|
|
10588
|
+
const readState = {};
|
|
10589
|
+
if (mapInfo.readSatisfiedStatus && globalObjective.satisfiedStatusKnown) {
|
|
10590
|
+
readState.satisfiedStatus = globalObjective.satisfiedStatus;
|
|
10591
|
+
}
|
|
10592
|
+
if (mapInfo.readNormalizedMeasure && globalObjective.normalizedMeasureKnown) {
|
|
10593
|
+
readState.normalizedMeasure = globalObjective.normalizedMeasure;
|
|
10594
|
+
if (objective.satisfiedByMeasure) {
|
|
10595
|
+
const threshold = objective.minNormalizedMeasure ?? activity.scaledPassingScore ?? 0.7;
|
|
10596
|
+
readState.satisfiedStatus = globalObjective.normalizedMeasure >= threshold;
|
|
10597
|
+
}
|
|
10598
|
+
}
|
|
10599
|
+
if (mapInfo.readCompletionStatus && globalObjective.completionStatusKnown) {
|
|
10600
|
+
readState.completionStatus = globalObjective.completionStatus;
|
|
10601
|
+
}
|
|
10602
|
+
if (mapInfo.readProgressMeasure && globalObjective.progressMeasureKnown) {
|
|
10603
|
+
readState.progressMeasure = globalObjective.progressMeasure;
|
|
10604
|
+
}
|
|
10605
|
+
if (mapInfo.readRawScore && globalObjective.rawScoreKnown) {
|
|
10606
|
+
readState.rawScore = globalObjective.rawScore;
|
|
10607
|
+
}
|
|
10608
|
+
if (mapInfo.readMinScore && globalObjective.minScoreKnown) {
|
|
10609
|
+
readState.minScore = globalObjective.minScore;
|
|
10610
|
+
}
|
|
10611
|
+
if (mapInfo.readMaxScore && globalObjective.maxScoreKnown) {
|
|
10612
|
+
readState.maxScore = globalObjective.maxScore;
|
|
10613
|
+
}
|
|
10614
|
+
return readState;
|
|
10615
|
+
}
|
|
10616
|
+
/**
|
|
10617
|
+
* Apply read-mapped state to an objective without marking those fields dirty.
|
|
10618
|
+
*
|
|
10619
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps are access to global state, not local writes
|
|
10620
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score read maps do not imply score writes
|
|
10621
|
+
*/
|
|
10622
|
+
applyGlobalObjectiveReadState(objective, readState) {
|
|
10623
|
+
objective.applyReadMappedState(readState);
|
|
10624
|
+
}
|
|
10178
10625
|
/**
|
|
10179
10626
|
* Ensure global objective entry exists
|
|
10180
10627
|
*
|
|
@@ -10182,19 +10629,30 @@ class GlobalObjectiveSynchronizer {
|
|
|
10182
10629
|
* @param targetId - Target objective ID
|
|
10183
10630
|
* @param objective - Source objective
|
|
10184
10631
|
* @returns The global objective entry
|
|
10632
|
+
*
|
|
10633
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective entries hold mapped objective state
|
|
10634
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - global entries hold score-map state
|
|
10185
10635
|
*/
|
|
10186
10636
|
ensureGlobalObjectiveEntry(globalObjectives, targetId, objective) {
|
|
10187
10637
|
if (!globalObjectives.has(targetId)) {
|
|
10188
10638
|
globalObjectives.set(targetId, {
|
|
10189
10639
|
id: targetId,
|
|
10190
|
-
satisfiedStatus:
|
|
10191
|
-
satisfiedStatusKnown:
|
|
10192
|
-
normalizedMeasure:
|
|
10193
|
-
normalizedMeasureKnown:
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10640
|
+
satisfiedStatus: false,
|
|
10641
|
+
satisfiedStatusKnown: false,
|
|
10642
|
+
normalizedMeasure: 0,
|
|
10643
|
+
normalizedMeasureKnown: false,
|
|
10644
|
+
// @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score fields
|
|
10645
|
+
// remain unknown until their corresponding write map explicitly writes them.
|
|
10646
|
+
rawScore: "",
|
|
10647
|
+
rawScoreKnown: false,
|
|
10648
|
+
minScore: "",
|
|
10649
|
+
minScoreKnown: false,
|
|
10650
|
+
maxScore: "",
|
|
10651
|
+
maxScoreKnown: false,
|
|
10652
|
+
progressMeasure: 0,
|
|
10653
|
+
progressMeasureKnown: false,
|
|
10654
|
+
completionStatus: CompletionStatus.UNKNOWN,
|
|
10655
|
+
completionStatusKnown: false,
|
|
10198
10656
|
satisfiedByMeasure: objective.satisfiedByMeasure,
|
|
10199
10657
|
minNormalizedMeasure: objective.minNormalizedMeasure
|
|
10200
10658
|
});
|
|
@@ -10209,6 +10667,9 @@ class GlobalObjectiveSynchronizer {
|
|
|
10209
10667
|
*
|
|
10210
10668
|
* @param objective - The objective to create default map info for
|
|
10211
10669
|
* @returns Default map info
|
|
10670
|
+
*
|
|
10671
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - mapInfo defaults are applied before objective synchronization
|
|
10672
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - score maps require explicit read/write flags
|
|
10212
10673
|
*/
|
|
10213
10674
|
createDefaultMapInfo(objective) {
|
|
10214
10675
|
return {
|
|
@@ -10221,9 +10682,34 @@ class GlobalObjectiveSynchronizer {
|
|
|
10221
10682
|
writeCompletionStatus: true,
|
|
10222
10683
|
readProgressMeasure: false,
|
|
10223
10684
|
writeProgressMeasure: true,
|
|
10685
|
+
readRawScore: false,
|
|
10686
|
+
writeRawScore: false,
|
|
10687
|
+
readMinScore: false,
|
|
10688
|
+
writeMinScore: false,
|
|
10689
|
+
readMaxScore: false,
|
|
10690
|
+
writeMaxScore: false,
|
|
10224
10691
|
updateAttemptData: objective.isPrimary
|
|
10225
10692
|
};
|
|
10226
10693
|
}
|
|
10694
|
+
/**
|
|
10695
|
+
* Return whether the local objective has known satisfaction state to write.
|
|
10696
|
+
*
|
|
10697
|
+
* @spec SCORM 2004 4th Ed. SN 4.2.1 Tracking Model - Objective Progress
|
|
10698
|
+
* Status identifies whether Objective Satisfied Status is known; Objective
|
|
10699
|
+
* Measure Status is independent measure knowledge.
|
|
10700
|
+
*/
|
|
10701
|
+
hasKnownSatisfiedStatus(objective) {
|
|
10702
|
+
return objective.progressStatus || objective.satisfiedStatusKnown;
|
|
10703
|
+
}
|
|
10704
|
+
/**
|
|
10705
|
+
* Return whether this activity is allowed to write tracked state to globals.
|
|
10706
|
+
*
|
|
10707
|
+
* @spec SCORM 2004 4th Ed. SN 3.13.1 Tracked - when False, the LMS
|
|
10708
|
+
* "does not initialize, manage or access any tracking status information".
|
|
10709
|
+
*/
|
|
10710
|
+
canWriteGlobalObjectives(activity) {
|
|
10711
|
+
return activity.sequencingControls.tracked !== false;
|
|
10712
|
+
}
|
|
10227
10713
|
/**
|
|
10228
10714
|
* Get local objective state
|
|
10229
10715
|
*
|
|
@@ -10231,6 +10717,9 @@ class GlobalObjectiveSynchronizer {
|
|
|
10231
10717
|
* @param objective - The objective
|
|
10232
10718
|
* @param isPrimary - Whether this is the primary objective
|
|
10233
10719
|
* @returns Local objective state
|
|
10720
|
+
*
|
|
10721
|
+
* @spec SCORM 2004 4th Ed. SN 3.10 Objective Description - local objective state used for synchronization
|
|
10722
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - local score fields are part of mapped objective state
|
|
10234
10723
|
*/
|
|
10235
10724
|
getLocalObjectiveState(activity, objective, isPrimary) {
|
|
10236
10725
|
if (isPrimary) {
|
|
@@ -10239,6 +10728,12 @@ class GlobalObjectiveSynchronizer {
|
|
|
10239
10728
|
satisfiedStatus: activity.objectiveSatisfiedStatus,
|
|
10240
10729
|
measureStatus: activity.objectiveMeasureStatus,
|
|
10241
10730
|
normalizedMeasure: activity.objectiveNormalizedMeasure,
|
|
10731
|
+
rawScore: objective.rawScore,
|
|
10732
|
+
rawScoreKnown: objective.rawScoreKnown,
|
|
10733
|
+
minScore: objective.minScore,
|
|
10734
|
+
minScoreKnown: objective.minScoreKnown,
|
|
10735
|
+
maxScore: objective.maxScore,
|
|
10736
|
+
maxScoreKnown: objective.maxScoreKnown,
|
|
10242
10737
|
progressMeasure: activity.progressMeasure,
|
|
10243
10738
|
progressMeasureStatus: activity.progressMeasureStatus,
|
|
10244
10739
|
completionStatus: activity.completionStatus,
|
|
@@ -10250,6 +10745,12 @@ class GlobalObjectiveSynchronizer {
|
|
|
10250
10745
|
satisfiedStatus: objective.satisfiedStatus,
|
|
10251
10746
|
measureStatus: objective.measureStatus,
|
|
10252
10747
|
normalizedMeasure: objective.normalizedMeasure,
|
|
10748
|
+
rawScore: objective.rawScore,
|
|
10749
|
+
rawScoreKnown: objective.rawScoreKnown,
|
|
10750
|
+
minScore: objective.minScore,
|
|
10751
|
+
minScoreKnown: objective.minScoreKnown,
|
|
10752
|
+
maxScore: objective.maxScore,
|
|
10753
|
+
maxScoreKnown: objective.maxScoreKnown,
|
|
10253
10754
|
progressMeasure: objective.progressMeasure,
|
|
10254
10755
|
progressMeasureStatus: objective.progressMeasureStatus,
|
|
10255
10756
|
completionStatus: objective.completionStatus,
|
|
@@ -10272,7 +10773,7 @@ class GlobalObjectiveSynchronizer {
|
|
|
10272
10773
|
(rule) => rule.action === "completed" || rule.action === "incomplete"
|
|
10273
10774
|
);
|
|
10274
10775
|
if (globalObjective.satisfiedStatusKnown && globalObjective.satisfiedStatus) {
|
|
10275
|
-
if (!hasCompletionRollupRules && (activity.completionStatus === CompletionStatus.UNKNOWN || activity.completionStatus === CompletionStatus.INCOMPLETE)) {
|
|
10776
|
+
if (!hasCompletionRollupRules && !activity.sequencingControls.completionSetByContent && !activity.attemptProgressStatus && (activity.completionStatus === CompletionStatus.UNKNOWN || activity.completionStatus === CompletionStatus.INCOMPLETE)) {
|
|
10276
10777
|
activity.completionStatus = CompletionStatus.COMPLETED;
|
|
10277
10778
|
}
|
|
10278
10779
|
if (activity.successStatus === "unknown") {
|
|
@@ -10718,6 +11219,9 @@ class RteDataTransferService {
|
|
|
10718
11219
|
* Transfer primary objective data from CMI to activity
|
|
10719
11220
|
* @param {Activity} activity - The activity to transfer data to
|
|
10720
11221
|
* @param {CMIDataForTransfer} cmiData - CMI data from runtime
|
|
11222
|
+
*
|
|
11223
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - primary objective status and score data
|
|
11224
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score write-map source data
|
|
10721
11225
|
*/
|
|
10722
11226
|
transferPrimaryObjective(activity, cmiData) {
|
|
10723
11227
|
let hasProgressMeasure = false;
|
|
@@ -10764,9 +11268,9 @@ class RteDataTransferService {
|
|
|
10764
11268
|
activity.objectiveSatisfiedStatus = successStatus;
|
|
10765
11269
|
activity.objectiveSatisfiedStatusKnown = true;
|
|
10766
11270
|
activity.successStatus = validatedSuccessStatus;
|
|
10767
|
-
activity.objectiveMeasureStatus = true;
|
|
10768
11271
|
}
|
|
10769
11272
|
if (cmiData.score) {
|
|
11273
|
+
activity.primaryObjective?.initializeScoreFromCMI(this.getObjectiveScoreState(cmiData.score));
|
|
10770
11274
|
const normalized = this.normalizeScore(cmiData.score);
|
|
10771
11275
|
if (normalized !== null) {
|
|
10772
11276
|
normalizedScore = normalized;
|
|
@@ -10778,7 +11282,7 @@ class RteDataTransferService {
|
|
|
10778
11282
|
if (activity.primaryObjective && (hasSuccessStatus || hasNormalizedMeasure)) {
|
|
10779
11283
|
const finalStatus = hasSuccessStatus ? successStatus : activity.primaryObjective.satisfiedStatus;
|
|
10780
11284
|
const finalMeasure = hasNormalizedMeasure ? normalizedScore : activity.primaryObjective.normalizedMeasure;
|
|
10781
|
-
const measureStatus =
|
|
11285
|
+
const measureStatus = hasNormalizedMeasure;
|
|
10782
11286
|
activity.primaryObjective.initializeFromCMI(finalStatus, finalMeasure, measureStatus);
|
|
10783
11287
|
if (hasSuccessStatus) {
|
|
10784
11288
|
activity.primaryObjective.satisfiedStatusKnown = true;
|
|
@@ -10787,10 +11291,16 @@ class RteDataTransferService {
|
|
|
10787
11291
|
}
|
|
10788
11292
|
}
|
|
10789
11293
|
/**
|
|
10790
|
-
* Transfer
|
|
10791
|
-
* Only transfers changed values to protect global objectives
|
|
11294
|
+
* Transfer objective-array data from CMI to matching activity objectives.
|
|
11295
|
+
* Only transfers changed values to protect global objectives.
|
|
10792
11296
|
* @param {Activity} activity - The activity to transfer data to
|
|
10793
11297
|
* @param {CMIDataForTransfer} cmiData - CMI data from runtime
|
|
11298
|
+
*
|
|
11299
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17 - cmi.objectives.n data, including
|
|
11300
|
+
* the primary objective entry, is part of the RTE objective data model.
|
|
11301
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 and ADLSEQ objectives extension -
|
|
11302
|
+
* mapped objective status and raw/min/max score data transfer through
|
|
11303
|
+
* objective maps to sequencing state.
|
|
10794
11304
|
*/
|
|
10795
11305
|
transferNonPrimaryObjectives(activity, cmiData) {
|
|
10796
11306
|
if (!cmiData.objectives || cmiData.objectives.length === 0) {
|
|
@@ -10801,14 +11311,17 @@ class RteDataTransferService {
|
|
|
10801
11311
|
continue;
|
|
10802
11312
|
}
|
|
10803
11313
|
const activityObjectiveMatch = activity.getObjectiveById(cmiObjective.id);
|
|
10804
|
-
if (!activityObjectiveMatch
|
|
11314
|
+
if (!activityObjectiveMatch) {
|
|
10805
11315
|
continue;
|
|
10806
11316
|
}
|
|
10807
11317
|
const activityObjective = activityObjectiveMatch.objective;
|
|
11318
|
+
const isPrimaryObjective = activityObjectiveMatch.isPrimary;
|
|
10808
11319
|
let hasSuccessStatus = false;
|
|
10809
11320
|
let successStatus = false;
|
|
10810
11321
|
let hasNormalizedMeasure = false;
|
|
10811
11322
|
let normalizedScore = 0;
|
|
11323
|
+
let hasCompletionStatus = false;
|
|
11324
|
+
let hasProgressMeasure = false;
|
|
10812
11325
|
const validatedObjSuccessStatus = validateSuccessStatus(cmiObjective.success_status);
|
|
10813
11326
|
if (validatedObjSuccessStatus && validatedObjSuccessStatus !== SuccessStatus.UNKNOWN) {
|
|
10814
11327
|
successStatus = validatedObjSuccessStatus === SuccessStatus.PASSED;
|
|
@@ -10818,8 +11331,10 @@ class RteDataTransferService {
|
|
|
10818
11331
|
const validatedObjCompletionStatus = validateCompletionStatus(cmiObjective.completion_status);
|
|
10819
11332
|
if (validatedObjCompletionStatus && validatedObjCompletionStatus !== CompletionStatus.UNKNOWN) {
|
|
10820
11333
|
activityObjective.completionStatus = validatedObjCompletionStatus;
|
|
11334
|
+
hasCompletionStatus = true;
|
|
10821
11335
|
}
|
|
10822
11336
|
if (cmiObjective.score) {
|
|
11337
|
+
activityObjective.initializeScoreFromCMI(this.getObjectiveScoreState(cmiObjective.score));
|
|
10823
11338
|
const normalized = this.normalizeScore(cmiObjective.score);
|
|
10824
11339
|
if (normalized !== null) {
|
|
10825
11340
|
normalizedScore = normalized;
|
|
@@ -10831,12 +11346,29 @@ class RteDataTransferService {
|
|
|
10831
11346
|
const finalMeasure = hasNormalizedMeasure ? normalizedScore : activityObjective.normalizedMeasure;
|
|
10832
11347
|
const measureStatus = hasNormalizedMeasure;
|
|
10833
11348
|
activityObjective.initializeFromCMI(finalStatus, finalMeasure, measureStatus);
|
|
11349
|
+
if (hasSuccessStatus) {
|
|
11350
|
+
activityObjective.satisfiedStatusKnown = true;
|
|
11351
|
+
}
|
|
10834
11352
|
}
|
|
10835
11353
|
if (cmiObjective.progress_measure && cmiObjective.progress_measure !== "") {
|
|
10836
11354
|
const progressMeasure = parseFloat(cmiObjective.progress_measure);
|
|
10837
11355
|
if (!isNaN(progressMeasure)) {
|
|
10838
11356
|
activityObjective.progressMeasure = progressMeasure;
|
|
10839
11357
|
activityObjective.progressMeasureStatus = true;
|
|
11358
|
+
hasProgressMeasure = true;
|
|
11359
|
+
}
|
|
11360
|
+
}
|
|
11361
|
+
if (isPrimaryObjective && (hasSuccessStatus || hasNormalizedMeasure || hasCompletionStatus || hasProgressMeasure)) {
|
|
11362
|
+
activityObjective.applyToActivity(activity);
|
|
11363
|
+
if (validatedObjSuccessStatus && validatedObjSuccessStatus !== SuccessStatus.UNKNOWN) {
|
|
11364
|
+
activity.successStatus = validatedObjSuccessStatus;
|
|
11365
|
+
}
|
|
11366
|
+
if (hasCompletionStatus) {
|
|
11367
|
+
activity.attemptProgressStatus = true;
|
|
11368
|
+
}
|
|
11369
|
+
if (hasProgressMeasure) {
|
|
11370
|
+
activity.attemptCompletionAmount = activityObjective.progressMeasure;
|
|
11371
|
+
activity.attemptCompletionAmountStatus = true;
|
|
10840
11372
|
}
|
|
10841
11373
|
}
|
|
10842
11374
|
}
|
|
@@ -10865,6 +11397,25 @@ class RteDataTransferService {
|
|
|
10865
11397
|
}
|
|
10866
11398
|
return null;
|
|
10867
11399
|
}
|
|
11400
|
+
/**
|
|
11401
|
+
* Convert RTE score data into objective score-map state without numeric reformatting.
|
|
11402
|
+
*
|
|
11403
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - score values transfer from RTE to sequencing state
|
|
11404
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score map fields are independent
|
|
11405
|
+
*/
|
|
11406
|
+
getObjectiveScoreState(score) {
|
|
11407
|
+
const scoreState = {};
|
|
11408
|
+
if (score.raw !== void 0) {
|
|
11409
|
+
scoreState.rawScore = score.raw;
|
|
11410
|
+
}
|
|
11411
|
+
if (score.min !== void 0) {
|
|
11412
|
+
scoreState.minScore = score.min;
|
|
11413
|
+
}
|
|
11414
|
+
if (score.max !== void 0) {
|
|
11415
|
+
scoreState.maxScore = score.max;
|
|
11416
|
+
}
|
|
11417
|
+
return scoreState;
|
|
11418
|
+
}
|
|
10868
11419
|
}
|
|
10869
11420
|
|
|
10870
11421
|
class TerminationHandler {
|
|
@@ -11226,13 +11777,7 @@ class TerminationHandler {
|
|
|
11226
11777
|
recursionDepth++;
|
|
11227
11778
|
const exitRules = activity.sequencingRules.exitConditionRules;
|
|
11228
11779
|
for (const rule of exitRules) {
|
|
11229
|
-
|
|
11230
|
-
if (rule.conditionCombination === "all") {
|
|
11231
|
-
conditionsMet = rule.conditions.every((condition) => condition.evaluate(activity));
|
|
11232
|
-
} else {
|
|
11233
|
-
conditionsMet = rule.conditions.some((condition) => condition.evaluate(activity));
|
|
11234
|
-
}
|
|
11235
|
-
if (conditionsMet) {
|
|
11780
|
+
if (rule.evaluate(activity)) {
|
|
11236
11781
|
if (rule.action === RuleActionType.EXIT) {
|
|
11237
11782
|
return { action: "EXIT", recursionDepth };
|
|
11238
11783
|
} else if (rule.action === RuleActionType.EXIT_PARENT) {
|
|
@@ -11398,13 +11943,7 @@ class TerminationHandler {
|
|
|
11398
11943
|
exitActionRulesSubprocess(activity) {
|
|
11399
11944
|
const exitRules = activity.sequencingRules.exitConditionRules;
|
|
11400
11945
|
for (const rule of exitRules) {
|
|
11401
|
-
|
|
11402
|
-
if (rule.conditionCombination === "all") {
|
|
11403
|
-
conditionsMet = rule.conditions.every((condition) => condition.evaluate(activity));
|
|
11404
|
-
} else {
|
|
11405
|
-
conditionsMet = rule.conditions.some((condition) => condition.evaluate(activity));
|
|
11406
|
-
}
|
|
11407
|
-
if (conditionsMet) {
|
|
11946
|
+
if (rule.evaluate(activity)) {
|
|
11408
11947
|
if (rule.action === RuleActionType.EXIT) {
|
|
11409
11948
|
return "EXIT";
|
|
11410
11949
|
} else if (rule.action === RuleActionType.EXIT_PARENT) {
|
|
@@ -12923,6 +13462,9 @@ class GlobalObjectiveService {
|
|
|
12923
13462
|
* Collect Global Objectives
|
|
12924
13463
|
* Recursively collects global objectives from the activity tree
|
|
12925
13464
|
* @param {Activity} activity - Activity to collect objectives from
|
|
13465
|
+
*
|
|
13466
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective map contains mapped objective state
|
|
13467
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score fields are unknown until written
|
|
12926
13468
|
*/
|
|
12927
13469
|
collectObjectives(activity) {
|
|
12928
13470
|
const objectives = activity.getAllObjectives();
|
|
@@ -12932,9 +13474,15 @@ class GlobalObjectiveService {
|
|
|
12932
13474
|
this.globalObjectiveMap.set(defaultId, {
|
|
12933
13475
|
id: defaultId,
|
|
12934
13476
|
satisfiedStatus: activity.objectiveSatisfiedStatus,
|
|
12935
|
-
satisfiedStatusKnown: activity.
|
|
13477
|
+
satisfiedStatusKnown: activity.objectiveSatisfiedStatusKnown,
|
|
12936
13478
|
normalizedMeasure: activity.objectiveNormalizedMeasure,
|
|
12937
13479
|
normalizedMeasureKnown: activity.objectiveMeasureStatus,
|
|
13480
|
+
rawScore: "",
|
|
13481
|
+
rawScoreKnown: false,
|
|
13482
|
+
minScore: "",
|
|
13483
|
+
minScoreKnown: false,
|
|
13484
|
+
maxScore: "",
|
|
13485
|
+
maxScoreKnown: false,
|
|
12938
13486
|
progressMeasure: activity.progressMeasure,
|
|
12939
13487
|
progressMeasureKnown: activity.progressMeasureStatus,
|
|
12940
13488
|
completionStatus: activity.completionStatus,
|
|
@@ -12947,6 +13495,12 @@ class GlobalObjectiveService {
|
|
|
12947
13495
|
writeCompletionStatus: true,
|
|
12948
13496
|
readProgressMeasure: true,
|
|
12949
13497
|
writeProgressMeasure: true,
|
|
13498
|
+
readRawScore: false,
|
|
13499
|
+
writeRawScore: false,
|
|
13500
|
+
readMinScore: false,
|
|
13501
|
+
writeMinScore: false,
|
|
13502
|
+
readMaxScore: false,
|
|
13503
|
+
writeMaxScore: false,
|
|
12950
13504
|
satisfiedByMeasure: activity.scaledPassingScore !== null,
|
|
12951
13505
|
minNormalizedMeasure: activity.scaledPassingScore,
|
|
12952
13506
|
updateAttemptData: true
|
|
@@ -12974,9 +13528,15 @@ class GlobalObjectiveService {
|
|
|
12974
13528
|
this.globalObjectiveMap.set(targetId, {
|
|
12975
13529
|
id: targetId,
|
|
12976
13530
|
satisfiedStatus: objective.satisfiedStatus,
|
|
12977
|
-
satisfiedStatusKnown: objective.
|
|
13531
|
+
satisfiedStatusKnown: objective.satisfiedStatusKnown || objective.progressStatus,
|
|
12978
13532
|
normalizedMeasure: objective.normalizedMeasure,
|
|
12979
13533
|
normalizedMeasureKnown: objective.measureStatus,
|
|
13534
|
+
rawScore: "",
|
|
13535
|
+
rawScoreKnown: false,
|
|
13536
|
+
minScore: "",
|
|
13537
|
+
minScoreKnown: false,
|
|
13538
|
+
maxScore: "",
|
|
13539
|
+
maxScoreKnown: false,
|
|
12980
13540
|
progressMeasure: objective.progressMeasure,
|
|
12981
13541
|
progressMeasureKnown: objective.progressMeasureStatus,
|
|
12982
13542
|
completionStatus: objective.completionStatus,
|
|
@@ -14959,6 +15519,9 @@ class SequencingService {
|
|
|
14959
15519
|
}
|
|
14960
15520
|
/**
|
|
14961
15521
|
* Update activity properties from current CMI values
|
|
15522
|
+
*
|
|
15523
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - current CMI values update activity objective state
|
|
15524
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max scores are available to objective write maps
|
|
14962
15525
|
*/
|
|
14963
15526
|
updateActivityFromCMI(activity) {
|
|
14964
15527
|
let hasProgressMeasure = false;
|
|
@@ -14990,7 +15553,6 @@ class SequencingService {
|
|
|
14990
15553
|
if (this.cmi.success_status !== "unknown") {
|
|
14991
15554
|
activity.successStatus = this.cmi.success_status;
|
|
14992
15555
|
activity.objectiveSatisfiedStatus = this.cmi.success_status === "passed";
|
|
14993
|
-
activity.objectiveMeasureStatus = true;
|
|
14994
15556
|
if (activity.primaryObjective) {
|
|
14995
15557
|
activity.primaryObjective.progressStatus = true;
|
|
14996
15558
|
}
|
|
@@ -15005,6 +15567,17 @@ class SequencingService {
|
|
|
15005
15567
|
}
|
|
15006
15568
|
}
|
|
15007
15569
|
}
|
|
15570
|
+
if (activity.primaryObjective && this.cmi.score) {
|
|
15571
|
+
if (this.cmi.score.raw !== "") {
|
|
15572
|
+
activity.primaryObjective.rawScore = this.cmi.score.raw;
|
|
15573
|
+
}
|
|
15574
|
+
if (this.cmi.score.min !== "") {
|
|
15575
|
+
activity.primaryObjective.minScore = this.cmi.score.min;
|
|
15576
|
+
}
|
|
15577
|
+
if (this.cmi.score.max !== "") {
|
|
15578
|
+
activity.primaryObjective.maxScore = this.cmi.score.max;
|
|
15579
|
+
}
|
|
15580
|
+
}
|
|
15008
15581
|
if (activity.primaryObjective) {
|
|
15009
15582
|
activity.primaryObjective.updateFromActivity(activity);
|
|
15010
15583
|
}
|
|
@@ -19682,7 +20255,7 @@ class CMIInteractionsObject extends BaseCMI {
|
|
|
19682
20255
|
if (check2004ValidFormat(
|
|
19683
20256
|
this._cmi_element + ".description",
|
|
19684
20257
|
description,
|
|
19685
|
-
scorm2004_regex.
|
|
20258
|
+
scorm2004_regex.CMILangString,
|
|
19686
20259
|
true
|
|
19687
20260
|
)) {
|
|
19688
20261
|
this._description = description;
|
|
@@ -20392,7 +20965,7 @@ class CMIObjectivesObject extends BaseCMI {
|
|
|
20392
20965
|
if (check2004ValidFormat(
|
|
20393
20966
|
this._cmi_element + ".description",
|
|
20394
20967
|
description,
|
|
20395
|
-
scorm2004_regex.
|
|
20968
|
+
scorm2004_regex.CMILangString,
|
|
20396
20969
|
true
|
|
20397
20970
|
)) {
|
|
20398
20971
|
this._description = description;
|
|
@@ -23235,6 +23808,9 @@ class SequencingConfigurationBuilder {
|
|
|
23235
23808
|
if (settings.objectiveSetByContent !== void 0) {
|
|
23236
23809
|
target.objectiveSetByContent = settings.objectiveSetByContent;
|
|
23237
23810
|
}
|
|
23811
|
+
if (settings.tracked !== void 0) {
|
|
23812
|
+
target.tracked = settings.tracked;
|
|
23813
|
+
}
|
|
23238
23814
|
}
|
|
23239
23815
|
/**
|
|
23240
23816
|
* Applies the sequencing rules settings to the specified target object.
|
|
@@ -23772,6 +24348,12 @@ class ActivityTreeBuilder {
|
|
|
23772
24348
|
activitySettings.sequencingControls
|
|
23773
24349
|
);
|
|
23774
24350
|
}
|
|
24351
|
+
if (activitySettings.deliveryControls) {
|
|
24352
|
+
this.sequencingConfigBuilder.applySequencingControlsSettings(
|
|
24353
|
+
activity.sequencingControls,
|
|
24354
|
+
activitySettings.deliveryControls
|
|
24355
|
+
);
|
|
24356
|
+
}
|
|
23775
24357
|
if (activitySettings.sequencingRules) {
|
|
23776
24358
|
this.sequencingConfigBuilder.applySequencingRulesSettings(
|
|
23777
24359
|
activity.sequencingRules,
|
|
@@ -23861,19 +24443,19 @@ class ActivityTreeBuilder {
|
|
|
23861
24443
|
createActivityObjectiveFromSettings(objectiveSettings, isPrimary) {
|
|
23862
24444
|
const mapInfo = (objectiveSettings.mapInfo || []).map((info) => ({
|
|
23863
24445
|
targetObjectiveID: info.targetObjectiveID,
|
|
23864
|
-
readSatisfiedStatus: info.readSatisfiedStatus ??
|
|
23865
|
-
readNormalizedMeasure: info.readNormalizedMeasure ??
|
|
24446
|
+
readSatisfiedStatus: info.readSatisfiedStatus ?? true,
|
|
24447
|
+
readNormalizedMeasure: info.readNormalizedMeasure ?? true,
|
|
23866
24448
|
writeSatisfiedStatus: info.writeSatisfiedStatus ?? false,
|
|
23867
24449
|
writeNormalizedMeasure: info.writeNormalizedMeasure ?? false,
|
|
23868
|
-
readCompletionStatus: info.readCompletionStatus ??
|
|
24450
|
+
readCompletionStatus: info.readCompletionStatus ?? true,
|
|
23869
24451
|
writeCompletionStatus: info.writeCompletionStatus ?? false,
|
|
23870
|
-
readProgressMeasure: info.readProgressMeasure ??
|
|
24452
|
+
readProgressMeasure: info.readProgressMeasure ?? true,
|
|
23871
24453
|
writeProgressMeasure: info.writeProgressMeasure ?? false,
|
|
23872
|
-
readRawScore: info.readRawScore ??
|
|
24454
|
+
readRawScore: info.readRawScore ?? true,
|
|
23873
24455
|
writeRawScore: info.writeRawScore ?? false,
|
|
23874
|
-
readMinScore: info.readMinScore ??
|
|
24456
|
+
readMinScore: info.readMinScore ?? true,
|
|
23875
24457
|
writeMinScore: info.writeMinScore ?? false,
|
|
23876
|
-
readMaxScore: info.readMaxScore ??
|
|
24458
|
+
readMaxScore: info.readMaxScore ?? true,
|
|
23877
24459
|
writeMaxScore: info.writeMaxScore ?? false,
|
|
23878
24460
|
updateAttemptData: info.updateAttemptData ?? false
|
|
23879
24461
|
}));
|
|
@@ -24055,6 +24637,9 @@ class GlobalObjectiveManager {
|
|
|
24055
24637
|
*
|
|
24056
24638
|
* @param {string} objectiveId - The global objective ID
|
|
24057
24639
|
* @param {CMIObjectivesObject} objective - The CMI objective object with updated values
|
|
24640
|
+
*
|
|
24641
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objectives synchronize CMI objective data
|
|
24642
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score fields synchronize independently
|
|
24058
24643
|
*/
|
|
24059
24644
|
updateGlobalObjectiveFromCMI(objectiveId, objective) {
|
|
24060
24645
|
if (!objectiveId || !this.context.sequencingService) {
|
|
@@ -24080,6 +24665,18 @@ class GlobalObjectiveManager {
|
|
|
24080
24665
|
updatePayload.normalizedMeasure = normalizedMeasure;
|
|
24081
24666
|
updatePayload.normalizedMeasureKnown = true;
|
|
24082
24667
|
}
|
|
24668
|
+
if (objective.score?.raw !== void 0 && objective.score.raw !== "") {
|
|
24669
|
+
updatePayload.rawScore = objective.score.raw;
|
|
24670
|
+
updatePayload.rawScoreKnown = true;
|
|
24671
|
+
}
|
|
24672
|
+
if (objective.score?.min !== void 0 && objective.score.min !== "") {
|
|
24673
|
+
updatePayload.minScore = objective.score.min;
|
|
24674
|
+
updatePayload.minScoreKnown = true;
|
|
24675
|
+
}
|
|
24676
|
+
if (objective.score?.max !== void 0 && objective.score.max !== "") {
|
|
24677
|
+
updatePayload.maxScore = objective.score.max;
|
|
24678
|
+
updatePayload.maxScoreKnown = true;
|
|
24679
|
+
}
|
|
24083
24680
|
const progressMeasure = this.parseObjectiveNumber(objective.progress_measure);
|
|
24084
24681
|
if (progressMeasure !== null) {
|
|
24085
24682
|
updatePayload.progressMeasure = progressMeasure;
|
|
@@ -24099,12 +24696,21 @@ class GlobalObjectiveManager {
|
|
|
24099
24696
|
*
|
|
24100
24697
|
* @param {CMIObjectivesObject} objective - The CMI objectives object containing data about a specific learning objective.
|
|
24101
24698
|
* @return {GlobalObjectiveMapEntry} An object containing mapped properties and their values based on the provided objective.
|
|
24699
|
+
*
|
|
24700
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective map entries capture objective state
|
|
24701
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score entries carry per-field known flags
|
|
24102
24702
|
*/
|
|
24103
24703
|
buildObjectiveMapEntryFromCMI(objective) {
|
|
24104
24704
|
const entry = {
|
|
24105
24705
|
id: objective.id,
|
|
24106
24706
|
satisfiedStatusKnown: false,
|
|
24107
24707
|
normalizedMeasureKnown: false,
|
|
24708
|
+
rawScore: "",
|
|
24709
|
+
rawScoreKnown: false,
|
|
24710
|
+
minScore: "",
|
|
24711
|
+
minScoreKnown: false,
|
|
24712
|
+
maxScore: "",
|
|
24713
|
+
maxScoreKnown: false,
|
|
24108
24714
|
progressMeasureKnown: false,
|
|
24109
24715
|
completionStatusKnown: false,
|
|
24110
24716
|
readSatisfiedStatus: true,
|
|
@@ -24114,7 +24720,13 @@ class GlobalObjectiveManager {
|
|
|
24114
24720
|
readCompletionStatus: true,
|
|
24115
24721
|
writeCompletionStatus: true,
|
|
24116
24722
|
readProgressMeasure: true,
|
|
24117
|
-
writeProgressMeasure: true
|
|
24723
|
+
writeProgressMeasure: true,
|
|
24724
|
+
readRawScore: true,
|
|
24725
|
+
writeRawScore: true,
|
|
24726
|
+
readMinScore: true,
|
|
24727
|
+
writeMinScore: true,
|
|
24728
|
+
readMaxScore: true,
|
|
24729
|
+
writeMaxScore: true
|
|
24118
24730
|
};
|
|
24119
24731
|
if (objective.success_status && objective.success_status !== SuccessStatus.UNKNOWN) {
|
|
24120
24732
|
entry.satisfiedStatus = objective.success_status === SuccessStatus.PASSED;
|
|
@@ -24125,6 +24737,18 @@ class GlobalObjectiveManager {
|
|
|
24125
24737
|
entry.normalizedMeasure = normalizedMeasure;
|
|
24126
24738
|
entry.normalizedMeasureKnown = true;
|
|
24127
24739
|
}
|
|
24740
|
+
if (objective.score?.raw !== void 0 && objective.score.raw !== "") {
|
|
24741
|
+
entry.rawScore = objective.score.raw;
|
|
24742
|
+
entry.rawScoreKnown = true;
|
|
24743
|
+
}
|
|
24744
|
+
if (objective.score?.min !== void 0 && objective.score.min !== "") {
|
|
24745
|
+
entry.minScore = objective.score.min;
|
|
24746
|
+
entry.minScoreKnown = true;
|
|
24747
|
+
}
|
|
24748
|
+
if (objective.score?.max !== void 0 && objective.score.max !== "") {
|
|
24749
|
+
entry.maxScore = objective.score.max;
|
|
24750
|
+
entry.maxScoreKnown = true;
|
|
24751
|
+
}
|
|
24128
24752
|
const progressMeasure = this.parseObjectiveNumber(objective.progress_measure);
|
|
24129
24753
|
if (progressMeasure !== null) {
|
|
24130
24754
|
entry.progressMeasure = progressMeasure;
|
|
@@ -24145,6 +24769,9 @@ class GlobalObjectiveManager {
|
|
|
24145
24769
|
* @return {CMIObjectivesObject[]} An array of `CMIObjectivesObject` instances built
|
|
24146
24770
|
* from the provided snapshot map. Returns an empty array
|
|
24147
24771
|
* if the snapshot is invalid or no valid objectives can be created.
|
|
24772
|
+
*
|
|
24773
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - global objective snapshots restore CMI objective state
|
|
24774
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score known fields restore independently
|
|
24148
24775
|
*/
|
|
24149
24776
|
buildCMIObjectivesFromMap(snapshot) {
|
|
24150
24777
|
const objectives = [];
|
|
@@ -24164,6 +24791,15 @@ class GlobalObjectiveManager {
|
|
|
24164
24791
|
if (entry.normalizedMeasureKnown === true && normalizedMeasure !== null) {
|
|
24165
24792
|
objective.score.scaled = String(normalizedMeasure);
|
|
24166
24793
|
}
|
|
24794
|
+
if (entry.rawScoreKnown === true) {
|
|
24795
|
+
objective.score.raw = String(entry.rawScore ?? "");
|
|
24796
|
+
}
|
|
24797
|
+
if (entry.minScoreKnown === true) {
|
|
24798
|
+
objective.score.min = String(entry.minScore ?? "");
|
|
24799
|
+
}
|
|
24800
|
+
if (entry.maxScoreKnown === true) {
|
|
24801
|
+
objective.score.max = String(entry.maxScore ?? "");
|
|
24802
|
+
}
|
|
24167
24803
|
const progressMeasure = this.parseObjectiveNumber(entry.progressMeasure);
|
|
24168
24804
|
if (entry.progressMeasureKnown === true && progressMeasure !== null) {
|
|
24169
24805
|
objective.progress_measure = String(progressMeasure);
|
|
@@ -24274,6 +24910,9 @@ class GlobalObjectiveManager {
|
|
|
24274
24910
|
* @param {CompletionStatus} completionStatus
|
|
24275
24911
|
* @param {SuccessStatus} successStatus
|
|
24276
24912
|
* @param {ScoreObject} scoreObject
|
|
24913
|
+
*
|
|
24914
|
+
* @spec SCORM 2004 4th Ed. RTE-to-SN Data Transfer - CMI score data updates the current primary objective
|
|
24915
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score data is available for write maps
|
|
24277
24916
|
*/
|
|
24278
24917
|
syncCmiToSequencingActivity(completionStatus, successStatus, scoreObject) {
|
|
24279
24918
|
if (!this.context.sequencing) {
|
|
@@ -24299,6 +24938,15 @@ class GlobalObjectiveManager {
|
|
|
24299
24938
|
primaryObjective.normalizedMeasure = scoreObject.scaled;
|
|
24300
24939
|
primaryObjective.measureStatus = true;
|
|
24301
24940
|
}
|
|
24941
|
+
if (scoreObject?.raw !== void 0 && scoreObject.raw !== null) {
|
|
24942
|
+
primaryObjective.rawScore = String(scoreObject.raw);
|
|
24943
|
+
}
|
|
24944
|
+
if (scoreObject?.min !== void 0 && scoreObject.min !== null) {
|
|
24945
|
+
primaryObjective.minScore = String(scoreObject.min);
|
|
24946
|
+
}
|
|
24947
|
+
if (scoreObject?.max !== void 0 && scoreObject.max !== null) {
|
|
24948
|
+
primaryObjective.maxScore = String(scoreObject.max);
|
|
24949
|
+
}
|
|
24302
24950
|
}
|
|
24303
24951
|
/**
|
|
24304
24952
|
* Find or create a global objective by ID
|
|
@@ -24851,16 +25499,232 @@ class Scorm2004API extends BaseAPI {
|
|
|
24851
25499
|
/**
|
|
24852
25500
|
* Apply launch-static activity data to CMI while the new SCO is pre-initialize.
|
|
24853
25501
|
*
|
|
24854
|
-
* @spec SCORM 2004 4th Ed. RTE 4.2.
|
|
25502
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold
|
|
25503
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
24855
25504
|
*/
|
|
24856
25505
|
applyCurrentActivityLaunchData() {
|
|
24857
25506
|
const currentActivity = this._sequencing?.getCurrentActivity();
|
|
24858
|
-
if (!
|
|
25507
|
+
if (!currentActivity) {
|
|
25508
|
+
return;
|
|
25509
|
+
}
|
|
25510
|
+
this.applyActivityLaunchData(currentActivity);
|
|
25511
|
+
}
|
|
25512
|
+
/**
|
|
25513
|
+
* Apply launch-static CMI data when sequencing delivers a new activity before SCO Initialize.
|
|
25514
|
+
*
|
|
25515
|
+
* @spec SCORM 2004 4th Ed. SN DB.2 - Content Delivery Environment Process establishes the delivered activity.
|
|
25516
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold is initialized before SCO access.
|
|
25517
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives is initialized before SCO access.
|
|
25518
|
+
*/
|
|
25519
|
+
applyDeliveredActivityLaunchData(activity) {
|
|
25520
|
+
if (!this.isNotInitialized()) {
|
|
25521
|
+
return;
|
|
25522
|
+
}
|
|
25523
|
+
this.applyActivityLaunchData(activity);
|
|
25524
|
+
}
|
|
25525
|
+
/**
|
|
25526
|
+
* Copy the delivered activity's static launch data into the fresh CMI model.
|
|
25527
|
+
*
|
|
25528
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold
|
|
25529
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
25530
|
+
*/
|
|
25531
|
+
applyActivityLaunchData(currentActivity) {
|
|
25532
|
+
if (!this.cmi) {
|
|
24859
25533
|
return;
|
|
24860
25534
|
}
|
|
24861
25535
|
const contentActivityData = this._sequencing.overallSequencingProcess?.getContentActivityData(currentActivity);
|
|
24862
25536
|
const completionThreshold = contentActivityData?.completionThreshold ?? currentActivity.completionThreshold?.toString() ?? "";
|
|
24863
25537
|
this.cmi.completion_threshold = completionThreshold;
|
|
25538
|
+
this.seedCurrentActivityObjectives(currentActivity);
|
|
25539
|
+
}
|
|
25540
|
+
/**
|
|
25541
|
+
* Seed CMI objective ids after Initialize when automatic sequencing starts during Initialize.
|
|
25542
|
+
*
|
|
25543
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
25544
|
+
*/
|
|
25545
|
+
applyCurrentActivityObjectiveData() {
|
|
25546
|
+
const currentActivity = this._sequencing?.getCurrentActivity();
|
|
25547
|
+
if (!this.cmi || !currentActivity) {
|
|
25548
|
+
return;
|
|
25549
|
+
}
|
|
25550
|
+
this.seedCurrentActivityObjectives(currentActivity);
|
|
25551
|
+
}
|
|
25552
|
+
/**
|
|
25553
|
+
* Seed CMI objectives from primary and secondary activity objectives.
|
|
25554
|
+
*
|
|
25555
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17 - Initialization of Run-Time Objectives from Sequencing Information
|
|
25556
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.id and success_status
|
|
25557
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 / ADLSEQ objectives extension - objective read maps seed the RTE view
|
|
25558
|
+
*/
|
|
25559
|
+
seedCurrentActivityObjectives(currentActivity) {
|
|
25560
|
+
const activityObjectives = [];
|
|
25561
|
+
if (currentActivity.primaryObjective) {
|
|
25562
|
+
activityObjectives.push(currentActivity.primaryObjective);
|
|
25563
|
+
}
|
|
25564
|
+
activityObjectives.push(...currentActivity.objectives);
|
|
25565
|
+
const seededObjectiveIds = /* @__PURE__ */ new Set();
|
|
25566
|
+
for (const activityObjective of activityObjectives) {
|
|
25567
|
+
const objectiveId = this.getSeedableObjectiveId(activityObjective);
|
|
25568
|
+
if (!objectiveId || seededObjectiveIds.has(objectiveId)) {
|
|
25569
|
+
continue;
|
|
25570
|
+
}
|
|
25571
|
+
seededObjectiveIds.add(objectiveId);
|
|
25572
|
+
const index = this.findOrSeedCMIObjective(objectiveId);
|
|
25573
|
+
if (index === null) {
|
|
25574
|
+
continue;
|
|
25575
|
+
}
|
|
25576
|
+
const cmiObjective = this.cmi.objectives.findObjectiveByIndex(index);
|
|
25577
|
+
if (cmiObjective && this.cmi.objectives.initialized && !cmiObjective.initialized) {
|
|
25578
|
+
cmiObjective.initialize();
|
|
25579
|
+
}
|
|
25580
|
+
const successStatus = this.getActivityObjectiveSuccessStatus(activityObjective);
|
|
25581
|
+
if (successStatus) {
|
|
25582
|
+
this._commonSetCMIValue(
|
|
25583
|
+
"SeedActivityObjective",
|
|
25584
|
+
true,
|
|
25585
|
+
`cmi.objectives.${index}.success_status`,
|
|
25586
|
+
successStatus
|
|
25587
|
+
);
|
|
25588
|
+
}
|
|
25589
|
+
this.seedObjectiveReadMapValues(currentActivity, activityObjective, index);
|
|
25590
|
+
}
|
|
25591
|
+
}
|
|
25592
|
+
/**
|
|
25593
|
+
* Seed CMI objective fields from this objective's read-mapped global objectives.
|
|
25594
|
+
*
|
|
25595
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17 - Run-Time Objectives are initialized from sequencing information
|
|
25596
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read mapInfo grants access to mapped global objective state
|
|
25597
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps seed RTE score fields
|
|
25598
|
+
*/
|
|
25599
|
+
seedObjectiveReadMapValues(currentActivity, activityObjective, objectiveIndex) {
|
|
25600
|
+
const globalObjectiveMap = this._sequencing.overallSequencingProcess?.getGlobalObjectiveMap();
|
|
25601
|
+
if (!globalObjectiveMap || activityObjective.mapInfo.length === 0) {
|
|
25602
|
+
return;
|
|
25603
|
+
}
|
|
25604
|
+
for (const mapInfo of activityObjective.mapInfo) {
|
|
25605
|
+
const targetObjectiveId = mapInfo.targetObjectiveID || activityObjective.id;
|
|
25606
|
+
const globalObjective = globalObjectiveMap.get(targetObjectiveId);
|
|
25607
|
+
if (!globalObjective) {
|
|
25608
|
+
continue;
|
|
25609
|
+
}
|
|
25610
|
+
const readState = GlobalObjectiveSynchronizer.getGlobalObjectiveReadState(
|
|
25611
|
+
currentActivity,
|
|
25612
|
+
activityObjective,
|
|
25613
|
+
mapInfo,
|
|
25614
|
+
globalObjective
|
|
25615
|
+
);
|
|
25616
|
+
this.applyObjectiveReadStateToCMI(objectiveIndex, readState);
|
|
25617
|
+
}
|
|
25618
|
+
}
|
|
25619
|
+
/**
|
|
25620
|
+
* Copy mapped global objective state into the seeded CMI objective entry.
|
|
25621
|
+
*
|
|
25622
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives launch-time initialization
|
|
25623
|
+
* @spec SCORM 2004 4th Ed. SN 3.10.3 - read maps populate the RTE view without creating local writes
|
|
25624
|
+
* @spec SCORM 2004 4th Ed. ADLSEQ objectives extension - raw/min/max score read maps populate CMI objective scores
|
|
25625
|
+
*/
|
|
25626
|
+
applyObjectiveReadStateToCMI(objectiveIndex, readState) {
|
|
25627
|
+
if (readState.satisfiedStatus !== void 0) {
|
|
25628
|
+
this._commonSetCMIValue(
|
|
25629
|
+
"SeedActivityObjectiveReadMap",
|
|
25630
|
+
true,
|
|
25631
|
+
`cmi.objectives.${objectiveIndex}.success_status`,
|
|
25632
|
+
readState.satisfiedStatus ? SuccessStatus.PASSED : SuccessStatus.FAILED
|
|
25633
|
+
);
|
|
25634
|
+
}
|
|
25635
|
+
if (readState.normalizedMeasure !== void 0) {
|
|
25636
|
+
this._commonSetCMIValue(
|
|
25637
|
+
"SeedActivityObjectiveReadMap",
|
|
25638
|
+
true,
|
|
25639
|
+
`cmi.objectives.${objectiveIndex}.score.scaled`,
|
|
25640
|
+
String(readState.normalizedMeasure)
|
|
25641
|
+
);
|
|
25642
|
+
}
|
|
25643
|
+
if (readState.completionStatus !== void 0) {
|
|
25644
|
+
this._commonSetCMIValue(
|
|
25645
|
+
"SeedActivityObjectiveReadMap",
|
|
25646
|
+
true,
|
|
25647
|
+
`cmi.objectives.${objectiveIndex}.completion_status`,
|
|
25648
|
+
readState.completionStatus
|
|
25649
|
+
);
|
|
25650
|
+
}
|
|
25651
|
+
if (readState.progressMeasure !== void 0) {
|
|
25652
|
+
this._commonSetCMIValue(
|
|
25653
|
+
"SeedActivityObjectiveReadMap",
|
|
25654
|
+
true,
|
|
25655
|
+
`cmi.objectives.${objectiveIndex}.progress_measure`,
|
|
25656
|
+
String(readState.progressMeasure)
|
|
25657
|
+
);
|
|
25658
|
+
}
|
|
25659
|
+
if (readState.rawScore !== void 0) {
|
|
25660
|
+
this._commonSetCMIValue(
|
|
25661
|
+
"SeedActivityObjectiveReadMap",
|
|
25662
|
+
true,
|
|
25663
|
+
`cmi.objectives.${objectiveIndex}.score.raw`,
|
|
25664
|
+
readState.rawScore
|
|
25665
|
+
);
|
|
25666
|
+
}
|
|
25667
|
+
if (readState.minScore !== void 0) {
|
|
25668
|
+
this._commonSetCMIValue(
|
|
25669
|
+
"SeedActivityObjectiveReadMap",
|
|
25670
|
+
true,
|
|
25671
|
+
`cmi.objectives.${objectiveIndex}.score.min`,
|
|
25672
|
+
readState.minScore
|
|
25673
|
+
);
|
|
25674
|
+
}
|
|
25675
|
+
if (readState.maxScore !== void 0) {
|
|
25676
|
+
this._commonSetCMIValue(
|
|
25677
|
+
"SeedActivityObjectiveReadMap",
|
|
25678
|
+
true,
|
|
25679
|
+
`cmi.objectives.${objectiveIndex}.score.max`,
|
|
25680
|
+
readState.maxScore
|
|
25681
|
+
);
|
|
25682
|
+
}
|
|
25683
|
+
}
|
|
25684
|
+
/**
|
|
25685
|
+
* Return a manifest-defined objective id that can initialize cmi.objectives.n.id.
|
|
25686
|
+
*
|
|
25687
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.id
|
|
25688
|
+
*/
|
|
25689
|
+
getSeedableObjectiveId(activityObjective) {
|
|
25690
|
+
const objectiveId = activityObjective.id;
|
|
25691
|
+
if (typeof objectiveId !== "string" || objectiveId.trim() === "") {
|
|
25692
|
+
return null;
|
|
25693
|
+
}
|
|
25694
|
+
return objectiveId;
|
|
25695
|
+
}
|
|
25696
|
+
/**
|
|
25697
|
+
* Find an existing CMI objective id or create the next CMI objective array entry.
|
|
25698
|
+
*
|
|
25699
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives._count
|
|
25700
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.id
|
|
25701
|
+
*/
|
|
25702
|
+
findOrSeedCMIObjective(objectiveId) {
|
|
25703
|
+
const existingIndex = this.cmi.objectives.childArray.findIndex((objective) => {
|
|
25704
|
+
return objective.id === objectiveId;
|
|
25705
|
+
});
|
|
25706
|
+
if (existingIndex >= 0) {
|
|
25707
|
+
return existingIndex;
|
|
25708
|
+
}
|
|
25709
|
+
const index = this.cmi.objectives.childArray.length;
|
|
25710
|
+
const result = this._commonSetCMIValue(
|
|
25711
|
+
"SeedActivityObjective",
|
|
25712
|
+
true,
|
|
25713
|
+
`cmi.objectives.${index}.id`,
|
|
25714
|
+
objectiveId
|
|
25715
|
+
);
|
|
25716
|
+
return result === global_constants.SCORM_TRUE ? index : null;
|
|
25717
|
+
}
|
|
25718
|
+
/**
|
|
25719
|
+
* Translate a known activity objective satisfied status to cmi.objectives.n.success_status.
|
|
25720
|
+
*
|
|
25721
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives.n.success_status
|
|
25722
|
+
*/
|
|
25723
|
+
getActivityObjectiveSuccessStatus(activityObjective) {
|
|
25724
|
+
if (activityObjective.progressStatus || activityObjective.satisfiedStatusKnown) {
|
|
25725
|
+
return activityObjective.satisfiedStatus ? SuccessStatus.PASSED : SuccessStatus.FAILED;
|
|
25726
|
+
}
|
|
25727
|
+
return null;
|
|
24864
25728
|
}
|
|
24865
25729
|
/**
|
|
24866
25730
|
* Getter for _version
|
|
@@ -24941,6 +25805,7 @@ class Scorm2004API extends BaseAPI {
|
|
|
24941
25805
|
);
|
|
24942
25806
|
if (result === global_constants.SCORM_TRUE && this._sequencingService) {
|
|
24943
25807
|
this._sequencingService.initialize();
|
|
25808
|
+
this.applyCurrentActivityObjectiveData();
|
|
24944
25809
|
}
|
|
24945
25810
|
if (result === global_constants.SCORM_TRUE) {
|
|
24946
25811
|
this._globalObjectiveManager.restoreGlobalObjectivesToCMI();
|
|
@@ -25195,7 +26060,7 @@ class Scorm2004API extends BaseAPI {
|
|
|
25195
26060
|
objective_id = objective ? objective.id : void 0;
|
|
25196
26061
|
}
|
|
25197
26062
|
const is_global = objective_id && this.settings.globalObjectiveIds?.includes(objective_id);
|
|
25198
|
-
if (is_global) {
|
|
26063
|
+
if (is_global && this.currentActivityAllowsGlobalObjectiveWrites()) {
|
|
25199
26064
|
const { index: global_index } = this._globalObjectiveManager.findOrCreateGlobalObjective(objective_id);
|
|
25200
26065
|
const global_element = CMIElement.replace(
|
|
25201
26066
|
element_base,
|
|
@@ -25210,6 +26075,15 @@ class Scorm2004API extends BaseAPI {
|
|
|
25210
26075
|
}
|
|
25211
26076
|
return this._commonSetCMIValue("SetValue", true, CMIElement, value);
|
|
25212
26077
|
}
|
|
26078
|
+
/**
|
|
26079
|
+
* Return whether the current activity can update shared global objectives.
|
|
26080
|
+
*
|
|
26081
|
+
* @spec SCORM 2004 4th Ed. SN 3.13.1 Tracked - when False, the LMS
|
|
26082
|
+
* "does not initialize, manage or access any tracking status information".
|
|
26083
|
+
*/
|
|
26084
|
+
currentActivityAllowsGlobalObjectiveWrites() {
|
|
26085
|
+
return this._sequencing?.getCurrentActivity()?.sequencingControls.tracked !== false;
|
|
26086
|
+
}
|
|
25213
26087
|
/**
|
|
25214
26088
|
* Gets or builds a new child element to add to the array
|
|
25215
26089
|
* @param {string} CMIElement
|
|
@@ -25554,9 +26428,9 @@ class Scorm2004API extends BaseAPI {
|
|
|
25554
26428
|
this.loggingService,
|
|
25555
26429
|
sequencingConfig
|
|
25556
26430
|
);
|
|
25557
|
-
|
|
25558
|
-
this.
|
|
25559
|
-
|
|
26431
|
+
this._sequencingService.setEventListeners(
|
|
26432
|
+
this.buildSequencingEventListeners(settings?.sequencing?.eventListeners)
|
|
26433
|
+
);
|
|
25560
26434
|
this._globalObjectiveManager.updateSequencingService(this._sequencingService);
|
|
25561
26435
|
this._dataSerializer.updateSequencingService(this._sequencingService);
|
|
25562
26436
|
if (settings?.sequencingStatePersistence) {
|
|
@@ -25579,6 +26453,22 @@ class Scorm2004API extends BaseAPI {
|
|
|
25579
26453
|
this._sequencingService = null;
|
|
25580
26454
|
}
|
|
25581
26455
|
}
|
|
26456
|
+
/**
|
|
26457
|
+
* Wrap LMS-provided sequencing listeners with API-owned delivery bookkeeping.
|
|
26458
|
+
*
|
|
26459
|
+
* @spec SCORM 2004 4th Ed. SN DB.2 - Content Delivery Environment Process
|
|
26460
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5, Table 4.2.5a - cmi.completion_threshold
|
|
26461
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.17, Table 4.2.17a - cmi.objectives
|
|
26462
|
+
*/
|
|
26463
|
+
buildSequencingEventListeners(listeners) {
|
|
26464
|
+
return {
|
|
26465
|
+
...listeners,
|
|
26466
|
+
onActivityDelivery: (activity) => {
|
|
26467
|
+
this.applyDeliveredActivityLaunchData(activity);
|
|
26468
|
+
listeners?.onActivityDelivery?.(activity);
|
|
26469
|
+
}
|
|
26470
|
+
};
|
|
26471
|
+
}
|
|
25582
26472
|
/**
|
|
25583
26473
|
* Get the sequencing service
|
|
25584
26474
|
* @return {SequencingService | null}
|
|
@@ -25589,10 +26479,13 @@ class Scorm2004API extends BaseAPI {
|
|
|
25589
26479
|
/**
|
|
25590
26480
|
* Set sequencing event listeners
|
|
25591
26481
|
* @param {SequencingEventListeners} listeners
|
|
26482
|
+
*
|
|
26483
|
+
* @spec SCORM 2004 4th Ed. SN DB.2 - Content Delivery Environment Process
|
|
26484
|
+
* @spec SCORM 2004 4th Ed. RTE 4.2.5 / 4.2.17 - launch-static CMI data
|
|
25592
26485
|
*/
|
|
25593
26486
|
setSequencingEventListeners(listeners) {
|
|
25594
26487
|
if (this._sequencingService) {
|
|
25595
|
-
this._sequencingService.setEventListeners(listeners);
|
|
26488
|
+
this._sequencingService.setEventListeners(this.buildSequencingEventListeners(listeners));
|
|
25596
26489
|
}
|
|
25597
26490
|
}
|
|
25598
26491
|
/**
|