@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/cli.js
CHANGED
|
@@ -4319,135 +4319,212 @@ function generateLrsBridgeCode(options) {
|
|
|
4319
4319
|
// Intercepts cmi.interactions.N.* SetValue calls to capture quiz answers
|
|
4320
4320
|
// directly from the SCORM data model \u2014 works regardless of Rise UI format
|
|
4321
4321
|
// (Knowledge Check blocks, quiz lessons, etc.)
|
|
4322
|
+
//
|
|
4323
|
+
// IMPORTANT: This finds and wraps the ACTUAL SCORM API that Rise uses
|
|
4324
|
+
// (window.API, parent.API, or global functions), NOT the bridge's copy.
|
|
4325
|
+
// It runs unconditionally at init, not gated by actor resolution.
|
|
4322
4326
|
// ========================================================================
|
|
4323
4327
|
var scormInteractions = {}; // Pending interactions keyed by index N
|
|
4324
4328
|
var scormInteractionsSent = {}; // Track which interactions were already sent
|
|
4325
4329
|
var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
|
|
4326
4330
|
|
|
4327
|
-
function
|
|
4328
|
-
if (
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
}
|
|
4333
|
-
|
|
4334
|
-
var api = LRS.scormApi;
|
|
4335
|
-
var apiType = LRS.scormApiType;
|
|
4331
|
+
function interceptScormSetValue(key, value) {
|
|
4332
|
+
if (typeof key !== 'string') return;
|
|
4333
|
+
var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
|
|
4334
|
+
var match = key.match(interactionPattern);
|
|
4335
|
+
if (!match) return;
|
|
4336
4336
|
|
|
4337
|
-
|
|
4338
|
-
var
|
|
4339
|
-
var originalSetValue = api[setValueFn];
|
|
4337
|
+
var idx = match[1];
|
|
4338
|
+
var field = match[2];
|
|
4340
4339
|
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4340
|
+
// Initialize interaction tracking for this index
|
|
4341
|
+
if (!scormInteractions[idx]) {
|
|
4342
|
+
scormInteractions[idx] = {};
|
|
4344
4343
|
}
|
|
4345
4344
|
|
|
4346
|
-
//
|
|
4347
|
-
|
|
4345
|
+
// Store the field value
|
|
4346
|
+
scormInteractions[idx][field] = String(value);
|
|
4348
4347
|
|
|
4349
|
-
|
|
4350
|
-
// Always call the original first
|
|
4351
|
-
var result = originalSetValue.apply(api, arguments);
|
|
4348
|
+
log('SCORM Interaction [' + idx + '].' + field + ' = ' + String(value).substring(0, 100));
|
|
4352
4349
|
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
var idx = match[1];
|
|
4358
|
-
var field = match[2];
|
|
4350
|
+
// When 'result' is set, the interaction is complete \u2014 fire xAPI statement
|
|
4351
|
+
if (field === 'result' && !scormInteractionsSent[idx]) {
|
|
4352
|
+
scormInteractionsSent[idx] = true;
|
|
4353
|
+
var interaction = scormInteractions[idx];
|
|
4359
4354
|
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4355
|
+
// Map SCORM interaction type to readable type
|
|
4356
|
+
var typeMap = {
|
|
4357
|
+
'choice': 'multiple-choice',
|
|
4358
|
+
'true-false': 'true-false',
|
|
4359
|
+
'fill-in': 'fill-in-blank',
|
|
4360
|
+
'matching': 'matching',
|
|
4361
|
+
'performance': 'performance',
|
|
4362
|
+
'sequencing': 'sequencing',
|
|
4363
|
+
'likert': 'likert',
|
|
4364
|
+
'numeric': 'numeric'
|
|
4365
|
+
};
|
|
4364
4366
|
|
|
4365
|
-
|
|
4366
|
-
|
|
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
|
-
};
|
|
4367
|
+
var scormType = interaction.type || 'unknown';
|
|
4368
|
+
var questionType = typeMap[scormType] || scormType;
|
|
4386
4369
|
|
|
4387
|
-
|
|
4388
|
-
|
|
4370
|
+
// Determine correctness from SCORM result value
|
|
4371
|
+
// SCORM 1.2: 'correct', 'wrong', 'unanticipated', 'neutral'
|
|
4372
|
+
// SCORM 2004: 'correct', 'incorrect', 'unanticipated', 'neutral'
|
|
4373
|
+
var isCorrect = String(value) === 'correct';
|
|
4389
4374
|
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
// SCORM 2004: 'correct', 'incorrect', 'unanticipated', 'neutral'
|
|
4393
|
-
var isCorrect = value === 'correct';
|
|
4375
|
+
// Get student response
|
|
4376
|
+
var studentResponse = interaction.student_response || '';
|
|
4394
4377
|
|
|
4395
|
-
|
|
4396
|
-
|
|
4378
|
+
// Get correct response pattern
|
|
4379
|
+
var correctResponse = '';
|
|
4380
|
+
Object.keys(interaction).forEach(function(k) {
|
|
4381
|
+
if (k.indexOf('correct_responses') > -1 && k.indexOf('pattern') > -1) {
|
|
4382
|
+
correctResponse = interaction[k];
|
|
4383
|
+
}
|
|
4384
|
+
});
|
|
4397
4385
|
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4386
|
+
// Rise interaction IDs encode the question text (underscored)
|
|
4387
|
+
// e.g. "Development_Week_Qui_I_want_a_clear_picture_of_..."
|
|
4388
|
+
var interactionId = interaction.id || ('interaction-' + idx);
|
|
4389
|
+
|
|
4390
|
+
// Try to make a readable question text from the interaction ID
|
|
4391
|
+
var questionText = interactionId
|
|
4392
|
+
.replace(/_\\d+$/, '') // remove trailing _0
|
|
4393
|
+
.replace(/_/g, ' ') // underscores to spaces
|
|
4394
|
+
.substring(0, 200);
|
|
4395
|
+
|
|
4396
|
+
// Get lesson context
|
|
4397
|
+
var lessonInfo = getCachedLessonInfo();
|
|
4398
|
+
|
|
4399
|
+
// Increment question counter
|
|
4400
|
+
kcQuestionCounter++;
|
|
4401
|
+
|
|
4402
|
+
log('SCORM Interaction complete [' + idx + ']:', {
|
|
4403
|
+
questionNumber: kcQuestionCounter,
|
|
4404
|
+
questionText: questionText.substring(0, 60) + '...',
|
|
4405
|
+
type: questionType,
|
|
4406
|
+
studentResponse: studentResponse,
|
|
4407
|
+
correctResponse: correctResponse,
|
|
4408
|
+
result: String(value),
|
|
4409
|
+
correct: isCorrect
|
|
4410
|
+
});
|
|
4407
4411
|
|
|
4408
|
-
|
|
4409
|
-
|
|
4412
|
+
// Send xAPI answered statement
|
|
4413
|
+
LRS.questionAnswered({
|
|
4414
|
+
questionId: interactionId,
|
|
4415
|
+
questionGuid: interactionId,
|
|
4416
|
+
questionNumber: kcQuestionCounter,
|
|
4417
|
+
questionText: questionText,
|
|
4418
|
+
questionType: questionType,
|
|
4419
|
+
answer: studentResponse,
|
|
4420
|
+
correctAnswer: correctResponse,
|
|
4421
|
+
correct: isCorrect,
|
|
4422
|
+
result: isCorrect ? 'correct' : 'incorrect',
|
|
4423
|
+
assessmentName: lessonInfo.name || 'Quiz',
|
|
4424
|
+
lessonName: lessonInfo.name,
|
|
4425
|
+
sectionName: lessonInfo.sectionName
|
|
4426
|
+
});
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4410
4429
|
|
|
4411
|
-
|
|
4412
|
-
|
|
4430
|
+
function setupScormInteractionTracker() {
|
|
4431
|
+
if (!TRACK_QUIZZES) return;
|
|
4413
4432
|
|
|
4414
|
-
|
|
4415
|
-
|
|
4433
|
+
// Find the ACTUAL SCORM API that Rise uses \u2014 NOT our bridge's copy.
|
|
4434
|
+
// Rise discovers the API via standard SCORM lookup (window.API, parent chain).
|
|
4435
|
+
// The Bravais CDS player's ProxyApi.injectLmsApi() sets this up.
|
|
4436
|
+
var wrapped = false;
|
|
4416
4437
|
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4438
|
+
// Try 1: window.API (SCORM 1.2) or window.API_1484_11 (SCORM 2004)
|
|
4439
|
+
try {
|
|
4440
|
+
if (window.API && typeof window.API.LMSSetValue === 'function') {
|
|
4441
|
+
var origSetValue = window.API.LMSSetValue;
|
|
4442
|
+
window.API.LMSSetValue = function(key, value) {
|
|
4443
|
+
var result = origSetValue.apply(window.API, arguments);
|
|
4444
|
+
interceptScormSetValue(key, value);
|
|
4445
|
+
return result;
|
|
4446
|
+
};
|
|
4447
|
+
wrapped = true;
|
|
4448
|
+
log('SCORM Interaction Tracker: Wrapped window.API.LMSSetValue');
|
|
4449
|
+
}
|
|
4450
|
+
} catch (e) { log('SCORM Tracker: Cannot access window.API:', e.message); }
|
|
4426
4451
|
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4452
|
+
if (!wrapped) {
|
|
4453
|
+
try {
|
|
4454
|
+
if (window.API_1484_11 && typeof window.API_1484_11.SetValue === 'function') {
|
|
4455
|
+
var origSetValue2004 = window.API_1484_11.SetValue;
|
|
4456
|
+
window.API_1484_11.SetValue = function(key, value) {
|
|
4457
|
+
var result = origSetValue2004.apply(window.API_1484_11, arguments);
|
|
4458
|
+
interceptScormSetValue(key, value);
|
|
4459
|
+
return result;
|
|
4460
|
+
};
|
|
4461
|
+
wrapped = true;
|
|
4462
|
+
log('SCORM Interaction Tracker: Wrapped window.API_1484_11.SetValue');
|
|
4463
|
+
}
|
|
4464
|
+
} catch (e) { log('SCORM Tracker: Cannot access window.API_1484_11:', e.message); }
|
|
4465
|
+
}
|
|
4466
|
+
|
|
4467
|
+
// Try 2: Parent frame API
|
|
4468
|
+
if (!wrapped) {
|
|
4469
|
+
try {
|
|
4470
|
+
if (window.parent && window.parent !== window) {
|
|
4471
|
+
if (window.parent.API && typeof window.parent.API.LMSSetValue === 'function') {
|
|
4472
|
+
var origParentSetValue = window.parent.API.LMSSetValue;
|
|
4473
|
+
window.parent.API.LMSSetValue = function(key, value) {
|
|
4474
|
+
var result = origParentSetValue.apply(window.parent.API, arguments);
|
|
4475
|
+
interceptScormSetValue(key, value);
|
|
4476
|
+
return result;
|
|
4477
|
+
};
|
|
4478
|
+
wrapped = true;
|
|
4479
|
+
log('SCORM Interaction Tracker: Wrapped window.parent.API.LMSSetValue');
|
|
4442
4480
|
}
|
|
4443
4481
|
}
|
|
4444
|
-
}
|
|
4482
|
+
} catch (e) { log('SCORM Tracker: Cannot access parent API (cross-origin)'); }
|
|
4483
|
+
}
|
|
4445
4484
|
|
|
4446
|
-
|
|
4447
|
-
|
|
4485
|
+
// Try 3: Global LMS functions (Bravais/Xyleme mock API)
|
|
4486
|
+
if (!wrapped) {
|
|
4487
|
+
try {
|
|
4488
|
+
if (typeof window.LMSSetValue === 'function') {
|
|
4489
|
+
var origGlobalSetValue = window.LMSSetValue;
|
|
4490
|
+
window.LMSSetValue = function(key, value) {
|
|
4491
|
+
var result = origGlobalSetValue.apply(window, arguments);
|
|
4492
|
+
interceptScormSetValue(key, value);
|
|
4493
|
+
return result;
|
|
4494
|
+
};
|
|
4495
|
+
wrapped = true;
|
|
4496
|
+
log('SCORM Interaction Tracker: Wrapped window.LMSSetValue (global)');
|
|
4497
|
+
}
|
|
4498
|
+
} catch (e) { log('SCORM Tracker: Cannot wrap global LMSSetValue:', e.message); }
|
|
4499
|
+
}
|
|
4500
|
+
|
|
4501
|
+
// Try 4: Fall back to bridge's copy (least likely to work but worth trying)
|
|
4502
|
+
if (!wrapped && LRS.scormApi) {
|
|
4503
|
+
var setValueFn = LRS.scormApiType === '2004' ? 'SetValue' : 'LMSSetValue';
|
|
4504
|
+
if (typeof LRS.scormApi[setValueFn] === 'function') {
|
|
4505
|
+
var origBridgeSetValue = LRS.scormApi[setValueFn];
|
|
4506
|
+
LRS.scormApi[setValueFn] = function(key, value) {
|
|
4507
|
+
var result = origBridgeSetValue.apply(LRS.scormApi, arguments);
|
|
4508
|
+
interceptScormSetValue(key, value);
|
|
4509
|
+
return result;
|
|
4510
|
+
};
|
|
4511
|
+
wrapped = true;
|
|
4512
|
+
log('SCORM Interaction Tracker: Wrapped LRS.scormApi.' + setValueFn + ' (bridge copy)');
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4448
4515
|
|
|
4449
|
-
|
|
4450
|
-
|
|
4516
|
+
if (wrapped) {
|
|
4517
|
+
scormTrackerActive = true;
|
|
4518
|
+
log('SCORM Interaction Tracker active \u2014 KC DOM handler will defer');
|
|
4519
|
+
} else {
|
|
4520
|
+
// API not available yet \u2014 retry in 2 seconds (Bravais proxy may not be ready)
|
|
4521
|
+
log('SCORM Interaction Tracker: No SCORM API found yet, retrying in 2s...');
|
|
4522
|
+
setTimeout(function() {
|
|
4523
|
+
if (!scormTrackerActive) {
|
|
4524
|
+
setupScormInteractionTracker();
|
|
4525
|
+
}
|
|
4526
|
+
}, 2000);
|
|
4527
|
+
}
|
|
4451
4528
|
}
|
|
4452
4529
|
|
|
4453
4530
|
function setupInteractionInterceptors() {
|
|
@@ -4668,9 +4745,6 @@ function generateLrsBridgeCode(options) {
|
|
|
4668
4745
|
LRS.scormApiFound = true;
|
|
4669
4746
|
LRS.scormApiType = result.type;
|
|
4670
4747
|
|
|
4671
|
-
// Wrap SCORM SetValue to intercept cmi.interactions for quiz tracking
|
|
4672
|
-
setupScormInteractionTracker();
|
|
4673
|
-
|
|
4674
4748
|
// Read learner_id for diagnostics
|
|
4675
4749
|
var scormLearnerId = 'n/a', scormLearnerName = 'n/a';
|
|
4676
4750
|
try {
|
|
@@ -4753,6 +4827,10 @@ function generateLrsBridgeCode(options) {
|
|
|
4753
4827
|
setupQuizInterceptors();
|
|
4754
4828
|
setupInteractionInterceptors();
|
|
4755
4829
|
|
|
4830
|
+
// Intercept SCORM cmi.interactions to capture quiz answers as xAPI statements
|
|
4831
|
+
// This wraps the ACTUAL SCORM API (window.API etc.) that Rise calls
|
|
4832
|
+
setupScormInteractionTracker();
|
|
4833
|
+
|
|
4756
4834
|
// Fetch document metadata from API to get GUIDs (async)
|
|
4757
4835
|
// Then send course launched event
|
|
4758
4836
|
var sharedLinkToken = LRS.courseInfo ? LRS.courseInfo.sharedLinkToken : null;
|