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