@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/index.js CHANGED
@@ -3822,7 +3822,11 @@ function generateLrsBridgeCode(options) {
3822
3822
  if (!TRACK_QUIZZES) return;
3823
3823
 
3824
3824
  // Intercept submit button clicks on Knowledge Check blocks
3825
+ // NOTE: If the SCORM tracker is active, it handles quiz tracking via
3826
+ // cmi.interactions \u2014 skip DOM scraping to avoid duplicate statements
3825
3827
  document.addEventListener('click', function(e) {
3828
+ if (scormTrackerActive) return; // SCORM tracker handles this
3829
+
3826
3830
  var submitBtn = e.target.closest('.quiz-card__button');
3827
3831
  if (!submitBtn) return;
3828
3832
 
@@ -3978,6 +3982,142 @@ function generateLrsBridgeCode(options) {
3978
3982
  return answerText;
3979
3983
  }
3980
3984
 
3985
+ // ========================================================================
3986
+ // SCORM INTERACTION TRACKER
3987
+ // Intercepts cmi.interactions.N.* SetValue calls to capture quiz answers
3988
+ // directly from the SCORM data model \u2014 works regardless of Rise UI format
3989
+ // (Knowledge Check blocks, quiz lessons, etc.)
3990
+ // ========================================================================
3991
+ var scormInteractions = {}; // Pending interactions keyed by index N
3992
+ var scormInteractionsSent = {}; // Track which interactions were already sent
3993
+ var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
3994
+
3995
+ function setupScormInteractionTracker() {
3996
+ if (!TRACK_QUIZZES) return;
3997
+ if (!LRS.scormApi) {
3998
+ log('SCORM Interaction Tracker: No SCORM API found, skipping');
3999
+ return;
4000
+ }
4001
+
4002
+ var api = LRS.scormApi;
4003
+ var apiType = LRS.scormApiType;
4004
+
4005
+ // Determine which SetValue function to wrap
4006
+ var setValueFn = apiType === '2004' ? 'SetValue' : 'LMSSetValue';
4007
+ var originalSetValue = api[setValueFn];
4008
+
4009
+ if (typeof originalSetValue !== 'function') {
4010
+ log('SCORM Interaction Tracker: No ' + setValueFn + ' function found');
4011
+ return;
4012
+ }
4013
+
4014
+ // SCORM 1.2 uses cmi.interactions.N.*, SCORM 2004 uses cmi.interactions.N.*
4015
+ var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
4016
+
4017
+ api[setValueFn] = function(key, value) {
4018
+ // Always call the original first
4019
+ var result = originalSetValue.apply(api, arguments);
4020
+
4021
+ // Check if this is an interaction data element
4022
+ if (typeof key === 'string') {
4023
+ var match = key.match(interactionPattern);
4024
+ if (match) {
4025
+ var idx = match[1];
4026
+ var field = match[2];
4027
+
4028
+ // Initialize interaction tracking for this index
4029
+ if (!scormInteractions[idx]) {
4030
+ scormInteractions[idx] = {};
4031
+ }
4032
+
4033
+ // Store the field value
4034
+ scormInteractions[idx][field] = value;
4035
+
4036
+ log('SCORM Interaction [' + idx + '].' + field + ' = ' + (value ? value.substring(0, 80) : value));
4037
+
4038
+ // When 'result' is set, the interaction is complete \u2014 fire xAPI statement
4039
+ if (field === 'result' && !scormInteractionsSent[idx]) {
4040
+ scormInteractionsSent[idx] = true;
4041
+ var interaction = scormInteractions[idx];
4042
+
4043
+ // Map SCORM interaction type to readable type
4044
+ var typeMap = {
4045
+ 'choice': 'multiple-choice',
4046
+ 'true-false': 'true-false',
4047
+ 'fill-in': 'fill-in-blank',
4048
+ 'matching': 'matching',
4049
+ 'performance': 'performance',
4050
+ 'sequencing': 'sequencing',
4051
+ 'likert': 'likert',
4052
+ 'numeric': 'numeric'
4053
+ };
4054
+
4055
+ var scormType = interaction.type || 'unknown';
4056
+ var questionType = typeMap[scormType] || scormType;
4057
+
4058
+ // Determine correctness from SCORM result value
4059
+ // SCORM 1.2: 'correct', 'wrong', 'unanticipated', 'neutral'
4060
+ // SCORM 2004: 'correct', 'incorrect', 'unanticipated', 'neutral'
4061
+ var isCorrect = value === 'correct';
4062
+
4063
+ // Get student response \u2014 for 'choice' type, this might be indices like '1,3'
4064
+ var studentResponse = interaction.student_response || interaction['student_response'] || '';
4065
+
4066
+ // Get correct response pattern
4067
+ var correctResponse = '';
4068
+ // SCORM 1.2: correct_responses.0.pattern
4069
+ // Check stored fields for correct_responses
4070
+ Object.keys(interaction).forEach(function(k) {
4071
+ if (k.indexOf('correct_responses') > -1 && k.indexOf('pattern') > -1) {
4072
+ correctResponse = interaction[k];
4073
+ }
4074
+ });
4075
+
4076
+ // Get interaction ID (Rise sets this to a unique question identifier)
4077
+ var interactionId = interaction.id || ('interaction-' + idx);
4078
+
4079
+ // Get lesson context
4080
+ var lessonInfo = getCachedLessonInfo();
4081
+
4082
+ // Increment question counter
4083
+ kcQuestionCounter++;
4084
+
4085
+ log('SCORM Interaction complete [' + idx + ']:', {
4086
+ id: interactionId,
4087
+ type: questionType,
4088
+ response: studentResponse,
4089
+ correctResponse: correctResponse,
4090
+ result: value,
4091
+ correct: isCorrect,
4092
+ questionNumber: kcQuestionCounter
4093
+ });
4094
+
4095
+ // Send xAPI answered statement
4096
+ LRS.questionAnswered({
4097
+ questionId: interactionId,
4098
+ questionGuid: interactionId,
4099
+ questionNumber: kcQuestionCounter,
4100
+ questionText: 'Question ' + kcQuestionCounter + ' (' + interactionId + ')',
4101
+ questionType: questionType,
4102
+ answer: studentResponse,
4103
+ correctAnswer: correctResponse,
4104
+ correct: isCorrect,
4105
+ result: isCorrect ? 'correct' : 'incorrect',
4106
+ assessmentName: lessonInfo.name || 'Quiz',
4107
+ lessonName: lessonInfo.name,
4108
+ sectionName: lessonInfo.sectionName
4109
+ });
4110
+ }
4111
+ }
4112
+ }
4113
+
4114
+ return result;
4115
+ };
4116
+
4117
+ scormTrackerActive = true;
4118
+ log('SCORM Interaction Tracker: Wrapped ' + setValueFn + ' (apiType=' + apiType + ') \u2014 KC DOM handler will defer to SCORM tracker');
4119
+ }
4120
+
3981
4121
  function setupInteractionInterceptors() {
3982
4122
  if (!TRACK_INTERACTIONS) return;
3983
4123
 
@@ -4196,6 +4336,9 @@ function generateLrsBridgeCode(options) {
4196
4336
  LRS.scormApiFound = true;
4197
4337
  LRS.scormApiType = result.type;
4198
4338
 
4339
+ // Wrap SCORM SetValue to intercept cmi.interactions for quiz tracking
4340
+ setupScormInteractionTracker();
4341
+
4199
4342
  // Read learner_id for diagnostics
4200
4343
  var scormLearnerId = 'n/a', scormLearnerName = 'n/a';
4201
4344
  try {