scorm-again 1.7.1 → 2.1.0
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/.babelrc +18 -7
- package/.github/dependabot.yml +5 -0
- package/.github/workflows/main.yml +79 -0
- package/.github/workflows/stale.yml +14 -0
- package/.jsdoc.json +4 -5
- package/.mocharc.json +8 -0
- package/.run/{Mocha Unit Tests.run.xml → Mocha Unit Tests (watch).run.xml } +6 -3
- package/.run/Template Mocha.run.xml +17 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +183 -71
- package/dist/aicc.js +3822 -7030
- package/dist/aicc.js.map +1 -1
- package/dist/aicc.min.js +2 -40
- package/dist/aicc.min.js.map +1 -0
- package/dist/scorm-again.js +5965 -10498
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +2 -52
- package/dist/scorm-again.min.js.map +1 -0
- package/dist/scorm12.js +3028 -5373
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +2 -34
- package/dist/scorm12.min.js.map +1 -0
- package/dist/scorm2004.js +4054 -6693
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +2 -40
- package/dist/scorm2004.min.js.map +1 -0
- package/eslint.config.js +21 -0
- package/package.json +76 -34
- package/results.json +34254 -0
- package/src/AICC.ts +72 -0
- package/src/BaseAPI.ts +1300 -0
- package/src/Scorm12API.ts +387 -0
- package/src/Scorm2004API.ts +688 -0
- package/src/cmi/aicc/attempts.ts +94 -0
- package/src/cmi/aicc/cmi.ts +100 -0
- package/src/cmi/aicc/core.ts +360 -0
- package/src/cmi/aicc/evaluation.ts +157 -0
- package/src/cmi/aicc/paths.ts +180 -0
- package/src/cmi/aicc/student_data.ts +86 -0
- package/src/cmi/aicc/student_demographics.ts +367 -0
- package/src/cmi/aicc/student_preferences.ts +176 -0
- package/src/cmi/aicc/tries.ts +116 -0
- package/src/cmi/aicc/validation.ts +25 -0
- package/src/cmi/common/array.ts +77 -0
- package/src/cmi/common/base_cmi.ts +46 -0
- package/src/cmi/common/score.ts +203 -0
- package/src/cmi/common/validation.ts +60 -0
- package/src/cmi/scorm12/cmi.ts +224 -0
- package/src/cmi/scorm12/interactions.ts +368 -0
- package/src/cmi/scorm12/nav.ts +54 -0
- package/src/cmi/scorm12/objectives.ts +112 -0
- package/src/cmi/scorm12/student_data.ts +130 -0
- package/src/cmi/scorm12/student_preference.ts +158 -0
- package/src/cmi/scorm12/validation.ts +48 -0
- package/src/cmi/scorm2004/adl.ts +272 -0
- package/src/cmi/scorm2004/cmi.ts +599 -0
- package/src/cmi/scorm2004/comments.ts +163 -0
- package/src/cmi/scorm2004/interactions.ts +466 -0
- package/src/cmi/scorm2004/learner_preference.ts +152 -0
- package/src/cmi/scorm2004/objectives.ts +212 -0
- package/src/cmi/scorm2004/score.ts +78 -0
- package/src/cmi/scorm2004/validation.ts +42 -0
- package/src/constants/api_constants.ts +318 -0
- package/src/constants/default_settings.ts +81 -0
- package/src/constants/enums.ts +5 -0
- package/src/constants/error_codes.ts +88 -0
- package/src/constants/language_constants.ts +394 -0
- package/src/constants/regex.ts +97 -0
- package/src/constants/{response_constants.js → response_constants.ts} +69 -62
- package/src/exceptions.ts +154 -0
- package/src/exports/aicc.js +1 -1
- package/src/exports/scorm-again.js +3 -3
- package/src/exports/scorm12.js +1 -1
- package/src/exports/scorm2004.js +1 -1
- package/src/helpers/scheduled_commit.ts +42 -0
- package/src/interfaces/IBaseAPI.ts +35 -0
- package/src/types/api_types.ts +32 -0
- package/src/utilities/debounce.ts +31 -0
- package/src/utilities.ts +338 -0
- package/tea.yaml +6 -0
- package/test/{AICC.spec.js → AICC.spec.ts} +79 -71
- package/test/Scorm12API.spec.ts +833 -0
- package/test/Scorm2004API.spec.ts +1298 -0
- package/test/api_helpers.ts +176 -0
- package/test/cmi/aicc_cmi.spec.ts +845 -0
- package/test/cmi/{scorm12_cmi.spec.js → scorm12_cmi.spec.ts} +253 -271
- package/test/cmi/scorm2004_cmi.spec.ts +1031 -0
- package/test/cmi_helpers.ts +207 -0
- package/test/exceptions.spec.ts +79 -0
- package/test/field_values.ts +202 -0
- package/test/types/api_types.spec.ts +126 -0
- package/test/utilities/debounce.spec.ts +56 -0
- package/test/utilities.spec.ts +322 -0
- package/tsconfig.json +18 -0
- package/webpack.config.js +65 -0
- package/.circleci/config.yml +0 -99
- package/.codeclimate.yml +0 -7
- package/.eslintrc.js +0 -36
- package/src/.flowconfig +0 -11
- package/src/AICC.js +0 -68
- package/src/BaseAPI.js +0 -1275
- package/src/Scorm12API.js +0 -308
- package/src/Scorm2004API.js +0 -572
- package/src/cmi/aicc_cmi.js +0 -1141
- package/src/cmi/common.js +0 -328
- package/src/cmi/scorm12_cmi.js +0 -1312
- package/src/cmi/scorm2004_cmi.js +0 -1692
- package/src/constants/api_constants.js +0 -218
- package/src/constants/error_codes.js +0 -87
- package/src/constants/language_constants.js +0 -76
- package/src/constants/regex.js +0 -84
- package/src/exceptions.js +0 -104
- package/src/utilities.js +0 -242
- package/test/Scorm12API.spec.js +0 -528
- package/test/Scorm2004API.spec.js +0 -775
- package/test/abstract_classes.spec.js +0 -17
- package/test/api_helpers.js +0 -128
- package/test/cmi/aicc_cmi.spec.js +0 -684
- package/test/cmi/scorm2004_cmi.spec.js +0 -1066
- package/test/cmi_helpers.js +0 -161
- package/test/exceptions.spec.js +0 -71
- package/test/field_values.js +0 -353
- package/test/utilities.spec.js +0 -339
- package/webpack.js +0 -78
package/src/Scorm2004API.js
DELETED
|
@@ -1,572 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import BaseAPI from './BaseAPI';
|
|
3
|
-
import {
|
|
4
|
-
ADL,
|
|
5
|
-
CMI,
|
|
6
|
-
CMICommentsObject,
|
|
7
|
-
CMIInteractionsCorrectResponsesObject,
|
|
8
|
-
CMIInteractionsObject,
|
|
9
|
-
CMIInteractionsObjectivesObject,
|
|
10
|
-
CMIObjectivesObject,
|
|
11
|
-
} from './cmi/scorm2004_cmi';
|
|
12
|
-
import * as Utilities from './utilities';
|
|
13
|
-
import APIConstants from './constants/api_constants';
|
|
14
|
-
import ErrorCodes from './constants/error_codes';
|
|
15
|
-
import Responses from './constants/response_constants';
|
|
16
|
-
import ValidLanguages from './constants/language_constants';
|
|
17
|
-
import Regex from './constants/regex';
|
|
18
|
-
|
|
19
|
-
const scorm2004_constants = APIConstants.scorm2004;
|
|
20
|
-
const global_constants = APIConstants.global;
|
|
21
|
-
const scorm2004_error_codes = ErrorCodes.scorm2004;
|
|
22
|
-
const correct_responses = Responses.correct;
|
|
23
|
-
const scorm2004_regex = Regex.scorm2004;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* API class for SCORM 2004
|
|
27
|
-
*/
|
|
28
|
-
export default class Scorm2004API extends BaseAPI {
|
|
29
|
-
#version: '1.0';
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Constructor for SCORM 2004 API
|
|
33
|
-
* @param {object} settings
|
|
34
|
-
*/
|
|
35
|
-
constructor(settings: {}) {
|
|
36
|
-
const finalSettings = {
|
|
37
|
-
...{
|
|
38
|
-
mastery_override: false,
|
|
39
|
-
}, ...settings,
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
super(scorm2004_error_codes, finalSettings);
|
|
43
|
-
|
|
44
|
-
this.cmi = new CMI();
|
|
45
|
-
this.adl = new ADL();
|
|
46
|
-
|
|
47
|
-
// Rename functions to match 2004 Spec and expose to modules
|
|
48
|
-
this.Initialize = this.lmsInitialize;
|
|
49
|
-
this.Terminate = this.lmsTerminate;
|
|
50
|
-
this.GetValue = this.lmsGetValue;
|
|
51
|
-
this.SetValue = this.lmsSetValue;
|
|
52
|
-
this.Commit = this.lmsCommit;
|
|
53
|
-
this.GetLastError = this.lmsGetLastError;
|
|
54
|
-
this.GetErrorString = this.lmsGetErrorString;
|
|
55
|
-
this.GetDiagnostic = this.lmsGetDiagnostic;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Getter for #version
|
|
60
|
-
* @return {string}
|
|
61
|
-
*/
|
|
62
|
-
get version() {
|
|
63
|
-
return this.#version;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @return {string} bool
|
|
68
|
-
*/
|
|
69
|
-
lmsInitialize() {
|
|
70
|
-
this.cmi.initialize();
|
|
71
|
-
return this.initialize('Initialize');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @return {string} bool
|
|
76
|
-
*/
|
|
77
|
-
lmsTerminate() {
|
|
78
|
-
const result = this.terminate('Terminate', true);
|
|
79
|
-
|
|
80
|
-
if (result === global_constants.SCORM_TRUE) {
|
|
81
|
-
if (this.adl.nav.request !== '_none_') {
|
|
82
|
-
switch (this.adl.nav.request) {
|
|
83
|
-
case 'continue':
|
|
84
|
-
this.processListeners('SequenceNext');
|
|
85
|
-
break;
|
|
86
|
-
case 'previous':
|
|
87
|
-
this.processListeners('SequencePrevious');
|
|
88
|
-
break;
|
|
89
|
-
case 'choice':
|
|
90
|
-
this.processListeners('SequenceChoice');
|
|
91
|
-
break;
|
|
92
|
-
case 'exit':
|
|
93
|
-
this.processListeners('SequenceExit');
|
|
94
|
-
break;
|
|
95
|
-
case 'exitAll':
|
|
96
|
-
this.processListeners('SequenceExitAll');
|
|
97
|
-
break;
|
|
98
|
-
case 'abandon':
|
|
99
|
-
this.processListeners('SequenceAbandon');
|
|
100
|
-
break;
|
|
101
|
-
case 'abandonAll':
|
|
102
|
-
this.processListeners('SequenceAbandonAll');
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
} else if (this.settings.autoProgress) {
|
|
106
|
-
this.processListeners('SequenceNext');
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return result;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* @param {string} CMIElement
|
|
115
|
-
* @return {string}
|
|
116
|
-
*/
|
|
117
|
-
lmsGetValue(CMIElement) {
|
|
118
|
-
return this.getValue('GetValue', true, CMIElement);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* @param {string} CMIElement
|
|
123
|
-
* @param {any} value
|
|
124
|
-
* @return {string}
|
|
125
|
-
*/
|
|
126
|
-
lmsSetValue(CMIElement, value) {
|
|
127
|
-
return this.setValue('SetValue', 'Commit', true, CMIElement, value);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Orders LMS to store all content parameters
|
|
132
|
-
*
|
|
133
|
-
* @return {string} bool
|
|
134
|
-
*/
|
|
135
|
-
lmsCommit() {
|
|
136
|
-
return this.commit('Commit');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Returns last error code
|
|
141
|
-
*
|
|
142
|
-
* @return {string}
|
|
143
|
-
*/
|
|
144
|
-
lmsGetLastError() {
|
|
145
|
-
return this.getLastError('GetLastError');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Returns the errorNumber error description
|
|
150
|
-
*
|
|
151
|
-
* @param {(string|number)} CMIErrorCode
|
|
152
|
-
* @return {string}
|
|
153
|
-
*/
|
|
154
|
-
lmsGetErrorString(CMIErrorCode) {
|
|
155
|
-
return this.getErrorString('GetErrorString', CMIErrorCode);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Returns a comprehensive description of the errorNumber error.
|
|
160
|
-
*
|
|
161
|
-
* @param {(string|number)} CMIErrorCode
|
|
162
|
-
* @return {string}
|
|
163
|
-
*/
|
|
164
|
-
lmsGetDiagnostic(CMIErrorCode) {
|
|
165
|
-
return this.getDiagnostic('GetDiagnostic', CMIErrorCode);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Sets a value on the CMI Object
|
|
170
|
-
*
|
|
171
|
-
* @param {string} CMIElement
|
|
172
|
-
* @param {any} value
|
|
173
|
-
* @return {string}
|
|
174
|
-
*/
|
|
175
|
-
setCMIValue(CMIElement, value) {
|
|
176
|
-
return this._commonSetCMIValue('SetValue', true, CMIElement, value);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Gets or builds a new child element to add to the array.
|
|
181
|
-
*
|
|
182
|
-
* @param {string} CMIElement
|
|
183
|
-
* @param {any} value
|
|
184
|
-
* @param {boolean} foundFirstIndex
|
|
185
|
-
* @return {any}
|
|
186
|
-
*/
|
|
187
|
-
getChildElement(CMIElement, value, foundFirstIndex) {
|
|
188
|
-
let newChild;
|
|
189
|
-
|
|
190
|
-
if (this.stringMatches(CMIElement, 'cmi\\.objectives\\.\\d+')) {
|
|
191
|
-
newChild = new CMIObjectivesObject();
|
|
192
|
-
} else if (foundFirstIndex && this.stringMatches(CMIElement,
|
|
193
|
-
'cmi\\.interactions\\.\\d+\\.correct_responses\\.\\d+')) {
|
|
194
|
-
const parts = CMIElement.split('.');
|
|
195
|
-
const index = Number(parts[2]);
|
|
196
|
-
const interaction = this.cmi.interactions.childArray[index];
|
|
197
|
-
if (this.isInitialized()) {
|
|
198
|
-
if (!interaction.type) {
|
|
199
|
-
this.throwSCORMError(
|
|
200
|
-
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
|
|
201
|
-
} else {
|
|
202
|
-
this.checkDuplicateChoiceResponse(interaction, value);
|
|
203
|
-
|
|
204
|
-
const response_type = correct_responses[interaction.type];
|
|
205
|
-
if (response_type) {
|
|
206
|
-
this.checkValidResponseType(response_type, value, interaction.type);
|
|
207
|
-
} else {
|
|
208
|
-
this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
|
|
209
|
-
'Incorrect Response Type: ' + interaction.type);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
if (this.lastErrorCode === 0) {
|
|
214
|
-
newChild = new CMIInteractionsCorrectResponsesObject();
|
|
215
|
-
}
|
|
216
|
-
} else if (foundFirstIndex && this.stringMatches(CMIElement,
|
|
217
|
-
'cmi\\.interactions\\.\\d+\\.objectives\\.\\d+')) {
|
|
218
|
-
newChild = new CMIInteractionsObjectivesObject();
|
|
219
|
-
} else if (!foundFirstIndex &&
|
|
220
|
-
this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d+')) {
|
|
221
|
-
newChild = new CMIInteractionsObject();
|
|
222
|
-
} else if (this.stringMatches(CMIElement,
|
|
223
|
-
'cmi\\.comments_from_learner\\.\\d+')) {
|
|
224
|
-
newChild = new CMICommentsObject();
|
|
225
|
-
} else if (this.stringMatches(CMIElement,
|
|
226
|
-
'cmi\\.comments_from_lms\\.\\d+')) {
|
|
227
|
-
newChild = new CMICommentsObject(true);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return newChild;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Checks for valid response types
|
|
235
|
-
* @param {object} response_type
|
|
236
|
-
* @param {any} value
|
|
237
|
-
* @param {string} interaction_type
|
|
238
|
-
*/
|
|
239
|
-
checkValidResponseType(response_type, value, interaction_type) {
|
|
240
|
-
let nodes = [];
|
|
241
|
-
if (response_type?.delimiter) {
|
|
242
|
-
nodes = String(value).split(response_type.delimiter);
|
|
243
|
-
} else {
|
|
244
|
-
nodes[0] = value;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (nodes.length > 0 && nodes.length <= response_type.max) {
|
|
248
|
-
this.checkCorrectResponseValue(interaction_type, nodes, value);
|
|
249
|
-
} else if (nodes.length > response_type.max) {
|
|
250
|
-
this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
|
|
251
|
-
'Data Model Element Pattern Too Long');
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Checks for duplicate 'choice' responses.
|
|
257
|
-
* @param {CMIInteractionsObject} interaction
|
|
258
|
-
* @param {any} value
|
|
259
|
-
*/
|
|
260
|
-
checkDuplicateChoiceResponse(interaction, value) {
|
|
261
|
-
const interaction_count = interaction.correct_responses._count;
|
|
262
|
-
if (interaction.type === 'choice') {
|
|
263
|
-
for (let i = 0; i < interaction_count && this.lastErrorCode ===
|
|
264
|
-
0; i++) {
|
|
265
|
-
const response = interaction.correct_responses.childArray[i];
|
|
266
|
-
if (response.pattern === value) {
|
|
267
|
-
this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Validate correct response.
|
|
275
|
-
* @param {string} CMIElement
|
|
276
|
-
* @param {*} value
|
|
277
|
-
*/
|
|
278
|
-
validateCorrectResponse(CMIElement, value) {
|
|
279
|
-
const parts = CMIElement.split('.');
|
|
280
|
-
const index = Number(parts[2]);
|
|
281
|
-
const pattern_index = Number(parts[4]);
|
|
282
|
-
const interaction = this.cmi.interactions.childArray[index];
|
|
283
|
-
|
|
284
|
-
const interaction_count = interaction.correct_responses._count;
|
|
285
|
-
this.checkDuplicateChoiceResponse(interaction, value);
|
|
286
|
-
|
|
287
|
-
const response_type = correct_responses[interaction.type];
|
|
288
|
-
if (typeof response_type.limit === 'undefined' || interaction_count <=
|
|
289
|
-
response_type.limit) {
|
|
290
|
-
this.checkValidResponseType(response_type, value, interaction.type);
|
|
291
|
-
|
|
292
|
-
if (this.lastErrorCode === 0 &&
|
|
293
|
-
(!response_type.duplicate ||
|
|
294
|
-
!this.checkDuplicatedPattern(interaction.correct_responses,
|
|
295
|
-
pattern_index, value)) ||
|
|
296
|
-
(this.lastErrorCode === 0 && value === '')) {
|
|
297
|
-
// do nothing, we want the inverse
|
|
298
|
-
} else {
|
|
299
|
-
if (this.lastErrorCode === 0) {
|
|
300
|
-
this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
|
|
301
|
-
'Data Model Element Pattern Already Exists');
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
} else {
|
|
305
|
-
this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
|
|
306
|
-
'Data Model Element Collection Limit Reached');
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Gets a value from the CMI Object
|
|
312
|
-
*
|
|
313
|
-
* @param {string} CMIElement
|
|
314
|
-
* @return {*}
|
|
315
|
-
*/
|
|
316
|
-
getCMIValue(CMIElement) {
|
|
317
|
-
return this._commonGetCMIValue('GetValue', true, CMIElement);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Returns the message that corresponds to errorNumber.
|
|
322
|
-
*
|
|
323
|
-
* @param {(string|number)} errorNumber
|
|
324
|
-
* @param {boolean} detail
|
|
325
|
-
* @return {string}
|
|
326
|
-
*/
|
|
327
|
-
getLmsErrorMessageDetails(errorNumber, detail) {
|
|
328
|
-
let basicMessage = '';
|
|
329
|
-
let detailMessage = '';
|
|
330
|
-
|
|
331
|
-
// Set error number to string since inconsistent from modules if string or number
|
|
332
|
-
errorNumber = String(errorNumber);
|
|
333
|
-
if (scorm2004_constants.error_descriptions[errorNumber]) {
|
|
334
|
-
basicMessage = scorm2004_constants.error_descriptions[errorNumber].basicMessage;
|
|
335
|
-
detailMessage = scorm2004_constants.error_descriptions[errorNumber].detailMessage;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return detail ? detailMessage : basicMessage;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Check to see if a correct_response value has been duplicated
|
|
343
|
-
* @param {CMIArray} correct_response
|
|
344
|
-
* @param {number} current_index
|
|
345
|
-
* @param {*} value
|
|
346
|
-
* @return {boolean}
|
|
347
|
-
*/
|
|
348
|
-
checkDuplicatedPattern = (correct_response, current_index, value) => {
|
|
349
|
-
let found = false;
|
|
350
|
-
const count = correct_response._count;
|
|
351
|
-
for (let i = 0; i < count && !found; i++) {
|
|
352
|
-
if (i !== current_index && correct_response.childArray[i] === value) {
|
|
353
|
-
found = true;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
return found;
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Checks for a valid correct_response value
|
|
361
|
-
* @param {string} interaction_type
|
|
362
|
-
* @param {Array} nodes
|
|
363
|
-
* @param {*} value
|
|
364
|
-
*/
|
|
365
|
-
checkCorrectResponseValue(interaction_type, nodes, value) {
|
|
366
|
-
const response = correct_responses[interaction_type];
|
|
367
|
-
const formatRegex = new RegExp(response.format);
|
|
368
|
-
for (let i = 0; i < nodes.length && this.lastErrorCode === 0; i++) {
|
|
369
|
-
if (interaction_type.match(
|
|
370
|
-
'^(fill-in|long-fill-in|matching|performance|sequencing)$')) {
|
|
371
|
-
nodes[i] = this.removeCorrectResponsePrefixes(nodes[i]);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (response?.delimiter2) {
|
|
375
|
-
const values = nodes[i].split(response.delimiter2);
|
|
376
|
-
if (values.length === 2) {
|
|
377
|
-
const matches = values[0].match(formatRegex);
|
|
378
|
-
if (!matches) {
|
|
379
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
380
|
-
} else {
|
|
381
|
-
if (!values[1].match(new RegExp(response.format2))) {
|
|
382
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
} else {
|
|
386
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
387
|
-
}
|
|
388
|
-
} else {
|
|
389
|
-
const matches = nodes[i].match(formatRegex);
|
|
390
|
-
if ((!matches && value !== '') ||
|
|
391
|
-
(!matches && interaction_type === 'true-false')) {
|
|
392
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
393
|
-
} else {
|
|
394
|
-
if (interaction_type === 'numeric' && nodes.length > 1) {
|
|
395
|
-
if (Number(nodes[0]) > Number(nodes[1])) {
|
|
396
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
397
|
-
}
|
|
398
|
-
} else {
|
|
399
|
-
if (nodes[i] !== '' && response.unique) {
|
|
400
|
-
for (let j = 0; j < i && this.lastErrorCode === 0; j++) {
|
|
401
|
-
if (nodes[i] === nodes[j]) {
|
|
402
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Remove prefixes from correct_response
|
|
414
|
-
* @param {string} node
|
|
415
|
-
* @return {*}
|
|
416
|
-
*/
|
|
417
|
-
removeCorrectResponsePrefixes(node) {
|
|
418
|
-
let seenOrder = false;
|
|
419
|
-
let seenCase = false;
|
|
420
|
-
let seenLang = false;
|
|
421
|
-
|
|
422
|
-
const prefixRegex = new RegExp(
|
|
423
|
-
'^({(lang|case_matters|order_matters)=([^}]+)})');
|
|
424
|
-
let matches = node.match(prefixRegex);
|
|
425
|
-
let langMatches = null;
|
|
426
|
-
while (matches) {
|
|
427
|
-
switch (matches[2]) {
|
|
428
|
-
case 'lang':
|
|
429
|
-
langMatches = node.match(scorm2004_regex.CMILangcr);
|
|
430
|
-
if (langMatches) {
|
|
431
|
-
const lang = langMatches[3];
|
|
432
|
-
if (lang !== undefined && lang.length > 0) {
|
|
433
|
-
if (ValidLanguages[lang.toLowerCase()] === undefined) {
|
|
434
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
seenLang = true;
|
|
439
|
-
break;
|
|
440
|
-
case 'case_matters':
|
|
441
|
-
if (!seenLang && !seenOrder && !seenCase) {
|
|
442
|
-
if (matches[3] !== 'true' && matches[3] !== 'false') {
|
|
443
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
seenCase = true;
|
|
448
|
-
break;
|
|
449
|
-
case 'order_matters':
|
|
450
|
-
if (!seenCase && !seenLang && !seenOrder) {
|
|
451
|
-
if (matches[3] !== 'true' && matches[3] !== 'false') {
|
|
452
|
-
this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
seenOrder = true;
|
|
457
|
-
break;
|
|
458
|
-
default:
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
node = node.substr(matches[1].length);
|
|
462
|
-
matches = node.match(prefixRegex);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return node;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Replace the whole API with another
|
|
470
|
-
* @param {Scorm2004API} newAPI
|
|
471
|
-
*/
|
|
472
|
-
replaceWithAnotherScormAPI(newAPI) {
|
|
473
|
-
// Data Model
|
|
474
|
-
this.cmi = newAPI.cmi;
|
|
475
|
-
this.adl = newAPI.adl;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Render the cmi object to the proper format for LMS commit
|
|
480
|
-
*
|
|
481
|
-
* @param {boolean} terminateCommit
|
|
482
|
-
* @return {object|Array}
|
|
483
|
-
*/
|
|
484
|
-
renderCommitCMI(terminateCommit: boolean) {
|
|
485
|
-
const cmiExport = this.renderCMIToJSONObject();
|
|
486
|
-
|
|
487
|
-
if (terminateCommit) {
|
|
488
|
-
cmiExport.cmi.total_time = this.cmi.getCurrentTotalTime();
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const result = [];
|
|
492
|
-
const flattened = Utilities.flatten(cmiExport);
|
|
493
|
-
switch (this.settings.dataCommitFormat) {
|
|
494
|
-
case 'flattened':
|
|
495
|
-
return Utilities.flatten(cmiExport);
|
|
496
|
-
case 'params':
|
|
497
|
-
for (const item in flattened) {
|
|
498
|
-
if ({}.hasOwnProperty.call(flattened, item)) {
|
|
499
|
-
result.push(`${item}=${flattened[item]}`);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return result;
|
|
503
|
-
case 'json':
|
|
504
|
-
default:
|
|
505
|
-
return cmiExport;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Attempts to store the data to the LMS
|
|
511
|
-
*
|
|
512
|
-
* @param {boolean} terminateCommit
|
|
513
|
-
* @return {string}
|
|
514
|
-
*/
|
|
515
|
-
storeData(terminateCommit: boolean) {
|
|
516
|
-
if (terminateCommit) {
|
|
517
|
-
if (this.cmi.mode === 'normal') {
|
|
518
|
-
if (this.cmi.credit === 'credit') {
|
|
519
|
-
if (this.cmi.completion_threshold && this.cmi.progress_measure) {
|
|
520
|
-
if (this.cmi.progress_measure >= this.cmi.completion_threshold) {
|
|
521
|
-
console.debug('Setting Completion Status: Completed');
|
|
522
|
-
this.cmi.completion_status = 'completed';
|
|
523
|
-
} else {
|
|
524
|
-
console.debug('Setting Completion Status: Incomplete');
|
|
525
|
-
this.cmi.completion_status = 'incomplete';
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
if (this.cmi.scaled_passing_score && this.cmi.score.scaled) {
|
|
529
|
-
if (this.cmi.score.scaled >= this.cmi.scaled_passing_score) {
|
|
530
|
-
console.debug('Setting Success Status: Passed');
|
|
531
|
-
this.cmi.success_status = 'passed';
|
|
532
|
-
} else {
|
|
533
|
-
console.debug('Setting Success Status: Failed');
|
|
534
|
-
this.cmi.success_status = 'failed';
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
let navRequest = false;
|
|
542
|
-
if (this.adl.nav.request !== (this.startingData?.adl?.nav?.request) &&
|
|
543
|
-
this.adl.nav.request !== '_none_') {
|
|
544
|
-
this.adl.nav.request = encodeURIComponent(this.adl.nav.request);
|
|
545
|
-
navRequest = true;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const commitObject = this.renderCommitCMI(terminateCommit ||
|
|
549
|
-
this.settings.alwaysSendTotalTime);
|
|
550
|
-
|
|
551
|
-
if (this.apiLogLevel === global_constants.LOG_LEVEL_DEBUG) {
|
|
552
|
-
console.debug('Commit (terminated: ' +
|
|
553
|
-
(terminateCommit ? 'yes' : 'no') + '): ');
|
|
554
|
-
console.debug(commitObject);
|
|
555
|
-
}
|
|
556
|
-
if (this.settings.lmsCommitUrl) {
|
|
557
|
-
const result = this.processHttpRequest(this.settings.lmsCommitUrl,
|
|
558
|
-
commitObject, terminateCommit);
|
|
559
|
-
|
|
560
|
-
// check if this is a sequencing call, and then call the necessary JS
|
|
561
|
-
{
|
|
562
|
-
if (navRequest && result.navRequest !== undefined &&
|
|
563
|
-
result.navRequest !== '') {
|
|
564
|
-
Function(`"use strict";(() => { ${result.navRequest} })()`)();
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return result;
|
|
568
|
-
} else {
|
|
569
|
-
return global_constants.SCORM_TRUE;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|