@lxpack/runtime 0.2.0 → 0.2.1

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 (45) hide show
  1. package/dist/client.js +1582 -1341
  2. package/dist/core/completion-evaluator.d.ts +13 -0
  3. package/dist/core/completion-evaluator.d.ts.map +1 -0
  4. package/dist/core/progress-state.d.ts +20 -0
  5. package/dist/core/progress-state.d.ts.map +1 -0
  6. package/dist/flow-conditions.d.ts +4 -0
  7. package/dist/flow-conditions.d.ts.map +1 -0
  8. package/dist/flow.d.ts +2 -2
  9. package/dist/flow.d.ts.map +1 -1
  10. package/dist/lms/bridge.d.ts +11 -0
  11. package/dist/lms/bridge.d.ts.map +1 -0
  12. package/dist/lms/completion-state.d.ts +10 -0
  13. package/dist/lms/completion-state.d.ts.map +1 -0
  14. package/dist/lms/factory.d.ts +5 -0
  15. package/dist/lms/factory.d.ts.map +1 -0
  16. package/dist/lms/local-bridge.d.ts +15 -0
  17. package/dist/lms/local-bridge.d.ts.map +1 -0
  18. package/dist/lms/scorm12-bridge.d.ts +15 -0
  19. package/dist/lms/scorm12-bridge.d.ts.map +1 -0
  20. package/dist/lms/scorm2004-bridge.d.ts +15 -0
  21. package/dist/lms/scorm2004-bridge.d.ts.map +1 -0
  22. package/dist/{progress-persist.d.ts → progress/codec.d.ts} +2 -3
  23. package/dist/progress/codec.d.ts.map +1 -0
  24. package/dist/progress/constants.d.ts +3 -0
  25. package/dist/progress/constants.d.ts.map +1 -0
  26. package/dist/progress/size-policy.d.ts +4 -0
  27. package/dist/progress/size-policy.d.ts.map +1 -0
  28. package/dist/quiz/host.d.ts +9 -0
  29. package/dist/quiz/host.d.ts.map +1 -0
  30. package/dist/quiz/score.d.ts +6 -0
  31. package/dist/quiz/score.d.ts.map +1 -0
  32. package/dist/runtime.d.ts +10 -14
  33. package/dist/runtime.d.ts.map +1 -1
  34. package/dist/runtime.js +276 -562
  35. package/dist/scorm-api.d.ts +1 -1
  36. package/dist/scorm-api.d.ts.map +1 -1
  37. package/dist/scorm12-DpGWvsHb.js +375 -0
  38. package/dist/scorm12.d.ts +3 -0
  39. package/dist/scorm12.d.ts.map +1 -0
  40. package/dist/scorm12.js +11 -0
  41. package/dist/scorm2004.d.ts +2 -0
  42. package/dist/scorm2004.d.ts.map +1 -0
  43. package/dist/scorm2004.js +153 -0
  44. package/package.json +11 -3
  45. package/dist/progress-persist.d.ts.map +0 -1
