@patch-adams/core 1.5.20 → 1.5.22

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
@@ -1452,8 +1452,8 @@ function generateLrsBridgeCode(options) {
1452
1452
  ', bravaisUserId=' + employeeData.bravaisUserId + ', tenantUrl=' + employeeData.tenantUrl
1453
1453
  : 'NO DATA'));
1454
1454
  if (employeeData && employeeData.email) {
1455
- actor.mbox = 'mailto:' + employeeData.email;
1456
- log('Enhanced actor with email:', employeeData.email);
1455
+ actor.mbox = 'mailto:' + employeeData.email.toLowerCase();
1456
+ log('Enhanced actor with email:', employeeData.email.toLowerCase());
1457
1457
 
1458
1458
  // Also update name if we got a better one
1459
1459
  if (employeeData.name && (!actor.name || actor.name === 'Unknown Learner')) {
@@ -1618,6 +1618,18 @@ function generateLrsBridgeCode(options) {
1618
1618
  return n === 'anonymous learner' || n === 'unknown learner' || n === 'anonymous' || n === 'unknown';
1619
1619
  }
1620
1620
 
1621
+ /**
1622
+ * Normalize actor IFI fields for consistent matching in Bravais Analytics.
1623
+ * Lowercases mbox email (RFC 5321: local-part is case-insensitive in practice).
1624
+ */
1625
+ function normalizeActor(actor) {
1626
+ if (!actor) return actor;
1627
+ if (actor.mbox && actor.mbox.indexOf('mailto:') === 0) {
1628
+ actor.mbox = 'mailto:' + actor.mbox.substring(7).toLowerCase();
1629
+ }
1630
+ return actor;
1631
+ }
1632
+
1621
1633
  /**
1622
1634
  * Persist actor to localStorage for cross-frame sharing.
1623
1635
  * Only stores non-anonymous actors.
@@ -1649,23 +1661,29 @@ function generateLrsBridgeCode(options) {
1649
1661
 
1650
1662
  /**
1651
1663
  * Get the best available actor for statement building.
1652
- * Priority: LRS.actor (if non-anonymous) > localStorage shared actor > LRS.actor > extractActor()
1664
+ * ALWAYS checks localStorage because the skin overlay in another frame
1665
+ * may have updated the actor AFTER this bridge instance initialized.
1666
+ * (e.g., user enters email in skin overlay \u2192 actor persisted to localStorage,
1667
+ * but the bridge in scormcontent/index.html already loaded a stale actor at init)
1653
1668
  */
1654
1669
  function getActor() {
1655
- // If current actor is non-anonymous, use it
1656
- if (LRS.actor && !isAnonymousActor(LRS.actor)) {
1657
- return LRS.actor;
1658
- }
1659
- // Check localStorage for actor set by another frame (e.g., skin overlay)
1670
+ // Always check localStorage for the freshest actor \u2014 it may have been
1671
+ // updated by the skin overlay in another frame since our init
1660
1672
  var shared = loadSharedActor();
1661
1673
  if (shared) {
1662
- // Update local actor so future calls are fast
1663
- LRS.actor = shared;
1664
- log('Actor loaded from cross-frame storage:', shared.name);
1665
- return shared;
1674
+ // Only update if different from current (avoid unnecessary log spam)
1675
+ if (!LRS.actor || LRS.actor.name !== shared.name ||
1676
+ (LRS.actor.account && shared.account && LRS.actor.account.name !== shared.account.name)) {
1677
+ log('Actor updated from cross-frame storage:', shared.name);
1678
+ }
1679
+ LRS.actor = normalizeActor(shared);
1680
+ return LRS.actor;
1666
1681
  }
1667
1682
  // Fallback to current actor or re-extract
1668
- return LRS.actor || extractActor();
1683
+ if (LRS.actor && !isAnonymousActor(LRS.actor)) {
1684
+ return normalizeActor(LRS.actor);
1685
+ }
1686
+ return normalizeActor(LRS.actor || extractActor());
1669
1687
  }
1670
1688
 
1671
1689
  /**
@@ -1675,7 +1693,9 @@ function generateLrsBridgeCode(options) {
1675
1693
  LRS.setActor = function(actor) {
1676
1694
  LRS.actor = actor;
1677
1695
  persistActor(actor);
1678
- // Also try to propagate to other frames in the hierarchy
1696
+
1697
+ // Propagate to ALL frames: walk UP parent chain AND DOWN into child iframes
1698
+ // UP: parent frames (e.g., if skin is in a child iframe)
1679
1699
  try {
1680
1700
  var w = window;
1681
1701
  for (var i = 0; i < 10; i++) {
@@ -1688,9 +1708,29 @@ function generateLrsBridgeCode(options) {
1688
1708
  w = w.parent;
1689
1709
  }
1690
1710
  } catch (e) {}
1711
+
1712
+ // DOWN: child iframes (e.g., scormcontent/index.html has its own bridge)
1713
+ function setActorInChildren(win) {
1714
+ try {
1715
+ var frames = win.frames;
1716
+ for (var j = 0; j < frames.length; j++) {
1717
+ try {
1718
+ if (frames[j].pa_patcher && frames[j].pa_patcher.lrs) {
1719
+ frames[j].pa_patcher.lrs.actor = actor;
1720
+ log('Actor propagated to child frame', j);
1721
+ }
1722
+ // Recurse into nested iframes
1723
+ setActorInChildren(frames[j]);
1724
+ } catch (e) { /* cross-origin child */ }
1725
+ }
1726
+ } catch (e) {}
1727
+ }
1728
+ setActorInChildren(window);
1729
+
1691
1730
  if (window.console && window.console.info) {
1692
1731
  console.info('[PA-LRS] Actor set:', actor.name,
1693
1732
  actor.mbox ? '(' + actor.mbox + ')' : '',
1733
+ actor.account ? '(account: ' + actor.account.name + ')' : '',
1694
1734
  '\u2014 shared across frames');
1695
1735
  }
1696
1736
  };
@@ -1705,9 +1745,9 @@ function generateLrsBridgeCode(options) {
1705
1745
  function finalizeActor(actor) {
1706
1746
  // Try to enhance actor with email if missing
1707
1747
  enhanceActorWithEmail(actor, function(enhancedActor) {
1708
- LRS.actor = enhancedActor;
1709
- persistActor(enhancedActor);
1710
- callback(enhancedActor);
1748
+ LRS.actor = normalizeActor(enhancedActor);
1749
+ persistActor(LRS.actor);
1750
+ callback(LRS.actor);
1711
1751
  });
1712
1752
  }
1713
1753