scorm-again 2.0.0 → 2.2.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 (77) hide show
  1. package/.github/workflows/stale.yml +14 -0
  2. package/.run/{Mocha Unit Tests.run.xml → Mocha Unit Tests (watch).run.xml } +1 -1
  3. package/.run/Template Mocha.run.xml +17 -0
  4. package/README.md +180 -72
  5. package/dist/aicc.js +1520 -1149
  6. package/dist/aicc.js.map +1 -1
  7. package/dist/aicc.min.js +1 -1
  8. package/dist/aicc.min.js.map +1 -1
  9. package/dist/scorm-again.js +2812 -2205
  10. package/dist/scorm-again.js.map +1 -1
  11. package/dist/scorm-again.min.js +1 -1
  12. package/dist/scorm-again.min.js.map +1 -1
  13. package/dist/scorm12.js +1129 -842
  14. package/dist/scorm12.js.map +1 -1
  15. package/dist/scorm12.min.js +1 -1
  16. package/dist/scorm12.min.js.map +1 -1
  17. package/dist/scorm2004.js +1921 -1564
  18. package/dist/scorm2004.js.map +1 -1
  19. package/dist/scorm2004.min.js +1 -1
  20. package/dist/scorm2004.min.js.map +1 -1
  21. package/package.json +20 -17
  22. package/src/AICC.ts +15 -17
  23. package/src/BaseAPI.ts +283 -420
  24. package/src/Scorm12API.ts +133 -41
  25. package/src/Scorm2004API.ts +224 -120
  26. package/src/cmi/aicc/attempts.ts +94 -0
  27. package/src/cmi/aicc/cmi.ts +100 -0
  28. package/src/cmi/aicc/core.ts +360 -0
  29. package/src/cmi/aicc/evaluation.ts +157 -0
  30. package/src/cmi/aicc/paths.ts +180 -0
  31. package/src/cmi/aicc/student_data.ts +86 -0
  32. package/src/cmi/aicc/student_demographics.ts +367 -0
  33. package/src/cmi/aicc/student_preferences.ts +176 -0
  34. package/src/cmi/aicc/tries.ts +116 -0
  35. package/src/cmi/aicc/validation.ts +25 -0
  36. package/src/cmi/common/array.ts +77 -0
  37. package/src/cmi/common/base_cmi.ts +46 -0
  38. package/src/cmi/common/score.ts +203 -0
  39. package/src/cmi/common/validation.ts +60 -0
  40. package/src/cmi/scorm12/cmi.ts +224 -0
  41. package/src/cmi/scorm12/interactions.ts +368 -0
  42. package/src/cmi/scorm12/nav.ts +54 -0
  43. package/src/cmi/scorm12/objectives.ts +112 -0
  44. package/src/cmi/scorm12/student_data.ts +130 -0
  45. package/src/cmi/scorm12/student_preference.ts +158 -0
  46. package/src/cmi/scorm12/validation.ts +48 -0
  47. package/src/cmi/scorm2004/adl.ts +272 -0
  48. package/src/cmi/scorm2004/cmi.ts +599 -0
  49. package/src/cmi/scorm2004/comments.ts +163 -0
  50. package/src/cmi/scorm2004/interactions.ts +466 -0
  51. package/src/cmi/scorm2004/learner_preference.ts +152 -0
  52. package/src/cmi/scorm2004/objectives.ts +212 -0
  53. package/src/cmi/scorm2004/score.ts +78 -0
  54. package/src/cmi/scorm2004/validation.ts +42 -0
  55. package/src/constants/default_settings.ts +82 -0
  56. package/src/constants/enums.ts +17 -0
  57. package/src/constants/regex.ts +2 -2
  58. package/src/constants/response_constants.ts +2 -0
  59. package/src/exceptions.ts +22 -1
  60. package/src/helpers/scheduled_commit.ts +42 -0
  61. package/src/interfaces/IBaseAPI.ts +35 -0
  62. package/src/types/api_types.ts +50 -0
  63. package/src/utilities/debounce.ts +31 -0
  64. package/src/utilities.ts +56 -0
  65. package/test/AICC.spec.ts +11 -1
  66. package/test/Scorm12API.spec.ts +372 -9
  67. package/test/Scorm2004API.spec.ts +558 -2
  68. package/test/cmi/aicc_cmi.spec.ts +188 -11
  69. package/test/cmi/scorm12_cmi.spec.ts +5 -5
  70. package/test/cmi/scorm2004_cmi.spec.ts +8 -8
  71. package/test/cmi_helpers.ts +1 -1
  72. package/test/types/api_types.spec.ts +126 -0
  73. package/test/utilities/debounce.spec.ts +56 -0
  74. package/src/cmi/aicc_cmi.ts +0 -1248
  75. package/src/cmi/common.ts +0 -411
  76. package/src/cmi/scorm12_cmi.ts +0 -1426
  77. package/src/cmi/scorm2004_cmi.ts +0 -1874
