@patch-adams/core 1.5.9 → 1.5.11

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 CHANGED
@@ -3720,6 +3720,64 @@ function generateLrsBridgeCode(options) {
3720
3720
  sendStatement(statement);
3721
3721
  };
3722
3722
 
3723
+ // Send a "launched" statement \u2014 called by skin after email gate / actor setup
3724
+ LRS.sendLaunchStatement = function(data) {
3725
+ data = data || {};
3726
+ var activityDetails = {
3727
+ launchSource: data.source || 'email-gate',
3728
+ employeeEmail: data.email || (LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined),
3729
+ employeeId: data.employeeId || LRS.employeeId || undefined
3730
+ };
3731
+
3732
+ if (data.employeeName) activityDetails.employeeName = data.employeeName;
3733
+
3734
+ var statement = buildStatement('launched', ACTIVITY_TYPES.course, activityDetails, null, null);
3735
+ log('Sending launch statement for:', activityDetails.employeeEmail || '(unknown)');
3736
+ sendStatement(statement);
3737
+ };
3738
+
3739
+ // Send a course completion statement \u2014 called by skin or auto-detected via SCORM
3740
+ LRS.sendCompletionStatement = function(data) {
3741
+ data = data || {};
3742
+ if (scormCompletionSent) {
3743
+ log('Completion statement already sent (SCORM auto-detected). Skipping duplicate.');
3744
+ return;
3745
+ }
3746
+ scormCompletionSent = true;
3747
+
3748
+ var status = data.status || 'completed';
3749
+ var courseTitle = data.courseTitle || (LRS.courseInfo && LRS.courseInfo.title ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course');
3750
+
3751
+ var verbKey = 'completed';
3752
+ if (status === 'passed') verbKey = 'passed';
3753
+ if (status === 'failed') verbKey = 'failed';
3754
+
3755
+ var result = { completion: true, success: (status === 'passed') };
3756
+
3757
+ if (typeof data.score === 'number') {
3758
+ var max = data.scoreMax || 100;
3759
+ var min = data.scoreMin || 0;
3760
+ result.score = {
3761
+ raw: data.score,
3762
+ max: max,
3763
+ min: min,
3764
+ scaled: max > min ? (data.score - min) / (max - min) : 0
3765
+ };
3766
+ }
3767
+
3768
+ var activityDetails = {
3769
+ courseTitle: courseTitle,
3770
+ completionStatus: status,
3771
+ employeeEmail: data.email || (LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined),
3772
+ employeeId: data.employeeId || LRS.employeeId || undefined
3773
+ };
3774
+ if (data.employeeName) activityDetails.employeeName = data.employeeName;
3775
+
3776
+ log('Sending completion statement:', verbKey, 'score:', result.score, 'title:', courseTitle);
3777
+ var statement = buildStatement(verbKey, ACTIVITY_TYPES.course, activityDetails, result, null);
3778
+ sendStatement(statement);
3779
+ };
3780
+
3723
3781
  /**
3724
3782
  * Re-extract actor from SCORM after LMSInitialize has been called.
3725
3783
  * Call this from the SCORM wrapper after scormInit() succeeds,
@@ -4390,9 +4448,37 @@ function generateLrsBridgeCode(options) {
4390
4448
  var scormInteractions = {}; // Pending interactions keyed by index N
4391
4449
  var scormInteractionsSent = {}; // Track which interactions were already sent
4392
4450
  var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
4451
+ var scormCourseData = {}; // Accumulates course-level SCORM data (score, status)
4452
+ var scormCompletionSent = false; // Prevent duplicate completion statements
4393
4453
 
4394
4454
  function interceptScormSetValue(key, value) {
4395
4455
  if (typeof key !== 'string') return;
4456
+
4457
+ // ---- Course-level SCORM data (score, status, completion) ----
4458
+ // SCORM 1.2
4459
+ if (key === 'cmi.core.score.raw') { scormCourseData.scoreRaw = parseFloat(value); }
4460
+ if (key === 'cmi.core.score.max') { scormCourseData.scoreMax = parseFloat(value); }
4461
+ if (key === 'cmi.core.score.min') { scormCourseData.scoreMin = parseFloat(value); }
4462
+ if (key === 'cmi.core.lesson_status') { scormCourseData.status = String(value).toLowerCase(); }
4463
+ // SCORM 2004
4464
+ if (key === 'cmi.score.raw') { scormCourseData.scoreRaw = parseFloat(value); }
4465
+ if (key === 'cmi.score.max') { scormCourseData.scoreMax = parseFloat(value); }
4466
+ if (key === 'cmi.score.min') { scormCourseData.scoreMin = parseFloat(value); }
4467
+ if (key === 'cmi.score.scaled') { scormCourseData.scoreScaled = parseFloat(value); }
4468
+ if (key === 'cmi.completion_status') { scormCourseData.completionStatus = String(value).toLowerCase(); }
4469
+ if (key === 'cmi.success_status') { scormCourseData.successStatus = String(value).toLowerCase(); }
4470
+
4471
+ // Fire course completion statement when status indicates pass/fail/complete
4472
+ if ((key === 'cmi.core.lesson_status' || key === 'cmi.success_status' || key === 'cmi.completion_status') && !scormCompletionSent) {
4473
+ var status = String(value).toLowerCase();
4474
+ if (status === 'passed' || status === 'failed' || status === 'completed') {
4475
+ scormCompletionSent = true;
4476
+ // Slight delay to let score values arrive (Rise often sets score just before status)
4477
+ setTimeout(function() { sendCourseCompletionStatement(status); }, 200);
4478
+ }
4479
+ }
4480
+
4481
+ // ---- Interaction-level tracking (existing) ----
4396
4482
  var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
4397
4483
  var match = key.match(interactionPattern);
4398
4484
  if (!match) return;
@@ -4492,6 +4578,51 @@ function generateLrsBridgeCode(options) {
4492
4578
  }
4493
4579
  }
4494
4580
 
4581
+ function sendCourseCompletionStatement(status) {
4582
+ logGroup('Course Completion');
4583
+ log('Status:', status);
4584
+ log('SCORM course data:', scormCourseData);
4585
+
4586
+ var courseTitle = (LRS.courseInfo && LRS.courseInfo.title) ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course';
4587
+
4588
+ // Determine verb
4589
+ var verbKey = 'completed';
4590
+ if (status === 'passed') verbKey = 'passed';
4591
+ if (status === 'failed') verbKey = 'failed';
4592
+
4593
+ // Build result with score
4594
+ var result = { completion: true };
4595
+
4596
+ var raw = scormCourseData.scoreRaw;
4597
+ var max = scormCourseData.scoreMax || 100;
4598
+ var min = scormCourseData.scoreMin || 0;
4599
+
4600
+ if (typeof raw === 'number' && !isNaN(raw)) {
4601
+ result.score = {
4602
+ raw: raw,
4603
+ max: max,
4604
+ min: min,
4605
+ scaled: scormCourseData.scoreScaled != null ? scormCourseData.scoreScaled : (max > min ? (raw - min) / (max - min) : 0)
4606
+ };
4607
+ }
4608
+
4609
+ result.success = (status === 'passed');
4610
+
4611
+ var activityDetails = {
4612
+ courseTitle: courseTitle,
4613
+ completionStatus: status,
4614
+ employeeEmail: LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined,
4615
+ employeeId: LRS.employeeId || undefined
4616
+ };
4617
+
4618
+ log('Sending course completion:', verbKey, 'score:', result.score, 'title:', courseTitle);
4619
+
4620
+ var statement = buildStatement(verbKey, ACTIVITY_TYPES.course, activityDetails, result, null);
4621
+ sendStatement(statement);
4622
+
4623
+ logGroupEnd();
4624
+ }
4625
+
4495
4626
  function setupScormInteractionTracker() {
4496
4627
  if (!TRACK_QUIZZES) return;
4497
4628