@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.js CHANGED
@@ -3710,6 +3710,64 @@ function generateLrsBridgeCode(options) {
3710
3710
  sendStatement(statement);
3711
3711
  };
3712
3712
 
3713
+ // Send a "launched" statement \u2014 called by skin after email gate / actor setup
3714
+ LRS.sendLaunchStatement = function(data) {
3715
+ data = data || {};
3716
+ var activityDetails = {
3717
+ launchSource: data.source || 'email-gate',
3718
+ employeeEmail: data.email || (LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined),
3719
+ employeeId: data.employeeId || LRS.employeeId || undefined
3720
+ };
3721
+
3722
+ if (data.employeeName) activityDetails.employeeName = data.employeeName;
3723
+
3724
+ var statement = buildStatement('launched', ACTIVITY_TYPES.course, activityDetails, null, null);
3725
+ log('Sending launch statement for:', activityDetails.employeeEmail || '(unknown)');
3726
+ sendStatement(statement);
3727
+ };
3728
+
3729
+ // Send a course completion statement \u2014 called by skin or auto-detected via SCORM
3730
+ LRS.sendCompletionStatement = function(data) {
3731
+ data = data || {};
3732
+ if (scormCompletionSent) {
3733
+ log('Completion statement already sent (SCORM auto-detected). Skipping duplicate.');
3734
+ return;
3735
+ }
3736
+ scormCompletionSent = true;
3737
+
3738
+ var status = data.status || 'completed';
3739
+ var courseTitle = data.courseTitle || (LRS.courseInfo && LRS.courseInfo.title ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course');
3740
+
3741
+ var verbKey = 'completed';
3742
+ if (status === 'passed') verbKey = 'passed';
3743
+ if (status === 'failed') verbKey = 'failed';
3744
+
3745
+ var result = { completion: true, success: (status === 'passed') };
3746
+
3747
+ if (typeof data.score === 'number') {
3748
+ var max = data.scoreMax || 100;
3749
+ var min = data.scoreMin || 0;
3750
+ result.score = {
3751
+ raw: data.score,
3752
+ max: max,
3753
+ min: min,
3754
+ scaled: max > min ? (data.score - min) / (max - min) : 0
3755
+ };
3756
+ }
3757
+
3758
+ var activityDetails = {
3759
+ courseTitle: courseTitle,
3760
+ completionStatus: status,
3761
+ employeeEmail: data.email || (LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined),
3762
+ employeeId: data.employeeId || LRS.employeeId || undefined
3763
+ };
3764
+ if (data.employeeName) activityDetails.employeeName = data.employeeName;
3765
+
3766
+ log('Sending completion statement:', verbKey, 'score:', result.score, 'title:', courseTitle);
3767
+ var statement = buildStatement(verbKey, ACTIVITY_TYPES.course, activityDetails, result, null);
3768
+ sendStatement(statement);
3769
+ };
3770
+
3713
3771
  /**
3714
3772
  * Re-extract actor from SCORM after LMSInitialize has been called.
3715
3773
  * Call this from the SCORM wrapper after scormInit() succeeds,
@@ -4380,9 +4438,37 @@ function generateLrsBridgeCode(options) {
4380
4438
  var scormInteractions = {}; // Pending interactions keyed by index N
4381
4439
  var scormInteractionsSent = {}; // Track which interactions were already sent
4382
4440
  var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
4441
+ var scormCourseData = {}; // Accumulates course-level SCORM data (score, status)
4442
+ var scormCompletionSent = false; // Prevent duplicate completion statements
4383
4443
 
4384
4444
  function interceptScormSetValue(key, value) {
4385
4445
  if (typeof key !== 'string') return;
4446
+
4447
+ // ---- Course-level SCORM data (score, status, completion) ----
4448
+ // SCORM 1.2
4449
+ if (key === 'cmi.core.score.raw') { scormCourseData.scoreRaw = parseFloat(value); }
4450
+ if (key === 'cmi.core.score.max') { scormCourseData.scoreMax = parseFloat(value); }
4451
+ if (key === 'cmi.core.score.min') { scormCourseData.scoreMin = parseFloat(value); }
4452
+ if (key === 'cmi.core.lesson_status') { scormCourseData.status = String(value).toLowerCase(); }
4453
+ // SCORM 2004
4454
+ if (key === 'cmi.score.raw') { scormCourseData.scoreRaw = parseFloat(value); }
4455
+ if (key === 'cmi.score.max') { scormCourseData.scoreMax = parseFloat(value); }
4456
+ if (key === 'cmi.score.min') { scormCourseData.scoreMin = parseFloat(value); }
4457
+ if (key === 'cmi.score.scaled') { scormCourseData.scoreScaled = parseFloat(value); }
4458
+ if (key === 'cmi.completion_status') { scormCourseData.completionStatus = String(value).toLowerCase(); }
4459
+ if (key === 'cmi.success_status') { scormCourseData.successStatus = String(value).toLowerCase(); }
4460
+
4461
+ // Fire course completion statement when status indicates pass/fail/complete
4462
+ if ((key === 'cmi.core.lesson_status' || key === 'cmi.success_status' || key === 'cmi.completion_status') && !scormCompletionSent) {
4463
+ var status = String(value).toLowerCase();
4464
+ if (status === 'passed' || status === 'failed' || status === 'completed') {
4465
+ scormCompletionSent = true;
4466
+ // Slight delay to let score values arrive (Rise often sets score just before status)
4467
+ setTimeout(function() { sendCourseCompletionStatement(status); }, 200);
4468
+ }
4469
+ }
4470
+
4471
+ // ---- Interaction-level tracking (existing) ----
4386
4472
  var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
4387
4473
  var match = key.match(interactionPattern);
4388
4474
  if (!match) return;
@@ -4482,6 +4568,51 @@ function generateLrsBridgeCode(options) {
4482
4568
  }
4483
4569
  }
4484
4570
 
4571
+ function sendCourseCompletionStatement(status) {
4572
+ logGroup('Course Completion');
4573
+ log('Status:', status);
4574
+ log('SCORM course data:', scormCourseData);
4575
+
4576
+ var courseTitle = (LRS.courseInfo && LRS.courseInfo.title) ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course';
4577
+
4578
+ // Determine verb
4579
+ var verbKey = 'completed';
4580
+ if (status === 'passed') verbKey = 'passed';
4581
+ if (status === 'failed') verbKey = 'failed';
4582
+
4583
+ // Build result with score
4584
+ var result = { completion: true };
4585
+
4586
+ var raw = scormCourseData.scoreRaw;
4587
+ var max = scormCourseData.scoreMax || 100;
4588
+ var min = scormCourseData.scoreMin || 0;
4589
+
4590
+ if (typeof raw === 'number' && !isNaN(raw)) {
4591
+ result.score = {
4592
+ raw: raw,
4593
+ max: max,
4594
+ min: min,
4595
+ scaled: scormCourseData.scoreScaled != null ? scormCourseData.scoreScaled : (max > min ? (raw - min) / (max - min) : 0)
4596
+ };
4597
+ }
4598
+
4599
+ result.success = (status === 'passed');
4600
+
4601
+ var activityDetails = {
4602
+ courseTitle: courseTitle,
4603
+ completionStatus: status,
4604
+ employeeEmail: LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined,
4605
+ employeeId: LRS.employeeId || undefined
4606
+ };
4607
+
4608
+ log('Sending course completion:', verbKey, 'score:', result.score, 'title:', courseTitle);
4609
+
4610
+ var statement = buildStatement(verbKey, ACTIVITY_TYPES.course, activityDetails, result, null);
4611
+ sendStatement(statement);
4612
+
4613
+ logGroupEnd();
4614
+ }
4615
+
4485
4616
  function setupScormInteractionTracker() {
4486
4617
  if (!TRACK_QUIZZES) return;
4487
4618