package/dist/runtime.js CHANGED
@@ -1,660 +1,374 @@
1
- var E = Object.defineProperty;
2
- var O = (e, s, t) => s in e ? E(e, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[s] = t;
3
- var i = (e, s, t) => O(e, typeof s != "symbol" ? s + "" : s, t);
4
- const g = "lxpack_scorm12_preview";
5
- function M(e = 500) {
6
- let s = window, t = 0;
7
- for (; s && t < e; ) {
8
- try {
9
- const r = s.API;
10
- if (r && typeof r.LMSInitialize == "function")
11
- return r;
12
- } catch {
13
- }
14
- try {
15
- if (s.parent && s.parent !== s)
16
- s = s.parent;
17
- else if (s.opener && !s.opener.closed)
18
- s = s.opener;
19
- else
20
- break;
21
- } catch {
22
- break;
23
- }
24
- t++;
1
+ var M = Object.defineProperty;
2
+ var T = (t, s, e) => s in t ? M(t, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[s] = e;
3
+ var a = (t, s, e) => T(t, typeof s != "symbol" ? s + "" : s, e);
4
+ import { p as f, d as F, s as I } from "./scorm12-DpGWvsHb.js";
5
+ import { S as Q, a as W, b as Y, c as Z, f as ss, i as es, t as ts } from "./scorm12-DpGWvsHb.js";
6
+ import { createScorm2004Connection as N } from "./scorm2004.js";
7
+ const x = "v:";
8
+ function g(t) {
9
+ return `${x}${t}`;
10
+ }
11
+ function y(t, s) {
12
+ for (const [e, n] of Object.entries(t.variables ?? {})) {
13
+ const i = g(e);
14
+ s[i] === void 0 && (s[i] = n.default);
25
15
  }
16
+ }
17
+ function _(t, s) {
18
+ return t[g(s)];
19
+ }
20
+ function $(t, s, e) {
21
+ t[g(s)] = e;
22
+ }
23
+ const R = [
24
+ (t, s) => {
25
+ var e;
26
+ if ("variable" in t && ((e = t.variable) != null && e.eq)) {
27
+ const [n, i] = t.variable.eq;
28
+ return s.getVariable(n) === i;
29
+ }
30
+ return null;
31
+ },
32
+ (t, s) => {
33
+ var e;
34
+ return "assessment" in t && ((e = t.assessment) != null && e.passed) ? s.isAssessmentPassed(t.assessment.passed) : null;
35
+ },
36
+ (t, s) => {
37
+ var e;
38
+ return "interaction" in t && ((e = t.interaction) != null && e.done) ? s.isInteractionDone(t.interaction.done) : null;
39
+ },
40
+ (t, s) => "all" in t && t.all ? t.all.every((e) => d(e, s)) : null,
41
+ (t, s) => "any" in t && t.any ? t.any.some((e) => d(e, s)) : null
42
+ ];
43
+ function d(t, s) {
44
+ for (const e of R) {
45
+ const n = e(t, s);
46
+ if (n !== null)
47
+ return n;
48
+ }
49
+ return !1;
50
+ }
51
+ function k(t, s) {
52
+ const e = t.flow;
53
+ if (!(e != null && e.length)) return null;
54
+ for (const n of e)
55
+ if (d(n.when, s))
56
+ return n.goto;
26
57
  return null;
27
58
  }
28
- class A {
59
+ class G {
29
60
  constructor(s) {
30
- i(this, "api");
31
- i(this, "initialized", !1);
32
- i(this, "terminated", !1);
33
- i(this, "lastError", "0");
34
- this.api = s;
35
- }
36
- LMSInitialize() {
37
- if (this.initialized)
38
- return this.lastError = "301", "false";
39
- const s = this.api.LMSInitialize("");
40
- return s === "true" && (this.initialized = !0, this.terminated = !1, this.lastError = "0"), s;
41
- }
42
- LMSFinish() {
43
- if (!this.initialized || this.terminated)
44
- return this.lastError = "302", "false";
45
- const s = this.api.LMSFinish("");
46
- return s === "true" && (this.terminated = !0, this.lastError = "0"), s;
47
- }
48
- LMSGetValue(s) {
49
- return !this.initialized || this.terminated ? (this.lastError = "301", "") : (this.lastError = "0", this.api.LMSGetValue(s));
50
- }
51
- LMSSetValue(s, t) {
52
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSSetValue(s, t));
53
- }
54
- LMSCommit() {
55
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSCommit(""));
56
- }
57
- LMSGetLastError() {
58
- return this.lastError;
59
- }
60
- LMSGetErrorString(s) {
61
- const t = s ?? this.lastError;
62
- return t === "301" ? "Not initialized" : t === "302" ? "Already terminated" : this.api.LMSGetErrorString(t);
63
- }
64
- LMSGetDiagnostic(s) {
65
- return this.api.LMSGetDiagnostic(s ?? this.lastError);
61
+ this.connection = s;
62
+ }
63
+ init() {
64
+ this.connection.LMSInitialize();
65
+ }
66
+ restoreProgress(s) {
67
+ const e = this.connection.LMSGetValue("cmi.suspend_data");
68
+ if (e) {
69
+ const { progress: i, parsed: c } = f(e, s);
70
+ if (c) return i;
71
+ const r = this.connection.LMSGetValue("cmi.core.lesson_location");
72
+ return r ? { ...s, currentLessonId: r } : s;
73
+ }
74
+ const n = this.connection.LMSGetValue("cmi.core.lesson_location");
75
+ return n ? { ...s, currentLessonId: n } : s;
66
76
  }
67
- setLessonStatus(s) {
68
- this.LMSSetValue("cmi.core.lesson_status", s);
77
+ persist(s, e) {
78
+ this.connection.setSuspendData(e), this.connection.LMSCommit();
69
79
  }
70
- setScore(s, t = 100) {
71
- this.LMSSetValue("cmi.core.score.raw", String(s)), this.LMSSetValue("cmi.core.score.max", String(t)), this.LMSSetValue("cmi.core.score.min", "0");
80
+ setLocation(s) {
81
+ this.connection.setLessonLocation(s);
72
82
  }
73
- setSuspendData(s) {
74
- const t = d(s);
75
- this.LMSSetValue("cmi.suspend_data", t);
83
+ applyCompletion(s) {
84
+ this.connection.setScore(s.scorePercent), s.anyAssessmentFailed ? this.connection.setLessonStatus("failed") : s.ratio >= s.completionThreshold && s.allLessonsComplete && s.allAssessmentsPassed ? this.connection.setLessonStatus(
85
+ s.hasAssessments ? "passed" : "completed"
86
+ ) : s.scorePercent > 0 && this.connection.setLessonStatus("incomplete");
76
87
  }
77
- setLessonLocation(s) {
78
- this.LMSSetValue("cmi.core.lesson_location", s);
88
+ terminate() {
89
+ this.connection.LMSFinish();
79
90
  }
80
91
  }
81
- class u {
82
- constructor(s = {}) {
83
- i(this, "data", {
84
- lessonStatus: "not attempted",
85
- scoreRaw: 0,
86
- scoreMin: 0,
87
- scoreMax: 100,
88
- suspendData: "",
89
- lessonLocation: "",
90
- entry: "ab-initio",
91
- exit: "",
92
- sessionTime: "00:00:00"
93
- });
94
- i(this, "initialized", !1);
95
- i(this, "terminated", !1);
96
- i(this, "lastError", "0");
97
- i(this, "persistToStorage");
98
- this.persistToStorage = s.persistToStorage ?? !1, s.initialData && (this.data = { ...this.data, ...s.initialData }), this.persistToStorage && this.loadFromStorage();
99
- }
100
- LMSInitialize() {
101
- return this.initialized ? (this.lastError = "301", "false") : (this.initialized = !0, this.terminated = !1, this.lastError = "0", "true");
102
- }
103
- LMSFinish() {
104
- return !this.initialized || this.terminated ? (this.lastError = "302", "false") : (this.data.exit = "suspend", this.terminated = !0, this.saveToStorage(), this.lastError = "0", "true");
105
- }
106
- LMSGetValue(s) {
107
- if (!this.initialized || this.terminated)
108
- return this.lastError = "301", "";
109
- switch (this.lastError = "0", s) {
110
- case "cmi.core.lesson_status":
111
- return this.data.lessonStatus;
112
- case "cmi.core.score.raw":
113
- return String(this.data.scoreRaw);
114
- case "cmi.core.score.min":
115
- return String(this.data.scoreMin);
116
- case "cmi.core.score.max":
117
- return String(this.data.scoreMax);
118
- case "cmi.suspend_data":
119
- return this.data.suspendData;
120
- case "cmi.core.lesson_location":
121
- return this.data.lessonLocation;
122
- case "cmi.core.student_id":
123
- return "lxpack-learner";
124
- case "cmi.core.student_name":
125
- return "LXPack Learner";
126
- case "cmi.core.entry":
127
- return this.data.entry;
128
- case "cmi.core.exit":
129
- return this.data.exit;
130
- case "cmi.core.session_time":
131
- return this.data.sessionTime;
132
- default:
133
- return "";
134
- }
135
- }
136
- LMSSetValue(s, t) {
137
- if (!this.initialized || this.terminated)
138
- return this.lastError = "301", "false";
139
- switch (this.lastError = "0", s) {
140
- case "cmi.core.lesson_status":
141
- this.data.lessonStatus = t;
142
- break;
143
- case "cmi.core.score.raw":
144
- this.data.scoreRaw = Number(t);
145
- break;
146
- case "cmi.core.score.min":
147
- this.data.scoreMin = Number(t);
148
- break;
149
- case "cmi.core.score.max":
150
- this.data.scoreMax = Number(t);
151
- break;
152
- case "cmi.suspend_data":
153
- this.data.suspendData = d(t);
154
- break;
155
- case "cmi.core.lesson_location":
156
- this.data.lessonLocation = t;
157
- break;
158
- case "cmi.core.exit":
159
- this.data.exit = t;
160
- break;
161
- case "cmi.core.session_time":
162
- this.data.sessionTime = t;
163
- break;
164
- default:
165
- return "false";
92
+ class B {
93
+ constructor(s) {
94
+ this.connection = s;
95
+ }
96
+ init() {
97
+ this.connection.Initialize();
98
+ }
99
+ restoreProgress(s) {
100
+ const e = this.connection.GetValue("cmi.suspend_data");
101
+ if (e) {
102
+ const { progress: i, parsed: c } = f(e, s);
103
+ if (c) return i;
104
+ const r = this.connection.GetValue("cmi.location");
105
+ return r ? { ...s, currentLessonId: r } : s;
166
106
  }
167
- return this.saveToStorage(), "true";
168
- }
169
- LMSCommit() {
170
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.saveToStorage(), this.lastError = "0", "true");
171
- }
172
- LMSGetLastError() {
173
- return this.lastError;
174
- }
175
- LMSGetErrorString(s) {
176
- const t = s ?? this.lastError;
177
- return t === "301" ? "Not initialized" : t === "302" ? "Already terminated" : "No error";
178
- }
179
- LMSGetDiagnostic() {
180
- return "";
181
- }
182
- setLessonStatus(s) {
183
- this.data.lessonStatus = s, this.saveToStorage();
184
- }
185
- setScore(s, t = 100) {
186
- this.data.scoreRaw = s, this.data.scoreMax = t, this.data.scoreMin = 0, this.saveToStorage();
107
+ const n = this.connection.GetValue("cmi.location");
108
+ return n ? { ...s, currentLessonId: n } : s;
187
109
  }
188
- setSuspendData(s) {
189
- this.data.suspendData = d(s), this.saveToStorage();
110
+ persist(s, e) {
111
+ this.connection.setSuspendData(e), this.connection.Commit();
190
112
  }
191
- setLessonLocation(s) {
192
- this.data.lessonLocation = s, this.saveToStorage();
113
+ setLocation(s) {
114
+ this.connection.setLocation(s);
193
115
  }
194
- loadFromStorage() {
195
- try {
196
- const s = localStorage.getItem(g);
197
- s && (this.data = { ...this.data, ...JSON.parse(s) });
198
- } catch {
199
- }
116
+ applyCompletion(s) {
117
+ this.connection.setScoreScaled(s.ratio), s.anyAssessmentFailed ? (this.connection.setSuccessStatus("failed"), this.connection.setCompletionStatus("completed")) : s.ratio >= s.completionThreshold && s.allLessonsComplete && s.allAssessmentsPassed ? (this.connection.setSuccessStatus("passed"), this.connection.setCompletionStatus("completed")) : s.ratio > 0 && (this.connection.setCompletionStatus("incomplete"), this.connection.setSuccessStatus("unknown"));
200
118
  }
201
- saveToStorage() {
202
- if (this.persistToStorage)
203
- try {
204
- localStorage.setItem(g, JSON.stringify(this.data));
205
- } catch {
206
- }
119
+ terminate() {
120
+ this.connection.Terminate();
207
121
  }
208
122
  }
209
- class F extends u {
210
- constructor() {
211
- super({ persistToStorage: !0 });
123
+ class D {
124
+ constructor(s) {
125
+ this.storageKey = s;
212
126
  }
213
- }
214
- function d(e) {
215
- return e.length <= 4096 ? e : (console.warn(
216
- "[lxpack] suspend_data exceeds 4096 chars; truncating"
217
- ), e.slice(0, 4096));
218
- }
219
- function L(e) {
220
- if (e === "preview")
221
- return new u({ persistToStorage: !0 });
222
- if (e === "scorm12") {
223
- const s = M();
224
- return s ? new A(s) : (console.warn(
225
- "[lxpack] No LMS SCORM API found; using in-memory simulator (progress will not persist to LMS)"
226
- ), new u({ persistToStorage: !1 }));
227
- }
228
- return new u({ persistToStorage: !1 });
229
- }
230
- function j(e = "preview") {
231
- const s = L(e);
232
- if (e === "preview") {
233
- const t = window;
234
- t.API = s;
127
+ init() {
235
128
  }
236
- return s;
237
- }
238
- const I = "0", R = "lxpack_scorm2004_preview";
239
- function N(e = 500) {
240
- let s = window, t = 0;
241
- for (; s && t < e; ) {
129
+ restoreProgress(s) {
242
130
  try {
243
- const r = s.API_1484_11;
244
- if (r && typeof r.Initialize == "function")
245
- return r;
131
+ const e = localStorage.getItem(this.storageKey);
132
+ if (e) {
133
+ const { progress: n, parsed: i } = f(e, s);
134
+ if (i) return n;
135
+ }
246
136
  } catch {
247
137
  }
138
+ return s;
139
+ }
140
+ persist(s, e) {
248
141
  try {
249
- if (s.parent && s.parent !== s)
250
- s = s.parent;
251
- else if (s.opener && !s.opener.closed)
252
- s = s.opener;
253
- else
254
- break;
142
+ localStorage.setItem(this.storageKey, e);
255
143
  } catch {
256
- break;
257
144
  }
258
- t++;
259
- }
260
- return null;
261
- }
262
- class T {
263
- constructor(s) {
264
- i(this, "initialized", !1);
265
- i(this, "terminated", !1);
266
- i(this, "data", {
267
- "cmi.completion_status": "unknown",
268
- "cmi.success_status": "unknown",
269
- "cmi.score.scaled": "",
270
- "cmi.suspend_data": "",
271
- "cmi.location": "",
272
- "cmi.entry": "ab-initio"
273
- });
274
- i(this, "persistToStorage");
275
- if (this.persistToStorage = (s == null ? void 0 : s.persistToStorage) ?? !1, this.persistToStorage)
276
- try {
277
- const t = localStorage.getItem(R);
278
- t && (this.data = { ...this.data, ...JSON.parse(t) });
279
- } catch {
280
- }
281
- }
282
- save() {
283
- if (this.persistToStorage)
284
- try {
285
- localStorage.setItem(R, JSON.stringify(this.data));
286
- } catch {
287
- }
288
- }
289
- Initialize() {
290
- return this.initialized ? "false" : (this.initialized = !0, this.terminated = !1, "true");
291
- }
292
- Terminate() {
293
- return !this.initialized || this.terminated ? "false" : (this.terminated = !0, this.save(), "true");
294
- }
295
- GetValue(s) {
296
- return !this.initialized || this.terminated ? "" : this.data[s] ?? "";
297
- }
298
- SetValue(s, t) {
299
- return !this.initialized || this.terminated ? "false" : (this.data[s] = t, this.save(), "true");
300
- }
301
- Commit() {
302
- return !this.initialized || this.terminated ? "false" : (this.save(), "true");
303
- }
304
- GetLastError() {
305
- return I;
306
- }
307
- GetErrorString() {
308
- return "No error";
309
- }
310
- GetDiagnostic() {
311
- return "";
312
- }
313
- setSuspendData(s) {
314
- this.data["cmi.suspend_data"] = s;
315
145
  }
316
146
  setLocation(s) {
317
- this.data["cmi.location"] = s;
318
- }
319
- setCompletionStatus(s) {
320
- this.data["cmi.completion_status"] = s;
321
147
  }
322
- setSuccessStatus(s) {
323
- this.data["cmi.success_status"] = s;
148
+ applyCompletion(s) {
324
149
  }
325
- setScoreScaled(s) {
326
- this.data["cmi.score.scaled"] = String(Math.max(0, Math.min(1, s)));
150
+ terminate() {
327
151
  }
328
152
  }
329
- class D {
330
- constructor(s) {
331
- i(this, "api");
332
- this.api = s;
333
- }
334
- Initialize() {
335
- return this.api.Initialize("");
336
- }
337
- Terminate() {
338
- return this.api.Terminate("");
339
- }
340
- GetValue(s) {
341
- return this.api.GetValue(s);
153
+ function K(t, s) {
154
+ const e = `${t}::${s}`;
155
+ let n = 0;
156
+ for (let i = 0; i < e.length; i++)
157
+ n = (n << 5) - n + e.charCodeAt(i), n |= 0;
158
+ return `lxpack_progress_${Math.abs(n)}`;
159
+ }
160
+ function z(t, s) {
161
+ return t === "scorm12" ? new G(F("scorm12")) : t === "scorm2004" ? new B(N("scorm2004")) : t === "preview" || t === "standalone" ? new D(s) : new D(s);
162
+ }
163
+ function S(t, s) {
164
+ const e = t[`assessment_attempts_${s}`];
165
+ return typeof e == "number" ? e : typeof e == "string" && e !== "" && !Number.isNaN(Number(e)) ? Number(e) : 0;
166
+ }
167
+ function O(t, s) {
168
+ const e = S(t, s) + 1;
169
+ return t[`assessment_attempts_${s}`] = e, e;
170
+ }
171
+ class j {
172
+ constructor(s, e, n) {
173
+ a(this, "progress");
174
+ a(this, "passedAssessments", /* @__PURE__ */ new Set());
175
+ this.manifest = e, this.defaultPassingScores = n, this.progress = s, this.syncPassedAssessments();
342
176
  }
343
- SetValue(s, t) {
344
- return this.api.SetValue(s, t);
177
+ getAssessmentPassingScore(s) {
178
+ const e = this.progress.suspendData[`assessment_passing_${s}`];
179
+ return typeof e == "number" ? e : s in this.defaultPassingScores ? this.defaultPassingScores[s] : 0.7;
345
180
  }
346
- Commit() {
347
- return this.api.Commit("");
181
+ syncPassedAssessments() {
182
+ this.passedAssessments.clear();
183
+ for (const [s, e] of Object.entries(this.progress.assessmentScores))
184
+ e >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
348
185
  }
349
- GetLastError() {
350
- return this.api.GetLastError();
186
+ applyAssessmentResult(s, e, n) {
187
+ this.progress.assessmentScores[s] = e, this.progress.suspendData[`assessment_passing_${s}`] = n, e >= n ? this.passedAssessments.add(s) : this.passedAssessments.delete(s);
351
188
  }
352
- GetErrorString(s) {
353
- return this.api.GetErrorString(s);
189
+ recordAssessmentAttempt(s) {
190
+ return O(this.progress.suspendData, s);
354
191
  }
355
- GetDiagnostic(s) {
356
- return this.api.GetDiagnostic(s);
192
+ getAssessmentAttemptCount(s) {
193
+ return S(this.progress.suspendData, s);
357
194
  }
358
- setSuspendData(s) {
359
- this.api.SetValue("cmi.suspend_data", s);
195
+ completeLesson(s) {
196
+ new Set(this.manifest.lessons.map((n) => n.id)).has(s) && (this.progress.completedLessons.includes(s) || this.progress.completedLessons.push(s));
360
197
  }
361
- setLocation(s) {
362
- this.api.SetValue("cmi.location", s);
198
+ setCurrentLesson(s) {
199
+ this.progress.currentLessonId = s;
363
200
  }
364
- setCompletionStatus(s) {
365
- this.api.SetValue("cmi.completion_status", s);
201
+ isLessonComplete(s) {
202
+ return this.progress.completedLessons.includes(s);
366
203
  }
367
- setSuccessStatus(s) {
368
- this.api.SetValue("cmi.success_status", s);
204
+ isAssessmentPassed(s) {
205
+ return this.passedAssessments.has(s);
369
206
  }
370
- setScoreScaled(s) {
371
- this.api.SetValue("cmi.score.scaled", String(Math.max(0, Math.min(1, s))));
207
+ getAssessmentPassingScoreForTrack(s) {
208
+ return this.getAssessmentPassingScore(s);
372
209
  }
373
210
  }
374
- function C(e) {
375
- const s = N();
376
- return s ? new D(s) : (console.warn(
377
- "[lxpack] No SCORM 2004 API found; using in-memory simulator"
378
- ), new T({ persistToStorage: !1 }));
379
- }
380
- function p(e) {
381
- var t;
382
- const s = {
383
- c: e.currentLessonId,
384
- l: e.completedLessons,
385
- a: e.assessmentScores,
386
- s: e.suspendData
387
- };
388
- return (t = s.l) != null && t.length || delete s.l, (!s.a || !Object.keys(s.a).length) && delete s.a, (!s.s || !Object.keys(s.s).length) && delete s.s, s;
389
- }
390
- function y(e, s) {
391
- return {
392
- currentLessonId: e.c ?? s.currentLessonId,
393
- completedLessons: Array.isArray(e.l) ? e.l : s.completedLessons,
394
- assessmentScores: e.a && typeof e.a == "object" ? e.a : s.assessmentScores,
395
- suspendData: e.s && typeof e.s == "object" ? e.s : s.suspendData
396
- };
397
- }
398
- function b(e) {
399
- const s = { ...e.suspendData }, t = Object.keys(s).filter((r) => r.startsWith("interaction_")).sort();
400
- for (const r of t)
401
- if (delete s[r], JSON.stringify(p({ ...e, suspendData: s })).length <= 4096)
402
- return { ...e, suspendData: s };
403
- return { ...e, suspendData: s };
404
- }
405
- function P(e) {
406
- let s = JSON.stringify(p(e));
407
- if (s.length <= 4096)
408
- return s;
409
- const t = b(e);
410
- return s = JSON.stringify(p(t)), s.length <= 4096 ? s : (console.warn(
411
- "[lxpack] suspend_data exceeds 4096 chars; truncating"
412
- ), s.slice(0, 4096));
413
- }
414
- function w(e) {
415
- if (!e || typeof e != "object") return !1;
416
- const s = e;
417
- return "currentLessonId" in s || "completedLessons" in s || "assessmentScores" in s;
418
- }
419
- function V(e, s) {
211
+ function w(t, s) {
212
+ var A, L, C, P;
213
+ const { manifest: e, completionThreshold: n, passedAssessments: i } = s, c = e.lessons.length, r = ((A = e.assessments) == null ? void 0 : A.length) ?? 0, l = c + r, m = t.completedLessons.filter(
214
+ (o) => e.lessons.some((p) => p.id === o)
215
+ ).length, u = l === 0 ? 0 : (m + i.size) / l, h = e.lessons.every(
216
+ (o) => t.completedLessons.includes(o.id)
217
+ ), V = !((L = e.assessments) != null && L.length) || e.assessments.every((o) => i.has(o.id)), v = (C = e.assessments) == null ? void 0 : C.some((o) => {
218
+ var b;
219
+ if (!(o.id in t.assessmentScores) || i.has(o.id)) return !1;
220
+ const p = ((b = s.assessmentConfigs[o.id]) == null ? void 0 : b.maxAttempts) ?? 1;
221
+ return S(t.suspendData, o.id) >= p;
222
+ });
420
223
  return {
421
- currentLessonId: e.currentLessonId ?? s.currentLessonId,
422
- completedLessons: Array.isArray(e.completedLessons) ? e.completedLessons : s.completedLessons,
423
- assessmentScores: e.assessmentScores && typeof e.assessmentScores == "object" ? e.assessmentScores : s.assessmentScores,
424
- suspendData: e.suspendData && typeof e.suspendData == "object" ? e.suspendData : s.suspendData
224
+ ratio: u,
225
+ scorePercent: Math.round(u * 100),
226
+ allLessonsComplete: h,
227
+ allAssessmentsPassed: V,
228
+ anyAssessmentFailed: !!v,
229
+ hasAssessments: (((P = e.assessments) == null ? void 0 : P.length) ?? 0) > 0,
230
+ completionThreshold: n
425
231
  };
426
232
  }
427
- function m(e, s) {
428
- try {
429
- const t = JSON.parse(e);
430
- return w(t) ? {
431
- progress: V(t, s),
432
- parsed: !0
433
- } : { progress: y(t, s), parsed: !0 };
434
- } catch {
435
- return { progress: s, parsed: !1 };
436
- }
437
- }
438
- const z = "v:";
439
- function f(e) {
440
- return `${z}${e}`;
441
- }
442
- function k(e, s) {
443
- for (const [t, r] of Object.entries(e.variables ?? {})) {
444
- const a = f(t);
445
- s[a] === void 0 && (s[a] = r.default);
446
- }
447
- }
448
- function _(e, s) {
449
- return e[f(s)];
450
- }
451
- function x(e, s, t) {
452
- e[f(s)] = t;
453
- }
454
- function S(e, s) {
455
- var t, r, a;
456
- if ("variable" in e && ((t = e.variable) != null && t.eq)) {
457
- const [n, o] = e.variable.eq;
458
- return s.getVariable(n) === o;
459
- }
460
- return "assessment" in e && ((r = e.assessment) != null && r.passed) ? s.isAssessmentPassed(e.assessment.passed) : "interaction" in e && ((a = e.interaction) != null && a.done) ? s.isInteractionDone(e.interaction.done) : "all" in e && e.all ? e.all.every((n) => S(n, s)) : "any" in e && e.any ? e.any.some((n) => S(n, s)) : !1;
461
- }
462
- function G(e, s) {
463
- const t = e.flow;
464
- if (!(t != null && t.length)) return null;
465
- for (const r of t)
466
- if (S(r.when, s))
467
- return r.goto;
468
- return null;
469
- }
470
- function $(e) {
471
- const s = `${e.title}::${e.version}`;
472
- let t = 0;
473
- for (let r = 0; r < s.length; r++)
474
- t = (t << 5) - t + s.charCodeAt(r), t |= 0;
475
- return `lxpack_progress_${Math.abs(t)}`;
476
- }
477
- class U {
233
+ class X {
478
234
  constructor(s) {
479
- i(this, "manifest");
480
- i(this, "progress");
481
- i(this, "scorm", null);
482
- i(this, "scorm2004", null);
483
- i(this, "completionThreshold");
484
- i(this, "storageKey");
485
- i(this, "mode");
486
- i(this, "passedAssessments", /* @__PURE__ */ new Set());
487
- i(this, "defaultPassingScores");
488
- i(this, "terminated", !1);
489
- var r, a, n;
490
- this.manifest = s.manifest, this.mode = s.mode, this.completionThreshold = ((a = (r = s.manifest.tracking) == null ? void 0 : r.completion) == null ? void 0 : a.threshold) ?? 0.9, this.storageKey = $(s.manifest), this.defaultPassingScores = {};
491
- for (const [o, c] of Object.entries(s.assessments ?? {}))
492
- this.defaultPassingScores[o] = c.passingScore;
493
- const t = s.activityId ?? ((n = s.manifest.lessons[0]) == null ? void 0 : n.id) ?? "";
494
- this.progress = s.progress ?? {
495
- currentLessonId: t,
235
+ a(this, "manifest");
236
+ a(this, "state");
237
+ a(this, "bridge");
238
+ a(this, "completionThreshold");
239
+ a(this, "assessmentConfigs");
240
+ a(this, "defaultPassingScores");
241
+ a(this, "terminated", !1);
242
+ var r, l, m;
243
+ this.manifest = s.manifest, this.completionThreshold = ((l = (r = s.manifest.tracking) == null ? void 0 : r.completion) == null ? void 0 : l.threshold) ?? 0.9;
244
+ const e = K(
245
+ s.manifest.title,
246
+ s.manifest.version
247
+ );
248
+ this.bridge = z(s.mode, e), this.defaultPassingScores = {};
249
+ for (const [u, h] of Object.entries(s.assessments ?? {}))
250
+ this.defaultPassingScores[u] = h.passingScore;
251
+ this.assessmentConfigs = s.assessmentConfigs ?? {};
252
+ const n = s.activityId ?? ((m = s.manifest.lessons[0]) == null ? void 0 : m.id) ?? "", i = s.progress ?? {
253
+ currentLessonId: n,
496
254
  completedLessons: [],
497
255
  assessmentScores: {},
498
256
  suspendData: {}
499
- }, k(this.manifest, this.progress.suspendData), s.mode === "scorm12" ? (this.scorm = L("scorm12"), this.scorm.LMSInitialize(), this.restoreScormProgress()) : s.mode === "scorm2004" ? (this.scorm2004 = C(), this.scorm2004.Initialize(), this.restoreScorm2004Progress()) : (s.mode === "preview" || s.mode === "standalone") && this.restoreLocalProgress(), s.activityId && (this.progress.currentLessonId = s.activityId);
500
- }
501
- restoreLessonLocation() {
502
- if (this.scorm) {
503
- const s = this.scorm.LMSGetValue("cmi.core.lesson_location");
504
- s && (this.progress.currentLessonId = s);
505
- return;
506
- }
507
- if (this.scorm2004) {
508
- const s = this.scorm2004.GetValue("cmi.location");
509
- s && (this.progress.currentLessonId = s);
510
- }
511
- }
512
- restoreScorm2004Progress() {
513
- if (!this.scorm2004) return;
514
- const s = this.scorm2004.GetValue("cmi.suspend_data");
515
- if (s) {
516
- const { progress: t, parsed: r } = m(s, this.progress);
517
- r ? this.progress = t : this.restoreLessonLocation();
518
- } else
519
- this.restoreLessonLocation();
520
- this.syncPassedAssessments();
521
- }
522
- restoreScormProgress() {
523
- if (!this.scorm) return;
524
- const s = this.scorm.LMSGetValue("cmi.suspend_data");
525
- if (s) {
526
- const { progress: t, parsed: r } = m(s, this.progress);
527
- r ? this.progress = t : this.restoreLessonLocation();
528
- } else
529
- this.restoreLessonLocation();
530
- this.syncPassedAssessments();
531
- }
532
- restoreLocalProgress() {
533
- try {
534
- const s = localStorage.getItem(this.storageKey);
535
- if (s) {
536
- const { progress: t, parsed: r } = m(s, this.progress);
537
- r && (this.progress = t);
538
- }
539
- } catch {
540
- }
541
- this.syncPassedAssessments();
542
- }
543
- syncPassedAssessments() {
544
- this.passedAssessments.clear();
545
- for (const [s, t] of Object.entries(this.progress.assessmentScores))
546
- t >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
547
- }
548
- getAssessmentPassingScore(s) {
549
- const t = this.progress.suspendData[`assessment_passing_${s}`];
550
- return typeof t == "number" ? t : s in this.defaultPassingScores ? this.defaultPassingScores[s] : 0.7;
257
+ };
258
+ y(this.manifest, i.suspendData), this.bridge.init();
259
+ const c = this.bridge.restoreProgress(i);
260
+ this.state = new j(
261
+ c,
262
+ this.manifest,
263
+ this.defaultPassingScores
264
+ ), y(this.manifest, this.state.progress.suspendData), this.state.syncPassedAssessments(), s.activityId && this.state.setCurrentLesson(s.activityId);
551
265
  }
552
266
  getAPI() {
553
267
  return {
554
268
  track: (s) => this.track(s),
555
269
  completeLesson: (s) => this.completeLesson(s),
556
- getProgress: () => ({
557
- ...this.progress,
558
- completedLessons: [...this.progress.completedLessons],
559
- assessmentScores: { ...this.progress.assessmentScores },
560
- suspendData: { ...this.progress.suspendData }
561
- }),
562
- setVariable: (s, t) => {
563
- this.manifest.variables && s in this.manifest.variables ? x(this.progress.suspendData, s, t) : this.progress.suspendData[s] = t, this.persist();
270
+ getProgress: () => this.getProgress(),
271
+ setVariable: (s, e) => {
272
+ this.manifest.variables && s in this.manifest.variables ? $(this.state.progress.suspendData, s, e) : this.state.progress.suspendData[s] = e, this.persist();
564
273
  },
565
- getVariable: (s) => this.manifest.variables && s in this.manifest.variables ? _(this.progress.suspendData, s) : this.progress.suspendData[s],
566
- submitAssessment: (s, t, r) => this.submitAssessment(s, t, r)
274
+ getVariable: (s) => this.manifest.variables && s in this.manifest.variables ? _(this.state.progress.suspendData, s) : this.state.progress.suspendData[s],
275
+ submitAssessment: (s, e, n) => this.submitAssessment(s, e, n)
567
276
  };
568
277
  }
569
- track(s, t) {
570
- var r, a;
571
- if (s.type === "interaction" && s.id && (this.progress.suspendData[`interaction_${s.id}`] = s.data ?? !0, console.debug("[lxpack] interaction:", s.id, s.data)), s.type === "assessment" && s.id) {
572
- const n = typeof ((r = s.data) == null ? void 0 : r.passingScore) == "number" ? Number(s.data.passingScore) : this.getAssessmentPassingScore(s.id), o = ((a = s.data) == null ? void 0 : a.score) != null ? Number(s.data.score) : void 0;
573
- if (o != null && !Number.isNaN(o)) {
574
- this.applyAssessmentResult(s.id, o, n), (t == null ? void 0 : t.persist) !== !1 && (this.updateCompletion(), this.persist());
278
+ track(s, e) {
279
+ var n, i;
280
+ if (s.type === "interaction" && s.id && (this.state.progress.suspendData[`interaction_${s.id}`] = s.data ?? !0, console.debug("[lxpack] interaction:", s.id, s.data)), s.type === "assessment" && s.id) {
281
+ const c = typeof ((n = s.data) == null ? void 0 : n.passingScore) == "number" ? Number(s.data.passingScore) : this.state.getAssessmentPassingScoreForTrack(s.id), r = ((i = s.data) == null ? void 0 : i.score) != null ? Number(s.data.score) : void 0;
282
+ if (r != null && !Number.isNaN(r)) {
283
+ this.state.applyAssessmentResult(s.id, r, c), (e == null ? void 0 : e.persist) !== !1 && (this.updateCompletion(), this.persist());
575
284
  return;
576
285
  }
577
286
  }
578
- (t == null ? void 0 : t.persist) !== !1 && this.persist();
287
+ (e == null ? void 0 : e.persist) !== !1 && this.persist();
579
288
  }
580
- applyAssessmentResult(s, t, r) {
581
- this.progress.assessmentScores[s] = t, this.progress.suspendData[`assessment_passing_${s}`] = r, t >= r ? this.passedAssessments.add(s) : this.passedAssessments.delete(s);
289
+ recordAssessmentAttempt(s) {
290
+ return this.state.recordAssessmentAttempt(s);
582
291
  }
583
- submitAssessment(s, t, r) {
584
- this.applyAssessmentResult(s, t, r), this.updateCompletion(), this.persist();
292
+ getAssessmentAttemptCount(s) {
293
+ return this.state.getAssessmentAttemptCount(s);
294
+ }
295
+ submitAssessment(s, e, n) {
296
+ this.state.recordAssessmentAttempt(s), this.state.applyAssessmentResult(s, e, n), this.updateCompletion(), this.persist();
585
297
  }
586
298
  completeLesson(s) {
587
- new Set(this.manifest.lessons.map((r) => r.id)).has(s) && (this.progress.completedLessons.includes(s) || this.progress.completedLessons.push(s), this.updateCompletion(), this.persist());
299
+ this.state.completeLesson(s), this.updateCompletion(), this.persist();
588
300
  }
589
301
  setCurrentLesson(s) {
590
- this.progress.currentLessonId = s, this.scorm && this.scorm.setLessonLocation(s), this.scorm2004 && this.scorm2004.setLocation(s), this.persist();
302
+ this.state.setCurrentLesson(s), this.bridge.setLocation(s), this.persist();
591
303
  }
592
304
  getProgress() {
593
- return this.getAPI().getProgress();
305
+ const s = this.state.progress;
306
+ return {
307
+ ...s,
308
+ completedLessons: [...s.completedLessons],
309
+ assessmentScores: { ...s.assessmentScores },
310
+ suspendData: { ...s.suspendData }
311
+ };
594
312
  }
595
313
  getManifest() {
596
314
  return this.manifest;
597
315
  }
598
316
  isLessonComplete(s) {
599
- return this.progress.completedLessons.includes(s);
317
+ return this.state.isLessonComplete(s);
600
318
  }
601
319
  isAssessmentPassed(s) {
602
- return this.passedAssessments.has(s);
320
+ return this.state.isAssessmentPassed(s);
603
321
  }
604
322
  getTotalUnits() {
605
- var t;
606
- const s = ((t = this.manifest.assessments) == null ? void 0 : t.length) ?? 0;
323
+ var e;
324
+ const s = ((e = this.manifest.assessments) == null ? void 0 : e.length) ?? 0;
607
325
  return this.manifest.lessons.length + s;
608
326
  }
609
327
  getCompletionRatio() {
610
- var o;
611
- if (this.getTotalUnits() === 0) return 0;
612
- const t = this.manifest.lessons.length, r = ((o = this.manifest.assessments) == null ? void 0 : o.length) ?? 0, a = this.progress.completedLessons.filter(
613
- (c) => this.manifest.lessons.some((h) => h.id === c)
614
- ).length, n = this.passedAssessments.size;
615
- return (a + n) / (t + r);
328
+ return w(this.state.progress, {
329
+ manifest: this.manifest,
330
+ completionThreshold: this.completionThreshold,
331
+ assessmentConfigs: this.assessmentConfigs,
332
+ defaultPassingScores: this.defaultPassingScores,
333
+ passedAssessments: this.state.passedAssessments
334
+ }).ratio;
616
335
  }
617
336
  updateCompletion() {
618
- var o, c, h;
619
- const s = this.getCompletionRatio(), t = Math.round(s * 100), r = this.manifest.lessons.every(
620
- (l) => this.progress.completedLessons.includes(l.id)
621
- ), a = !((o = this.manifest.assessments) != null && o.length) || this.manifest.assessments.every((l) => this.passedAssessments.has(l.id)), n = (c = this.manifest.assessments) == null ? void 0 : c.some(
622
- (l) => l.id in this.progress.assessmentScores && !this.passedAssessments.has(l.id)
623
- );
624
- this.scorm && (this.scorm.setScore(t), n ? this.scorm.setLessonStatus("failed") : s >= this.completionThreshold && r && a ? this.scorm.setLessonStatus(
625
- (((h = this.manifest.assessments) == null ? void 0 : h.length) ?? 0) > 0 ? "passed" : "completed"
626
- ) : t > 0 && this.scorm.setLessonStatus("incomplete")), this.scorm2004 && (this.scorm2004.setScoreScaled(s), n ? (this.scorm2004.setSuccessStatus("failed"), this.scorm2004.setCompletionStatus("completed")) : s >= this.completionThreshold && r && a ? (this.scorm2004.setSuccessStatus("passed"), this.scorm2004.setCompletionStatus("completed")) : s > 0 && (this.scorm2004.setCompletionStatus("incomplete"), this.scorm2004.setSuccessStatus("unknown")));
337
+ const s = w(this.state.progress, {
338
+ manifest: this.manifest,
339
+ completionThreshold: this.completionThreshold,
340
+ assessmentConfigs: this.assessmentConfigs,
341
+ defaultPassingScores: this.defaultPassingScores,
342
+ passedAssessments: this.state.passedAssessments
343
+ });
344
+ this.bridge.applyCompletion(s);
627
345
  }
628
346
  persist() {
629
- const s = P(this.progress);
630
- if (this.mode === "preview" || this.mode === "standalone")
631
- try {
632
- localStorage.setItem(this.storageKey, s);
633
- } catch {
634
- }
635
- this.scorm && (this.scorm.setSuspendData(s), this.scorm.LMSCommit()), this.scorm2004 && (this.scorm2004.setSuspendData(s), this.scorm2004.Commit());
347
+ const s = I(this.state.progress);
348
+ this.bridge.persist(this.state.progress, s);
636
349
  }
637
350
  getFlowContext() {
638
351
  return {
639
- getVariable: (s) => _(this.progress.suspendData, s),
352
+ getVariable: (s) => _(this.state.progress.suspendData, s),
640
353
  isAssessmentPassed: (s) => this.isAssessmentPassed(s),
641
- isInteractionDone: (s) => this.progress.suspendData[`interaction_${s}`] !== void 0
354
+ isInteractionDone: (s) => this.state.progress.suspendData[`interaction_${s}`] !== void 0
642
355
  };
643
356
  }
644
357
  resolveFlowNavigation() {
645
- return G(this.manifest, this.getFlowContext());
358
+ return k(this.manifest, this.getFlowContext());
646
359
  }
647
360
  terminate() {
648
- this.terminated || (this.terminated = !0, this.scorm && this.scorm.LMSFinish(), this.scorm2004 && this.scorm2004.Terminate());
361
+ this.terminated || (this.terminated = !0, this.bridge.terminate());
649
362
  }
650
363
  }
651
364
  export {
652
- U as LxpackRuntime,
653
- F as Scorm12API,
654
- A as Scorm12Adapter,
655
- u as Scorm12Simulator,
656
- L as createScormConnection,
657
- M as findLmsApi,
658
- j as installScormAPI,
659
- d as trimSuspendData
365
+ X as LxpackRuntime,
366
+ Q as SCORM_SUSPEND_DATA_MAX,
367
+ W as Scorm12API,
368
+ Y as Scorm12Adapter,
369
+ Z as Scorm12Simulator,
370
+ F as createScormConnection,
371
+ ss as findLmsApi,
372
+ es as installScormAPI,
373
+ ts as trimSuspendData
660
374
  };