package/src/Scorm12API.ts CHANGED
@@ -1,20 +1,27 @@
1
- import BaseAPI, { RefObject, ResultObject, Settings } from "./BaseAPI";
1
+ import BaseAPI from "./BaseAPI";
2
+ import { CMI } from "./cmi/scorm12/cmi";
3
+ import * as Utilities from "./utilities";
4
+ import { stringMatches } from "./utilities";
5
+ import APIConstants from "./constants/api_constants";
6
+ import ErrorCodes from "./constants/error_codes";
7
+
8
+ import { BaseCMI } from "./cmi/common/base_cmi";
9
+ import { CMIObjectivesObject } from "./cmi/scorm12/objectives";
2
10
  import {
3
- CMI,
4
11
  CMIInteractionsCorrectResponsesObject,
5
12
  CMIInteractionsObject,
6
13
  CMIInteractionsObjectivesObject,
7
- CMIObjectivesObject,
8
- NAV,
9
- } from "./cmi/scorm12_cmi";
10
- import * as Utilities from "./utilities";
11
- import APIConstants from "./constants/api_constants";
12
- import ErrorCodes, { ErrorCode } from "./constants/error_codes";
13
- import { BaseCMI } from "./cmi/common";
14
-
15
- const scorm12_constants = APIConstants.scorm12;
16
- const global_constants = APIConstants.global;
17
- const scorm12_error_codes: ErrorCode = ErrorCodes.scorm12;
14
+ } from "./cmi/scorm12/interactions";
15
+ import { NAV } from "./cmi/scorm12/nav";
16
+ import {
17
+ CommitObject,
18
+ RefObject,
19
+ ResultObject,
20
+ ScoreObject,
21
+ Settings,
22
+ } from "./types/api_types";
23
+ import Regex from "./constants/regex";
24
+ import { CompletionStatus, SuccessStatus } from "./constants/enums";
18
25
 
19
26
  /**
20
27
  * API class for SCORM 1.2
@@ -31,7 +38,7 @@ export default class Scorm12API extends BaseAPI {
31
38
  }
32
39
  }
33
40
 
34
- super(scorm12_error_codes, settings);
41
+ super(ErrorCodes.scorm12, settings);
35
42
 
36
43
  this.cmi = new CMI();
37
44
  this.nav = new NAV();
@@ -47,6 +54,8 @@ export default class Scorm12API extends BaseAPI {
47
54
  this.LMSGetDiagnostic = this.lmsGetDiagnostic;
48
55
  }
49
56
 
57
+ public statusSetByModule = false;
58
+
50
59
  public cmi: CMI;
51
60
  public nav: NAV;
52
61
 
@@ -59,6 +68,16 @@ export default class Scorm12API extends BaseAPI {
59
68
  public LMSGetErrorString: (CMIErrorCode: string) => string;
60
69
  public LMSGetDiagnostic: (CMIErrorCode: string) => string;
61
70
 
71
+ /**
72
+ * Called when the API needs to be reset
73
+ */
74
+ reset(settings?: Settings) {
75
+ this.commonReset(settings);
76
+
77
+ this.cmi = new CMI();
78
+ this.nav = new NAV();
79
+ }
80
+
62
81
  /**
63
82
  * lmsInitialize function from SCORM 1.2 Spec
64
83
  *
@@ -66,6 +85,11 @@ export default class Scorm12API extends BaseAPI {
66
85
  */
