@thanh01.pmt/interactive-quiz-kit 1.0.24 → 1.0.25
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/dist/ai-ecosystem-BJ5RR5Ys.d.ts +228 -0
- package/dist/ai-ecosystem-CL30v1Lg.d.cts +228 -0
- package/dist/ai.cjs +181 -227
- package/dist/ai.d.cts +1881 -0
- package/dist/ai.d.ts +1881 -0
- package/dist/ai.js +181 -227
- package/dist/authoring.cjs +5213 -3105
- package/dist/authoring.d.cts +12 -0
- package/dist/authoring.d.ts +12 -0
- package/dist/authoring.js +4978 -2870
- package/dist/index.cjs +126 -182
- package/dist/index.d.cts +444 -0
- package/dist/index.d.ts +444 -0
- package/dist/index.js +126 -182
- package/dist/player.cjs +1491 -1086
- package/dist/player.d.cts +13 -0
- package/dist/player.d.ts +13 -0
- package/dist/player.js +1424 -1019
- package/dist/quiz-config-1gNNhljP.d.cts +197 -0
- package/dist/quiz-config-1gNNhljP.d.ts +197 -0
- package/dist/react-ui.cjs +7473 -4083
- package/dist/react-ui.d.cts +207 -0
- package/dist/react-ui.d.ts +207 -0
- package/dist/react-ui.js +7054 -3664
- package/dist/toaster-CtnxWhfE.d.ts +133 -0
- package/dist/toaster-DUq851l_.d.cts +133 -0
- package/package.json +13 -11
package/dist/index.cjs
CHANGED
|
@@ -11,26 +11,6 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
11
11
|
|
|
12
12
|
var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
|
|
13
13
|
|
|
14
|
-
var __defProp = Object.defineProperty;
|
|
15
|
-
var __defProps = Object.defineProperties;
|
|
16
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
17
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
18
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
19
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
20
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
21
|
-
var __spreadValues = (a, b) => {
|
|
22
|
-
for (var prop in b || (b = {}))
|
|
23
|
-
if (__hasOwnProp.call(b, prop))
|
|
24
|
-
__defNormalProp(a, prop, b[prop]);
|
|
25
|
-
if (__getOwnPropSymbols)
|
|
26
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
27
|
-
if (__propIsEnum.call(b, prop))
|
|
28
|
-
__defNormalProp(a, prop, b[prop]);
|
|
29
|
-
}
|
|
30
|
-
return a;
|
|
31
|
-
};
|
|
32
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
33
|
-
|
|
34
14
|
// src/services/SCORMService.ts
|
|
35
15
|
var SCORM_TRUE = "true";
|
|
36
16
|
var SCORM_NO_ERROR = "0";
|
|
@@ -50,11 +30,12 @@ var SCORMService = class {
|
|
|
50
30
|
this.isInitialized = false;
|
|
51
31
|
this.isTerminated = false;
|
|
52
32
|
this.studentName = null;
|
|
53
|
-
this.settings =
|
|
33
|
+
this.settings = {
|
|
54
34
|
setCompletionOnFinish: true,
|
|
55
35
|
setSuccessOnPass: true,
|
|
56
|
-
autoCommit: true
|
|
57
|
-
|
|
36
|
+
autoCommit: true,
|
|
37
|
+
...settings
|
|
38
|
+
};
|
|
58
39
|
if (typeof window !== "undefined") {
|
|
59
40
|
this._findAPI();
|
|
60
41
|
}
|
|
@@ -160,14 +141,13 @@ var SCORMService = class {
|
|
|
160
141
|
}
|
|
161
142
|
}
|
|
162
143
|
getValue(element) {
|
|
163
|
-
var _a;
|
|
164
144
|
if (!this.hasAPI() || !this.isInitialized) return null;
|
|
165
145
|
const value = this.scormVersionFound === "2004" ? this.scormAPI.GetValue(element) : this.scormAPI.LMSGetValue(element);
|
|
166
146
|
const error = this.getLastError();
|
|
167
147
|
if (error.code !== SCORM_NO_ERROR && error.code !== "403" && error.code !== "0") {
|
|
168
148
|
console.warn(`SCORMService: GetValue for ${element} produced an error ${error.code}: ${error.message}. Returning raw value:`, value);
|
|
169
149
|
}
|
|
170
|
-
return
|
|
150
|
+
return value?.toString() ?? null;
|
|
171
151
|
}
|
|
172
152
|
commit() {
|
|
173
153
|
if (!this.hasAPI() || !this.isInitialized) {
|
|
@@ -239,7 +219,6 @@ var SCORMService = class {
|
|
|
239
219
|
}
|
|
240
220
|
}
|
|
241
221
|
getLastError() {
|
|
242
|
-
var _a, _b;
|
|
243
222
|
if (!this.hasAPI()) return { code: "-1", message: "SCORM API not found." };
|
|
244
223
|
const errorCode = this.scormVersionFound === "2004" ? this.scormAPI.GetLastError() : this.scormAPI.LMSGetLastError();
|
|
245
224
|
if (errorCode === SCORM_NO_ERROR || errorCode === 0 || errorCode === "0") {
|
|
@@ -249,8 +228,8 @@ var SCORMService = class {
|
|
|
249
228
|
const diagnostic = this.scormVersionFound === "2004" ? this.scormAPI.GetDiagnostic(errorCode.toString()) : this.scormAPI.LMSGetDiagnostic(errorCode.toString());
|
|
250
229
|
return {
|
|
251
230
|
code: errorCode.toString(),
|
|
252
|
-
message:
|
|
253
|
-
diagnostic:
|
|
231
|
+
message: errorMessage?.toString() ?? "Unknown error.",
|
|
232
|
+
diagnostic: diagnostic?.toString() ?? void 0
|
|
254
233
|
};
|
|
255
234
|
}
|
|
256
235
|
formatCMITime(totalSeconds) {
|
|
@@ -281,14 +260,13 @@ var SCORMService = class {
|
|
|
281
260
|
// src/services/evaluators/multiple-choice-evaluator.ts
|
|
282
261
|
var MultipleChoiceEvaluator = class {
|
|
283
262
|
async evaluate(question, answer) {
|
|
284
|
-
|
|
285
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
263
|
+
const points = question.points ?? 0;
|
|
286
264
|
const correctAnswerId = question.correctAnswerId;
|
|
287
265
|
const isCorrect = answer === correctAnswerId;
|
|
288
266
|
const correctOption = question.options.find((opt) => opt.id === correctAnswerId);
|
|
289
267
|
const correctAnswerDetail = {
|
|
290
268
|
id: correctAnswerId,
|
|
291
|
-
value:
|
|
269
|
+
value: correctOption?.text || ""
|
|
292
270
|
};
|
|
293
271
|
return Promise.resolve({
|
|
294
272
|
isCorrect,
|
|
@@ -301,8 +279,7 @@ var MultipleChoiceEvaluator = class {
|
|
|
301
279
|
// src/services/evaluators/multiple-response-evaluator.ts
|
|
302
280
|
var MultipleResponseEvaluator = class {
|
|
303
281
|
async evaluate(question, answer) {
|
|
304
|
-
|
|
305
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
282
|
+
const points = question.points ?? 0;
|
|
306
283
|
const correctAnswerIds = question.correctAnswerIds;
|
|
307
284
|
let isCorrect = false;
|
|
308
285
|
if (Array.isArray(answer)) {
|
|
@@ -311,10 +288,7 @@ var MultipleResponseEvaluator = class {
|
|
|
311
288
|
isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
|
|
312
289
|
}
|
|
313
290
|
const correctValues = correctAnswerIds.map(
|
|
314
|
-
(id) =>
|
|
315
|
-
var _a2;
|
|
316
|
-
return ((_a2 = question.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
|
|
317
|
-
}
|
|
291
|
+
(id) => question.options.find((opt) => opt.id === id)?.text || ""
|
|
318
292
|
);
|
|
319
293
|
const correctAnswerDetail = {
|
|
320
294
|
id: correctAnswerIds,
|
|
@@ -331,8 +305,7 @@ var MultipleResponseEvaluator = class {
|
|
|
331
305
|
// src/services/evaluators/true-false-evaluator.ts
|
|
332
306
|
var TrueFalseEvaluator = class {
|
|
333
307
|
async evaluate(question, answer) {
|
|
334
|
-
|
|
335
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
308
|
+
const points = question.points ?? 0;
|
|
336
309
|
const correctAnswer = question.correctAnswer;
|
|
337
310
|
let userAnswer = answer;
|
|
338
311
|
if (typeof answer === "string") {
|
|
@@ -354,12 +327,11 @@ var TrueFalseEvaluator = class {
|
|
|
354
327
|
// src/services/evaluators/short-answer-evaluator.ts
|
|
355
328
|
var ShortAnswerEvaluator = class {
|
|
356
329
|
async evaluate(question, answer) {
|
|
357
|
-
|
|
358
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
330
|
+
const points = question.points ?? 0;
|
|
359
331
|
let isCorrect = false;
|
|
360
332
|
if (typeof answer === "string") {
|
|
361
333
|
const userAnswerTrimmed = answer.trim();
|
|
362
|
-
const caseSensitive =
|
|
334
|
+
const caseSensitive = question.isCaseSensitive ?? false;
|
|
363
335
|
isCorrect = question.acceptedAnswers.some(
|
|
364
336
|
(accAns) => caseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
|
|
365
337
|
);
|
|
@@ -379,8 +351,7 @@ var ShortAnswerEvaluator = class {
|
|
|
379
351
|
// src/services/evaluators/numeric-evaluator.ts
|
|
380
352
|
var NumericEvaluator = class {
|
|
381
353
|
async evaluate(question, answer) {
|
|
382
|
-
|
|
383
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
354
|
+
const points = question.points ?? 0;
|
|
384
355
|
let isCorrect = false;
|
|
385
356
|
if (typeof answer === "string" || typeof answer === "number") {
|
|
386
357
|
const userAnswerNum = parseFloat(String(answer));
|
|
@@ -403,17 +374,13 @@ var NumericEvaluator = class {
|
|
|
403
374
|
// src/services/evaluators/sequence-evaluator.ts
|
|
404
375
|
var SequenceEvaluator = class {
|
|
405
376
|
async evaluate(question, answer) {
|
|
406
|
-
|
|
407
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
377
|
+
const points = question.points ?? 0;
|
|
408
378
|
let isCorrect = false;
|
|
409
379
|
if (Array.isArray(answer) && answer.length === question.correctOrder.length) {
|
|
410
380
|
isCorrect = answer.every((itemId, index) => itemId === question.correctOrder[index]);
|
|
411
381
|
}
|
|
412
382
|
const correctValues = question.correctOrder.map(
|
|
413
|
-
(id) =>
|
|
414
|
-
var _a2;
|
|
415
|
-
return ((_a2 = question.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
|
|
416
|
-
}
|
|
383
|
+
(id) => question.items.find((item) => item.id === id)?.content || ""
|
|
417
384
|
);
|
|
418
385
|
const correctAnswerDetail = {
|
|
419
386
|
id: question.correctOrder,
|
|
@@ -430,17 +397,15 @@ var SequenceEvaluator = class {
|
|
|
430
397
|
// src/services/evaluators/matching-evaluator.ts
|
|
431
398
|
var MatchingEvaluator = class {
|
|
432
399
|
async evaluate(question, answer) {
|
|
433
|
-
|
|
434
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
400
|
+
const points = question.points ?? 0;
|
|
435
401
|
let isCorrect = false;
|
|
436
402
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
437
403
|
const userAnswerMap = answer;
|
|
438
404
|
isCorrect = question.correctAnswerMap.length === Object.keys(userAnswerMap).length && question.correctAnswerMap.every((map) => userAnswerMap[map.promptId] === map.optionId);
|
|
439
405
|
}
|
|
440
406
|
const correctMap = question.correctAnswerMap.reduce((acc, curr) => {
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
const optionText = ((_b = question.options.find((o) => o.id === curr.optionId)) == null ? void 0 : _b.content) || "";
|
|
407
|
+
const promptText = question.prompts.find((p) => p.id === curr.promptId)?.content || "";
|
|
408
|
+
const optionText = question.options.find((o) => o.id === curr.optionId)?.content || "";
|
|
444
409
|
acc[promptText] = optionText;
|
|
445
410
|
return acc;
|
|
446
411
|
}, {});
|
|
@@ -459,16 +424,14 @@ var MatchingEvaluator = class {
|
|
|
459
424
|
// src/services/evaluators/fill-in-the-blanks-evaluator.ts
|
|
460
425
|
var FillInTheBlanksEvaluator = class {
|
|
461
426
|
async evaluate(question, answer) {
|
|
462
|
-
|
|
463
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
427
|
+
const points = question.points ?? 0;
|
|
464
428
|
let isCorrect = false;
|
|
465
429
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
466
430
|
const userAnswerMap = answer;
|
|
467
431
|
isCorrect = question.answers.length > 0 && question.answers.every((correctAnsDef) => {
|
|
468
|
-
|
|
469
|
-
const userValForBlank = (_a2 = userAnswerMap[correctAnsDef.blankId]) == null ? void 0 : _a2.trim();
|
|
432
|
+
const userValForBlank = userAnswerMap[correctAnsDef.blankId]?.trim();
|
|
470
433
|
if (userValForBlank === void 0) return false;
|
|
471
|
-
const caseSensitive =
|
|
434
|
+
const caseSensitive = question.isCaseSensitive ?? false;
|
|
472
435
|
return correctAnsDef.acceptedValues.some(
|
|
473
436
|
(accVal) => caseSensitive ? accVal.trim() === userValForBlank : accVal.trim().toLowerCase() === userValForBlank.toLowerCase()
|
|
474
437
|
);
|
|
@@ -493,17 +456,15 @@ var FillInTheBlanksEvaluator = class {
|
|
|
493
456
|
// src/services/evaluators/drag-and-drop-evaluator.ts
|
|
494
457
|
var DragAndDropEvaluator = class {
|
|
495
458
|
async evaluate(question, answer) {
|
|
496
|
-
|
|
497
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
459
|
+
const points = question.points ?? 0;
|
|
498
460
|
let isCorrect = false;
|
|
499
461
|
if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
|
|
500
462
|
const userAnswerMap = answer;
|
|
501
463
|
isCorrect = question.answerMap.length === Object.keys(userAnswerMap).length && question.answerMap.every((map) => userAnswerMap[map.draggableId] === map.dropZoneId);
|
|
502
464
|
}
|
|
503
465
|
const correctMap = question.answerMap.reduce((acc, curr) => {
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
const dropZoneText = ((_b = question.dropZones.find((z4) => z4.id === curr.dropZoneId)) == null ? void 0 : _b.label) || "";
|
|
466
|
+
const draggableText = question.draggableItems.find((d) => d.id === curr.draggableId)?.content || "";
|
|
467
|
+
const dropZoneText = question.dropZones.find((z4) => z4.id === curr.dropZoneId)?.label || "";
|
|
507
468
|
acc[draggableText] = dropZoneText;
|
|
508
469
|
return acc;
|
|
509
470
|
}, {});
|
|
@@ -522,8 +483,7 @@ var DragAndDropEvaluator = class {
|
|
|
522
483
|
// src/services/evaluators/hotspot-evaluator.ts
|
|
523
484
|
var HotspotEvaluator = class {
|
|
524
485
|
async evaluate(question, answer) {
|
|
525
|
-
|
|
526
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
486
|
+
const points = question.points ?? 0;
|
|
527
487
|
let isCorrect = false;
|
|
528
488
|
if (Array.isArray(answer)) {
|
|
529
489
|
const userAnswerSet = new Set(answer);
|
|
@@ -531,10 +491,7 @@ var HotspotEvaluator = class {
|
|
|
531
491
|
isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
|
|
532
492
|
}
|
|
533
493
|
const correctValues = question.correctHotspotIds.map(
|
|
534
|
-
(id) =>
|
|
535
|
-
var _a2;
|
|
536
|
-
return ((_a2 = question.hotspots.find((h) => h.id === id)) == null ? void 0 : _a2.description) || id;
|
|
537
|
-
}
|
|
494
|
+
(id) => question.hotspots.find((h) => h.id === id)?.description || id
|
|
538
495
|
);
|
|
539
496
|
const correctAnswerDetail = {
|
|
540
497
|
id: question.correctHotspotIds,
|
|
@@ -551,11 +508,10 @@ var HotspotEvaluator = class {
|
|
|
551
508
|
// src/services/evaluators/programming-evaluator.ts
|
|
552
509
|
var ProgrammingEvaluator = class {
|
|
553
510
|
async evaluate(question, answer) {
|
|
554
|
-
|
|
555
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
511
|
+
const points = question.points ?? 0;
|
|
556
512
|
let isCorrect = false;
|
|
557
513
|
if (typeof answer === "string" && typeof question.solutionGeneratedCode === "string") {
|
|
558
|
-
if (typeof window !== "undefined" &&
|
|
514
|
+
if (typeof window !== "undefined" && window.Blockly?.JavaScript) {
|
|
559
515
|
const LocalBlockly = window.Blockly;
|
|
560
516
|
let generatedUserCode = "";
|
|
561
517
|
try {
|
|
@@ -629,10 +585,10 @@ var JsonRepairEngine = class {
|
|
|
629
585
|
if (breakIndex !== -1) {
|
|
630
586
|
const stringContent = afterUnterminated.substring(0, breakIndex);
|
|
631
587
|
const remainder = afterUnterminated.substring(breakIndex);
|
|
632
|
-
const escapedContent = stringContent.replace(
|
|
588
|
+
const escapedContent = stringContent.replace(/(?<!\\)"/g, '\\"');
|
|
633
589
|
repaired = beforeUnterminated + escapedContent + '"' + remainder;
|
|
634
590
|
} else {
|
|
635
|
-
const escapedContent = afterUnterminated.replace(
|
|
591
|
+
const escapedContent = afterUnterminated.replace(/(?<!\\)"/g, '\\"');
|
|
636
592
|
repaired = beforeUnterminated + escapedContent + '"';
|
|
637
593
|
}
|
|
638
594
|
}
|
|
@@ -708,7 +664,6 @@ var JsonRepairEngine = class {
|
|
|
708
664
|
* Main repair function that attempts multiple strategies.
|
|
709
665
|
*/
|
|
710
666
|
static repairJson(jsonStr) {
|
|
711
|
-
var _a;
|
|
712
667
|
let current = jsonStr.trim();
|
|
713
668
|
const maxAttempts = 5;
|
|
714
669
|
let lastError = "";
|
|
@@ -738,7 +693,7 @@ var JsonRepairEngine = class {
|
|
|
738
693
|
}
|
|
739
694
|
lastError = validation.error || "";
|
|
740
695
|
lastPosition = validation.position;
|
|
741
|
-
if (
|
|
696
|
+
if (validation.error?.includes("Unterminated string")) {
|
|
742
697
|
current = this.repairUnterminatedStrings(current);
|
|
743
698
|
} else {
|
|
744
699
|
current = this.applyCommonFixes(current);
|
|
@@ -1033,9 +988,10 @@ var CodeEvaluationService = class {
|
|
|
1033
988
|
userCode,
|
|
1034
989
|
testCase
|
|
1035
990
|
}, this.apiKey);
|
|
1036
|
-
return
|
|
1037
|
-
testCaseId: testCase.id
|
|
1038
|
-
|
|
991
|
+
return {
|
|
992
|
+
testCaseId: testCase.id,
|
|
993
|
+
...aiResult
|
|
994
|
+
};
|
|
1039
995
|
}
|
|
1040
996
|
/**
|
|
1041
997
|
* Evaluates user's code against all test cases for a given question.
|
|
@@ -1072,8 +1028,7 @@ var CodeEvaluationService = class {
|
|
|
1072
1028
|
// src/services/evaluators/coding-evaluator.ts
|
|
1073
1029
|
var CodingEvaluator = class {
|
|
1074
1030
|
async evaluate(question, answer) {
|
|
1075
|
-
|
|
1076
|
-
const points = (_a = question.points) != null ? _a : 0;
|
|
1031
|
+
const points = question.points ?? 0;
|
|
1077
1032
|
if (typeof answer !== "string" || !answer.trim()) {
|
|
1078
1033
|
return {
|
|
1079
1034
|
isCorrect: false,
|
|
@@ -1130,17 +1085,16 @@ var QuizEngine = class {
|
|
|
1130
1085
|
this.quizResultState = { scormStatus: "idle" };
|
|
1131
1086
|
this.questionStartTime = null;
|
|
1132
1087
|
this.questionTimings = /* @__PURE__ */ new Map();
|
|
1133
|
-
var _a, _b, _c, _d, _e;
|
|
1134
1088
|
this.config = options.config;
|
|
1135
1089
|
this.callbacks = options.callbacks || {};
|
|
1136
|
-
this.questions =
|
|
1090
|
+
this.questions = this.config.settings?.shuffleQuestions ? [...this.config.questions].sort(() => Math.random() - 0.5) : this.config.questions;
|
|
1137
1091
|
this.overallStartTime = Date.now();
|
|
1138
1092
|
this.evaluators = /* @__PURE__ */ new Map();
|
|
1139
1093
|
this.registerEvaluators();
|
|
1140
|
-
if (
|
|
1094
|
+
if (this.config.settings?.timeLimitMinutes && this.config.settings.timeLimitMinutes > 0) {
|
|
1141
1095
|
this.timeLeftInSeconds = this.config.settings.timeLimitMinutes * 60;
|
|
1142
1096
|
}
|
|
1143
|
-
if (
|
|
1097
|
+
if (this.config.settings?.scorm) {
|
|
1144
1098
|
this.quizResultState.scormStatus = "initializing";
|
|
1145
1099
|
this.scormService = new SCORMService(this.config.settings.scorm);
|
|
1146
1100
|
if (this.scormService.hasAPI()) {
|
|
@@ -1173,7 +1127,7 @@ var QuizEngine = class {
|
|
|
1173
1127
|
if (this.timeLeftInSeconds !== null) {
|
|
1174
1128
|
this.startTimer();
|
|
1175
1129
|
}
|
|
1176
|
-
|
|
1130
|
+
this.callbacks.onQuestionChange?.(initialQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1177
1131
|
}
|
|
1178
1132
|
registerEvaluators() {
|
|
1179
1133
|
this.evaluators.set("multiple_choice", new MultipleChoiceEvaluator());
|
|
@@ -1211,15 +1165,14 @@ var QuizEngine = class {
|
|
|
1211
1165
|
}
|
|
1212
1166
|
}
|
|
1213
1167
|
handleTick() {
|
|
1214
|
-
var _a, _b, _c, _d;
|
|
1215
1168
|
if (this.timeLeftInSeconds === null) return;
|
|
1216
1169
|
if (this.timeLeftInSeconds > 0) {
|
|
1217
1170
|
this.timeLeftInSeconds--;
|
|
1218
|
-
|
|
1171
|
+
this.callbacks.onTimeTick?.(this.timeLeftInSeconds);
|
|
1219
1172
|
}
|
|
1220
1173
|
if (this.timeLeftInSeconds <= 0) {
|
|
1221
1174
|
this.stopTimer();
|
|
1222
|
-
|
|
1175
|
+
this.callbacks.onQuizTimeUp?.();
|
|
1223
1176
|
this.calculateResults();
|
|
1224
1177
|
}
|
|
1225
1178
|
}
|
|
@@ -1242,43 +1195,39 @@ var QuizEngine = class {
|
|
|
1242
1195
|
return this.quizResultState.score !== void 0;
|
|
1243
1196
|
}
|
|
1244
1197
|
submitAnswer(questionId, answer) {
|
|
1245
|
-
var _a, _b;
|
|
1246
1198
|
this.userAnswers.set(questionId, answer);
|
|
1247
1199
|
const question = this.questions.find((q) => q.id === questionId);
|
|
1248
|
-
if (question)
|
|
1200
|
+
if (question) this.callbacks.onAnswerSubmit?.(question, answer);
|
|
1249
1201
|
}
|
|
1250
1202
|
nextQuestion() {
|
|
1251
|
-
var _a, _b;
|
|
1252
1203
|
this._recordCurrentQuestionTime();
|
|
1253
1204
|
if (this.currentQuestionIndex < this.questions.length - 1) {
|
|
1254
1205
|
this.currentQuestionIndex++;
|
|
1255
1206
|
const currentQ = this.getCurrentQuestion();
|
|
1256
1207
|
this.questionStartTime = Date.now();
|
|
1257
|
-
|
|
1208
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1258
1209
|
return currentQ;
|
|
1259
1210
|
}
|
|
1260
1211
|
return null;
|
|
1261
1212
|
}
|
|
1262
1213
|
previousQuestion() {
|
|
1263
|
-
var _a, _b;
|
|
1264
1214
|
this._recordCurrentQuestionTime();
|
|
1265
1215
|
if (this.currentQuestionIndex > 0) {
|
|
1266
1216
|
this.currentQuestionIndex--;
|
|
1267
1217
|
const currentQ = this.getCurrentQuestion();
|
|
1268
1218
|
this.questionStartTime = Date.now();
|
|
1269
|
-
|
|
1219
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1270
1220
|
return currentQ;
|
|
1271
1221
|
}
|
|
1272
1222
|
return null;
|
|
1273
1223
|
}
|
|
1274
1224
|
goToQuestion(index) {
|
|
1275
|
-
var _a, _b;
|
|
1276
1225
|
if (index >= 0 && index < this.questions.length && index !== this.currentQuestionIndex) {
|
|
1277
1226
|
this._recordCurrentQuestionTime();
|
|
1278
1227
|
this.currentQuestionIndex = index;
|
|
1279
1228
|
const currentQ = this.getCurrentQuestion();
|
|
1280
1229
|
this.questionStartTime = Date.now();
|
|
1281
|
-
|
|
1230
|
+
this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
|
|
1282
1231
|
return currentQ;
|
|
1283
1232
|
}
|
|
1284
1233
|
return this.getCurrentQuestion();
|
|
@@ -1304,7 +1253,6 @@ var QuizEngine = class {
|
|
|
1304
1253
|
}
|
|
1305
1254
|
// (Tiếp theo từ Phần 1)
|
|
1306
1255
|
async calculateResults() {
|
|
1307
|
-
var _a, _b, _c, _d, _e;
|
|
1308
1256
|
this.stopTimer();
|
|
1309
1257
|
this._recordCurrentQuestionTime();
|
|
1310
1258
|
let totalScore = 0;
|
|
@@ -1313,7 +1261,7 @@ var QuizEngine = class {
|
|
|
1313
1261
|
let accumulatedTotalTimeSpent = 0;
|
|
1314
1262
|
for (const question of this.questions) {
|
|
1315
1263
|
const userAnswerRaw = this.userAnswers.get(question.id) || null;
|
|
1316
|
-
maxScore +=
|
|
1264
|
+
maxScore += question.points ?? 0;
|
|
1317
1265
|
const evaluator = this.evaluators.get(question.questionType);
|
|
1318
1266
|
if (!evaluator) {
|
|
1319
1267
|
console.warn(`No evaluator found for question type: ${question.questionType}`);
|
|
@@ -1353,13 +1301,13 @@ var QuizEngine = class {
|
|
|
1353
1301
|
}
|
|
1354
1302
|
const percentage = maxScore > 0 ? parseFloat((totalScore / maxScore * 100).toFixed(2)) : 0;
|
|
1355
1303
|
let passed = void 0;
|
|
1356
|
-
if (
|
|
1304
|
+
if (this.config.settings?.passingScorePercent != null) {
|
|
1357
1305
|
passed = percentage >= this.config.settings.passingScorePercent;
|
|
1358
1306
|
}
|
|
1359
1307
|
const totalQuizTimeSpentSeconds = parseFloat(accumulatedTotalTimeSpent.toFixed(2));
|
|
1360
1308
|
const averageTimePerQuestionSeconds = this.questions.length > 0 ? parseFloat((totalQuizTimeSpentSeconds / this.questions.length).toFixed(2)) : 0;
|
|
1361
1309
|
const metadataPerformance = await this._calculateMetadataPerformance();
|
|
1362
|
-
const finalResults =
|
|
1310
|
+
const finalResults = {
|
|
1363
1311
|
score: totalScore,
|
|
1364
1312
|
maxScore,
|
|
1365
1313
|
percentage,
|
|
@@ -1371,39 +1319,33 @@ var QuizEngine = class {
|
|
|
1371
1319
|
scormError: this.quizResultState.scormError,
|
|
1372
1320
|
studentName: this.quizResultState.studentName,
|
|
1373
1321
|
totalTimeSpentSeconds: totalQuizTimeSpentSeconds,
|
|
1374
|
-
averageTimePerQuestionSeconds
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1322
|
+
averageTimePerQuestionSeconds,
|
|
1323
|
+
...metadataPerformance
|
|
1324
|
+
};
|
|
1325
|
+
this.quizResultState = { ...this.quizResultState, ...finalResults };
|
|
1326
|
+
if (this.config.settings?.scorm) this._sendResultsToSCORM(finalResults);
|
|
1378
1327
|
await this._sendResultsToWebhook(finalResults);
|
|
1379
|
-
|
|
1328
|
+
this.callbacks.onQuizFinish?.(finalResults);
|
|
1380
1329
|
return finalResults;
|
|
1381
1330
|
}
|
|
1382
1331
|
formatUserAnswerDetail(question, userAnswerRaw) {
|
|
1383
|
-
var _a, _b, _c, _d, _e;
|
|
1384
1332
|
if (userAnswerRaw === null) return null;
|
|
1385
1333
|
switch (question.questionType) {
|
|
1386
1334
|
case "multiple_choice": {
|
|
1387
1335
|
const q = question;
|
|
1388
1336
|
const id = userAnswerRaw;
|
|
1389
|
-
return { id, value:
|
|
1337
|
+
return { id, value: q.options.find((opt) => opt.id === id)?.text || "" };
|
|
1390
1338
|
}
|
|
1391
1339
|
case "multiple_response": {
|
|
1392
1340
|
const q = question;
|
|
1393
1341
|
const ids = userAnswerRaw;
|
|
1394
|
-
const values = ids.map((id) =>
|
|
1395
|
-
var _a2;
|
|
1396
|
-
return ((_a2 = q.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
|
|
1397
|
-
});
|
|
1342
|
+
const values = ids.map((id) => q.options.find((opt) => opt.id === id)?.text || "");
|
|
1398
1343
|
return { id: ids, value: values };
|
|
1399
1344
|
}
|
|
1400
1345
|
case "sequence": {
|
|
1401
1346
|
const q = question;
|
|
1402
1347
|
const ids = userAnswerRaw;
|
|
1403
|
-
const values = ids.map((id) =>
|
|
1404
|
-
var _a2;
|
|
1405
|
-
return ((_a2 = q.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
|
|
1406
|
-
});
|
|
1348
|
+
const values = ids.map((id) => q.items.find((item) => item.id === id)?.content || "");
|
|
1407
1349
|
return { id: ids, value: values };
|
|
1408
1350
|
}
|
|
1409
1351
|
case "matching": {
|
|
@@ -1412,8 +1354,8 @@ var QuizEngine = class {
|
|
|
1412
1354
|
const valueMap = {};
|
|
1413
1355
|
for (const promptId in userAnswerMap) {
|
|
1414
1356
|
const optionId = userAnswerMap[promptId];
|
|
1415
|
-
const promptText =
|
|
1416
|
-
const optionText =
|
|
1357
|
+
const promptText = q.prompts.find((p) => p.id === promptId)?.content || "";
|
|
1358
|
+
const optionText = q.options.find((o) => o.id === optionId)?.content || "";
|
|
1417
1359
|
valueMap[promptText] = optionText;
|
|
1418
1360
|
}
|
|
1419
1361
|
return { id: null, value: valueMap };
|
|
@@ -1425,8 +1367,8 @@ var QuizEngine = class {
|
|
|
1425
1367
|
const enrichedUserAnswerMap = {};
|
|
1426
1368
|
for (const draggableId in userAnswerMapByIds) {
|
|
1427
1369
|
const dropZoneId = userAnswerMapByIds[draggableId];
|
|
1428
|
-
const draggableText =
|
|
1429
|
-
const dropZoneText =
|
|
1370
|
+
const draggableText = q.draggableItems.find((d) => d.id === draggableId)?.content || `(ID: ${draggableId})`;
|
|
1371
|
+
const dropZoneText = q.dropZones.find((z4) => z4.id === dropZoneId)?.label || `(ID: ${dropZoneId})`;
|
|
1430
1372
|
enrichedUserAnswerMap[draggableText] = dropZoneText;
|
|
1431
1373
|
}
|
|
1432
1374
|
return { id: null, value: enrichedUserAnswerMap };
|
|
@@ -1438,7 +1380,6 @@ var QuizEngine = class {
|
|
|
1438
1380
|
}
|
|
1439
1381
|
}
|
|
1440
1382
|
async _calculateMetadataPerformance() {
|
|
1441
|
-
var _a;
|
|
1442
1383
|
const loPerformanceMap = /* @__PURE__ */ new Map();
|
|
1443
1384
|
const categoryPerformanceMap = /* @__PURE__ */ new Map();
|
|
1444
1385
|
const topicPerformanceMap = /* @__PURE__ */ new Map();
|
|
@@ -1460,7 +1401,7 @@ var QuizEngine = class {
|
|
|
1460
1401
|
const evaluator = this.evaluators.get(q.questionType);
|
|
1461
1402
|
if (evaluator) {
|
|
1462
1403
|
const { isCorrect } = await evaluator.evaluate(q, userAnswer);
|
|
1463
|
-
const pointsForThisQuestion =
|
|
1404
|
+
const pointsForThisQuestion = q.points ?? 0;
|
|
1464
1405
|
updateMap(loPerformanceMap, q.learningObjective, pointsForThisQuestion, isCorrect);
|
|
1465
1406
|
updateMap(categoryPerformanceMap, q.category, pointsForThisQuestion, isCorrect);
|
|
1466
1407
|
updateMap(topicPerformanceMap, q.topic, pointsForThisQuestion, isCorrect);
|
|
@@ -1487,8 +1428,7 @@ var QuizEngine = class {
|
|
|
1487
1428
|
};
|
|
1488
1429
|
}
|
|
1489
1430
|
async _sendResultsToWebhook(results) {
|
|
1490
|
-
|
|
1491
|
-
if (!((_a = this.config.settings) == null ? void 0 : _a.webhookUrl)) {
|
|
1431
|
+
if (!this.config.settings?.webhookUrl) {
|
|
1492
1432
|
results.webhookStatus = "idle";
|
|
1493
1433
|
return;
|
|
1494
1434
|
}
|
|
@@ -1516,12 +1456,11 @@ var QuizEngine = class {
|
|
|
1516
1456
|
}
|
|
1517
1457
|
}
|
|
1518
1458
|
_sendResultsToSCORM(results) {
|
|
1519
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1520
1459
|
if (!this.scormService || !this.scormService.hasAPI() || this.quizResultState.scormStatus === "no_api") {
|
|
1521
1460
|
results.scormStatus = this.quizResultState.scormStatus || "idle";
|
|
1522
1461
|
return;
|
|
1523
1462
|
}
|
|
1524
|
-
if (this.quizResultState.scormStatus === "error" &&
|
|
1463
|
+
if (this.quizResultState.scormStatus === "error" && this.quizResultState.scormError?.includes("initialization failed")) {
|
|
1525
1464
|
results.scormStatus = "error";
|
|
1526
1465
|
results.scormError = this.quizResultState.scormError;
|
|
1527
1466
|
return;
|
|
@@ -1530,15 +1469,15 @@ var QuizEngine = class {
|
|
|
1530
1469
|
try {
|
|
1531
1470
|
this.scormService.setScore(results.score, results.maxScore, 0);
|
|
1532
1471
|
let lessonStatusSetting = "completed";
|
|
1533
|
-
if (
|
|
1472
|
+
if (this.config.settings?.passingScorePercent !== void 0 && this.config.settings?.passingScorePercent !== null) {
|
|
1534
1473
|
lessonStatusSetting = results.passed ? "passed" : "failed";
|
|
1535
|
-
} else if (
|
|
1474
|
+
} else if (this.config.settings?.scorm?.setCompletionOnFinish) {
|
|
1536
1475
|
lessonStatusSetting = "completed";
|
|
1537
1476
|
}
|
|
1538
1477
|
this.scormService.setLessonStatus(lessonStatusSetting, results.passed);
|
|
1539
1478
|
if (results.totalTimeSpentSeconds !== void 0 && this.scormService.formatCMITime) {
|
|
1540
1479
|
const cmiTime = this.scormService.formatCMITime(results.totalTimeSpentSeconds);
|
|
1541
|
-
const sessionTimeVar =
|
|
1480
|
+
const sessionTimeVar = this.config.settings?.scorm?.sessionTimeVar || (this.scormService.getSCORMVersion() === "2004" ? "cmi.session_time" : "cmi.core.session_time");
|
|
1542
1481
|
if (sessionTimeVar) this.scormService.setValue(sessionTimeVar, cmiTime);
|
|
1543
1482
|
}
|
|
1544
1483
|
const commitResult = this.scormService.commit();
|
|
@@ -1578,18 +1517,19 @@ var QuizEditorService = class {
|
|
|
1578
1517
|
};
|
|
1579
1518
|
switch (type) {
|
|
1580
1519
|
case "true_false":
|
|
1581
|
-
return
|
|
1520
|
+
return { ...baseNewQuestion, questionType: "true_false", correctAnswer: true };
|
|
1582
1521
|
case "multiple_choice":
|
|
1583
|
-
return
|
|
1522
|
+
return { ...baseNewQuestion, questionType: "multiple_choice", options: [], correctAnswerId: "" };
|
|
1584
1523
|
case "multiple_response":
|
|
1585
|
-
return
|
|
1524
|
+
return { ...baseNewQuestion, questionType: "multiple_response", options: [], correctAnswerIds: [] };
|
|
1586
1525
|
case "short_answer":
|
|
1587
|
-
return
|
|
1526
|
+
return { ...baseNewQuestion, questionType: "short_answer", acceptedAnswers: [""], isCaseSensitive: false };
|
|
1588
1527
|
case "numeric":
|
|
1589
|
-
return
|
|
1528
|
+
return { ...baseNewQuestion, questionType: "numeric", answer: 0 };
|
|
1590
1529
|
case "fill_in_the_blanks": {
|
|
1591
1530
|
const blankId = generateUniqueId("blank_");
|
|
1592
|
-
return
|
|
1531
|
+
return {
|
|
1532
|
+
...baseNewQuestion,
|
|
1593
1533
|
questionType: "fill_in_the_blanks",
|
|
1594
1534
|
segments: [
|
|
1595
1535
|
{ type: "text", content: "Your text before " },
|
|
@@ -1598,49 +1538,52 @@ var QuizEditorService = class {
|
|
|
1598
1538
|
],
|
|
1599
1539
|
answers: [{ blankId, acceptedValues: [""] }],
|
|
1600
1540
|
isCaseSensitive: false
|
|
1601
|
-
}
|
|
1541
|
+
};
|
|
1602
1542
|
}
|
|
1603
1543
|
case "sequence":
|
|
1604
|
-
return
|
|
1544
|
+
return { ...baseNewQuestion, questionType: "sequence", items: [], correctOrder: [] };
|
|
1605
1545
|
case "matching":
|
|
1606
|
-
return
|
|
1546
|
+
return { ...baseNewQuestion, questionType: "matching", prompts: [], options: [], correctAnswerMap: [], shuffleOptions: true };
|
|
1607
1547
|
case "drag_and_drop":
|
|
1608
|
-
return
|
|
1548
|
+
return { ...baseNewQuestion, questionType: "drag_and_drop", draggableItems: [], dropZones: [], answerMap: [] };
|
|
1609
1549
|
case "hotspot":
|
|
1610
|
-
return
|
|
1550
|
+
return { ...baseNewQuestion, questionType: "hotspot", imageUrl: "", hotspots: [], correctHotspotIds: [] };
|
|
1611
1551
|
case "blockly_programming":
|
|
1612
|
-
return
|
|
1552
|
+
return {
|
|
1553
|
+
...baseNewQuestion,
|
|
1613
1554
|
questionType: "blockly_programming",
|
|
1614
1555
|
toolboxDefinition: '<xml xmlns="https://developers.google.com/blockly/xml"></xml>',
|
|
1615
1556
|
initialWorkspace: "",
|
|
1616
1557
|
solutionWorkspaceXML: "",
|
|
1617
1558
|
solutionGeneratedCode: ""
|
|
1618
|
-
}
|
|
1559
|
+
};
|
|
1619
1560
|
case "scratch_programming":
|
|
1620
|
-
return
|
|
1561
|
+
return {
|
|
1562
|
+
...baseNewQuestion,
|
|
1621
1563
|
questionType: "scratch_programming",
|
|
1622
1564
|
toolboxDefinition: '<xml xmlns="https://developers.google.com/blockly/xml"></xml>',
|
|
1623
1565
|
initialWorkspace: "",
|
|
1624
1566
|
solutionWorkspaceXML: "",
|
|
1625
1567
|
solutionGeneratedCode: ""
|
|
1626
|
-
}
|
|
1568
|
+
};
|
|
1627
1569
|
case "coding":
|
|
1628
|
-
return
|
|
1570
|
+
return {
|
|
1571
|
+
...baseNewQuestion,
|
|
1629
1572
|
questionType: "coding",
|
|
1630
|
-
|
|
1573
|
+
codingLanguage: "javascript",
|
|
1631
1574
|
solutionCode: "",
|
|
1632
1575
|
testCases: [],
|
|
1633
1576
|
functionSignature: "",
|
|
1634
1577
|
points: 25
|
|
1635
1578
|
// Coding questions are worth more by default
|
|
1636
|
-
}
|
|
1579
|
+
};
|
|
1637
1580
|
default:
|
|
1638
1581
|
const _exhaustiveCheck = type;
|
|
1639
1582
|
throw new Error(`Question type "${_exhaustiveCheck}" is not supported for creation.`);
|
|
1640
1583
|
}
|
|
1641
1584
|
}
|
|
1642
1585
|
addQuestion(question) {
|
|
1643
|
-
const newQuestion =
|
|
1586
|
+
const newQuestion = { ...question };
|
|
1644
1587
|
if (newQuestion.id.startsWith("new_")) {
|
|
1645
1588
|
newQuestion.id = generateUniqueId(`${newQuestion.questionType}_`);
|
|
1646
1589
|
}
|
|
@@ -1754,8 +1697,7 @@ var QuestionImportService = class {
|
|
|
1754
1697
|
const values = line.split(" ");
|
|
1755
1698
|
const rowObject = {};
|
|
1756
1699
|
header.forEach((h, i) => {
|
|
1757
|
-
|
|
1758
|
-
rowObject[h] = ((_a = values[i]) == null ? void 0 : _a.trim()) || "";
|
|
1700
|
+
rowObject[h] = values[i]?.trim() || "";
|
|
1759
1701
|
});
|
|
1760
1702
|
try {
|
|
1761
1703
|
const transformedObject = this.transformTsvRowToRawObject(rowObject);
|
|
@@ -1797,17 +1739,17 @@ var QuestionImportService = class {
|
|
|
1797
1739
|
};
|
|
1798
1740
|
switch (questionType) {
|
|
1799
1741
|
case "multiple_choice":
|
|
1800
|
-
return
|
|
1742
|
+
return { ...base, options: options.split("|"), correctAnswer };
|
|
1801
1743
|
case "multiple_response":
|
|
1802
|
-
return
|
|
1744
|
+
return { ...base, options: options.split("|"), correctAnswers: correctAnswer.split("|") };
|
|
1803
1745
|
case "true_false":
|
|
1804
|
-
return
|
|
1746
|
+
return { ...base, correctAnswer: correctAnswer.toLowerCase() === "true" };
|
|
1805
1747
|
case "short_answer":
|
|
1806
|
-
return
|
|
1748
|
+
return { ...base, acceptedAnswers: correctAnswer.split("|") };
|
|
1807
1749
|
case "numeric":
|
|
1808
|
-
return
|
|
1750
|
+
return { ...base, answer: parseFloat(correctAnswer), tolerance: tolerance ? parseFloat(tolerance) : void 0 };
|
|
1809
1751
|
case "sequence":
|
|
1810
|
-
return
|
|
1752
|
+
return { ...base, items: options.split("|"), correctOrder: correctAnswer.split("|") };
|
|
1811
1753
|
case "matching": {
|
|
1812
1754
|
const [promptsStr, optionsStr] = options.split("#");
|
|
1813
1755
|
const prompts = promptsStr.replace("prompts:", "").split("|");
|
|
@@ -1817,7 +1759,7 @@ var QuestionImportService = class {
|
|
|
1817
1759
|
acc[key] = valParts.join(":");
|
|
1818
1760
|
return acc;
|
|
1819
1761
|
}, {});
|
|
1820
|
-
return
|
|
1762
|
+
return { ...base, prompts, options: opts, correctAnswerMap };
|
|
1821
1763
|
}
|
|
1822
1764
|
case "fill_in_the_blanks": {
|
|
1823
1765
|
const blanks = correctAnswer.split("#").reduce((acc, part) => {
|
|
@@ -1825,7 +1767,7 @@ var QuestionImportService = class {
|
|
|
1825
1767
|
acc[key] = valuesStr.split("|");
|
|
1826
1768
|
return acc;
|
|
1827
1769
|
}, {});
|
|
1828
|
-
return
|
|
1770
|
+
return { ...base, sentenceWithPlaceholders: options, blanks };
|
|
1829
1771
|
}
|
|
1830
1772
|
default:
|
|
1831
1773
|
throw new Error(`Unsupported questionType "${questionType}" in TSV.`);
|
|
@@ -1846,20 +1788,20 @@ var QuestionImportService = class {
|
|
|
1846
1788
|
const options = validatedRawQ.options.map((text) => ({ id: generateUniqueId("opt_"), text }));
|
|
1847
1789
|
const correctOption = options.find((opt) => opt.text === validatedRawQ.correctAnswer);
|
|
1848
1790
|
if (!correctOption) throw new Error(`Correct answer "${validatedRawQ.correctAnswer}" not found in options.`);
|
|
1849
|
-
return
|
|
1791
|
+
return { ...baseQuestionData, questionType: "multiple_choice", options, correctAnswerId: correctOption.id };
|
|
1850
1792
|
}
|
|
1851
1793
|
case "multiple_response": {
|
|
1852
1794
|
const options = validatedRawQ.options.map((text) => ({ id: generateUniqueId("opt_mr_"), text }));
|
|
1853
1795
|
const correctIds = options.filter((opt) => validatedRawQ.correctAnswers.includes(opt.text)).map((opt) => opt.id);
|
|
1854
1796
|
if (correctIds.length !== validatedRawQ.correctAnswers.length) throw new Error("Some correct answers were not found in options.");
|
|
1855
|
-
return
|
|
1797
|
+
return { ...baseQuestionData, questionType: "multiple_response", options, correctAnswerIds: correctIds };
|
|
1856
1798
|
}
|
|
1857
1799
|
case "true_false":
|
|
1858
|
-
return
|
|
1800
|
+
return { ...baseQuestionData, questionType: "true_false", correctAnswer: validatedRawQ.correctAnswer };
|
|
1859
1801
|
case "short_answer":
|
|
1860
|
-
return
|
|
1802
|
+
return { ...baseQuestionData, questionType: "short_answer", acceptedAnswers: validatedRawQ.acceptedAnswers, isCaseSensitive: false };
|
|
1861
1803
|
case "numeric":
|
|
1862
|
-
return
|
|
1804
|
+
return { ...baseQuestionData, questionType: "numeric", answer: validatedRawQ.answer, tolerance: validatedRawQ.tolerance };
|
|
1863
1805
|
case "sequence": {
|
|
1864
1806
|
if (validatedRawQ.items.length !== validatedRawQ.correctOrder.length) {
|
|
1865
1807
|
throw new Error("The number of items must match the number of items in the correct order for a sequence question.");
|
|
@@ -1870,7 +1812,7 @@ var QuestionImportService = class {
|
|
|
1870
1812
|
if (!foundItem) throw new Error(`Sequence item "${orderText}" in correctOrder not found in items list.`);
|
|
1871
1813
|
return foundItem.id;
|
|
1872
1814
|
});
|
|
1873
|
-
return
|
|
1815
|
+
return { ...baseQuestionData, questionType: "sequence", items, correctOrder };
|
|
1874
1816
|
}
|
|
1875
1817
|
case "matching": {
|
|
1876
1818
|
if (validatedRawQ.prompts.length !== Object.keys(validatedRawQ.correctAnswerMap).length) {
|
|
@@ -1884,7 +1826,7 @@ var QuestionImportService = class {
|
|
|
1884
1826
|
if (!prompt || !option) throw new Error(`Matching pair "${promptText}":"${optionText}" not found in prompts/options.`);
|
|
1885
1827
|
return { promptId: prompt.id, optionId: option.id };
|
|
1886
1828
|
});
|
|
1887
|
-
return
|
|
1829
|
+
return { ...baseQuestionData, questionType: "matching", prompts, options, correctAnswerMap, shuffleOptions: true };
|
|
1888
1830
|
}
|
|
1889
1831
|
case "fill_in_the_blanks": {
|
|
1890
1832
|
const { sentenceWithPlaceholders, blanks } = validatedRawQ;
|
|
@@ -1912,7 +1854,7 @@ var QuestionImportService = class {
|
|
|
1912
1854
|
if (lastIndex < sentenceWithPlaceholders.length) {
|
|
1913
1855
|
segments.push({ type: "text", content: sentenceWithPlaceholders.substring(lastIndex) });
|
|
1914
1856
|
}
|
|
1915
|
-
return
|
|
1857
|
+
return { ...baseQuestionData, questionType: "fill_in_the_blanks", segments, answers, isCaseSensitive: false };
|
|
1916
1858
|
}
|
|
1917
1859
|
}
|
|
1918
1860
|
throw new Error(`Unhandled question type in createQuestionFromRawObject: ${validatedRawQ.questionType}`);
|
|
@@ -1973,8 +1915,7 @@ var UserConfigService = class {
|
|
|
1973
1915
|
this.setConfig("weeklyGoal", goal);
|
|
1974
1916
|
}
|
|
1975
1917
|
static getLanguage() {
|
|
1976
|
-
|
|
1977
|
-
return (_a = this.getConfig("language", "en")) != null ? _a : "en";
|
|
1918
|
+
return this.getConfig("language", "en") ?? "en";
|
|
1978
1919
|
}
|
|
1979
1920
|
static setLanguage(language) {
|
|
1980
1921
|
this.setConfig("language", language);
|
|
@@ -1992,10 +1933,11 @@ var UserConfigService = class {
|
|
|
1992
1933
|
*/
|
|
1993
1934
|
static addGoal(newGoal) {
|
|
1994
1935
|
const goals = this.getGoals();
|
|
1995
|
-
const goalToAdd =
|
|
1936
|
+
const goalToAdd = {
|
|
1937
|
+
...newGoal,
|
|
1996
1938
|
id: generateUniqueId("goal_"),
|
|
1997
1939
|
isAchieved: false
|
|
1998
|
-
}
|
|
1940
|
+
};
|
|
1999
1941
|
this.saveGoals([...goals, goalToAdd]);
|
|
2000
1942
|
}
|
|
2001
1943
|
static updateGoal(updatedGoal) {
|
|
@@ -2656,7 +2598,7 @@ var LocalStorageManager = class {
|
|
|
2656
2598
|
if (items.some((i) => i.code === item.code)) {
|
|
2657
2599
|
throw new Error(`An item with code "${item.code}" already exists for ${this.key}.`);
|
|
2658
2600
|
}
|
|
2659
|
-
const newItem =
|
|
2601
|
+
const newItem = { ...item, id: generateUniqueId(`${this.key}_`) };
|
|
2660
2602
|
this.saveAll([...items, newItem]);
|
|
2661
2603
|
return newItem;
|
|
2662
2604
|
}
|
|
@@ -2669,7 +2611,7 @@ var LocalStorageManager = class {
|
|
|
2669
2611
|
console.warn(`Item with id "${id}" not found in ${this.key} for update.`);
|
|
2670
2612
|
return null;
|
|
2671
2613
|
}
|
|
2672
|
-
const updatedItem =
|
|
2614
|
+
const updatedItem = { ...items[index], ...updates };
|
|
2673
2615
|
items[index] = updatedItem;
|
|
2674
2616
|
this.saveAll(items);
|
|
2675
2617
|
return updatedItem;
|
|
@@ -2763,10 +2705,10 @@ _MetadataService.deleteContext = (code) => contextManager.delete(code);
|
|
|
2763
2705
|
_MetadataService.getApproaches = () => approachManager.getAll().sort((a, b) => a.code.localeCompare(b.code));
|
|
2764
2706
|
_MetadataService.addApproach = (approachData) => {
|
|
2765
2707
|
const difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
|
|
2766
|
-
return approachManager.add(
|
|
2708
|
+
return approachManager.add({ ...approachData, difficulty });
|
|
2767
2709
|
};
|
|
2768
2710
|
_MetadataService.updateApproach = (id, approachData) => {
|
|
2769
|
-
const updates =
|
|
2711
|
+
const updates = { ...approachData };
|
|
2770
2712
|
if (approachData.rawDifficulty) {
|
|
2771
2713
|
updates.difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
|
|
2772
2714
|
}
|
|
@@ -2849,10 +2791,11 @@ var QuestionBankService = class {
|
|
|
2849
2791
|
if (allQuestions.some((q) => q.code === questionData.code)) {
|
|
2850
2792
|
throw new Error(`A question with code "${questionData.code}" already exists.`);
|
|
2851
2793
|
}
|
|
2852
|
-
const newQuestion =
|
|
2794
|
+
const newQuestion = {
|
|
2795
|
+
...questionData,
|
|
2853
2796
|
id: generateUniqueId("qb_"),
|
|
2854
2797
|
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
2855
|
-
}
|
|
2798
|
+
};
|
|
2856
2799
|
questionBankManager.saveAll([...allQuestions, newQuestion]);
|
|
2857
2800
|
return newQuestion;
|
|
2858
2801
|
}
|
|
@@ -2864,9 +2807,11 @@ var QuestionBankService = class {
|
|
|
2864
2807
|
console.warn(`Question with id "${id}" not found for update.`);
|
|
2865
2808
|
return null;
|
|
2866
2809
|
}
|
|
2867
|
-
const updatedQuestion =
|
|
2810
|
+
const updatedQuestion = {
|
|
2811
|
+
...allQuestions[index],
|
|
2812
|
+
...updates,
|
|
2868
2813
|
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
2869
|
-
}
|
|
2814
|
+
};
|
|
2870
2815
|
allQuestions[index] = updatedQuestion;
|
|
2871
2816
|
questionBankManager.saveAll(allQuestions);
|
|
2872
2817
|
return updatedQuestion;
|
|
@@ -3014,13 +2959,12 @@ var escapeXML = (unsafe) => {
|
|
|
3014
2959
|
});
|
|
3015
2960
|
};
|
|
3016
2961
|
var generateSCORMManifest = (quizConfig, scormVersion, launcherFile = "index.html", libraryJSPath = "scorm-bundle/player.js", quizDataPath = "quiz_data.json", blocklyCSSPath = "blockly-styles.css", mainCSSPath = "styles.css") => {
|
|
3017
|
-
var _a;
|
|
3018
2962
|
const uniqueId = `iqk_${quizConfig.id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
3019
2963
|
const organizationId = `ORG-${uniqueId}`;
|
|
3020
2964
|
const itemId = `ITEM-${uniqueId}`;
|
|
3021
2965
|
const resourceId = `RES-${uniqueId}`;
|
|
3022
2966
|
const quizTitle = escapeXML(quizConfig.title);
|
|
3023
|
-
const passingScore =
|
|
2967
|
+
const passingScore = quizConfig.settings?.passingScorePercent;
|
|
3024
2968
|
const effectiveScormVersion = scormVersion;
|
|
3025
2969
|
const schemaVersion = effectiveScormVersion === "2004" ? "2004 4th Edition" : "1.2";
|
|
3026
2970
|
const adlcpNamespace = effectiveScormVersion === "2004" ? "http://www.adlnet.org/xsd/adlcp_v1p3" : "http://www.adlnet.org/xsd/adlcp_rootv1p2";
|