@patch-adams/core 1.5.3 → 1.5.4

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/cli.cjs CHANGED
@@ -4164,7 +4164,11 @@ function generateLrsBridgeCode(options) {
4164
4164
  if (!TRACK_QUIZZES) return;
4165
4165
 
4166
4166
  // Intercept submit button clicks on Knowledge Check blocks
4167
+ // NOTE: If the SCORM tracker is active, it handles quiz tracking via
4168
+ // cmi.interactions \u2014 skip DOM scraping to avoid duplicate statements
4167
4169
  document.addEventListener('click', function(e) {
4170
+ if (scormTrackerActive) return; // SCORM tracker handles this
4171
+
4168
4172
  var submitBtn = e.target.closest('.quiz-card__button');
4169
4173
  if (!submitBtn) return;
4170
4174
 
@@ -4320,6 +4324,142 @@ function generateLrsBridgeCode(options) {
4320
4324
  return answerText;
4321
4325
  }
4322
4326
 
4327
+ // ========================================================================
4328
+ // SCORM INTERACTION TRACKER
4329
+ // Intercepts cmi.interactions.N.* SetValue calls to capture quiz answers
4330
+ // directly from the SCORM data model \u2014 works regardless of Rise UI format
4331
+ // (Knowledge Check blocks, quiz lessons, etc.)
4332
+ // ========================================================================
4333
+ var scormInteractions = {}; // Pending interactions keyed by index N
4334
+ var scormInteractionsSent = {}; // Track which interactions were already sent
4335
+ var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
4336
+
4337
+ function setupScormInteractionTracker() {
4338
+ if (!TRACK_QUIZZES) return;
4339
+ if (!LRS.scormApi) {
4340
+ log('SCORM Interaction Tracker: No SCORM API found, skipping');
4341
+ return;
4342
+ }
4343
+
4344
+ var api = LRS.scormApi;
4345
+ var apiType = LRS.scormApiType;
4346
+
4347
+ // Determine which SetValue function to wrap
4348
+ var setValueFn = apiType === '2004' ? 'SetValue' : 'LMSSetValue';
4349
+ var originalSetValue = api[setValueFn];
4350
+
4351
+ if (typeof originalSetValue !== 'function') {
4352
+ log('SCORM Interaction Tracker: No ' + setValueFn + ' function found');
4353
+ return;
4354
+ }
4355
+
4356
+ // SCORM 1.2 uses cmi.interactions.N.*, SCORM 2004 uses cmi.interactions.N.*
4357
+ var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
4358
+
4359
+ api[setValueFn] = function(key, value) {
4360
+ // Always call the original first
4361
+ var result = originalSetValue.apply(api, arguments);
4362
+
4363
+ // Check if this is an interaction data element
4364
+ if (typeof key === 'string') {
4365
+ var match = key.match(interactionPattern);
4366
+ if (match) {
4367
+ var idx = match[1];
4368
+ var field = match[2];
4369
+
4370
+ // Initialize interaction tracking for this index
4371
+ if (!scormInteractions[idx]) {
4372
+ scormInteractions[idx] = {};
4373
+ }
4374
+
4375
+ // Store the field value
4376
+ scormInteractions[idx][field] = value;
4377
+
4378
+ log('SCORM Interaction [' + idx + '].' + field + ' = ' + (value ? value.substring(0, 80) : value));
4379
+
4380
+ // When 'result' is set, the interaction is complete \u2014 fire xAPI statement
4381
+ if (field === 'result' && !scormInteractionsSent[idx]) {
4382
+ scormInteractionsSent[idx] = true;
4383
+ var interaction = scormInteractions[idx];
4384
+
4385
+ // Map SCORM interaction type to readable type
4386
+ var typeMap = {
4387
+ 'choice': 'multiple-choice',
4388
+ 'true-false': 'true-false',
4389
+ 'fill-in': 'fill-in-blank',
4390
+ 'matching': 'matching',
4391
+ 'performance': 'performance',
4392
+ 'sequencing': 'sequencing',
4393
+ 'likert': 'likert',
4394
+ 'numeric': 'numeric'
4395
+ };
4396
+
4397
+ var scormType = interaction.type || 'unknown';
4398
+ var questionType = typeMap[scormType] || scormType;
4399
+
4400
+ // Determine correctness from SCORM result value
4401
+ // SCORM 1.2: 'correct', 'wrong', 'unanticipated', 'neutral'
4402
+ // SCORM 2004: 'correct', 'incorrect', 'unanticipated', 'neutral'
4403
+ var isCorrect = value === 'correct';
4404
+
4405
+ // Get student response \u2014 for 'choice' type, this might be indices like '1,3'
4406
+ var studentResponse = interaction.student_response || interaction['student_response'] || '';
4407
+
4408
+ // Get correct response pattern
4409
+ var correctResponse = '';
4410
+ // SCORM 1.2: correct_responses.0.pattern
4411
+ // Check stored fields for correct_responses
4412
+ Object.keys(interaction).forEach(function(k) {
4413
+ if (k.indexOf('correct_responses') > -1 && k.indexOf('pattern') > -1) {
4414
+ correctResponse = interaction[k];
4415
+ }
4416
+ });
4417
+
4418
+ // Get interaction ID (Rise sets this to a unique question identifier)
4419
+ var interactionId = interaction.id || ('interaction-' + idx);
4420
+
4421
+ // Get lesson context
4422
+ var lessonInfo = getCachedLessonInfo();
4423
+
4424
+ // Increment question counter
4425
+ kcQuestionCounter++;
4426
+
4427
+ log('SCORM Interaction complete [' + idx + ']:', {
4428
+ id: interactionId,
4429
+ type: questionType,
4430
+ response: studentResponse,
4431
+ correctResponse: correctResponse,
4432
+ result: value,
4433
+ correct: isCorrect,
4434
+ questionNumber: kcQuestionCounter
4435
+ });
4436
+
4437
+ // Send xAPI answered statement
4438
+ LRS.questionAnswered({
4439
+ questionId: interactionId,
4440
+ questionGuid: interactionId,
4441
+ questionNumber: kcQuestionCounter,
4442
+ questionText: 'Question ' + kcQuestionCounter + ' (' + interactionId + ')',
4443
+ questionType: questionType,
4444
+ answer: studentResponse,
4445
+ correctAnswer: correctResponse,
4446
+ correct: isCorrect,
4447
+ result: isCorrect ? 'correct' : 'incorrect',
4448
+ assessmentName: lessonInfo.name || 'Quiz',
4449
+ lessonName: lessonInfo.name,
4450
+ sectionName: lessonInfo.sectionName
4451
+ });
4452
+ }
4453
+ }
4454
+ }
4455
+
4456
+ return result;
4457
+ };
4458
+
4459
+ scormTrackerActive = true;
4460
+ log('SCORM Interaction Tracker: Wrapped ' + setValueFn + ' (apiType=' + apiType + ') \u2014 KC DOM handler will defer to SCORM tracker');
4461
+ }
4462
+
4323
4463
  function setupInteractionInterceptors() {
4324
4464
  if (!TRACK_INTERACTIONS) return;
4325
4465
 
@@ -4538,6 +4678,9 @@ function generateLrsBridgeCode(options) {
4538
4678
  LRS.scormApiFound = true;
4539
4679
  LRS.scormApiType = result.type;
4540
4680
 
4681
+ // Wrap SCORM SetValue to intercept cmi.interactions for quiz tracking
4682
+ setupScormInteractionTracker();
4683
+
4541
4684
  // Read learner_id for diagnostics
4542
4685
  var scormLearnerId = 'n/a', scormLearnerName = 'n/a';
4543
4686
  try {