@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/index.cjs CHANGED
@@ -3387,6 +3387,64 @@ function generateLrsBridgeCode(options) {
3387
3387
  sendStatement(statement);
3388
3388
  };
3389
3389
 
3390
+ // Send a "launched" statement \u2014 called by skin after email gate / actor setup
3391
+ LRS.sendLaunchStatement = function(data) {
3392
+ data = data || {};
3393
+ var activityDetails = {
3394
+ launchSource: data.source || 'email-gate',
3395
+ employeeEmail: data.email || (LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined),
3396
+ employeeId: data.employeeId || LRS.employeeId || undefined
3397
+ };
3398
+
3399
+ if (data.employeeName) activityDetails.employeeName = data.employeeName;
3400
+
3401
+ var statement = buildStatement('launched', ACTIVITY_TYPES.course, activityDetails, null, null);
3402
+ log('Sending launch statement for:', activityDetails.employeeEmail || '(unknown)');
3403
+ sendStatement(statement);
3404
+ };
3405
+
3406
+ // Send a course completion statement \u2014 called by skin or auto-detected via SCORM
3407
+ LRS.sendCompletionStatement = function(data) {
3408
+ data = data || {};
3409
+ if (scormCompletionSent) {
3410
+ log('Completion statement already sent (SCORM auto-detected). Skipping duplicate.');
3411
+ return;
3412
+ }
3413
+ scormCompletionSent = true;
3414
+
3415
+ var status = data.status || 'completed';
3416
+ var courseTitle = data.courseTitle || (LRS.courseInfo && LRS.courseInfo.title ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course');
3417
+
3418
+ var verbKey = 'completed';
3419
+ if (status === 'passed') verbKey = 'passed';
3420
+ if (status === 'failed') verbKey = 'failed';
3421
+
3422
+ var result = { completion: true, success: (status === 'passed') };
3423
+
3424
+ if (typeof data.score === 'number') {
3425
+ var max = data.scoreMax || 100;
3426
+ var min = data.scoreMin || 0;
3427
+ result.score = {
3428
+ raw: data.score,
3429
+ max: max,
3430
+ min: min,
3431
+ scaled: max > min ? (data.score - min) / (max - min) : 0
3432
+ };
3433
+ }
3434
+
3435
+ var activityDetails = {
3436
+ courseTitle: courseTitle,
3437
+ completionStatus: status,
3438
+ employeeEmail: data.email || (LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined),
3439
+ employeeId: data.employeeId || LRS.employeeId || undefined
3440
+ };
3441
+ if (data.employeeName) activityDetails.employeeName = data.employeeName;
3442
+
3443
+ log('Sending completion statement:', verbKey, 'score:', result.score, 'title:', courseTitle);
3444
+ var statement = buildStatement(verbKey, ACTIVITY_TYPES.course, activityDetails, result, null);
3445
+ sendStatement(statement);
3446
+ };
3447
+
3390
3448
  /**
3391
3449
  * Re-extract actor from SCORM after LMSInitialize has been called.
3392
3450
  * Call this from the SCORM wrapper after scormInit() succeeds,
@@ -4057,9 +4115,37 @@ function generateLrsBridgeCode(options) {
4057
4115
  var scormInteractions = {}; // Pending interactions keyed by index N
4058
4116
  var scormInteractionsSent = {}; // Track which interactions were already sent
4059
4117
  var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
4118
+ var scormCourseData = {}; // Accumulates course-level SCORM data (score, status)
4119
+ var scormCompletionSent = false; // Prevent duplicate completion statements
4060
4120
 
4061
4121
  function interceptScormSetValue(key, value) {
4062
4122
  if (typeof key !== 'string') return;
4123
+
4124
+ // ---- Course-level SCORM data (score, status, completion) ----
4125
+ // SCORM 1.2
4126
+ if (key === 'cmi.core.score.raw') { scormCourseData.scoreRaw = parseFloat(value); }
4127
+ if (key === 'cmi.core.score.max') { scormCourseData.scoreMax = parseFloat(value); }
4128
+ if (key === 'cmi.core.score.min') { scormCourseData.scoreMin = parseFloat(value); }
4129
+ if (key === 'cmi.core.lesson_status') { scormCourseData.status = String(value).toLowerCase(); }
4130
+ // SCORM 2004
4131
+ if (key === 'cmi.score.raw') { scormCourseData.scoreRaw = parseFloat(value); }
4132
+ if (key === 'cmi.score.max') { scormCourseData.scoreMax = parseFloat(value); }
4133
+ if (key === 'cmi.score.min') { scormCourseData.scoreMin = parseFloat(value); }
4134
+ if (key === 'cmi.score.scaled') { scormCourseData.scoreScaled = parseFloat(value); }
4135
+ if (key === 'cmi.completion_status') { scormCourseData.completionStatus = String(value).toLowerCase(); }
4136
+ if (key === 'cmi.success_status') { scormCourseData.successStatus = String(value).toLowerCase(); }
4137
+
4138
+ // Fire course completion statement when status indicates pass/fail/complete
4139
+ if ((key === 'cmi.core.lesson_status' || key === 'cmi.success_status' || key === 'cmi.completion_status') && !scormCompletionSent) {
4140
+ var status = String(value).toLowerCase();
4141
+ if (status === 'passed' || status === 'failed' || status === 'completed') {
4142
+ scormCompletionSent = true;
4143
+ // Slight delay to let score values arrive (Rise often sets score just before status)
4144
+ setTimeout(function() { sendCourseCompletionStatement(status); }, 200);
4145
+ }
4146
+ }
4147
+
4148
+ // ---- Interaction-level tracking (existing) ----
4063
4149
  var interactionPattern = /^cmi\\.interactions\\.(\\d+)\\.(.+)$/;
4064
4150
  var match = key.match(interactionPattern);
4065
4151
  if (!match) return;
@@ -4159,6 +4245,51 @@ function generateLrsBridgeCode(options) {
4159
4245
  }
4160
4246
  }
4161
4247
 
4248
+ function sendCourseCompletionStatement(status) {
4249
+ logGroup('Course Completion');
4250
+ log('Status:', status);
4251
+ log('SCORM course data:', scormCourseData);
4252
+
4253
+ var courseTitle = (LRS.courseInfo && LRS.courseInfo.title) ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course';
4254
+
4255
+ // Determine verb
4256
+ var verbKey = 'completed';
4257
+ if (status === 'passed') verbKey = 'passed';
4258
+ if (status === 'failed') verbKey = 'failed';
4259
+
4260
+ // Build result with score
4261
+ var result = { completion: true };
4262
+
4263
+ var raw = scormCourseData.scoreRaw;
4264
+ var max = scormCourseData.scoreMax || 100;
4265
+ var min = scormCourseData.scoreMin || 0;
4266
+
4267
+ if (typeof raw === 'number' && !isNaN(raw)) {
4268
+ result.score = {
4269
+ raw: raw,
4270
+ max: max,
4271
+ min: min,
4272
+ scaled: scormCourseData.scoreScaled != null ? scormCourseData.scoreScaled : (max > min ? (raw - min) / (max - min) : 0)
4273
+ };
4274
+ }
4275
+
4276
+ result.success = (status === 'passed');
4277
+
4278
+ var activityDetails = {
4279
+ courseTitle: courseTitle,
4280
+ completionStatus: status,
4281
+ employeeEmail: LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined,
4282
+ employeeId: LRS.employeeId || undefined
4283
+ };
4284
+
4285
+ log('Sending course completion:', verbKey, 'score:', result.score, 'title:', courseTitle);
4286
+
4287
+ var statement = buildStatement(verbKey, ACTIVITY_TYPES.course, activityDetails, result, null);
4288
+ sendStatement(statement);
4289
+
4290
+ logGroupEnd();
4291
+ }
4292
+
4162
4293
  function setupScormInteractionTracker() {
4163
4294
  if (!TRACK_QUIZZES) return;
4164
4295