@lxpack/runtime 0.1.0 → 0.1.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.
- package/README.md +112 -0
- package/dist/client.js +1282 -1193
- package/dist/progress-persist.d.ts +17 -0
- package/dist/progress-persist.d.ts.map +1 -0
- package/dist/runtime.d.ts +7 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +180 -128
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CourseProgress } from "./types.js";
|
|
2
|
+
interface CompactProgress {
|
|
3
|
+
c?: string;
|
|
4
|
+
l?: string[];
|
|
5
|
+
a?: Record<string, number>;
|
|
6
|
+
s?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export declare function compactProgress(progress: CourseProgress): CompactProgress;
|
|
9
|
+
export declare function expandProgress(compact: CompactProgress, defaults: CourseProgress): CourseProgress;
|
|
10
|
+
export declare function serializeProgressForStorage(progress: CourseProgress): string;
|
|
11
|
+
export declare function expandLegacyProgress(saved: Partial<CourseProgress>, defaults: CourseProgress): CourseProgress;
|
|
12
|
+
export declare function parseStoredProgress(raw: string, defaults: CourseProgress): {
|
|
13
|
+
progress: CourseProgress;
|
|
14
|
+
parsed: boolean;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=progress-persist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-persist.d.ts","sourceRoot":"","sources":["../src/progress-persist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,UAAU,eAAe;IACvB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,eAAe,CAWzE;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,GACvB,cAAc,CAahB;AAqBD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAkB5E;AAYD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,EAC9B,QAAQ,EAAE,cAAc,GACvB,cAAc,CAehB;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,cAAc,GACvB;IAAE,QAAQ,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAa/C"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -7,13 +7,19 @@ export declare class LxpackRuntime {
|
|
|
7
7
|
private storageKey;
|
|
8
8
|
private mode;
|
|
9
9
|
private passedAssessments;
|
|
10
|
+
private defaultPassingScores;
|
|
11
|
+
private terminated;
|
|
10
12
|
constructor(config: RuntimeConfig);
|
|
13
|
+
private restoreLessonLocation;
|
|
11
14
|
private restoreScormProgress;
|
|
12
15
|
private restoreLocalProgress;
|
|
13
16
|
private syncPassedAssessments;
|
|
14
17
|
private getAssessmentPassingScore;
|
|
15
18
|
getAPI(): LxpackAPI;
|
|
16
|
-
track(event: TrackEvent
|
|
19
|
+
track(event: TrackEvent, options?: {
|
|
20
|
+
persist?: boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
private applyAssessmentResult;
|
|
17
23
|
submitAssessment(assessmentId: string, score: number, passingScore: number): void;
|
|
18
24
|
completeLesson(lessonId: string): void;
|
|
19
25
|
setCurrentLesson(lessonId: string): void;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,SAAS,EACT,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,SAAS,EACT,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AAoBpB,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,oBAAoB,CAAyB;IACrD,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,aAAa;IA4BjC,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,yBAAyB;IASjC,MAAM,IAAI,SAAS;IAoBnB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IA2B/D,OAAO,CAAC,qBAAqB;IAe7B,gBAAgB,CACd,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB,IAAI;IAMP,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWtC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQxC,WAAW,IAAI,cAAc;IAI7B,WAAW,IAAI,cAAc;IAI7B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI3C,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAIjD,aAAa,IAAI,MAAM;IAKvB,kBAAkB,IAAI,MAAM;IAc5B,OAAO,CAAC,gBAAgB;IAkCxB,OAAO,CAAC,OAAO;IAiBf,SAAS,IAAI,IAAI;CAOlB;AAED,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var i = (
|
|
4
|
-
const
|
|
5
|
-
function
|
|
6
|
-
let s = window,
|
|
7
|
-
for (; s &&
|
|
1
|
+
var f = Object.defineProperty;
|
|
2
|
+
var g = (t, s, e) => s in t ? f(t, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[s] = e;
|
|
3
|
+
var i = (t, s, e) => g(t, typeof s != "symbol" ? s + "" : s, e);
|
|
4
|
+
const S = "lxpack_scorm12_preview";
|
|
5
|
+
function _(t = 500) {
|
|
6
|
+
let s = window, e = 0;
|
|
7
|
+
for (; s && e < t; ) {
|
|
8
8
|
try {
|
|
9
9
|
const r = s.API;
|
|
10
10
|
if (r && typeof r.LMSInitialize == "function")
|
|
@@ -21,11 +21,11 @@ function f(e = 500) {
|
|
|
21
21
|
} catch {
|
|
22
22
|
break;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
e++;
|
|
25
25
|
}
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
|
-
class
|
|
28
|
+
class L {
|
|
29
29
|
constructor(s) {
|
|
30
30
|
i(this, "api");
|
|
31
31
|
i(this, "initialized", !1);
|
|
@@ -48,8 +48,8 @@ class E {
|
|
|
48
48
|
LMSGetValue(s) {
|
|
49
49
|
return !this.initialized || this.terminated ? (this.lastError = "301", "") : (this.lastError = "0", this.api.LMSGetValue(s));
|
|
50
50
|
}
|
|
51
|
-
LMSSetValue(s,
|
|
52
|
-
return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSSetValue(s,
|
|
51
|
+
LMSSetValue(s, e) {
|
|
52
|
+
return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSSetValue(s, e));
|
|
53
53
|
}
|
|
54
54
|
LMSCommit() {
|
|
55
55
|
return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSCommit(""));
|
|
@@ -58,8 +58,8 @@ class E {
|
|
|
58
58
|
return this.lastError;
|
|
59
59
|
}
|
|
60
60
|
LMSGetErrorString(s) {
|
|
61
|
-
const
|
|
62
|
-
return
|
|
61
|
+
const e = s ?? this.lastError;
|
|
62
|
+
return e === "301" ? "Not initialized" : e === "302" ? "Already terminated" : this.api.LMSGetErrorString(e);
|
|
63
63
|
}
|
|
64
64
|
LMSGetDiagnostic(s) {
|
|
65
65
|
return this.api.LMSGetDiagnostic(s ?? this.lastError);
|
|
@@ -67,12 +67,12 @@ class E {
|
|
|
67
67
|
setLessonStatus(s) {
|
|
68
68
|
this.LMSSetValue("cmi.core.lesson_status", s);
|
|
69
69
|
}
|
|
70
|
-
setScore(s,
|
|
71
|
-
this.LMSSetValue("cmi.core.score.raw", String(s)), this.LMSSetValue("cmi.core.score.max", String(
|
|
70
|
+
setScore(s, e = 100) {
|
|
71
|
+
this.LMSSetValue("cmi.core.score.raw", String(s)), this.LMSSetValue("cmi.core.score.max", String(e)), this.LMSSetValue("cmi.core.score.min", "0");
|
|
72
72
|
}
|
|
73
73
|
setSuspendData(s) {
|
|
74
|
-
const
|
|
75
|
-
this.LMSSetValue("cmi.suspend_data",
|
|
74
|
+
const e = m(s);
|
|
75
|
+
this.LMSSetValue("cmi.suspend_data", e);
|
|
76
76
|
}
|
|
77
77
|
setLessonLocation(s) {
|
|
78
78
|
this.LMSSetValue("cmi.core.lesson_location", s);
|
|
@@ -133,33 +133,33 @@ class h {
|
|
|
133
133
|
return "";
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
-
LMSSetValue(s,
|
|
136
|
+
LMSSetValue(s, e) {
|
|
137
137
|
if (!this.initialized || this.terminated)
|
|
138
138
|
return this.lastError = "301", "false";
|
|
139
139
|
switch (this.lastError = "0", s) {
|
|
140
140
|
case "cmi.core.lesson_status":
|
|
141
|
-
this.data.lessonStatus =
|
|
141
|
+
this.data.lessonStatus = e;
|
|
142
142
|
break;
|
|
143
143
|
case "cmi.core.score.raw":
|
|
144
|
-
this.data.scoreRaw = Number(
|
|
144
|
+
this.data.scoreRaw = Number(e);
|
|
145
145
|
break;
|
|
146
146
|
case "cmi.core.score.min":
|
|
147
|
-
this.data.scoreMin = Number(
|
|
147
|
+
this.data.scoreMin = Number(e);
|
|
148
148
|
break;
|
|
149
149
|
case "cmi.core.score.max":
|
|
150
|
-
this.data.scoreMax = Number(
|
|
150
|
+
this.data.scoreMax = Number(e);
|
|
151
151
|
break;
|
|
152
152
|
case "cmi.suspend_data":
|
|
153
|
-
this.data.suspendData =
|
|
153
|
+
this.data.suspendData = m(e);
|
|
154
154
|
break;
|
|
155
155
|
case "cmi.core.lesson_location":
|
|
156
|
-
this.data.lessonLocation =
|
|
156
|
+
this.data.lessonLocation = e;
|
|
157
157
|
break;
|
|
158
158
|
case "cmi.core.exit":
|
|
159
|
-
this.data.exit =
|
|
159
|
+
this.data.exit = e;
|
|
160
160
|
break;
|
|
161
161
|
case "cmi.core.session_time":
|
|
162
|
-
this.data.sessionTime =
|
|
162
|
+
this.data.sessionTime = e;
|
|
163
163
|
break;
|
|
164
164
|
default:
|
|
165
165
|
return "false";
|
|
@@ -173,8 +173,8 @@ class h {
|
|
|
173
173
|
return this.lastError;
|
|
174
174
|
}
|
|
175
175
|
LMSGetErrorString(s) {
|
|
176
|
-
const
|
|
177
|
-
return
|
|
176
|
+
const e = s ?? this.lastError;
|
|
177
|
+
return e === "301" ? "Not initialized" : e === "302" ? "Already terminated" : "No error";
|
|
178
178
|
}
|
|
179
179
|
LMSGetDiagnostic() {
|
|
180
180
|
return "";
|
|
@@ -182,18 +182,18 @@ class h {
|
|
|
182
182
|
setLessonStatus(s) {
|
|
183
183
|
this.data.lessonStatus = s, this.saveToStorage();
|
|
184
184
|
}
|
|
185
|
-
setScore(s,
|
|
186
|
-
this.data.scoreRaw = s, this.data.scoreMax =
|
|
185
|
+
setScore(s, e = 100) {
|
|
186
|
+
this.data.scoreRaw = s, this.data.scoreMax = e, this.data.scoreMin = 0, this.saveToStorage();
|
|
187
187
|
}
|
|
188
188
|
setSuspendData(s) {
|
|
189
|
-
this.data.suspendData =
|
|
189
|
+
this.data.suspendData = m(s), this.saveToStorage();
|
|
190
190
|
}
|
|
191
191
|
setLessonLocation(s) {
|
|
192
192
|
this.data.lessonLocation = s, this.saveToStorage();
|
|
193
193
|
}
|
|
194
194
|
loadFromStorage() {
|
|
195
195
|
try {
|
|
196
|
-
const s = localStorage.getItem(
|
|
196
|
+
const s = localStorage.getItem(S);
|
|
197
197
|
s && (this.data = { ...this.data, ...JSON.parse(s) });
|
|
198
198
|
} catch {
|
|
199
199
|
}
|
|
@@ -201,63 +201,106 @@ class h {
|
|
|
201
201
|
saveToStorage() {
|
|
202
202
|
if (this.persistToStorage)
|
|
203
203
|
try {
|
|
204
|
-
localStorage.setItem(
|
|
204
|
+
localStorage.setItem(S, JSON.stringify(this.data));
|
|
205
205
|
} catch {
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
class
|
|
209
|
+
class D extends h {
|
|
210
210
|
constructor() {
|
|
211
211
|
super({ persistToStorage: !0 });
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
-
function
|
|
215
|
-
return
|
|
214
|
+
function m(t) {
|
|
215
|
+
return t.length <= 4096 ? t : (console.warn(
|
|
216
216
|
"[lxpack] suspend_data exceeds 4096 chars; truncating"
|
|
217
|
-
),
|
|
217
|
+
), t.slice(0, 4096));
|
|
218
218
|
}
|
|
219
|
-
function
|
|
220
|
-
if (
|
|
219
|
+
function R(t) {
|
|
220
|
+
if (t === "preview")
|
|
221
221
|
return new h({ persistToStorage: !0 });
|
|
222
|
-
if (
|
|
223
|
-
const s =
|
|
224
|
-
return s ? new
|
|
222
|
+
if (t === "scorm12") {
|
|
223
|
+
const s = _();
|
|
224
|
+
return s ? new L(s) : (console.warn(
|
|
225
225
|
"[lxpack] No LMS SCORM API found; using in-memory simulator (progress will not persist to LMS)"
|
|
226
226
|
), new h({ persistToStorage: !1 }));
|
|
227
227
|
}
|
|
228
228
|
return new h({ persistToStorage: !1 });
|
|
229
229
|
}
|
|
230
|
-
function
|
|
231
|
-
const s =
|
|
232
|
-
if (
|
|
233
|
-
const
|
|
234
|
-
|
|
230
|
+
function C(t = "preview") {
|
|
231
|
+
const s = R(t);
|
|
232
|
+
if (t === "preview") {
|
|
233
|
+
const e = window;
|
|
234
|
+
e.API = s;
|
|
235
235
|
}
|
|
236
236
|
return s;
|
|
237
237
|
}
|
|
238
|
-
function
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
function d(t) {
|
|
239
|
+
var e;
|
|
240
|
+
const s = {
|
|
241
|
+
c: t.currentLessonId,
|
|
242
|
+
l: t.completedLessons,
|
|
243
|
+
a: t.assessmentScores,
|
|
244
|
+
s: t.suspendData
|
|
245
|
+
};
|
|
246
|
+
return (e = s.l) != null && e.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;
|
|
244
247
|
}
|
|
245
|
-
function
|
|
248
|
+
function E(t, s) {
|
|
246
249
|
return {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
250
|
+
currentLessonId: t.c ?? s.currentLessonId,
|
|
251
|
+
completedLessons: Array.isArray(t.l) ? t.l : s.completedLessons,
|
|
252
|
+
assessmentScores: t.a && typeof t.a == "object" ? t.a : s.assessmentScores,
|
|
253
|
+
suspendData: t.s && typeof t.s == "object" ? t.s : s.suspendData
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function O(t) {
|
|
257
|
+
const s = { ...t.suspendData }, e = Object.keys(s).filter((r) => r.startsWith("interaction_")).sort();
|
|
258
|
+
for (const r of e)
|
|
259
|
+
if (delete s[r], JSON.stringify(d({ ...t, suspendData: s })).length <= 4096)
|
|
260
|
+
return { ...t, suspendData: s };
|
|
261
|
+
return { ...t, suspendData: s };
|
|
262
|
+
}
|
|
263
|
+
function M(t) {
|
|
264
|
+
let s = JSON.stringify(d(t));
|
|
265
|
+
if (s.length <= 4096)
|
|
266
|
+
return s;
|
|
267
|
+
const e = O(t);
|
|
268
|
+
return s = JSON.stringify(d(e)), s.length <= 4096 ? s : (console.warn(
|
|
269
|
+
"[lxpack] suspend_data exceeds 4096 chars; truncating"
|
|
270
|
+
), s.slice(0, 4096));
|
|
271
|
+
}
|
|
272
|
+
function A(t) {
|
|
273
|
+
if (!t || typeof t != "object") return !1;
|
|
274
|
+
const s = t;
|
|
275
|
+
return "currentLessonId" in s || "completedLessons" in s || "assessmentScores" in s;
|
|
276
|
+
}
|
|
277
|
+
function N(t, s) {
|
|
278
|
+
return {
|
|
279
|
+
currentLessonId: t.currentLessonId ?? s.currentLessonId,
|
|
280
|
+
completedLessons: Array.isArray(t.completedLessons) ? t.completedLessons : s.completedLessons,
|
|
281
|
+
assessmentScores: t.assessmentScores && typeof t.assessmentScores == "object" ? t.assessmentScores : s.assessmentScores,
|
|
282
|
+
suspendData: t.suspendData && typeof t.suspendData == "object" ? t.suspendData : s.suspendData
|
|
258
283
|
};
|
|
259
284
|
}
|
|
260
|
-
|
|
285
|
+
function p(t, s) {
|
|
286
|
+
try {
|
|
287
|
+
const e = JSON.parse(t);
|
|
288
|
+
return A(e) ? {
|
|
289
|
+
progress: N(e, s),
|
|
290
|
+
parsed: !0
|
|
291
|
+
} : { progress: E(e, s), parsed: !0 };
|
|
292
|
+
} catch {
|
|
293
|
+
return { progress: s, parsed: !1 };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function I(t) {
|
|
297
|
+
const s = `${t.title}::${t.version}`;
|
|
298
|
+
let e = 0;
|
|
299
|
+
for (let r = 0; r < s.length; r++)
|
|
300
|
+
e = (e << 5) - e + s.charCodeAt(r), e |= 0;
|
|
301
|
+
return `lxpack_progress_${Math.abs(e)}`;
|
|
302
|
+
}
|
|
303
|
+
class y {
|
|
261
304
|
constructor(s) {
|
|
262
305
|
i(this, "manifest");
|
|
263
306
|
i(this, "progress");
|
|
@@ -266,37 +309,41 @@ class N {
|
|
|
266
309
|
i(this, "storageKey");
|
|
267
310
|
i(this, "mode");
|
|
268
311
|
i(this, "passedAssessments", /* @__PURE__ */ new Set());
|
|
312
|
+
i(this, "defaultPassingScores");
|
|
313
|
+
i(this, "terminated", !1);
|
|
269
314
|
var r, a, o;
|
|
270
|
-
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 =
|
|
271
|
-
|
|
315
|
+
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 = I(s.manifest), this.defaultPassingScores = {};
|
|
316
|
+
for (const [n, l] of Object.entries(s.assessments ?? {}))
|
|
317
|
+
this.defaultPassingScores[n] = l.passingScore;
|
|
318
|
+
const e = ((o = s.manifest.lessons[0]) == null ? void 0 : o.id) ?? "";
|
|
272
319
|
this.progress = s.progress ?? {
|
|
273
|
-
currentLessonId:
|
|
320
|
+
currentLessonId: e,
|
|
274
321
|
completedLessons: [],
|
|
275
322
|
assessmentScores: {},
|
|
276
323
|
suspendData: {}
|
|
277
|
-
}, s.mode === "scorm12" ? (this.scorm =
|
|
324
|
+
}, s.mode === "scorm12" ? (this.scorm = R("scorm12"), this.scorm.LMSInitialize(), this.restoreScormProgress()) : (s.mode === "preview" || s.mode === "standalone") && this.restoreLocalProgress();
|
|
325
|
+
}
|
|
326
|
+
restoreLessonLocation() {
|
|
327
|
+
if (!this.scorm) return;
|
|
328
|
+
const s = this.scorm.LMSGetValue("cmi.core.lesson_location");
|
|
329
|
+
s && (this.progress.currentLessonId = s);
|
|
278
330
|
}
|
|
279
331
|
restoreScormProgress() {
|
|
280
332
|
if (!this.scorm) return;
|
|
281
333
|
const s = this.scorm.LMSGetValue("cmi.suspend_data");
|
|
282
|
-
if (s)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
const t = this.scorm.LMSGetValue("cmi.core.lesson_location");
|
|
290
|
-
t && (this.progress.currentLessonId = t);
|
|
291
|
-
}
|
|
334
|
+
if (s) {
|
|
335
|
+
const { progress: e, parsed: r } = p(s, this.progress);
|
|
336
|
+
r ? this.progress = e : this.restoreLessonLocation();
|
|
337
|
+
} else
|
|
338
|
+
this.restoreLessonLocation();
|
|
292
339
|
this.syncPassedAssessments();
|
|
293
340
|
}
|
|
294
341
|
restoreLocalProgress() {
|
|
295
342
|
try {
|
|
296
343
|
const s = localStorage.getItem(this.storageKey);
|
|
297
344
|
if (s) {
|
|
298
|
-
const
|
|
299
|
-
|
|
345
|
+
const { progress: e, parsed: r } = p(s, this.progress);
|
|
346
|
+
r && (this.progress = e);
|
|
300
347
|
}
|
|
301
348
|
} catch {
|
|
302
349
|
}
|
|
@@ -304,41 +351,46 @@ class N {
|
|
|
304
351
|
}
|
|
305
352
|
syncPassedAssessments() {
|
|
306
353
|
this.passedAssessments.clear();
|
|
307
|
-
for (const [s,
|
|
308
|
-
|
|
354
|
+
for (const [s, e] of Object.entries(this.progress.assessmentScores))
|
|
355
|
+
e >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
|
|
309
356
|
}
|
|
310
357
|
getAssessmentPassingScore(s) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const r = this.progress.suspendData[`assessment_passing_${s}`];
|
|
314
|
-
return typeof r == "number" ? r : 0.7;
|
|
358
|
+
const e = this.progress.suspendData[`assessment_passing_${s}`];
|
|
359
|
+
return typeof e == "number" ? e : s in this.defaultPassingScores ? this.defaultPassingScores[s] : 0.7;
|
|
315
360
|
}
|
|
316
361
|
getAPI() {
|
|
317
362
|
return {
|
|
318
363
|
track: (s) => this.track(s),
|
|
319
364
|
completeLesson: (s) => this.completeLesson(s),
|
|
320
|
-
getProgress: () => ({
|
|
321
|
-
|
|
322
|
-
this.progress.
|
|
365
|
+
getProgress: () => ({
|
|
366
|
+
...this.progress,
|
|
367
|
+
completedLessons: [...this.progress.completedLessons],
|
|
368
|
+
assessmentScores: { ...this.progress.assessmentScores },
|
|
369
|
+
suspendData: { ...this.progress.suspendData }
|
|
370
|
+
}),
|
|
371
|
+
setVariable: (s, e) => {
|
|
372
|
+
this.progress.suspendData[s] = e, this.persist();
|
|
323
373
|
},
|
|
324
374
|
getVariable: (s) => this.progress.suspendData[s],
|
|
325
|
-
submitAssessment: (s,
|
|
375
|
+
submitAssessment: (s, e, r) => this.submitAssessment(s, e, r)
|
|
326
376
|
};
|
|
327
377
|
}
|
|
328
|
-
track(s) {
|
|
329
|
-
var
|
|
330
|
-
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
|
|
331
|
-
const r = Number(s.data.score);
|
|
332
|
-
|
|
378
|
+
track(s, e) {
|
|
379
|
+
var r, a;
|
|
380
|
+
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) {
|
|
381
|
+
const o = typeof ((r = s.data) == null ? void 0 : r.passingScore) == "number" ? Number(s.data.passingScore) : this.getAssessmentPassingScore(s.id), n = ((a = s.data) == null ? void 0 : a.score) != null ? Number(s.data.score) : void 0;
|
|
382
|
+
if (n != null && !Number.isNaN(n)) {
|
|
383
|
+
this.applyAssessmentResult(s.id, n, o), (e == null ? void 0 : e.persist) !== !1 && (this.updateCompletion(), this.persist());
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
333
386
|
}
|
|
334
|
-
this.persist();
|
|
387
|
+
(e == null ? void 0 : e.persist) !== !1 && this.persist();
|
|
388
|
+
}
|
|
389
|
+
applyAssessmentResult(s, e, r) {
|
|
390
|
+
this.progress.assessmentScores[s] = e, this.progress.suspendData[`assessment_passing_${s}`] = r, e >= r ? this.passedAssessments.add(s) : this.passedAssessments.delete(s);
|
|
335
391
|
}
|
|
336
|
-
submitAssessment(s,
|
|
337
|
-
this.
|
|
338
|
-
type: "assessment",
|
|
339
|
-
id: s,
|
|
340
|
-
data: { score: t, passingScore: r, passed: t >= r }
|
|
341
|
-
}), this.updateCompletion();
|
|
392
|
+
submitAssessment(s, e, r) {
|
|
393
|
+
this.applyAssessmentResult(s, e, r), this.updateCompletion(), this.persist();
|
|
342
394
|
}
|
|
343
395
|
completeLesson(s) {
|
|
344
396
|
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());
|
|
@@ -347,7 +399,7 @@ class N {
|
|
|
347
399
|
this.progress.currentLessonId = s, this.scorm && this.scorm.setLessonLocation(s), this.persist();
|
|
348
400
|
}
|
|
349
401
|
getProgress() {
|
|
350
|
-
return
|
|
402
|
+
return this.getAPI().getProgress();
|
|
351
403
|
}
|
|
352
404
|
getManifest() {
|
|
353
405
|
return this.manifest;
|
|
@@ -359,35 +411,35 @@ class N {
|
|
|
359
411
|
return this.passedAssessments.has(s);
|
|
360
412
|
}
|
|
361
413
|
getTotalUnits() {
|
|
362
|
-
var
|
|
363
|
-
const s = ((
|
|
414
|
+
var e;
|
|
415
|
+
const s = ((e = this.manifest.assessments) == null ? void 0 : e.length) ?? 0;
|
|
364
416
|
return this.manifest.lessons.length + s;
|
|
365
417
|
}
|
|
366
418
|
getCompletionRatio() {
|
|
367
|
-
var
|
|
419
|
+
var n;
|
|
368
420
|
if (this.getTotalUnits() === 0) return 0;
|
|
369
|
-
const
|
|
370
|
-
(
|
|
421
|
+
const e = this.manifest.lessons.length, r = ((n = this.manifest.assessments) == null ? void 0 : n.length) ?? 0, a = this.progress.completedLessons.filter(
|
|
422
|
+
(l) => this.manifest.lessons.some((u) => u.id === l)
|
|
371
423
|
).length, o = this.passedAssessments.size;
|
|
372
|
-
return (a + o) / (
|
|
424
|
+
return (a + o) / (e + r);
|
|
373
425
|
}
|
|
374
426
|
updateCompletion() {
|
|
375
427
|
var r, a, o;
|
|
376
|
-
const s = this.getCompletionRatio(),
|
|
428
|
+
const s = this.getCompletionRatio(), e = Math.round(s * 100);
|
|
377
429
|
if (this.scorm) {
|
|
378
|
-
this.scorm.setScore(
|
|
379
|
-
const
|
|
380
|
-
(
|
|
381
|
-
),
|
|
430
|
+
this.scorm.setScore(e);
|
|
431
|
+
const n = this.manifest.lessons.every(
|
|
432
|
+
(c) => this.progress.completedLessons.includes(c.id)
|
|
433
|
+
), l = !((r = this.manifest.assessments) != null && r.length) || this.manifest.assessments.every((c) => this.passedAssessments.has(c.id));
|
|
382
434
|
((a = this.manifest.assessments) == null ? void 0 : a.some(
|
|
383
|
-
(
|
|
384
|
-
)) ? this.scorm.setLessonStatus("failed") : s >= this.completionThreshold &&
|
|
435
|
+
(c) => c.id in this.progress.assessmentScores && !this.passedAssessments.has(c.id)
|
|
436
|
+
)) ? this.scorm.setLessonStatus("failed") : s >= this.completionThreshold && n && l ? this.scorm.setLessonStatus(
|
|
385
437
|
(((o = this.manifest.assessments) == null ? void 0 : o.length) ?? 0) > 0 ? "passed" : "completed"
|
|
386
|
-
) :
|
|
438
|
+
) : e > 0 && this.scorm.setLessonStatus("incomplete");
|
|
387
439
|
}
|
|
388
440
|
}
|
|
389
441
|
persist() {
|
|
390
|
-
const s =
|
|
442
|
+
const s = M(this.progress);
|
|
391
443
|
if (this.mode === "preview" || this.mode === "standalone")
|
|
392
444
|
try {
|
|
393
445
|
localStorage.setItem(this.storageKey, s);
|
|
@@ -396,16 +448,16 @@ class N {
|
|
|
396
448
|
this.scorm && (this.scorm.setSuspendData(s), this.scorm.LMSCommit());
|
|
397
449
|
}
|
|
398
450
|
terminate() {
|
|
399
|
-
this.scorm && this.scorm.LMSFinish();
|
|
451
|
+
this.terminated || (this.terminated = !0, this.scorm && this.scorm.LMSFinish());
|
|
400
452
|
}
|
|
401
453
|
}
|
|
402
454
|
export {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
455
|
+
y as LxpackRuntime,
|
|
456
|
+
D as Scorm12API,
|
|
457
|
+
L as Scorm12Adapter,
|
|
406
458
|
h as Scorm12Simulator,
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
459
|
+
R as createScormConnection,
|
|
460
|
+
_ as findLmsApi,
|
|
461
|
+
C as installScormAPI,
|
|
462
|
+
m as trimSuspendData
|
|
411
463
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CourseManifest, Lesson } from "@lxpack/validators";
|
|
1
|
+
import type { CourseManifest, LearnerAssessment, Lesson, RuntimeAssessmentBundle } from "@lxpack/validators";
|
|
2
2
|
export interface CourseProgress {
|
|
3
3
|
currentLessonId: string;
|
|
4
4
|
completedLessons: string[];
|
|
@@ -10,6 +10,8 @@ export interface RuntimeConfig {
|
|
|
10
10
|
baseUrl: string;
|
|
11
11
|
mode: "preview" | "standalone" | "scorm12";
|
|
12
12
|
progress?: CourseProgress;
|
|
13
|
+
assessments?: RuntimeAssessmentBundle["assessments"];
|
|
14
|
+
answerKeys?: RuntimeAssessmentBundle["answerKeys"];
|
|
13
15
|
}
|
|
14
16
|
export interface TrackEvent {
|
|
15
17
|
type: string;
|
|
@@ -24,5 +26,5 @@ export interface LxpackAPI {
|
|
|
24
26
|
getVariable: (key: string) => unknown;
|
|
25
27
|
submitAssessment: (assessmentId: string, score: number, passingScore: number) => void;
|
|
26
28
|
}
|
|
27
|
-
export type { CourseManifest, Lesson };
|
|
29
|
+
export type { CourseManifest, LearnerAssessment, Lesson };
|
|
28
30
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,MAAM,EACN,uBAAuB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,WAAW,CAAC,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;IACrD,UAAU,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,MAAM,cAAc,CAAC;IAClC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,gBAAgB,EAAE,CAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,KACjB,IAAI,CAAC;CACX;AAED,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lxpack/runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Web-native course runtime for LXPack",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"marked": "^15.0.7",
|
|
41
41
|
"yaml": "^2.7.0",
|
|
42
|
-
"@lxpack/validators": "0.1.
|
|
42
|
+
"@lxpack/validators": "0.1.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^22.13.10",
|