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.
Files changed (124) hide show
  1. package/.babelrc +18 -7
  2. package/.github/dependabot.yml +5 -0
  3. package/.github/workflows/main.yml +79 -0
  4. package/.github/workflows/stale.yml +14 -0
  5. package/.jsdoc.json +4 -5
  6. package/.mocharc.json +8 -0
  7. package/.run/{Mocha Unit Tests.run.xml → Mocha Unit Tests (watch).run.xml } +6 -3
  8. package/.run/Template Mocha.run.xml +17 -0
  9. package/CONTRIBUTING.md +1 -1
  10. package/README.md +183 -71
  11. package/dist/aicc.js +3822 -7030
  12. package/dist/aicc.js.map +1 -1
  13. package/dist/aicc.min.js +2 -40
  14. package/dist/aicc.min.js.map +1 -0
  15. package/dist/scorm-again.js +5965 -10498
  16. package/dist/scorm-again.js.map +1 -1
  17. package/dist/scorm-again.min.js +2 -52
  18. package/dist/scorm-again.min.js.map +1 -0
  19. package/dist/scorm12.js +3028 -5373
  20. package/dist/scorm12.js.map +1 -1
  21. package/dist/scorm12.min.js +2 -34
  22. package/dist/scorm12.min.js.map +1 -0
  23. package/dist/scorm2004.js +4054 -6693
  24. package/dist/scorm2004.js.map +1 -1
  25. package/dist/scorm2004.min.js +2 -40
  26. package/dist/scorm2004.min.js.map +1 -0
  27. package/eslint.config.js +21 -0
  28. package/package.json +76 -34
  29. package/results.json +34254 -0
  30. package/src/AICC.ts +72 -0
  31. package/src/BaseAPI.ts +1300 -0
  32. package/src/Scorm12API.ts +387 -0
  33. package/src/Scorm2004API.ts +688 -0
  34. package/src/cmi/aicc/attempts.ts +94 -0
  35. package/src/cmi/aicc/cmi.ts +100 -0
  36. package/src/cmi/aicc/core.ts +360 -0
  37. package/src/cmi/aicc/evaluation.ts +157 -0
  38. package/src/cmi/aicc/paths.ts +180 -0
  39. package/src/cmi/aicc/student_data.ts +86 -0
  40. package/src/cmi/aicc/student_demographics.ts +367 -0
  41. package/src/cmi/aicc/student_preferences.ts +176 -0
  42. package/src/cmi/aicc/tries.ts +116 -0
  43. package/src/cmi/aicc/validation.ts +25 -0
  44. package/src/cmi/common/array.ts +77 -0
  45. package/src/cmi/common/base_cmi.ts +46 -0
  46. package/src/cmi/common/score.ts +203 -0
  47. package/src/cmi/common/validation.ts +60 -0
  48. package/src/cmi/scorm12/cmi.ts +224 -0
  49. package/src/cmi/scorm12/interactions.ts +368 -0
  50. package/src/cmi/scorm12/nav.ts +54 -0
  51. package/src/cmi/scorm12/objectives.ts +112 -0
  52. package/src/cmi/scorm12/student_data.ts +130 -0
  53. package/src/cmi/scorm12/student_preference.ts +158 -0
  54. package/src/cmi/scorm12/validation.ts +48 -0
  55. package/src/cmi/scorm2004/adl.ts +272 -0
  56. package/src/cmi/scorm2004/cmi.ts +599 -0
  57. package/src/cmi/scorm2004/comments.ts +163 -0
  58. package/src/cmi/scorm2004/interactions.ts +466 -0
  59. package/src/cmi/scorm2004/learner_preference.ts +152 -0
  60. package/src/cmi/scorm2004/objectives.ts +212 -0
  61. package/src/cmi/scorm2004/score.ts +78 -0
  62. package/src/cmi/scorm2004/validation.ts +42 -0
  63. package/src/constants/api_constants.ts +318 -0
  64. package/src/constants/default_settings.ts +81 -0
  65. package/src/constants/enums.ts +5 -0
  66. package/src/constants/error_codes.ts +88 -0
  67. package/src/constants/language_constants.ts +394 -0
  68. package/src/constants/regex.ts +97 -0
  69. package/src/constants/{response_constants.js → response_constants.ts} +69 -62
  70. package/src/exceptions.ts +154 -0
  71. package/src/exports/aicc.js +1 -1
  72. package/src/exports/scorm-again.js +3 -3
  73. package/src/exports/scorm12.js +1 -1
  74. package/src/exports/scorm2004.js +1 -1
  75. package/src/helpers/scheduled_commit.ts +42 -0
  76. package/src/interfaces/IBaseAPI.ts +35 -0
  77. package/src/types/api_types.ts +32 -0
  78. package/src/utilities/debounce.ts +31 -0
  79. package/src/utilities.ts +338 -0
  80. package/tea.yaml +6 -0
  81. package/test/{AICC.spec.js → AICC.spec.ts} +79 -71
  82. package/test/Scorm12API.spec.ts +833 -0
  83. package/test/Scorm2004API.spec.ts +1298 -0
  84. package/test/api_helpers.ts +176 -0
  85. package/test/cmi/aicc_cmi.spec.ts +845 -0
  86. package/test/cmi/{scorm12_cmi.spec.js → scorm12_cmi.spec.ts} +253 -271
  87. package/test/cmi/scorm2004_cmi.spec.ts +1031 -0
  88. package/test/cmi_helpers.ts +207 -0
  89. package/test/exceptions.spec.ts +79 -0
  90. package/test/field_values.ts +202 -0
  91. package/test/types/api_types.spec.ts +126 -0
  92. package/test/utilities/debounce.spec.ts +56 -0
  93. package/test/utilities.spec.ts +322 -0
  94. package/tsconfig.json +18 -0
  95. package/webpack.config.js +65 -0
  96. package/.circleci/config.yml +0 -99
  97. package/.codeclimate.yml +0 -7
  98. package/.eslintrc.js +0 -36
  99. package/src/.flowconfig +0 -11
  100. package/src/AICC.js +0 -68
  101. package/src/BaseAPI.js +0 -1275
  102. package/src/Scorm12API.js +0 -308
  103. package/src/Scorm2004API.js +0 -572
  104. package/src/cmi/aicc_cmi.js +0 -1141
  105. package/src/cmi/common.js +0 -328
  106. package/src/cmi/scorm12_cmi.js +0 -1312
  107. package/src/cmi/scorm2004_cmi.js +0 -1692
  108. package/src/constants/api_constants.js +0 -218
  109. package/src/constants/error_codes.js +0 -87
  110. package/src/constants/language_constants.js +0 -76
  111. package/src/constants/regex.js +0 -84
  112. package/src/exceptions.js +0 -104
  113. package/src/utilities.js +0 -242
  114. package/test/Scorm12API.spec.js +0 -528
  115. package/test/Scorm2004API.spec.js +0 -775
  116. package/test/abstract_classes.spec.js +0 -17
  117. package/test/api_helpers.js +0 -128
  118. package/test/cmi/aicc_cmi.spec.js +0 -684
  119. package/test/cmi/scorm2004_cmi.spec.js +0 -1066
  120. package/test/cmi_helpers.js +0 -161
  121. package/test/exceptions.spec.js +0 -71
  122. package/test/field_values.js +0 -353
  123. package/test/utilities.spec.js +0 -339
  124. package/webpack.js +0 -78
