scorm-again 2.0.0 → 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/.github/workflows/stale.yml +14 -0
- package/.run/{Mocha Unit Tests.run.xml → Mocha Unit Tests (watch).run.xml } +1 -1
- package/.run/Template Mocha.run.xml +17 -0
- package/README.md +171 -72
- package/dist/aicc.js +1441 -1140
- package/dist/aicc.js.map +1 -1
- package/dist/aicc.min.js +1 -1
- package/dist/aicc.min.js.map +1 -1
- package/dist/scorm-again.js +2703 -2212
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +1 -1
- package/dist/scorm-again.min.js.map +1 -1
- package/dist/scorm12.js +1069 -852
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +1 -1
- package/dist/scorm12.min.js.map +1 -1
- package/dist/scorm2004.js +1861 -1571
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +1 -1
- package/dist/scorm2004.min.js.map +1 -1
- package/package.json +10 -6
- package/src/AICC.ts +15 -17
- package/src/BaseAPI.ts +268 -417
- package/src/Scorm12API.ts +65 -38
- package/src/Scorm2004API.ts +151 -117
- 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/default_settings.ts +81 -0
- package/src/constants/enums.ts +5 -0
- package/src/constants/regex.ts +2 -2
- package/src/constants/response_constants.ts +2 -0
- package/src/exceptions.ts +22 -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 +56 -0
- package/test/AICC.spec.ts +11 -1
- package/test/Scorm12API.spec.ts +262 -9
- package/test/Scorm2004API.spec.ts +488 -2
- package/test/cmi/aicc_cmi.spec.ts +188 -11
- package/test/cmi/scorm12_cmi.spec.ts +5 -5
- package/test/cmi/scorm2004_cmi.spec.ts +8 -8
- package/test/cmi_helpers.ts +1 -1
- package/test/types/api_types.spec.ts +126 -0
- package/test/utilities/debounce.spec.ts +56 -0
- package/src/cmi/aicc_cmi.ts +0 -1248
- package/src/cmi/common.ts +0 -411
- package/src/cmi/scorm12_cmi.ts +0 -1426
- package/src/cmi/scorm2004_cmi.ts +0 -1874
package/src/BaseAPI.ts
CHANGED
|
@@ -1,154 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CMIArray } from "./cmi/common/array";
|
|
2
2
|
import { ValidationError } from "./exceptions";
|
|
3
3
|
import ErrorCodes, { ErrorCode } from "./constants/error_codes";
|
|
4
4
|
import APIConstants from "./constants/api_constants";
|
|
5
|
-
import { unflatten } from "./utilities";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
* @param func - The function to debounce.
|
|
14
|
-
* @param wait - The number of milliseconds to delay.
|
|
15
|
-
* @param immediate - If `true`, the function will be triggered on the leading edge instead of the trailing.
|
|
16
|
-
* @returns A debounced version of the provided function.
|
|
17
|
-
*/
|
|
18
|
-
function debounce<T extends (...args: any[]) => void>(
|
|
19
|
-
func: T,
|
|
20
|
-
wait: number,
|
|
21
|
-
immediate = false,
|
|
22
|
-
): (...args: Parameters<T>) => void {
|
|
23
|
-
let timeout: ReturnType<typeof setTimeout> | null;
|
|
24
|
-
|
|
25
|
-
return function (this: any, ...args: Parameters<T>) {
|
|
26
|
-
const context = this;
|
|
27
|
-
|
|
28
|
-
const later = () => {
|
|
29
|
-
timeout = null;
|
|
30
|
-
if (!immediate) func.apply(context, args);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const callNow = immediate && !timeout;
|
|
34
|
-
|
|
35
|
-
if (timeout) clearTimeout(timeout);
|
|
36
|
-
timeout = setTimeout(later, wait);
|
|
37
|
-
|
|
38
|
-
if (callNow) func.apply(context, args);
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type Settings = {
|
|
43
|
-
autocommit: boolean;
|
|
44
|
-
autocommitSeconds: number;
|
|
45
|
-
asyncCommit: boolean;
|
|
46
|
-
sendBeaconCommit: boolean;
|
|
47
|
-
lmsCommitUrl: boolean | string;
|
|
48
|
-
dataCommitFormat: string;
|
|
49
|
-
commitRequestDataType: string;
|
|
50
|
-
autoProgress: boolean;
|
|
51
|
-
logLevel: number;
|
|
52
|
-
selfReportSessionTime: boolean;
|
|
53
|
-
alwaysSendTotalTime: boolean;
|
|
54
|
-
strict_errors: boolean;
|
|
55
|
-
xhrHeaders: RefObject;
|
|
56
|
-
xhrWithCredentials: boolean;
|
|
57
|
-
responseHandler: (response: Response) => Promise<ResultObject>;
|
|
58
|
-
requestHandler: (commitObject: any) => any;
|
|
59
|
-
onLogMessage: (messageLevel: number, logMessage: string) => void;
|
|
60
|
-
mastery_override?: boolean;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export type RefObject = {
|
|
64
|
-
[key: string]: any;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export type ResultObject = {
|
|
68
|
-
result: string;
|
|
69
|
-
errorCode: number;
|
|
70
|
-
navRequest?: string;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const DefaultSettings: Settings = {
|
|
74
|
-
autocommit: false,
|
|
75
|
-
autocommitSeconds: 10,
|
|
76
|
-
asyncCommit: false,
|
|
77
|
-
sendBeaconCommit: false,
|
|
78
|
-
lmsCommitUrl: false,
|
|
79
|
-
dataCommitFormat: "json",
|
|
80
|
-
commitRequestDataType: "application/json;charset=UTF-8",
|
|
81
|
-
autoProgress: false,
|
|
82
|
-
logLevel: global_constants.LOG_LEVEL_ERROR,
|
|
83
|
-
selfReportSessionTime: false,
|
|
84
|
-
alwaysSendTotalTime: false,
|
|
85
|
-
strict_errors: true,
|
|
86
|
-
xhrHeaders: {},
|
|
87
|
-
xhrWithCredentials: false,
|
|
88
|
-
responseHandler: async function (response: Response): Promise<ResultObject> {
|
|
89
|
-
if (typeof response !== "undefined") {
|
|
90
|
-
const httpResult = JSON.parse(await response.text());
|
|
91
|
-
if (
|
|
92
|
-
httpResult === null ||
|
|
93
|
-
!{}.hasOwnProperty.call(httpResult, "result")
|
|
94
|
-
) {
|
|
95
|
-
if (response.status === 200) {
|
|
96
|
-
return {
|
|
97
|
-
result: global_constants.SCORM_TRUE,
|
|
98
|
-
errorCode: 0,
|
|
99
|
-
};
|
|
100
|
-
} else {
|
|
101
|
-
return {
|
|
102
|
-
result: global_constants.SCORM_FALSE,
|
|
103
|
-
errorCode: 101,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
return {
|
|
108
|
-
result: httpResult.result,
|
|
109
|
-
errorCode: httpResult.errorCode
|
|
110
|
-
? httpResult.errorCode
|
|
111
|
-
: httpResult.result === global_constants.SCORM_TRUE
|
|
112
|
-
? 0
|
|
113
|
-
: 101,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
result: global_constants.SCORM_FALSE,
|
|
119
|
-
errorCode: 101,
|
|
120
|
-
};
|
|
121
|
-
},
|
|
122
|
-
requestHandler: function (commitObject) {
|
|
123
|
-
return commitObject;
|
|
124
|
-
},
|
|
125
|
-
onLogMessage: function (messageLevel, logMessage) {
|
|
126
|
-
switch (messageLevel) {
|
|
127
|
-
case global_constants.LOG_LEVEL_ERROR:
|
|
128
|
-
console.error(logMessage);
|
|
129
|
-
break;
|
|
130
|
-
case global_constants.LOG_LEVEL_WARNING:
|
|
131
|
-
console.warn(logMessage);
|
|
132
|
-
break;
|
|
133
|
-
case global_constants.LOG_LEVEL_INFO:
|
|
134
|
-
console.info(logMessage);
|
|
135
|
-
break;
|
|
136
|
-
case global_constants.LOG_LEVEL_DEBUG:
|
|
137
|
-
if (console.debug) {
|
|
138
|
-
console.debug(logMessage);
|
|
139
|
-
} else {
|
|
140
|
-
console.log(logMessage);
|
|
141
|
-
}
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
};
|
|
5
|
+
import { formatMessage, stringMatches, unflatten } from "./utilities";
|
|
6
|
+
import { BaseCMI } from "./cmi/common/base_cmi";
|
|
7
|
+
import { debounce } from "./utilities/debounce";
|
|
8
|
+
import { RefObject, ResultObject, Settings } from "./types/api_types";
|
|
9
|
+
import { DefaultSettings } from "./constants/default_settings";
|
|
10
|
+
import { IBaseAPI } from "./interfaces/IBaseAPI";
|
|
11
|
+
import { ScheduledCommit } from "./helpers/scheduled_commit";
|
|
146
12
|
|
|
147
13
|
/**
|
|
148
14
|
* Base API class for AICC, SCORM 1.2, and SCORM 2004. Should be considered
|
|
149
15
|
* abstract, and never initialized on its own.
|
|
150
16
|
*/
|
|
151
|
-
export default abstract class BaseAPI {
|
|
17
|
+
export default abstract class BaseAPI implements IBaseAPI {
|
|
152
18
|
private _timeout?: ScheduledCommit;
|
|
153
19
|
private readonly _error_codes: ErrorCode;
|
|
154
20
|
private _settings: Settings = DefaultSettings;
|
|
@@ -163,7 +29,7 @@ export default abstract class BaseAPI {
|
|
|
163
29
|
if (new.target === BaseAPI) {
|
|
164
30
|
throw new TypeError("Cannot construct BaseAPI instances directly");
|
|
165
31
|
}
|
|
166
|
-
this.currentState =
|
|
32
|
+
this.currentState = APIConstants.global.STATE_NOT_INITIALIZED;
|
|
167
33
|
this.lastErrorCode = "0";
|
|
168
34
|
this.listenerArray = [];
|
|
169
35
|
|
|
@@ -185,6 +51,21 @@ export default abstract class BaseAPI {
|
|
|
185
51
|
public apiLogLevel: number;
|
|
186
52
|
public selfReportSessionTime: boolean;
|
|
187
53
|
|
|
54
|
+
abstract reset(settings?: Settings): void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Common reset method for all APIs. New settings are merged with the existing settings.
|
|
58
|
+
* @param {Settings} settings
|
|
59
|
+
* @protected
|
|
60
|
+
*/
|
|
61
|
+
commonReset(settings?: Settings): void {
|
|
62
|
+
this.settings = { ...this.settings, ...settings };
|
|
63
|
+
|
|
64
|
+
this.currentState = APIConstants.global.STATE_NOT_INITIALIZED;
|
|
65
|
+
this.lastErrorCode = "0";
|
|
66
|
+
this.listenerArray = [];
|
|
67
|
+
}
|
|
68
|
+
|
|
188
69
|
/**
|
|
189
70
|
* Initialize the API
|
|
190
71
|
* @param {string} callbackName
|
|
@@ -197,7 +78,7 @@ export default abstract class BaseAPI {
|
|
|
197
78
|
initializeMessage?: string,
|
|
198
79
|
terminationMessage?: string,
|
|
199
80
|
): string {
|
|
200
|
-
let returnValue =
|
|
81
|
+
let returnValue = APIConstants.global.SCORM_FALSE;
|
|
201
82
|
|
|
202
83
|
if (this.isInitialized()) {
|
|
203
84
|
this.throwSCORMError(this._error_codes.INITIALIZED, initializeMessage);
|
|
@@ -208,16 +89,16 @@ export default abstract class BaseAPI {
|
|
|
208
89
|
this.cmi.setStartTime();
|
|
209
90
|
}
|
|
210
91
|
|
|
211
|
-
this.currentState =
|
|
92
|
+
this.currentState = APIConstants.global.STATE_INITIALIZED;
|
|
212
93
|
this.lastErrorCode = "0";
|
|
213
|
-
returnValue =
|
|
94
|
+
returnValue = APIConstants.global.SCORM_TRUE;
|
|
214
95
|
this.processListeners(callbackName);
|
|
215
96
|
}
|
|
216
97
|
|
|
217
98
|
this.apiLog(
|
|
218
99
|
callbackName,
|
|
219
100
|
"returned: " + returnValue,
|
|
220
|
-
|
|
101
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
221
102
|
);
|
|
222
103
|
this.clearSCORMError(returnValue);
|
|
223
104
|
|
|
@@ -240,6 +121,71 @@ export default abstract class BaseAPI {
|
|
|
240
121
|
|
|
241
122
|
abstract lmsGetDiagnostic(CMIErrorCode: string | number): string;
|
|
242
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Abstract method for validating that a response is correct.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} _CMIElement
|
|
128
|
+
* @param {any} _value
|
|
129
|
+
*/
|
|
130
|
+
abstract validateCorrectResponse(_CMIElement: string, _value: any): void;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Gets or builds a new child element to add to the array.
|
|
134
|
+
* APIs that inherit BaseAPI should override this method.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} _CMIElement - unused
|
|
137
|
+
* @param {*} _value - unused
|
|
138
|
+
* @param {boolean} _foundFirstIndex - unused
|
|
139
|
+
* @return {BaseCMI|null}
|
|
140
|
+
* @abstract
|
|
141
|
+
*/
|
|
142
|
+
abstract getChildElement(
|
|
143
|
+
_CMIElement: string,
|
|
144
|
+
_value: any,
|
|
145
|
+
_foundFirstIndex: boolean,
|
|
146
|
+
): BaseCMI | null;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Attempts to store the data to the LMS, logs data if no LMS configured
|
|
150
|
+
* APIs that inherit BaseAPI should override this function
|
|
151
|
+
*
|
|
152
|
+
* @param {boolean} _calculateTotalTime
|
|
153
|
+
* @return {ResultObject}
|
|
154
|
+
* @abstract
|
|
155
|
+
*/
|
|
156
|
+
abstract storeData(_calculateTotalTime: boolean): Promise<ResultObject>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Render the cmi object to the proper format for LMS commit
|
|
160
|
+
* APIs that inherit BaseAPI should override this function
|
|
161
|
+
*
|
|
162
|
+
* @param {boolean} _terminateCommit
|
|
163
|
+
* @return {RefObject|Array}
|
|
164
|
+
* @abstract
|
|
165
|
+
*/
|
|
166
|
+
abstract renderCommitCMI(_terminateCommit: boolean): RefObject | Array<any>;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Logging for all SCORM actions
|
|
170
|
+
*
|
|
171
|
+
* @param {string} functionName
|
|
172
|
+
* @param {string} logMessage
|
|
173
|
+
* @param {number} messageLevel
|
|
174
|
+
* @param {string} CMIElement
|
|
175
|
+
*/
|
|
176
|
+
apiLog(
|
|
177
|
+
functionName: string,
|
|
178
|
+
logMessage: string,
|
|
179
|
+
messageLevel: number,
|
|
180
|
+
CMIElement?: string,
|
|
181
|
+
) {
|
|
182
|
+
logMessage = formatMessage(functionName, logMessage, CMIElement);
|
|
183
|
+
|
|
184
|
+
if (messageLevel >= this.apiLogLevel) {
|
|
185
|
+
this.settings.onLogMessage(messageLevel, logMessage);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
243
189
|
/**
|
|
244
190
|
* Getter for _error_codes
|
|
245
191
|
* @return {ErrorCode}
|
|
@@ -270,8 +216,11 @@ export default abstract class BaseAPI {
|
|
|
270
216
|
* @param {boolean} checkTerminated
|
|
271
217
|
* @return {string}
|
|
272
218
|
*/
|
|
273
|
-
terminate(
|
|
274
|
-
|
|
219
|
+
async terminate(
|
|
220
|
+
callbackName: string,
|
|
221
|
+
checkTerminated: boolean,
|
|
222
|
+
): Promise<string> {
|
|
223
|
+
let returnValue = APIConstants.global.SCORM_FALSE;
|
|
275
224
|
|
|
276
225
|
if (
|
|
277
226
|
this.checkState(
|
|
@@ -280,27 +229,27 @@ export default abstract class BaseAPI {
|
|
|
280
229
|
this._error_codes.MULTIPLE_TERMINATION,
|
|
281
230
|
)
|
|
282
231
|
) {
|
|
283
|
-
this.currentState =
|
|
232
|
+
this.currentState = APIConstants.global.STATE_TERMINATED;
|
|
284
233
|
|
|
285
|
-
const result: ResultObject = this.storeData(true);
|
|
234
|
+
const result: ResultObject = await this.storeData(true);
|
|
286
235
|
if (typeof result.errorCode !== "undefined" && result.errorCode > 0) {
|
|
287
236
|
this.throwSCORMError(result.errorCode);
|
|
288
237
|
}
|
|
289
238
|
returnValue =
|
|
290
239
|
typeof result !== "undefined" && result.result
|
|
291
240
|
? result.result
|
|
292
|
-
:
|
|
241
|
+
: APIConstants.global.SCORM_FALSE;
|
|
293
242
|
|
|
294
243
|
if (checkTerminated) this.lastErrorCode = "0";
|
|
295
244
|
|
|
296
|
-
returnValue =
|
|
245
|
+
returnValue = APIConstants.global.SCORM_TRUE;
|
|
297
246
|
this.processListeners(callbackName);
|
|
298
247
|
}
|
|
299
248
|
|
|
300
249
|
this.apiLog(
|
|
301
250
|
callbackName,
|
|
302
251
|
"returned: " + returnValue,
|
|
303
|
-
|
|
252
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
304
253
|
);
|
|
305
254
|
this.clearSCORMError(returnValue);
|
|
306
255
|
|
|
@@ -341,9 +290,14 @@ export default abstract class BaseAPI {
|
|
|
341
290
|
this.apiLog(
|
|
342
291
|
callbackName,
|
|
343
292
|
": returned: " + returnValue,
|
|
344
|
-
|
|
293
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
345
294
|
CMIElement,
|
|
346
295
|
);
|
|
296
|
+
|
|
297
|
+
if (returnValue === undefined) {
|
|
298
|
+
return "";
|
|
299
|
+
}
|
|
300
|
+
|
|
347
301
|
this.clearSCORMError(returnValue);
|
|
348
302
|
|
|
349
303
|
return returnValue;
|
|
@@ -369,7 +323,7 @@ export default abstract class BaseAPI {
|
|
|
369
323
|
if (value !== undefined) {
|
|
370
324
|
value = String(value);
|
|
371
325
|
}
|
|
372
|
-
let returnValue: string =
|
|
326
|
+
let returnValue: string = APIConstants.global.SCORM_FALSE;
|
|
373
327
|
|
|
374
328
|
if (
|
|
375
329
|
this.checkState(
|
|
@@ -388,7 +342,7 @@ export default abstract class BaseAPI {
|
|
|
388
342
|
}
|
|
389
343
|
|
|
390
344
|
if (returnValue === undefined) {
|
|
391
|
-
returnValue =
|
|
345
|
+
returnValue = APIConstants.global.SCORM_FALSE;
|
|
392
346
|
}
|
|
393
347
|
|
|
394
348
|
// If we didn't have any errors while setting the data, go ahead and
|
|
@@ -405,7 +359,7 @@ export default abstract class BaseAPI {
|
|
|
405
359
|
this.apiLog(
|
|
406
360
|
callbackName,
|
|
407
361
|
": " + value + ": result: " + returnValue,
|
|
408
|
-
|
|
362
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
409
363
|
CMIElement,
|
|
410
364
|
);
|
|
411
365
|
this.clearSCORMError(returnValue);
|
|
@@ -419,10 +373,13 @@ export default abstract class BaseAPI {
|
|
|
419
373
|
* @param {boolean} checkTerminated
|
|
420
374
|
* @return {string}
|
|
421
375
|
*/
|
|
422
|
-
commit(
|
|
376
|
+
async commit(
|
|
377
|
+
callbackName: string,
|
|
378
|
+
checkTerminated: boolean = false,
|
|
379
|
+
): Promise<string> {
|
|
423
380
|
this.clearScheduledCommit();
|
|
424
381
|
|
|
425
|
-
let returnValue =
|
|
382
|
+
let returnValue = APIConstants.global.SCORM_FALSE;
|
|
426
383
|
|
|
427
384
|
if (
|
|
428
385
|
this.checkState(
|
|
@@ -431,19 +388,19 @@ export default abstract class BaseAPI {
|
|
|
431
388
|
this._error_codes.COMMIT_AFTER_TERM,
|
|
432
389
|
)
|
|
433
390
|
) {
|
|
434
|
-
const result = this.storeData(false);
|
|
391
|
+
const result = await this.storeData(false);
|
|
435
392
|
if (result.errorCode && result.errorCode > 0) {
|
|
436
393
|
this.throwSCORMError(result.errorCode);
|
|
437
394
|
}
|
|
438
395
|
returnValue =
|
|
439
396
|
typeof result !== "undefined" && result.result
|
|
440
397
|
? result.result
|
|
441
|
-
:
|
|
398
|
+
: APIConstants.global.SCORM_FALSE;
|
|
442
399
|
|
|
443
400
|
this.apiLog(
|
|
444
401
|
callbackName,
|
|
445
402
|
" Result: " + returnValue,
|
|
446
|
-
|
|
403
|
+
APIConstants.global.LOG_LEVEL_DEBUG,
|
|
447
404
|
"HttpRequest",
|
|
448
405
|
);
|
|
449
406
|
|
|
@@ -455,7 +412,7 @@ export default abstract class BaseAPI {
|
|
|
455
412
|
this.apiLog(
|
|
456
413
|
callbackName,
|
|
457
414
|
"returned: " + returnValue,
|
|
458
|
-
|
|
415
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
459
416
|
);
|
|
460
417
|
this.clearSCORMError(returnValue);
|
|
461
418
|
|
|
@@ -475,7 +432,7 @@ export default abstract class BaseAPI {
|
|
|
475
432
|
this.apiLog(
|
|
476
433
|
callbackName,
|
|
477
434
|
"returned: " + returnValue,
|
|
478
|
-
|
|
435
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
479
436
|
);
|
|
480
437
|
|
|
481
438
|
return returnValue;
|
|
@@ -499,7 +456,7 @@ export default abstract class BaseAPI {
|
|
|
499
456
|
this.apiLog(
|
|
500
457
|
callbackName,
|
|
501
458
|
"returned: " + returnValue,
|
|
502
|
-
|
|
459
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
503
460
|
);
|
|
504
461
|
|
|
505
462
|
return returnValue;
|
|
@@ -523,7 +480,7 @@ export default abstract class BaseAPI {
|
|
|
523
480
|
this.apiLog(
|
|
524
481
|
callbackName,
|
|
525
482
|
"returned: " + returnValue,
|
|
526
|
-
|
|
483
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
527
484
|
);
|
|
528
485
|
|
|
529
486
|
return returnValue;
|
|
@@ -553,104 +510,6 @@ export default abstract class BaseAPI {
|
|
|
553
510
|
return true;
|
|
554
511
|
}
|
|
555
512
|
|
|
556
|
-
/**
|
|
557
|
-
* Logging for all SCORM actions
|
|
558
|
-
*
|
|
559
|
-
* @param {string} functionName
|
|
560
|
-
* @param {string} logMessage
|
|
561
|
-
* @param {number}messageLevel
|
|
562
|
-
* @param {string} CMIElement
|
|
563
|
-
*/
|
|
564
|
-
apiLog(
|
|
565
|
-
functionName: string,
|
|
566
|
-
logMessage: string,
|
|
567
|
-
messageLevel: number,
|
|
568
|
-
CMIElement?: string,
|
|
569
|
-
) {
|
|
570
|
-
logMessage = this.formatMessage(functionName, logMessage, CMIElement);
|
|
571
|
-
|
|
572
|
-
if (messageLevel >= this.apiLogLevel) {
|
|
573
|
-
this.settings.onLogMessage(messageLevel, logMessage);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Formats the SCORM messages for easy reading
|
|
579
|
-
*
|
|
580
|
-
* @param {string} functionName
|
|
581
|
-
* @param {string} message
|
|
582
|
-
* @param {string} CMIElement
|
|
583
|
-
* @return {string}
|
|
584
|
-
*/
|
|
585
|
-
formatMessage(
|
|
586
|
-
functionName: string,
|
|
587
|
-
message: string,
|
|
588
|
-
CMIElement?: string,
|
|
589
|
-
): string {
|
|
590
|
-
const baseLength = 20;
|
|
591
|
-
let messageString = "";
|
|
592
|
-
|
|
593
|
-
messageString += functionName;
|
|
594
|
-
|
|
595
|
-
let fillChars = baseLength - messageString.length;
|
|
596
|
-
|
|
597
|
-
for (let i = 0; i < fillChars; i++) {
|
|
598
|
-
messageString += " ";
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
messageString += ": ";
|
|
602
|
-
|
|
603
|
-
if (CMIElement) {
|
|
604
|
-
const CMIElementBaseLength = 70;
|
|
605
|
-
|
|
606
|
-
messageString += CMIElement;
|
|
607
|
-
|
|
608
|
-
fillChars = CMIElementBaseLength - messageString.length;
|
|
609
|
-
|
|
610
|
-
for (let j = 0; j < fillChars; j++) {
|
|
611
|
-
messageString += " ";
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
if (message) {
|
|
616
|
-
messageString += message;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
return messageString;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* Checks to see if {str} contains {tester}
|
|
624
|
-
*
|
|
625
|
-
* @param {string} str String to check against
|
|
626
|
-
* @param {string} tester String to check for
|
|
627
|
-
* @return {boolean}
|
|
628
|
-
*/
|
|
629
|
-
stringMatches(str: string, tester: string): boolean {
|
|
630
|
-
return str?.match(tester) !== null;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Check to see if the specific object has the given property
|
|
635
|
-
* @param {RefObject} refObject
|
|
636
|
-
* @param {string} attribute
|
|
637
|
-
* @return {boolean}
|
|
638
|
-
* @private
|
|
639
|
-
*/
|
|
640
|
-
private _checkObjectHasProperty(
|
|
641
|
-
refObject: RefObject,
|
|
642
|
-
attribute: string,
|
|
643
|
-
): boolean {
|
|
644
|
-
return (
|
|
645
|
-
Object.hasOwnProperty.call(refObject, attribute) ||
|
|
646
|
-
Object.getOwnPropertyDescriptor(
|
|
647
|
-
Object.getPrototypeOf(refObject),
|
|
648
|
-
attribute,
|
|
649
|
-
) != null ||
|
|
650
|
-
attribute in refObject
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
513
|
/**
|
|
655
514
|
* Returns the message that corresponds to errorNumber
|
|
656
515
|
* APIs that inherit BaseAPI should override this function
|
|
@@ -710,12 +569,12 @@ export default abstract class BaseAPI {
|
|
|
710
569
|
value: any,
|
|
711
570
|
): string {
|
|
712
571
|
if (!CMIElement || CMIElement === "") {
|
|
713
|
-
return
|
|
572
|
+
return APIConstants.global.SCORM_FALSE;
|
|
714
573
|
}
|
|
715
574
|
|
|
716
575
|
const structure = CMIElement.split(".");
|
|
717
576
|
let refObject: RefObject = this;
|
|
718
|
-
let returnValue =
|
|
577
|
+
let returnValue = APIConstants.global.SCORM_FALSE;
|
|
719
578
|
let foundFirstIndex = false;
|
|
720
579
|
|
|
721
580
|
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
|
@@ -727,25 +586,28 @@ export default abstract class BaseAPI {
|
|
|
727
586
|
const attribute = structure[idx];
|
|
728
587
|
|
|
729
588
|
if (idx === structure.length - 1) {
|
|
730
|
-
if (
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
589
|
+
if (scorm2004 && attribute.substring(0, 8) === "{target=") {
|
|
590
|
+
if (this.isInitialized()) {
|
|
591
|
+
this.throwSCORMError(this._error_codes.READ_ONLY_ELEMENT);
|
|
592
|
+
} else {
|
|
593
|
+
refObject = {
|
|
594
|
+
...refObject,
|
|
595
|
+
attribute: value,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
736
598
|
} else if (!this._checkObjectHasProperty(refObject, attribute)) {
|
|
737
599
|
this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
|
|
738
600
|
} else {
|
|
739
601
|
if (
|
|
740
|
-
|
|
741
|
-
this.
|
|
602
|
+
stringMatches(CMIElement, "\\.correct_responses\\.\\d+") &&
|
|
603
|
+
this.isInitialized()
|
|
742
604
|
) {
|
|
743
605
|
this.validateCorrectResponse(CMIElement, value);
|
|
744
606
|
}
|
|
745
607
|
|
|
746
608
|
if (!scorm2004 || this.lastErrorCode === "0") {
|
|
747
609
|
refObject[attribute] = value;
|
|
748
|
-
returnValue =
|
|
610
|
+
returnValue = APIConstants.global.SCORM_TRUE;
|
|
749
611
|
}
|
|
750
612
|
}
|
|
751
613
|
} else {
|
|
@@ -790,41 +652,17 @@ export default abstract class BaseAPI {
|
|
|
790
652
|
}
|
|
791
653
|
}
|
|
792
654
|
|
|
793
|
-
if (returnValue ===
|
|
655
|
+
if (returnValue === APIConstants.global.SCORM_FALSE) {
|
|
794
656
|
this.apiLog(
|
|
795
657
|
methodName,
|
|
796
658
|
`There was an error setting the value for: ${CMIElement}, value of: ${value}`,
|
|
797
|
-
|
|
659
|
+
APIConstants.global.LOG_LEVEL_WARNING,
|
|
798
660
|
);
|
|
799
661
|
}
|
|
800
662
|
|
|
801
663
|
return returnValue;
|
|
802
664
|
}
|
|
803
665
|
|
|
804
|
-
/**
|
|
805
|
-
* Abstract method for validating that a response is correct.
|
|
806
|
-
*
|
|
807
|
-
* @param {string} _CMIElement
|
|
808
|
-
* @param {any} _value
|
|
809
|
-
*/
|
|
810
|
-
abstract validateCorrectResponse(_CMIElement: string, _value: any): void;
|
|
811
|
-
|
|
812
|
-
/**
|
|
813
|
-
* Gets or builds a new child element to add to the array.
|
|
814
|
-
* APIs that inherit BaseAPI should override this method.
|
|
815
|
-
*
|
|
816
|
-
* @param {string} _CMIElement - unused
|
|
817
|
-
* @param {*} _value - unused
|
|
818
|
-
* @param {boolean} _foundFirstIndex - unused
|
|
819
|
-
* @return {BaseCMI|null}
|
|
820
|
-
* @abstract
|
|
821
|
-
*/
|
|
822
|
-
abstract getChildElement(
|
|
823
|
-
_CMIElement: string,
|
|
824
|
-
_value: any,
|
|
825
|
-
_foundFirstIndex: boolean,
|
|
826
|
-
): BaseCMI | null;
|
|
827
|
-
|
|
828
666
|
/**
|
|
829
667
|
* Gets a value from the CMI Object
|
|
830
668
|
*
|
|
@@ -910,9 +748,9 @@ export default abstract class BaseAPI {
|
|
|
910
748
|
if (refObject === null || refObject === undefined) {
|
|
911
749
|
if (!scorm2004) {
|
|
912
750
|
if (attribute === "_children") {
|
|
913
|
-
this.throwSCORMError(
|
|
751
|
+
this.throwSCORMError(ErrorCodes.scorm12.CHILDREN_ERROR);
|
|
914
752
|
} else if (attribute === "_count") {
|
|
915
|
-
this.throwSCORMError(
|
|
753
|
+
this.throwSCORMError(ErrorCodes.scorm12.COUNT_ERROR);
|
|
916
754
|
}
|
|
917
755
|
}
|
|
918
756
|
} else {
|
|
@@ -926,7 +764,7 @@ export default abstract class BaseAPI {
|
|
|
926
764
|
* @return {boolean}
|
|
927
765
|
*/
|
|
928
766
|
isInitialized(): boolean {
|
|
929
|
-
return this.currentState ===
|
|
767
|
+
return this.currentState === APIConstants.global.STATE_INITIALIZED;
|
|
930
768
|
}
|
|
931
769
|
|
|
932
770
|
/**
|
|
@@ -935,7 +773,7 @@ export default abstract class BaseAPI {
|
|
|
935
773
|
* @return {boolean}
|
|
936
774
|
*/
|
|
937
775
|
isNotInitialized(): boolean {
|
|
938
|
-
return this.currentState ===
|
|
776
|
+
return this.currentState === APIConstants.global.STATE_NOT_INITIALIZED;
|
|
939
777
|
}
|
|
940
778
|
|
|
941
779
|
/**
|
|
@@ -944,7 +782,7 @@ export default abstract class BaseAPI {
|
|
|
944
782
|
* @return {boolean}
|
|
945
783
|
*/
|
|
946
784
|
isTerminated(): boolean {
|
|
947
|
-
return this.currentState ===
|
|
785
|
+
return this.currentState === APIConstants.global.STATE_TERMINATED;
|
|
948
786
|
}
|
|
949
787
|
|
|
950
788
|
/**
|
|
@@ -977,7 +815,7 @@ export default abstract class BaseAPI {
|
|
|
977
815
|
this.apiLog(
|
|
978
816
|
"on",
|
|
979
817
|
`Added event listener: ${this.listenerArray.length}`,
|
|
980
|
-
|
|
818
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
981
819
|
functionName,
|
|
982
820
|
);
|
|
983
821
|
}
|
|
@@ -1015,7 +853,7 @@ export default abstract class BaseAPI {
|
|
|
1015
853
|
this.apiLog(
|
|
1016
854
|
"off",
|
|
1017
855
|
`Removed event listener: ${this.listenerArray.length}`,
|
|
1018
|
-
|
|
856
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
1019
857
|
functionName,
|
|
1020
858
|
);
|
|
1021
859
|
}
|
|
@@ -1058,7 +896,7 @@ export default abstract class BaseAPI {
|
|
|
1058
896
|
this.apiLog(
|
|
1059
897
|
functionName,
|
|
1060
898
|
value,
|
|
1061
|
-
|
|
899
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
1062
900
|
CMIElement,
|
|
1063
901
|
);
|
|
1064
902
|
for (let i = 0; i < this.listenerArray.length; i++) {
|
|
@@ -1083,7 +921,7 @@ export default abstract class BaseAPI {
|
|
|
1083
921
|
this.apiLog(
|
|
1084
922
|
"processListeners",
|
|
1085
923
|
`Processing listener: ${listener.functionName}`,
|
|
1086
|
-
|
|
924
|
+
APIConstants.global.LOG_LEVEL_INFO,
|
|
1087
925
|
CMIElement,
|
|
1088
926
|
);
|
|
1089
927
|
listener.callback(CMIElement, value);
|
|
@@ -1105,7 +943,7 @@ export default abstract class BaseAPI {
|
|
|
1105
943
|
this.apiLog(
|
|
1106
944
|
"throwSCORMError",
|
|
1107
945
|
errorNumber + ": " + message,
|
|
1108
|
-
|
|
946
|
+
APIConstants.global.LOG_LEVEL_ERROR,
|
|
1109
947
|
);
|
|
1110
948
|
|
|
1111
949
|
this.lastErrorCode = String(errorNumber);
|
|
@@ -1117,27 +955,21 @@ export default abstract class BaseAPI {
|
|
|
1117
955
|
* @param {string} success
|
|
1118
956
|
*/
|
|
1119
957
|
clearSCORMError(success: string) {
|
|
1120
|
-
if (success !== undefined && success !==
|
|
958
|
+
if (success !== undefined && success !== APIConstants.global.SCORM_FALSE) {
|
|
1121
959
|
this.lastErrorCode = "0";
|
|
1122
960
|
}
|
|
1123
961
|
}
|
|
1124
962
|
|
|
1125
|
-
/**
|
|
1126
|
-
* Attempts to store the data to the LMS, logs data if no LMS configured
|
|
1127
|
-
* APIs that inherit BaseAPI should override this function
|
|
1128
|
-
*
|
|
1129
|
-
* @param {boolean} _calculateTotalTime
|
|
1130
|
-
* @return {ResultObject}
|
|
1131
|
-
* @abstract
|
|
1132
|
-
*/
|
|
1133
|
-
abstract storeData(_calculateTotalTime: boolean): ResultObject;
|
|
1134
|
-
|
|
1135
963
|
/**
|
|
1136
964
|
* Load the CMI from a flattened JSON object
|
|
1137
965
|
* @param {RefObject} json
|
|
1138
966
|
* @param {string} CMIElement
|
|
1139
967
|
*/
|
|
1140
|
-
loadFromFlattenedJSON(json: RefObject, CMIElement
|
|
968
|
+
loadFromFlattenedJSON(json: RefObject, CMIElement?: string) {
|
|
969
|
+
if (!CMIElement) {
|
|
970
|
+
// by default, we start from a blank string because we're expecting each element to start with `cmi`
|
|
971
|
+
CMIElement = "";
|
|
972
|
+
}
|
|
1141
973
|
if (!this.isNotInitialized()) {
|
|
1142
974
|
console.error(
|
|
1143
975
|
"loadFromFlattenedJSON can only be called before the call to lmsInitialize.",
|
|
@@ -1146,12 +978,12 @@ export default abstract class BaseAPI {
|
|
|
1146
978
|
}
|
|
1147
979
|
|
|
1148
980
|
/**
|
|
1149
|
-
*
|
|
981
|
+
* Tests two strings against a given regular expression pattern and determines a numeric or null result based on the matching criterion.
|
|
1150
982
|
*
|
|
1151
|
-
* @param {string} a
|
|
1152
|
-
* @param {string} c
|
|
1153
|
-
* @param {RegExp} a_pattern
|
|
1154
|
-
* @return {number}
|
|
983
|
+
* @param {string} a - The first string to be tested against the pattern.
|
|
984
|
+
* @param {string} c - The second string to be tested against the pattern.
|
|
985
|
+
* @param {RegExp} a_pattern - The regular expression pattern to test the strings against.
|
|
986
|
+
* @return {number | null} A numeric result based on the matching criterion, or null if the strings do not match the pattern.
|
|
1155
987
|
*/
|
|
1156
988
|
function testPattern(
|
|
1157
989
|
a: string,
|
|
@@ -1265,8 +1097,10 @@ export default abstract class BaseAPI {
|
|
|
1265
1097
|
renderCMIToJSONString(): string {
|
|
1266
1098
|
const cmi = this.cmi;
|
|
1267
1099
|
// Do we want/need to return fields that have no set value?
|
|
1268
|
-
|
|
1269
|
-
|
|
1100
|
+
if (this.settings.sendFullCommit) {
|
|
1101
|
+
return JSON.stringify({ cmi });
|
|
1102
|
+
}
|
|
1103
|
+
return JSON.stringify({ cmi }, (k, v) => (v === undefined ? null : v), 2);
|
|
1270
1104
|
}
|
|
1271
1105
|
|
|
1272
1106
|
/**
|
|
@@ -1274,21 +1108,9 @@ export default abstract class BaseAPI {
|
|
|
1274
1108
|
* @return {object}
|
|
1275
1109
|
*/
|
|
1276
1110
|
renderCMIToJSONObject(): object {
|
|
1277
|
-
// Do we want/need to return fields that have no set value?
|
|
1278
|
-
// return JSON.stringify({ cmi }, (k, v) => v === undefined ? null : v, 2);
|
|
1279
1111
|
return JSON.parse(this.renderCMIToJSONString());
|
|
1280
1112
|
}
|
|
1281
1113
|
|
|
1282
|
-
/**
|
|
1283
|
-
* Render the cmi object to the proper format for LMS commit
|
|
1284
|
-
* APIs that inherit BaseAPI should override this function
|
|
1285
|
-
*
|
|
1286
|
-
* @param {boolean} _terminateCommit
|
|
1287
|
-
* @return {RefObject|Array}
|
|
1288
|
-
* @abstract
|
|
1289
|
-
*/
|
|
1290
|
-
abstract renderCommitCMI(_terminateCommit: boolean): RefObject | Array<any>;
|
|
1291
|
-
|
|
1292
1114
|
/**
|
|
1293
1115
|
* Send the request to the LMS
|
|
1294
1116
|
* @param {string} url
|
|
@@ -1296,17 +1118,29 @@ export default abstract class BaseAPI {
|
|
|
1296
1118
|
* @param {boolean} immediate
|
|
1297
1119
|
* @return {ResultObject}
|
|
1298
1120
|
*/
|
|
1299
|
-
processHttpRequest(
|
|
1121
|
+
async processHttpRequest(
|
|
1300
1122
|
url: string,
|
|
1301
1123
|
params: RefObject | Array<any>,
|
|
1302
1124
|
immediate: boolean = false,
|
|
1303
|
-
): ResultObject {
|
|
1125
|
+
): Promise<ResultObject> {
|
|
1304
1126
|
const api = this;
|
|
1305
1127
|
const genericError: ResultObject = {
|
|
1306
|
-
result:
|
|
1128
|
+
result: APIConstants.global.SCORM_FALSE,
|
|
1307
1129
|
errorCode: this.error_codes.GENERAL,
|
|
1308
1130
|
};
|
|
1309
1131
|
|
|
1132
|
+
// if we are terminating the module or closing the browser window/tab, we need to make this fetch ASAP.
|
|
1133
|
+
// Some browsers, especially Chrome, do not like synchronous requests to be made when the window is closing.
|
|
1134
|
+
if (immediate) {
|
|
1135
|
+
this.performFetch(url, params).then(async (response) => {
|
|
1136
|
+
await this.transformResponse(response);
|
|
1137
|
+
});
|
|
1138
|
+
return {
|
|
1139
|
+
result: APIConstants.global.SCORM_TRUE,
|
|
1140
|
+
errorCode: 0,
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1310
1144
|
const process = async (
|
|
1311
1145
|
url: string,
|
|
1312
1146
|
params: RefObject | Array<any>,
|
|
@@ -1314,49 +1148,31 @@ export default abstract class BaseAPI {
|
|
|
1314
1148
|
): Promise<ResultObject> => {
|
|
1315
1149
|
try {
|
|
1316
1150
|
params = settings.requestHandler(params);
|
|
1317
|
-
const response = await
|
|
1318
|
-
method: "POST",
|
|
1319
|
-
body:
|
|
1320
|
-
params instanceof Array ? params.join("&") : JSON.stringify(params),
|
|
1321
|
-
headers: {
|
|
1322
|
-
...settings.xhrHeaders,
|
|
1323
|
-
"Content-Type": settings.commitRequestDataType,
|
|
1324
|
-
},
|
|
1325
|
-
credentials: settings.xhrWithCredentials ? "include" : undefined,
|
|
1326
|
-
keepalive: true,
|
|
1327
|
-
});
|
|
1328
|
-
|
|
1329
|
-
const result =
|
|
1330
|
-
typeof settings.responseHandler === "function"
|
|
1331
|
-
? await settings.responseHandler(response)
|
|
1332
|
-
: await response.json();
|
|
1333
|
-
|
|
1334
|
-
if (
|
|
1335
|
-
response.status >= 200 &&
|
|
1336
|
-
response.status <= 299 &&
|
|
1337
|
-
(result.result === true ||
|
|
1338
|
-
result.result === global_constants.SCORM_TRUE)
|
|
1339
|
-
) {
|
|
1340
|
-
api.processListeners("CommitSuccess");
|
|
1341
|
-
} else {
|
|
1342
|
-
api.processListeners("CommitError");
|
|
1343
|
-
}
|
|
1151
|
+
const response = await this.performFetch(url, params);
|
|
1344
1152
|
|
|
1345
|
-
return
|
|
1153
|
+
return this.transformResponse(response);
|
|
1346
1154
|
} catch (e) {
|
|
1347
|
-
this.apiLog(
|
|
1155
|
+
this.apiLog(
|
|
1156
|
+
"processHttpRequest",
|
|
1157
|
+
e,
|
|
1158
|
+
APIConstants.global.LOG_LEVEL_ERROR,
|
|
1159
|
+
);
|
|
1348
1160
|
api.processListeners("CommitError");
|
|
1349
1161
|
return genericError;
|
|
1350
1162
|
}
|
|
1351
1163
|
};
|
|
1352
1164
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1165
|
+
if (this.settings.asyncCommit) {
|
|
1166
|
+
const debouncedProcess = debounce(process, 500, immediate);
|
|
1167
|
+
debouncedProcess(url, params, this.settings);
|
|
1355
1168
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1169
|
+
return {
|
|
1170
|
+
result: APIConstants.global.SCORM_TRUE,
|
|
1171
|
+
errorCode: 0,
|
|
1172
|
+
};
|
|
1173
|
+
} else {
|
|
1174
|
+
return await process(url, params, this.settings);
|
|
1175
|
+
}
|
|
1360
1176
|
}
|
|
1361
1177
|
|
|
1362
1178
|
/**
|
|
@@ -1370,7 +1186,7 @@ export default abstract class BaseAPI {
|
|
|
1370
1186
|
this.apiLog(
|
|
1371
1187
|
"scheduleCommit",
|
|
1372
1188
|
"scheduled",
|
|
1373
|
-
|
|
1189
|
+
APIConstants.global.LOG_LEVEL_DEBUG,
|
|
1374
1190
|
"",
|
|
1375
1191
|
);
|
|
1376
1192
|
}
|
|
@@ -1385,16 +1201,44 @@ export default abstract class BaseAPI {
|
|
|
1385
1201
|
this.apiLog(
|
|
1386
1202
|
"clearScheduledCommit",
|
|
1387
1203
|
"cleared",
|
|
1388
|
-
|
|
1204
|
+
APIConstants.global.LOG_LEVEL_DEBUG,
|
|
1389
1205
|
"",
|
|
1390
1206
|
);
|
|
1391
1207
|
}
|
|
1392
1208
|
}
|
|
1393
1209
|
|
|
1394
|
-
|
|
1210
|
+
/**
|
|
1211
|
+
* Check to see if the specific object has the given property
|
|
1212
|
+
* @param {RefObject} refObject
|
|
1213
|
+
* @param {string} attribute
|
|
1214
|
+
* @return {boolean}
|
|
1215
|
+
* @private
|
|
1216
|
+
*/
|
|
1217
|
+
private _checkObjectHasProperty(
|
|
1218
|
+
refObject: RefObject,
|
|
1219
|
+
attribute: string,
|
|
1220
|
+
): boolean {
|
|
1221
|
+
return (
|
|
1222
|
+
Object.hasOwnProperty.call(refObject, attribute) ||
|
|
1223
|
+
Object.getOwnPropertyDescriptor(
|
|
1224
|
+
Object.getPrototypeOf(refObject),
|
|
1225
|
+
attribute,
|
|
1226
|
+
) != null ||
|
|
1227
|
+
attribute in refObject
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* Handles the error that occurs when trying to access a value
|
|
1233
|
+
* @param {any} e
|
|
1234
|
+
* @param {string} returnValue
|
|
1235
|
+
* @return {string}
|
|
1236
|
+
* @private
|
|
1237
|
+
*/
|
|
1238
|
+
private handleValueAccessException(e: any, returnValue: string): string {
|
|
1395
1239
|
if (e instanceof ValidationError) {
|
|
1396
1240
|
this.lastErrorCode = String(e.errorCode);
|
|
1397
|
-
returnValue =
|
|
1241
|
+
returnValue = APIConstants.global.SCORM_FALSE;
|
|
1398
1242
|
} else {
|
|
1399
1243
|
if (e instanceof Error && e.message) {
|
|
1400
1244
|
console.error(e.message);
|
|
@@ -1405,45 +1249,52 @@ export default abstract class BaseAPI {
|
|
|
1405
1249
|
}
|
|
1406
1250
|
return returnValue;
|
|
1407
1251
|
}
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
/**
|
|
1411
|
-
* Private class that wraps a timeout call to the commit() function
|
|
1412
|
-
*/
|
|
1413
|
-
class ScheduledCommit {
|
|
1414
|
-
private _API;
|
|
1415
|
-
private _cancelled = false;
|
|
1416
|
-
private readonly _timeout;
|
|
1417
|
-
private readonly _callback;
|
|
1418
1252
|
|
|
1419
1253
|
/**
|
|
1420
|
-
*
|
|
1421
|
-
* @param {
|
|
1422
|
-
* @param {
|
|
1423
|
-
* @
|
|
1254
|
+
* Perform the fetch request to the LMS
|
|
1255
|
+
* @param {string} url
|
|
1256
|
+
* @param {RefObject|Array} params
|
|
1257
|
+
* @return {Promise<Response>}
|
|
1258
|
+
* @private
|
|
1424
1259
|
*/
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1260
|
+
private async performFetch(
|
|
1261
|
+
url: string,
|
|
1262
|
+
params: RefObject | Array<any>,
|
|
1263
|
+
): Promise<Response> {
|
|
1264
|
+
return fetch(url, {
|
|
1265
|
+
method: "POST",
|
|
1266
|
+
body: params instanceof Array ? params.join("&") : JSON.stringify(params),
|
|
1267
|
+
headers: {
|
|
1268
|
+
...this.settings.xhrHeaders,
|
|
1269
|
+
"Content-Type": this.settings.commitRequestDataType,
|
|
1270
|
+
},
|
|
1271
|
+
credentials: this.settings.xhrWithCredentials ? "include" : undefined,
|
|
1272
|
+
keepalive: true,
|
|
1273
|
+
});
|
|
1429
1274
|
}
|
|
1430
1275
|
|
|
1431
1276
|
/**
|
|
1432
|
-
*
|
|
1277
|
+
* Transforms the response from the LMS to a ResultObject
|
|
1278
|
+
* @param {Response} response
|
|
1279
|
+
* @return {Promise<ResultObject>}
|
|
1280
|
+
* @private
|
|
1433
1281
|
*/
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
}
|
|
1282
|
+
private async transformResponse(response: Response): Promise<ResultObject> {
|
|
1283
|
+
const result =
|
|
1284
|
+
typeof this.settings.responseHandler === "function"
|
|
1285
|
+
? await this.settings.responseHandler(response)
|
|
1286
|
+
: await response.json();
|
|
1440
1287
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1288
|
+
if (
|
|
1289
|
+
response.status >= 200 &&
|
|
1290
|
+
response.status <= 299 &&
|
|
1291
|
+
(result.result === true ||
|
|
1292
|
+
result.result === APIConstants.global.SCORM_TRUE)
|
|
1293
|
+
) {
|
|
1294
|
+
this.processListeners("CommitSuccess");
|
|
1295
|
+
} else {
|
|
1296
|
+
this.processListeners("CommitError");
|
|
1447
1297
|
}
|
|
1298
|
+
return result;
|
|
1448
1299
|
}
|
|
1449
1300
|
}
|