67
86
  lmsInitialize(): string {
68
87
  this.cmi.initialize();
88
+ if (this.cmi.core.lesson_status) {
89
+ this.statusSetByModule = true;
90
+ } else {
91
+ this.cmi.core.lesson_status = "not attempted";
92
+ }
69
93
  return this.initialize(
70
94
  "LMSInitialize",
71
95
  "LMS was already initialized!",
@@ -79,9 +103,16 @@ export default class Scorm12API extends BaseAPI {
79
103
  * @return {string} bool
80
104
  */
81
105
  lmsFinish(): string {
82
- const result = this.terminate("LMSFinish", true);
106
+ (async () => {
107
+ await this.internalFinish();
108
+ })();
109
+ return APIConstants.global.SCORM_TRUE;
110
+ }
83
111
 
84
- if (result === global_constants.SCORM_TRUE) {
112
+ async internalFinish(): Promise<string> {
113
+ const result = await this.terminate("LMSFinish", true);
114
+
115
+ if (result === APIConstants.global.SCORM_TRUE) {
85
116
  if (this.nav.event !== "") {
86
117
  if (this.nav.event === "continue") {
87
118
  this.processListeners("SequenceNext");
@@ -114,6 +145,9 @@ export default class Scorm12API extends BaseAPI {
114
145
  * @return {string}
115
146
  */
116
147
  lmsSetValue(CMIElement: string, value: any): string {
148
+ if (CMIElement === "cmi.core.lesson_status") {
149
+ this.statusSetByModule = true;
150
+ }
117
151
  return this.setValue("LMSSetValue", "LMSCommit", false, CMIElement, value);
118
152
  }
119
153
 
@@ -123,7 +157,10 @@ export default class Scorm12API extends BaseAPI {
123
157
  * @return {string} bool
124
158
  */
125
159
  lmsCommit(): string {
126
- return this.commit("LMSCommit", false);
160
+ (async () => {
161
+ await this.commit("LMSCommit", false);
162
+ })();
163
+ return APIConstants.global.SCORM_TRUE;
127
164
  }
128
165
 
129
166
  /**
@@ -189,11 +226,11 @@ export default class Scorm12API extends BaseAPI {
189
226
  _value: any,
190
227
  foundFirstIndex: boolean,
191
228
  ): BaseCMI | null {
192
- if (this.stringMatches(CMIElement, "cmi\\.objectives\\.\\d+")) {
229
+ if (stringMatches(CMIElement, "cmi\\.objectives\\.\\d+")) {
193
230
  return new CMIObjectivesObject();
194
231
  } else if (
195
232
  foundFirstIndex &&
196
- this.stringMatches(
233
+ stringMatches(
197
234
  CMIElement,
198
235
  "cmi\\.interactions\\.\\d+\\.correct_responses\\.\\d+",
199
236
  )
@@ -201,15 +238,12 @@ export default class Scorm12API extends BaseAPI {
201
238
  return new CMIInteractionsCorrectResponsesObject();
202
239
  } else if (
203
240
  foundFirstIndex &&
204
- this.stringMatches(
205
- CMIElement,
206
- "cmi\\.interactions\\.\\d+\\.objectives\\.\\d+",
207
- )
241
+ stringMatches(CMIElement, "cmi\\.interactions\\.\\d+\\.objectives\\.\\d+")
208
242
  ) {
209
243
  return new CMIInteractionsObjectivesObject();
210
244
  } else if (
211
245
  !foundFirstIndex &&
212
- this.stringMatches(CMIElement, "cmi\\.interactions\\.\\d+")
246
+ stringMatches(CMIElement, "cmi\\.interactions\\.\\d+")
213
247
  ) {
214
248
  return new CMIInteractionsObject();
215
249
  }
@@ -243,11 +277,11 @@ export default class Scorm12API extends BaseAPI {
243
277
 
244
278
  // Set error number to string since inconsistent from modules if string or number
245
279
  errorNumber = String(errorNumber);
246
- if (scorm12_constants.error_descriptions[errorNumber]) {
280
+ if (APIConstants.scorm12.error_descriptions[errorNumber]) {
247
281
  basicMessage =
248
- scorm12_constants.error_descriptions[errorNumber].basicMessage;
282
+ APIConstants.scorm12.error_descriptions[errorNumber].basicMessage;
249
283
  detailMessage =
250
- scorm12_constants.error_descriptions[errorNumber].detailMessage;
284
+ APIConstants.scorm12.error_descriptions[errorNumber].detailMessage;
251
285
  }
252
286
 
253
287
  return detail ? detailMessage : basicMessage;
@@ -294,16 +328,75 @@ export default class Scorm12API extends BaseAPI {
294
328
  }
295
329
  }
296
330
 
331
+ /**
332
+ * Render the cmi object to the proper format for LMS commit
333
+ * @param {boolean} terminateCommit
334
+ * @return {CommitObject}
335
+ */
336
+ renderCommitObject(terminateCommit: boolean): CommitObject {
337
+ const cmiExport = this.renderCommitCMI(terminateCommit);
338
+ const totalTimeHHMMSS = this.cmi.getCurrentTotalTime();
339
+ const totalTimeSeconds = Utilities.getTimeAsSeconds(
340
+ totalTimeHHMMSS,
341
+ Regex.scorm12.CMITimespan,
342
+ );
343
+ const lessonStatus = this.cmi.core.lesson_status;
344
+ let completionStatus = CompletionStatus.unknown;
345
+ let successStatus = SuccessStatus.unknown;
346
+ if (lessonStatus) {
347
+ completionStatus =
348
+ lessonStatus === "completed" || lessonStatus === "passed"
349
+ ? CompletionStatus.completed
350
+ : CompletionStatus.incomplete;
351
+ if (lessonStatus === "passed") {
352
+ successStatus = SuccessStatus.passed;
353
+ } else if (lessonStatus === "failed") {
354
+ successStatus = SuccessStatus.failed;
355
+ }
356
+ }
357
+
358
+ const score = this.cmi.core.score;
359
+ let scoreObject: ScoreObject = null;
360
+ if (score) {
361
+ scoreObject = {};
362
+
363
+ if (!Number.isNaN(Number.parseFloat(score.raw))) {
364
+ scoreObject.raw = Number.parseFloat(score.raw);
365
+ }
366
+ if (!Number.isNaN(Number.parseFloat(score.min))) {
367
+ scoreObject.min = Number.parseFloat(score.min);
368
+ }
369
+ if (!Number.isNaN(Number.parseFloat(score.max))) {
370
+ scoreObject.max = Number.parseFloat(score.max);
371
+ }
372
+ }
373
+
374
+ const commitObject: CommitObject = {
375
+ successStatus: successStatus,
376
+ completionStatus: completionStatus,
377
+ runtimeData: cmiExport,
378
+ totalTimeSeconds: totalTimeSeconds,
379
+ };
380
+ if (scoreObject) {
381
+ commitObject.score = scoreObject;
382
+ }
383
+ return commitObject;
384
+ }
385
+
297
386
  /**
298
387
  * Attempts to store the data to the LMS
299
388
  *
300
389
  * @param {boolean} terminateCommit
301
390
  * @return {ResultObject}
302
391
  */
303
- storeData(terminateCommit: boolean): ResultObject {
392
+ async storeData(terminateCommit: boolean): Promise<ResultObject> {
304
393
  if (terminateCommit) {
305
394
  const originalStatus = this.cmi.core.lesson_status;
306
- if (originalStatus === "not attempted") {
395
+ if (
396
+ !this.cmi.core.lesson_status ||
397
+ (!this.statusSetByModule &&
398
+ this.cmi.core.lesson_status === "not attempted")
399
+ ) {
307
400
  this.cmi.core.lesson_status = "completed";
308
401
  }
309
402
 
@@ -314,14 +407,11 @@ export default class Scorm12API extends BaseAPI {
314
407
  this.cmi.student_data.mastery_score !== "" &&
315
408
  this.cmi.core.score.raw !== ""
316
409
  ) {
317
- if (
410
+ this.cmi.core.lesson_status =
318
411
  parseFloat(this.cmi.core.score.raw) >=
319
412
  parseFloat(this.cmi.student_data.mastery_score)
320
- ) {
321
- this.cmi.core.lesson_status = "passed";
322
- } else {
323
- this.cmi.core.lesson_status = "failed";
324
- }
413
+ ? "passed"
414
+ : "failed";
325
415
  }
326
416
  }
327
417
  } else if (this.cmi.core.lesson_mode === "browse") {
@@ -334,25 +424,27 @@ export default class Scorm12API extends BaseAPI {
334
424
  }
335
425
  }
336
426
 
337
- const commitObject = this.renderCommitCMI(
338
- terminateCommit || this.settings.alwaysSendTotalTime,
339
- );
427
+ const shouldTerminateCommit =
428
+ terminateCommit || this.settings.alwaysSendTotalTime;
429
+ const commitObject = this.settings.renderCommonCommitFields
430
+ ? this.renderCommitObject(shouldTerminateCommit)
431
+ : this.renderCommitCMI(shouldTerminateCommit);
340
432
 
341
- if (this.apiLogLevel === global_constants.LOG_LEVEL_DEBUG) {
433
+ if (this.apiLogLevel === APIConstants.global.LOG_LEVEL_DEBUG) {
342
434
  console.debug(
343
435
  "Commit (terminated: " + (terminateCommit ? "yes" : "no") + "): ",
344
436
  );
345
437
  console.debug(commitObject);
346
438
  }
347
439
  if (typeof this.settings.lmsCommitUrl === "string") {
348
- return this.processHttpRequest(
440
+ return await this.processHttpRequest(
349
441
  this.settings.lmsCommitUrl,
350
442
  commitObject,
351
443
  terminateCommit,
352
444
  );
353
445
  } else {
354
446
  return {
355
- result: global_constants.SCORM_TRUE,
447
+ result: APIConstants.global.SCORM_TRUE,
356
448
  errorCode: 0,
357
449
  };
358
450
  }