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