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