@patch-adams/core 1.5.4 → 1.5.5
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 +184 -106
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +184 -106
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +184 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +184 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3996,135 +3996,212 @@ function generateLrsBridgeCode(options) {
|
|
|
3996
3996
|
// Intercepts cmi.interactions.N.* SetValue calls to capture quiz answers
|
|
3997
3997
|
// directly from the SCORM data model \u2014 works regardless of Rise UI format
|
|
3998
3998
|
// (Knowledge Check blocks, quiz lessons, etc.)
|
|
3999
|
+
//
|
|
4000
|
+
// IMPORTANT: This finds and wraps the ACTUAL SCORM API that Rise uses
|
|
4001
|
+
// (window.API, parent.API, or global functions), NOT the bridge's copy.
|
|
4002
|
+
// It runs unconditionally at init, not gated by actor resolution.
|
|
3999
4003
|
// ========================================================================
|
|
4000
4004
|
var scormInteractions = {}; // Pending interactions keyed by index N
|
|
4001
4005
|
var scormInteractionsSent = {}; // Track which interactions were already sent
|
|
4002
4006
|
var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
|
|
4003
4007
|
|
|
4004
|
-
function
|
|
4005
|
-
if (
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
|
-
var api = LRS.scormApi;
|
|
4012
|
-
var apiType = LRS.scormApiType;
|
|
4008
|
+
function interceptScormSetValue(key, value) {
|
|
4009
|
+
if (typeof key !== 'string') return;
|
|
4010
|
+
var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
|
|
4011
|
+
var match = key.match(interactionPattern);
|
|
4012
|
+
if (!match) return;
|
|
4013
4013
|
|
|
4014
|
-
|
|
4015
|
-
var
|
|
4016
|
-
var originalSetValue = api[setValueFn];
|
|
4014
|
+
var idx = match[1];
|
|
4015
|
+
var field = match[2];
|
|
4017
4016
|
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4017
|
+
// Initialize interaction tracking for this index
|
|
4018
|
+
if (!scormInteractions[idx]) {
|
|
4019
|
+
scormInteractions[idx] = {};
|
|
4021
4020
|
}
|
|
4022
4021
|
|
|
4023
|
-
//
|
|
4024
|
-
|
|
4022
|
+
// Store the field value
|
|
4023
|
+
scormInteractions[idx][field] = String(value);
|
|
4025
4024
|
|
|
4026
|
-
|
|
4027
|
-
// Always call the original first
|
|
4028
|
-
var result = originalSetValue.apply(api, arguments);
|
|
4025
|
+
log('SCORM Interaction [' + idx + '].' + field + ' = ' + String(value).substring(0, 100));
|
|
4029
4026
|
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
var idx = match[1];
|
|
4035
|
-
var field = match[2];
|
|
4027
|
+
// When 'result' is set, the interaction is complete \u2014 fire xAPI statement
|
|
4028
|
+
if (field === 'result' && !scormInteractionsSent[idx]) {
|
|
4029
|
+
scormInteractionsSent[idx] = true;
|
|
4030
|
+
var interaction = scormInteractions[idx];
|
|
4036
4031
|
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4032
|
+
// Map SCORM interaction type to readable type
|
|
4033
|
+
var typeMap = {
|
|
4034
|
+
'choice': 'multiple-choice',
|
|
4035
|
+
'true-false': 'true-false',
|
|
4036
|
+
'fill-in': 'fill-in-blank',
|
|
4037
|
+
'matching': 'matching',
|
|
4038
|
+
'performance': 'performance',
|
|
4039
|
+
'sequencing': 'sequencing',
|
|
4040
|
+
'likert': 'likert',
|
|
4041
|
+
'numeric': 'numeric'
|
|
4042
|
+
};
|
|
4041
4043
|
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
log('SCORM Interaction [' + idx + '].' + field + ' = ' + (value ? value.substring(0, 80) : value));
|
|
4046
|
-
|
|
4047
|
-
// When 'result' is set, the interaction is complete \u2014 fire xAPI statement
|
|
4048
|
-
if (field === 'result' && !scormInteractionsSent[idx]) {
|
|
4049
|
-
scormInteractionsSent[idx] = true;
|
|
4050
|
-
var interaction = scormInteractions[idx];
|
|
4051
|
-
|
|
4052
|
-
// Map SCORM interaction type to readable type
|
|
4053
|
-
var typeMap = {
|
|
4054
|
-
'choice': 'multiple-choice',
|
|
4055
|
-
'true-false': 'true-false',
|
|
4056
|
-
'fill-in': 'fill-in-blank',
|
|
4057
|
-
'matching': 'matching',
|
|
4058
|
-
'performance': 'performance',
|
|
4059
|
-
'sequencing': 'sequencing',
|
|
4060
|
-
'likert': 'likert',
|
|
4061
|
-
'numeric': 'numeric'
|
|
4062
|
-
};
|
|
4044
|
+
var scormType = interaction.type || 'unknown';
|
|
4045
|
+
var questionType = typeMap[scormType] || scormType;
|
|
4063
4046
|
|
|
4064
|
-
|
|
4065
|
-
|
|
4047
|
+
// Determine correctness from SCORM result value
|
|
4048
|
+
// SCORM 1.2: 'correct', 'wrong', 'unanticipated', 'neutral'
|
|
4049
|
+
// SCORM 2004: 'correct', 'incorrect', 'unanticipated', 'neutral'
|
|
4050
|
+
var isCorrect = String(value) === 'correct';
|
|
4066
4051
|
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
// SCORM 2004: 'correct', 'incorrect', 'unanticipated', 'neutral'
|
|
4070
|
-
var isCorrect = value === 'correct';
|
|
4052
|
+
// Get student response
|
|
4053
|
+
var studentResponse = interaction.student_response || '';
|
|
4071
4054
|
|
|
4072
|
-
|
|
4073
|
-
|
|
4055
|
+
// Get correct response pattern
|
|
4056
|
+
var correctResponse = '';
|
|
4057
|
+
Object.keys(interaction).forEach(function(k) {
|
|
4058
|
+
if (k.indexOf('correct_responses') > -1 && k.indexOf('pattern') > -1) {
|
|
4059
|
+
correctResponse = interaction[k];
|
|
4060
|
+
}
|
|
4061
|
+
});
|
|
4074
4062
|
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4063
|
+
// Rise interaction IDs encode the question text (underscored)
|
|
4064
|
+
// e.g. "Development_Week_Qui_I_want_a_clear_picture_of_..."
|
|
4065
|
+
var interactionId = interaction.id || ('interaction-' + idx);
|
|
4066
|
+
|
|
4067
|
+
// Try to make a readable question text from the interaction ID
|
|
4068
|
+
var questionText = interactionId
|
|
4069
|
+
.replace(/_\\d+$/, '') // remove trailing _0
|
|
4070
|
+
.replace(/_/g, ' ') // underscores to spaces
|
|
4071
|
+
.substring(0, 200);
|
|
4072
|
+
|
|
4073
|
+
// Get lesson context
|
|
4074
|
+
var lessonInfo = getCachedLessonInfo();
|
|
4075
|
+
|
|
4076
|
+
// Increment question counter
|
|
4077
|
+
kcQuestionCounter++;
|
|
4078
|
+
|
|
4079
|
+
log('SCORM Interaction complete [' + idx + ']:', {
|
|
4080
|
+
questionNumber: kcQuestionCounter,
|
|
4081
|
+
questionText: questionText.substring(0, 60) + '...',
|
|
4082
|
+
type: questionType,
|
|
4083
|
+
studentResponse: studentResponse,
|
|
4084
|
+
correctResponse: correctResponse,
|
|
4085
|
+
result: String(value),
|
|
4086
|
+
correct: isCorrect
|
|
4087
|
+
});
|
|
4084
4088
|
|
|
4085
|
-
|
|
4086
|
-
|
|
4089
|
+
// Send xAPI answered statement
|
|
4090
|
+
LRS.questionAnswered({
|
|
4091
|
+
questionId: interactionId,
|
|
4092
|
+
questionGuid: interactionId,
|
|
4093
|
+
questionNumber: kcQuestionCounter,
|
|
4094
|
+
questionText: questionText,
|
|
4095
|
+
questionType: questionType,
|
|
4096
|
+
answer: studentResponse,
|
|
4097
|
+
correctAnswer: correctResponse,
|
|
4098
|
+
correct: isCorrect,
|
|
4099
|
+
result: isCorrect ? 'correct' : 'incorrect',
|
|
4100
|
+
assessmentName: lessonInfo.name || 'Quiz',
|
|
4101
|
+
lessonName: lessonInfo.name,
|
|
4102
|
+
sectionName: lessonInfo.sectionName
|
|
4103
|
+
});
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4087
4106
|
|
|
4088
|
-
|
|
4089
|
-
|
|
4107
|
+
function setupScormInteractionTracker() {
|
|
4108
|
+
if (!TRACK_QUIZZES) return;
|
|
4090
4109
|
|
|
4091
|
-
|
|
4092
|
-
|
|
4110
|
+
// Find the ACTUAL SCORM API that Rise uses \u2014 NOT our bridge's copy.
|
|
4111
|
+
// Rise discovers the API via standard SCORM lookup (window.API, parent chain).
|
|
4112
|
+
// The Bravais CDS player's ProxyApi.injectLmsApi() sets this up.
|
|
4113
|
+
var wrapped = false;
|
|
4093
4114
|
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4115
|
+
// Try 1: window.API (SCORM 1.2) or window.API_1484_11 (SCORM 2004)
|
|
4116
|
+
try {
|
|
4117
|
+
if (window.API && typeof window.API.LMSSetValue === 'function') {
|
|
4118
|
+
var origSetValue = window.API.LMSSetValue;
|
|
4119
|
+
window.API.LMSSetValue = function(key, value) {
|
|
4120
|
+
var result = origSetValue.apply(window.API, arguments);
|
|
4121
|
+
interceptScormSetValue(key, value);
|
|
4122
|
+
return result;
|
|
4123
|
+
};
|
|
4124
|
+
wrapped = true;
|
|
4125
|
+
log('SCORM Interaction Tracker: Wrapped window.API.LMSSetValue');
|
|
4126
|
+
}
|
|
4127
|
+
} catch (e) { log('SCORM Tracker: Cannot access window.API:', e.message); }
|
|
4103
4128
|
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4129
|
+
if (!wrapped) {
|
|
4130
|
+
try {
|
|
4131
|
+
if (window.API_1484_11 && typeof window.API_1484_11.SetValue === 'function') {
|
|
4132
|
+
var origSetValue2004 = window.API_1484_11.SetValue;
|
|
4133
|
+
window.API_1484_11.SetValue = function(key, value) {
|
|
4134
|
+
var result = origSetValue2004.apply(window.API_1484_11, arguments);
|
|
4135
|
+
interceptScormSetValue(key, value);
|
|
4136
|
+
return result;
|
|
4137
|
+
};
|
|
4138
|
+
wrapped = true;
|
|
4139
|
+
log('SCORM Interaction Tracker: Wrapped window.API_1484_11.SetValue');
|
|
4140
|
+
}
|
|
4141
|
+
} catch (e) { log('SCORM Tracker: Cannot access window.API_1484_11:', e.message); }
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
// Try 2: Parent frame API
|
|
4145
|
+
if (!wrapped) {
|
|
4146
|
+
try {
|
|
4147
|
+
if (window.parent && window.parent !== window) {
|
|
4148
|
+
if (window.parent.API && typeof window.parent.API.LMSSetValue === 'function') {
|
|
4149
|
+
var origParentSetValue = window.parent.API.LMSSetValue;
|
|
4150
|
+
window.parent.API.LMSSetValue = function(key, value) {
|
|
4151
|
+
var result = origParentSetValue.apply(window.parent.API, arguments);
|
|
4152
|
+
interceptScormSetValue(key, value);
|
|
4153
|
+
return result;
|
|
4154
|
+
};
|
|
4155
|
+
wrapped = true;
|
|
4156
|
+
log('SCORM Interaction Tracker: Wrapped window.parent.API.LMSSetValue');
|
|
4119
4157
|
}
|
|
4120
4158
|
}
|
|
4121
|
-
}
|
|
4159
|
+
} catch (e) { log('SCORM Tracker: Cannot access parent API (cross-origin)'); }
|
|
4160
|
+
}
|
|
4122
4161
|
|
|
4123
|
-
|
|
4124
|
-
|
|
4162
|
+
// Try 3: Global LMS functions (Bravais/Xyleme mock API)
|
|
4163
|
+
if (!wrapped) {
|
|
4164
|
+
try {
|
|
4165
|
+
if (typeof window.LMSSetValue === 'function') {
|
|
4166
|
+
var origGlobalSetValue = window.LMSSetValue;
|
|
4167
|
+
window.LMSSetValue = function(key, value) {
|
|
4168
|
+
var result = origGlobalSetValue.apply(window, arguments);
|
|
4169
|
+
interceptScormSetValue(key, value);
|
|
4170
|
+
return result;
|
|
4171
|
+
};
|
|
4172
|
+
wrapped = true;
|
|
4173
|
+
log('SCORM Interaction Tracker: Wrapped window.LMSSetValue (global)');
|
|
4174
|
+
}
|
|
4175
|
+
} catch (e) { log('SCORM Tracker: Cannot wrap global LMSSetValue:', e.message); }
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
// Try 4: Fall back to bridge's copy (least likely to work but worth trying)
|
|
4179
|
+
if (!wrapped && LRS.scormApi) {
|
|
4180
|
+
var setValueFn = LRS.scormApiType === '2004' ? 'SetValue' : 'LMSSetValue';
|
|
4181
|
+
if (typeof LRS.scormApi[setValueFn] === 'function') {
|
|
4182
|
+
var origBridgeSetValue = LRS.scormApi[setValueFn];
|
|
4183
|
+
LRS.scormApi[setValueFn] = function(key, value) {
|
|
4184
|
+
var result = origBridgeSetValue.apply(LRS.scormApi, arguments);
|
|
4185
|
+
interceptScormSetValue(key, value);
|
|
4186
|
+
return result;
|
|
4187
|
+
};
|
|
4188
|
+
wrapped = true;
|
|
4189
|
+
log('SCORM Interaction Tracker: Wrapped LRS.scormApi.' + setValueFn + ' (bridge copy)');
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4125
4192
|
|
|
4126
|
-
|
|
4127
|
-
|
|
4193
|
+
if (wrapped) {
|
|
4194
|
+
scormTrackerActive = true;
|
|
4195
|
+
log('SCORM Interaction Tracker active \u2014 KC DOM handler will defer');
|
|
4196
|
+
} else {
|
|
4197
|
+
// API not available yet \u2014 retry in 2 seconds (Bravais proxy may not be ready)
|
|
4198
|
+
log('SCORM Interaction Tracker: No SCORM API found yet, retrying in 2s...');
|
|
4199
|
+
setTimeout(function() {
|
|
4200
|
+
if (!scormTrackerActive) {
|
|
4201
|
+
setupScormInteractionTracker();
|
|
4202
|
+
}
|
|
4203
|
+
}, 2000);
|
|
4204
|
+
}
|
|
4128
4205
|
}
|
|
4129
4206
|
|
|
4130
4207
|
function setupInteractionInterceptors() {
|
|
@@ -4345,9 +4422,6 @@ function generateLrsBridgeCode(options) {
|
|
|
4345
4422
|
LRS.scormApiFound = true;
|
|
4346
4423
|
LRS.scormApiType = result.type;
|
|
4347
4424
|
|
|
4348
|
-
// Wrap SCORM SetValue to intercept cmi.interactions for quiz tracking
|
|
4349
|
-
setupScormInteractionTracker();
|
|
4350
|
-
|
|
4351
4425
|
// Read learner_id for diagnostics
|
|
4352
4426
|
var scormLearnerId = 'n/a', scormLearnerName = 'n/a';
|
|
4353
4427
|
try {
|
|
@@ -4430,6 +4504,10 @@ function generateLrsBridgeCode(options) {
|
|
|
4430
4504
|
setupQuizInterceptors();
|
|
4431
4505
|
setupInteractionInterceptors();
|
|
4432
4506
|
|
|
4507
|
+
// Intercept SCORM cmi.interactions to capture quiz answers as xAPI statements
|
|
4508
|
+
// This wraps the ACTUAL SCORM API (window.API etc.) that Rise calls
|
|
4509
|
+
setupScormInteractionTracker();
|
|
4510
|
+
|
|
4433
4511
|
// Fetch document metadata from API to get GUIDs (async)
|
|
4434
4512
|
// Then send course launched event
|
|
4435
4513
|
var sharedLinkToken = LRS.courseInfo ? LRS.courseInfo.sharedLinkToken : null;
|