@@ -0,0 +1,387 @@
1
+ import BaseAPI from "./BaseAPI";
2
+ import { CMI } from "./cmi/scorm12/cmi";
3
+ import * as Utilities from "./utilities";
4
+ import APIConstants from "./constants/api_constants";
5
+ import ErrorCodes from "./constants/error_codes";
6
+
7
+ import { BaseCMI } from "./cmi/common/base_cmi";
8
+ import { CMIObjectivesObject } from "./cmi/scorm12/objectives";
9
+ import {
10
+ CMIInteractionsCorrectResponsesObject,
11
+ CMIInteractionsObject,
12
+ CMIInteractionsObjectivesObject,
13
+ } from "./cmi/scorm12/interactions";
14
+ import { NAV } from "./cmi/scorm12/nav";
15
+ import { RefObject, ResultObject, Settings } from "./types/api_types";
16
+ import { stringMatches } from "./utilities";
17
+
18
+ /**
19
+ * API class for SCORM 1.2
20
+ */
21
+ export default class Scorm12API extends BaseAPI {
22
+ /**
23
+ * Constructor for SCORM 1.2 API
24
+ * @param {object} settings
25
+ */
26
+ constructor(settings?: Settings) {
27
+ if (settings) {
28
+ if (settings.mastery_override === undefined) {
29
+ settings.mastery_override = false;
30
+ }
31
+ }
32
+
33
+ super(ErrorCodes.scorm12, settings);
34
+
35
+ this.cmi = new CMI();
36
+ this.nav = new NAV();
37
+
38
+ // Rename functions to match 1.2 Spec and expose to modules
39
+ this.LMSInitialize = this.lmsInitialize;
40
+ this.LMSFinish = this.lmsFinish;
41
+ this.LMSGetValue = this.lmsGetValue;
42
+ this.LMSSetValue = this.lmsSetValue;
43
+ this.LMSCommit = this.lmsCommit;
44
+ this.LMSGetLastError = this.lmsGetLastError;
45
+ this.LMSGetErrorString = this.lmsGetErrorString;
46
+ this.LMSGetDiagnostic = this.lmsGetDiagnostic;
47
+ }
48
+
49
+ public statusSetByModule = false;
50
+
51
+ public cmi: CMI;
52
+ public nav: NAV;
53
+
54
+ public LMSInitialize: () => string;
55
+ public LMSFinish: () => string;
56
+ public LMSGetValue: (CMIElement: string) => string;
57
+ public LMSSetValue: (CMIElement: string, value: any) => string;
58
+ public LMSCommit: () => string;
59
+ public LMSGetLastError: () => string;
60
+ public LMSGetErrorString: (CMIErrorCode: string) => string;
61
+ public LMSGetDiagnostic: (CMIErrorCode: string) => string;
62
+
63
+ /**
64
+ * Called when the API needs to be reset
65
+ */
66
+ reset(settings?: Settings) {
67
+ this.commonReset(settings);
68
+
69
+ this.cmi = new CMI();
70
+ this.nav = new NAV();
71
+ }
72
+
73
+ /**
74
+ * lmsInitialize function from SCORM 1.2 Spec
75
+ *
76
+ * @return {string} bool
77
+ */
78
+ lmsInitialize(): string {
79
+ this.cmi.initialize();
80
+ if (this.cmi.core.lesson_status) {
81
+ this.statusSetByModule = true;
82
+ } else {
83
+ this.cmi.core.lesson_status = "not attempted";
84
+ }
85
+ return this.initialize(
86
+ "LMSInitialize",
87
+ "LMS was already initialized!",
88
+ "LMS is already finished!",
89
+ );
90
+ }
91
+
92
+ /**
93
+ * LMSFinish function from SCORM 1.2 Spec
94
+ *
95
+ * @return {string} bool
96
+ */
97
+ lmsFinish(): string {
98
+ (async () => {
99
+ await this.internalFinish();
100
+ })();
101
+ return APIConstants.global.SCORM_TRUE;
102
+ }
103
+
104
+ async internalFinish(): Promise<string> {
105
+ const result = await this.terminate("LMSFinish", true);
106
+
107
+ if (result === APIConstants.global.SCORM_TRUE) {
108
+ if (this.nav.event !== "") {
109
+ if (this.nav.event === "continue") {
110
+ this.processListeners("SequenceNext");
111
+ } else {
112
+ this.processListeners("SequencePrevious");
113
+ }
114
+ } else if (this.settings.autoProgress) {
115
+ this.processListeners("SequenceNext");
116
+ }
117
+ }
118
+
119
+ return result;
120
+ }
121
+
122
+ /**
123
+ * LMSGetValue function from SCORM 1.2 Spec
124
+ *
125
+ * @param {string} CMIElement
126
+ * @return {string}
127
+ */
128
+ lmsGetValue(CMIElement: string): string {
129
+ return this.getValue("LMSGetValue", false, CMIElement);
130
+ }
131
+
132
+ /**
133
+ * LMSSetValue function from SCORM 1.2 Spec
134
+ *
135
+ * @param {string} CMIElement
136
+ * @param {*} value
137
+ * @return {string}
138
+ */
139
+ lmsSetValue(CMIElement: string, value: any): string {
140
+ if (CMIElement === "cmi.core.lesson_status") {
141
+ this.statusSetByModule = true;
142
+ }
143
+ return this.setValue("LMSSetValue", "LMSCommit", false, CMIElement, value);
144
+ }
145
+
146
+ /**
147
+ * LMSCommit function from SCORM 1.2 Spec
148
+ *
149
+ * @return {string} bool
150
+ */
151
+ lmsCommit(): string {
152
+ (async () => {
153
+ await this.commit("LMSCommit", false);
154
+ })();
155
+ return APIConstants.global.SCORM_TRUE;
156
+ }
157
+
158
+ /**
159
+ * LMSGetLastError function from SCORM 1.2 Spec
160
+ *
161
+ * @return {string}
162
+ */
163
+ lmsGetLastError(): string {
164
+ return this.getLastError("LMSGetLastError");
165
+ }
166
+
167
+ /**
168
+ * LMSGetErrorString function from SCORM 1.2 Spec
169
+ *
170
+ * @param {string} CMIErrorCode
171
+ * @return {string}
172
+ */
173
+ lmsGetErrorString(CMIErrorCode: string): string {
174
+ return this.getErrorString("LMSGetErrorString", CMIErrorCode);
175
+ }
176
+
177
+ /**
178
+ * LMSGetDiagnostic function from SCORM 1.2 Spec
179
+ *
180
+ * @param {string} CMIErrorCode
181
+ * @return {string}
182
+ */
183
+ lmsGetDiagnostic(CMIErrorCode: string): string {
184
+ return this.getDiagnostic("LMSGetDiagnostic", CMIErrorCode);
185
+ }
186
+
187
+ /**
188
+ * Sets a value on the CMI Object
189
+ *
190
+ * @param {string} CMIElement
191
+ * @param {*} value
192
+ * @return {string}
193
+ */
194
+ setCMIValue(CMIElement: string, value: any): string {
195
+ return this._commonSetCMIValue("LMSSetValue", false, CMIElement, value);
196
+ }
197
+
198
+ /**
199
+ * Gets a value from the CMI Object
200
+ *
201
+ * @param {string} CMIElement
202
+ * @return {*}
203
+ */
204
+ getCMIValue(CMIElement: string): any {
205
+ return this._commonGetCMIValue("getCMIValue", false, CMIElement);
206
+ }
207
+
208
+ /**
209
+ * Gets or builds a new child element to add to the array.
210
+ *
211
+ * @param {string} CMIElement
212
+ * @param {*} _value
213
+ * @param {boolean} foundFirstIndex
214
+ * @return {BaseCMI|null}
215
+ */
216
+ getChildElement(
217
+ CMIElement: string,
218
+ _value: any,
219
+ foundFirstIndex: boolean,
220
+ ): BaseCMI | null {
221
+ if (stringMatches(CMIElement, "cmi\\.objectives\\.\\d+")) {
222
+ return new CMIObjectivesObject();
223
+ } else if (
224
+ foundFirstIndex &&
225
+ stringMatches(
226
+ CMIElement,
227
+ "cmi\\.interactions\\.\\d+\\.correct_responses\\.\\d+",
228
+ )
229
+ ) {
230
+ return new CMIInteractionsCorrectResponsesObject();
231
+ } else if (
232
+ foundFirstIndex &&
233
+ stringMatches(CMIElement, "cmi\\.interactions\\.\\d+\\.objectives\\.\\d+")
234
+ ) {
235
+ return new CMIInteractionsObjectivesObject();
236
+ } else if (
237
+ !foundFirstIndex &&
238
+ stringMatches(CMIElement, "cmi\\.interactions\\.\\d+")
239
+ ) {
240
+ return new CMIInteractionsObject();
241
+ }
242
+
243
+ return null;
244
+ }
245
+
246
+ /**
247
+ * Validates Correct Response values
248
+ *
249
+ * @param {string} _CMIElement
250
+ * @param {*} _value
251
+ */
252
+ validateCorrectResponse(_CMIElement: string, _value: any) {
253
+ // do nothing
254
+ }
255
+
256
+ /**
257
+ * Returns the message that corresponds to errorNumber.
258
+ *
259
+ * @param {number|string} errorNumber
260
+ * @param {boolean} detail
261
+ * @return {string}
262
+ */
263
+ getLmsErrorMessageDetails(
264
+ errorNumber: number | string,
265
+ detail: boolean,
266
+ ): string {
267
+ let basicMessage = "No Error";
268
+ let detailMessage = "No Error";
269
+
270
+ // Set error number to string since inconsistent from modules if string or number
271
+ errorNumber = String(errorNumber);
272
+ if (APIConstants.scorm12.error_descriptions[errorNumber]) {
273
+ basicMessage =
274
+ APIConstants.scorm12.error_descriptions[errorNumber].basicMessage;
275
+ detailMessage =
276
+ APIConstants.scorm12.error_descriptions[errorNumber].detailMessage;
277
+ }
278
+
279
+ return detail ? detailMessage : basicMessage;
280
+ }
281
+
282
+ /**
283
+ * Replace the whole API with another
284
+ *
285
+ * @param {Scorm12API} newAPI
286
+ */
287
+ replaceWithAnotherScormAPI(newAPI: Scorm12API) {
288
+ // Data Model
289
+ this.cmi = newAPI.cmi;
290
+ }
291
+
292
+ /**
293
+ * Render the cmi object to the proper format for LMS commit
294
+ *
295
+ * @param {boolean} terminateCommit
296
+ * @return {object|Array}
297
+ */
298
+ renderCommitCMI(terminateCommit: boolean): object | Array<any> {
299
+ const cmiExport: RefObject = this.renderCMIToJSONObject();
300
+
301
+ if (terminateCommit) {
302
+ cmiExport.cmi.core.total_time = this.cmi.getCurrentTotalTime();
303
+ }
304
+
305
+ const result = [];
306
+ const flattened: RefObject = Utilities.flatten(cmiExport);
307
+ switch (this.settings.dataCommitFormat) {
308
+ case "flattened":
309
+ return Utilities.flatten(cmiExport);
310
+ case "params":
311
+ for (const item in flattened) {
312
+ if ({}.hasOwnProperty.call(flattened, item)) {
313
+ result.push(`${item}=${flattened[item]}`);
314
+ }
315
+ }
316
+ return result;
317
+ case "json":
318
+ default:
319
+ return cmiExport;
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Attempts to store the data to the LMS
325
+ *
326
+ * @param {boolean} terminateCommit
327
+ * @return {ResultObject}
328
+ */
329
+ async storeData(terminateCommit: boolean): Promise<ResultObject> {
330
+ if (terminateCommit) {
331
+ const originalStatus = this.cmi.core.lesson_status;
332
+ if (
333
+ !this.cmi.core.lesson_status ||
334
+ (!this.statusSetByModule &&
335
+ this.cmi.core.lesson_status === "not attempted")
336
+ ) {
337
+ this.cmi.core.lesson_status = "completed";
338
+ }
339
+
340
+ if (this.cmi.core.lesson_mode === "normal") {
341
+ if (this.cmi.core.credit === "credit") {
342
+ if (
343
+ this.settings.mastery_override &&
344
+ this.cmi.student_data.mastery_score !== "" &&
345
+ this.cmi.core.score.raw !== ""
346
+ ) {
347
+ this.cmi.core.lesson_status =
348
+ parseFloat(this.cmi.core.score.raw) >=
349
+ parseFloat(this.cmi.student_data.mastery_score)
350
+ ? "passed"
351
+ : "failed";
352
+ }
353
+ }
354
+ } else if (this.cmi.core.lesson_mode === "browse") {
355
+ if (
356
+ (this.startingData?.cmi?.core?.lesson_status || "") === "" &&
357
+ originalStatus === "not attempted"
358
+ ) {
359
+ this.cmi.core.lesson_status = "browsed";
360
+ }
361
+ }
362
+ }
363
+
364
+ const commitObject = this.renderCommitCMI(
365
+ terminateCommit || this.settings.alwaysSendTotalTime,
366
+ );
367
+
368
+ if (this.apiLogLevel === APIConstants.global.LOG_LEVEL_DEBUG) {
369
+ console.debug(
370
+ "Commit (terminated: " + (terminateCommit ? "yes" : "no") + "): ",
371
+ );
372
+ console.debug(commitObject);
373
+ }
374
+ if (typeof this.settings.lmsCommitUrl === "string") {
375
+ return await this.processHttpRequest(
376
+ this.settings.lmsCommitUrl,
377
+ commitObject,
378
+ terminateCommit,
379
+ );
380
+ } else {
381
+ return {
382
+ result: APIConstants.global.SCORM_TRUE,
383
+ errorCode: 0,
384
+ };
385
+ }
386
+ }
387
+ }