@appsurify-testmap/rrweb-all 2.1.0-alpha.7 → 2.1.1-alpha.1

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.
@@ -170,6 +170,41 @@ function patch$1(source, name, replacement) {
170
170
  };
171
171
  }
172
172
  }
173
+ function describeNode$1(el) {
174
+ const tag = el.tagName.toLowerCase();
175
+ const id = el.id ? `#${el.id}` : "";
176
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
177
+ return `${tag}${id}${classes}`;
178
+ }
179
+ function getElementVisibility$1(el) {
180
+ var _a2, _b2;
181
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
182
+ const rect = el.getBoundingClientRect();
183
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
184
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
185
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
186
+ const style = (_b2 = win.getComputedStyle) == null ? void 0 : _b2.call(win, el);
187
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
188
+ const isVisible = isStyleVisible2 && isRectVisible;
189
+ let ratio = 0;
190
+ if (isVisible) {
191
+ const xOverlap = Math.max(
192
+ 0,
193
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
194
+ );
195
+ const yOverlap = Math.max(
196
+ 0,
197
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
198
+ );
199
+ const intersectionArea = xOverlap * yOverlap;
200
+ const elementArea = rect.width * rect.height;
201
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
202
+ }
203
+ return {
204
+ isVisible,
205
+ ratio
206
+ };
207
+ }
173
208
  const index$1 = {
174
209
  childNodes: childNodes$1,
175
210
  parentNode: parentNode$1,
@@ -183,7 +218,9 @@ const index$1 = {
183
218
  querySelector: querySelector$1,
184
219
  querySelectorAll: querySelectorAll$1,
185
220
  mutationObserver: mutationObserverCtor$1,
186
- patch: patch$1
221
+ patch: patch$1,
222
+ describeNode: describeNode$1,
223
+ getElementVisibility: getElementVisibility$1
187
224
  };
188
225
  function isElement(n2) {
189
226
  return n2.nodeType === n2.ELEMENT_NODE;
@@ -569,71 +606,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
569
606
  function markCssSplits(cssText, style) {
570
607
  return splitCssText(cssText, style).join("/* rr_split */");
571
608
  }
572
- function getXPath(node2) {
573
- if (node2.nodeType === Node.DOCUMENT_NODE) {
574
- return "/";
609
+ function isSelectorUnique(selector, target) {
610
+ try {
611
+ const matches = document.querySelectorAll(selector);
612
+ return matches.length === 1 && matches[0] === target;
613
+ } catch {
614
+ return false;
575
615
  }
576
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
577
- return "/html/doctype";
616
+ }
617
+ function buildSelector(node2) {
618
+ if (!(node2 instanceof Element)) return null;
619
+ if (node2.id) {
620
+ return `#${CSS.escape(node2.id)}`;
578
621
  }
579
- if (node2.nodeType === Node.ELEMENT_NODE) {
580
- const element = node2;
581
- if (element.id) {
582
- return `//*[@id="${element.id}"]`;
583
- }
584
- if (element.tagName && element.tagName.toLowerCase() === "html") {
585
- return "/html";
586
- }
587
- if (element === document.head) {
588
- return "/html/head";
589
- }
590
- if (element === document.body) {
591
- return "/html/body";
592
- }
593
- const parentNode2 = element.parentNode;
594
- if (!parentNode2) {
595
- return "";
596
- }
597
- const siblings = Array.from(parentNode2.children).filter(
598
- (sibling) => sibling.tagName === element.tagName
599
- );
600
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
601
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
622
+ const parts = [];
623
+ const tag = node2.tagName.toLowerCase();
624
+ if (node2.classList.length) {
625
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
602
626
  }
603
- if (node2.nodeType === Node.TEXT_NODE) {
604
- const parent = node2.parentNode;
605
- if (!parent) {
606
- return "";
627
+ Array.from(node2.attributes).forEach((attr) => {
628
+ if (attr.name.startsWith("data-")) {
629
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
607
630
  }
608
- const textSiblings = Array.from(parent.childNodes).filter(
609
- (sibling) => sibling.nodeType === Node.TEXT_NODE
610
- );
611
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
612
- return `${getXPath(parent)}/text()${index2}`;
613
- }
614
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
615
- const parent = node2.parentNode;
616
- if (!parent) {
617
- return "";
631
+ });
632
+ const shortSelector = `${tag}${parts.join("")}`;
633
+ if (isSelectorUnique(shortSelector, node2)) {
634
+ return shortSelector;
635
+ }
636
+ const pathParts = [];
637
+ let current = node2;
638
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
639
+ const parent = current.parentElement;
640
+ const tagName = current.tagName.toLowerCase();
641
+ let nth = "";
642
+ if (parent) {
643
+ const siblings = Array.from(parent.children).filter(
644
+ (el) => el.tagName.toLowerCase() === tagName
645
+ );
646
+ if (siblings.length > 1) {
647
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
648
+ }
618
649
  }
619
- const cdataSiblings = Array.from(parent.childNodes).filter(
620
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
621
- );
622
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
623
- return `${getXPath(parent)}/text()${index2}`;
650
+ pathParts.unshift(`${tagName}${nth}`);
651
+ current = parent;
624
652
  }
625
- if (node2.nodeType === Node.COMMENT_NODE) {
626
- const parent = node2.parentNode;
627
- if (!parent) {
628
- return "";
653
+ return pathParts.join(" > ") || null;
654
+ }
655
+ function buildXPath(node2) {
656
+ switch (node2.nodeType) {
657
+ case Node.DOCUMENT_NODE:
658
+ return "/";
659
+ case Node.DOCUMENT_TYPE_NODE:
660
+ return "/html/doctype";
661
+ case Node.ELEMENT_NODE: {
662
+ const element = node2;
663
+ if (element.id) {
664
+ return `//*[@id="${CSS.escape(element.id)}"]`;
665
+ }
666
+ if (element.tagName.toLowerCase() === "html") return "/html";
667
+ if (element === document.head) return "/html/head";
668
+ if (element === document.body) return "/html/body";
669
+ const parent = element.parentNode;
670
+ if (!parent) return "";
671
+ const tag = element.tagName.toLowerCase();
672
+ const siblings = Array.from(parent.children).filter(
673
+ (el) => el.tagName.toLowerCase() === tag
674
+ );
675
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
676
+ return `${buildXPath(parent)}/${tag}${index2}`;
677
+ }
678
+ case Node.TEXT_NODE:
679
+ case Node.CDATA_SECTION_NODE:
680
+ case Node.COMMENT_NODE: {
681
+ const parent = node2.parentNode;
682
+ if (!parent) return "";
683
+ const typeMap = {
684
+ [Node.TEXT_NODE]: "text()",
685
+ [Node.CDATA_SECTION_NODE]: "text()",
686
+ // CDATA ≡ text() в XPath
687
+ [Node.COMMENT_NODE]: "comment()"
688
+ };
689
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
690
+ (sibling) => sibling.nodeType === node2.nodeType
691
+ );
692
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
693
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
629
694
  }
630
- const commentSiblings = Array.from(parent.childNodes).filter(
631
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
632
- );
633
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
634
- return `${getXPath(parent)}/comment()${index2}`;
695
+ default:
696
+ return "";
635
697
  }
636
- return "";
637
698
  }
638
699
  function isTextVisible(n2) {
639
700
  var _a2;
@@ -649,20 +710,9 @@ function isTextVisible(n2) {
649
710
  const textContent2 = (_a2 = n2.textContent) == null ? void 0 : _a2.trim();
650
711
  return textContent2 !== "";
651
712
  }
652
- function isElementVisible(n2) {
653
- var _a2;
654
- const win = ((_a2 = n2.ownerDocument) == null ? void 0 : _a2.defaultView) ?? null;
655
- const style = win ? win.getComputedStyle(n2) : null;
656
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
657
- const rect = n2.getBoundingClientRect();
658
- const result2 = isStyleVisible && isRectVisible(rect);
659
- return result2;
660
- }
661
- function isRectVisible(rect, win = window) {
662
- var _a2, _b2, _c, _d;
663
- const height = (win == null ? void 0 : win.innerHeight) ?? ((_b2 = (_a2 = win == null ? void 0 : win.document) == null ? void 0 : _a2.documentElement) == null ? void 0 : _b2.clientHeight) ?? 0;
664
- const width = (win == null ? void 0 : win.innerWidth) ?? ((_d = (_c = win == null ? void 0 : win.document) == null ? void 0 : _c.documentElement) == null ? void 0 : _d.clientWidth) ?? 0;
665
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
713
+ function isElementVisible(el) {
714
+ const visibility = index$1.getElementVisibility(el);
715
+ return visibility.isVisible;
666
716
  }
667
717
  const interactiveEvents$1 = [
668
718
  "change",
@@ -689,19 +739,6 @@ const interactiveEvents$1 = [
689
739
  "touchend",
690
740
  "touchcancel"
691
741
  ];
692
- const interactiveTags = [
693
- "a",
694
- "button",
695
- "input",
696
- "select",
697
- "textarea",
698
- "label",
699
- "details",
700
- "summary",
701
- "dialog",
702
- "video",
703
- "audio"
704
- ];
705
742
  const inlineEventAttributes$1 = [
706
743
  "onclick",
707
744
  "ondblclick",
@@ -741,25 +778,6 @@ const originalRemoveEventListener$1 = EventTarget.prototype.removeEventListener;
741
778
  EventTarget.prototype.removeEventListener = function(type, listener, options) {
742
779
  originalRemoveEventListener$1.call(this, type, listener, options);
743
780
  };
744
- function hasEventListeners(n2) {
745
- return n2 instanceof Element && interactiveElementsRegistry$1.has(n2);
746
- }
747
- function isElementInteractive(n2) {
748
- if (n2.nodeType === Node.ELEMENT_NODE) {
749
- const element = n2;
750
- const tagName = element.tagName.toLowerCase();
751
- if (interactiveTags.includes(tagName)) {
752
- return true;
753
- }
754
- const hasTabIndex = element.hasAttribute("tabindex") && element.getAttribute("tabindex") !== "-1";
755
- const hasRoleInteractive = ["button", "link", "checkbox", "switch", "menuitem"].includes(
756
- element.getAttribute("role") || ""
757
- );
758
- const result2 = hasEventListeners(element) || hasTabIndex || hasRoleInteractive || element instanceof HTMLAnchorElement && element.hasAttribute("href") || element instanceof HTMLButtonElement && !element.disabled;
759
- return result2;
760
- }
761
- return false;
762
- }
763
781
  function inspectInlineEventHandlers$1() {
764
782
  const allElements = document.querySelectorAll("*");
765
783
  allElements.forEach((el) => {
@@ -895,9 +913,6 @@ function transformAttribute(doc, tagName, name, value) {
895
913
  function ignoreAttribute(tagName, name, _value) {
896
914
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
897
915
  }
898
- function isIncludeAttribute(name, include) {
899
- return typeof include === "string" ? name.includes(include) : include.test(name);
900
- }
901
916
  function isExcludeAttribute(name, exclude) {
902
917
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
903
918
  }
@@ -1031,7 +1046,6 @@ function serializeNode(n2, options) {
1031
1046
  blockClass,
1032
1047
  blockSelector,
1033
1048
  excludeAttribute,
1034
- includeAttribute,
1035
1049
  needsMask,
1036
1050
  inlineStylesheet,
1037
1051
  maskInputOptions = {},
@@ -1045,22 +1059,19 @@ function serializeNode(n2, options) {
1045
1059
  cssCaptured = false
1046
1060
  } = options;
1047
1061
  const rootId = getRootId(doc, mirror2);
1048
- const xPath = getXPath(n2);
1049
1062
  switch (n2.nodeType) {
1050
1063
  case n2.DOCUMENT_NODE:
1051
1064
  if (n2.compatMode !== "CSS1Compat") {
1052
1065
  return {
1053
1066
  type: NodeType$3.Document,
1054
1067
  childNodes: [],
1055
- xPath,
1056
1068
  compatMode: n2.compatMode
1057
1069
  // probably "BackCompat"
1058
1070
  };
1059
1071
  } else {
1060
1072
  return {
1061
1073
  type: NodeType$3.Document,
1062
- childNodes: [],
1063
- xPath
1074
+ childNodes: []
1064
1075
  };
1065
1076
  }
1066
1077
  case n2.DOCUMENT_TYPE_NODE:
@@ -1069,8 +1080,7 @@ function serializeNode(n2, options) {
1069
1080
  name: n2.name,
1070
1081
  publicId: n2.publicId,
1071
1082
  systemId: n2.systemId,
1072
- rootId,
1073
- xPath
1083
+ rootId
1074
1084
  };
1075
1085
  case n2.ELEMENT_NODE:
1076
1086
  return serializeElementNode(n2, {
@@ -1078,7 +1088,6 @@ function serializeNode(n2, options) {
1078
1088
  blockClass,
1079
1089
  blockSelector,
1080
1090
  excludeAttribute,
1081
- includeAttribute,
1082
1091
  inlineStylesheet,
1083
1092
  maskInputOptions,
1084
1093
  maskInputFn,
@@ -1087,8 +1096,7 @@ function serializeNode(n2, options) {
1087
1096
  recordCanvas,
1088
1097
  keepIframeSrcFn,
1089
1098
  newlyAddedElement,
1090
- rootId,
1091
- xPath
1099
+ rootId
1092
1100
  });
1093
1101
  case n2.TEXT_NODE:
1094
1102
  return serializeTextNode(n2, {
@@ -1096,22 +1104,19 @@ function serializeNode(n2, options) {
1096
1104
  needsMask,
1097
1105
  maskTextFn,
1098
1106
  rootId,
1099
- cssCaptured,
1100
- xPath
1107
+ cssCaptured
1101
1108
  });
1102
1109
  case n2.CDATA_SECTION_NODE:
1103
1110
  return {
1104
1111
  type: NodeType$3.CDATA,
1105
1112
  textContent: "",
1106
- rootId,
1107
- xPath
1113
+ rootId
1108
1114
  };
1109
1115
  case n2.COMMENT_NODE:
1110
1116
  return {
1111
1117
  type: NodeType$3.Comment,
1112
1118
  textContent: index$1.textContent(n2) || "",
1113
- rootId,
1114
- xPath
1119
+ rootId
1115
1120
  };
1116
1121
  default:
1117
1122
  return false;
@@ -1123,7 +1128,7 @@ function getRootId(doc, mirror2) {
1123
1128
  return docId === 1 ? void 0 : docId;
1124
1129
  }
1125
1130
  function serializeTextNode(n2, options) {
1126
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1131
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1127
1132
  const parent = index$1.parentNode(n2);
1128
1133
  const parentTagName = parent && parent.tagName;
1129
1134
  let textContent2 = "";
@@ -1140,15 +1145,10 @@ function serializeTextNode(n2, options) {
1140
1145
  if (!isStyle && !isScript && textContent2 && needsMask) {
1141
1146
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1142
1147
  }
1143
- const isVisible = isTextVisible(n2);
1144
- const isInteractive = isElementInteractive(n2);
1145
1148
  return {
1146
1149
  type: NodeType$3.Text,
1147
1150
  textContent: textContent2 || "",
1148
- rootId,
1149
- isVisible,
1150
- isInteractive,
1151
- xPath
1151
+ rootId
1152
1152
  };
1153
1153
  }
1154
1154
  function serializeElementNode(n2, options) {
@@ -1157,7 +1157,6 @@ function serializeElementNode(n2, options) {
1157
1157
  blockClass,
1158
1158
  blockSelector,
1159
1159
  excludeAttribute,
1160
- includeAttribute,
1161
1160
  inlineStylesheet,
1162
1161
  maskInputOptions = {},
1163
1162
  maskInputFn,
@@ -1166,8 +1165,7 @@ function serializeElementNode(n2, options) {
1166
1165
  recordCanvas,
1167
1166
  keepIframeSrcFn,
1168
1167
  newlyAddedElement = false,
1169
- rootId,
1170
- xPath
1168
+ rootId
1171
1169
  } = options;
1172
1170
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1173
1171
  const tagName = getValidTagName$1(n2);
@@ -1175,7 +1173,7 @@ function serializeElementNode(n2, options) {
1175
1173
  const len = n2.attributes.length;
1176
1174
  for (let i2 = 0; i2 < len; i2++) {
1177
1175
  const attr = n2.attributes[i2];
1178
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1176
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1179
1177
  continue;
1180
1178
  }
1181
1179
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1337,8 +1335,6 @@ function serializeElementNode(n2, options) {
1337
1335
  if (customElements.get(tagName)) isCustomElement = true;
1338
1336
  } catch (e2) {
1339
1337
  }
1340
- const isVisible = isElementVisible(n2);
1341
- const isInteractive = isElementInteractive(n2);
1342
1338
  return {
1343
1339
  type: NodeType$3.Element,
1344
1340
  tagName,
@@ -1347,10 +1343,7 @@ function serializeElementNode(n2, options) {
1347
1343
  isSVG: isSVGElement(n2) || void 0,
1348
1344
  needBlock,
1349
1345
  rootId,
1350
- isCustom: isCustomElement,
1351
- isVisible,
1352
- isInteractive,
1353
- xPath
1346
+ isCustom: isCustomElement
1354
1347
  };
1355
1348
  }
1356
1349
  function lowerIfExists(maybeAttr) {
@@ -1401,7 +1394,6 @@ function serializeNodeWithId(n2, options) {
1401
1394
  maskTextClass,
1402
1395
  maskTextSelector,
1403
1396
  excludeAttribute,
1404
- includeAttribute,
1405
1397
  skipChild = false,
1406
1398
  inlineStylesheet = true,
1407
1399
  maskInputOptions = {},
@@ -1437,7 +1429,6 @@ function serializeNodeWithId(n2, options) {
1437
1429
  blockClass,
1438
1430
  blockSelector,
1439
1431
  excludeAttribute,
1440
- includeAttribute,
1441
1432
  needsMask,
1442
1433
  inlineStylesheet,
1443
1434
  maskInputOptions,
@@ -1463,6 +1454,21 @@ function serializeNodeWithId(n2, options) {
1463
1454
  id = genId();
1464
1455
  }
1465
1456
  const serializedNode = Object.assign(_serializedNode, { id });
1457
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1458
+ serializedNode.xpath = buildXPath(n2);
1459
+ if (isElement(n2)) {
1460
+ const selector = buildSelector(n2);
1461
+ if (selector) {
1462
+ serializedNode.selector = selector;
1463
+ }
1464
+ }
1465
+ if (n2.nodeType === Node.TEXT_NODE) {
1466
+ serializedNode.isVisible = isTextVisible(n2);
1467
+ }
1468
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1469
+ serializedNode.isVisible = isElementVisible(n2);
1470
+ }
1471
+ }
1466
1472
  mirror2.add(n2, serializedNode);
1467
1473
  if (id === IGNORED_NODE) {
1468
1474
  return null;
@@ -1491,7 +1497,6 @@ function serializeNodeWithId(n2, options) {
1491
1497
  maskTextClass,
1492
1498
  maskTextSelector,
1493
1499
  excludeAttribute,
1494
- includeAttribute,
1495
1500
  skipChild,
1496
1501
  inlineStylesheet,
1497
1502
  maskInputOptions,
@@ -1552,7 +1557,6 @@ function serializeNodeWithId(n2, options) {
1552
1557
  maskTextClass,
1553
1558
  maskTextSelector,
1554
1559
  excludeAttribute,
1555
- includeAttribute,
1556
1560
  skipChild: false,
1557
1561
  inlineStylesheet,
1558
1562
  maskInputOptions,
@@ -1595,7 +1599,6 @@ function serializeNodeWithId(n2, options) {
1595
1599
  maskTextClass,
1596
1600
  maskTextSelector,
1597
1601
  excludeAttribute,
1598
- includeAttribute,
1599
1602
  skipChild: false,
1600
1603
  inlineStylesheet,
1601
1604
  maskInputOptions,
@@ -1633,8 +1636,7 @@ function snapshot(n2, options) {
1633
1636
  blockSelector = null,
1634
1637
  maskTextClass = "rr-mask",
1635
1638
  maskTextSelector = null,
1636
- excludeAttribute = /^$a/,
1637
- includeAttribute = /.+/i,
1639
+ excludeAttribute = /.^/,
1638
1640
  inlineStylesheet = true,
1639
1641
  inlineImages = false,
1640
1642
  recordCanvas = false,
@@ -1695,7 +1697,6 @@ function snapshot(n2, options) {
1695
1697
  maskTextClass,
1696
1698
  maskTextSelector,
1697
1699
  excludeAttribute,
1698
- includeAttribute,
1699
1700
  skipChild: false,
1700
1701
  inlineStylesheet,
1701
1702
  maskInputOptions,
@@ -10834,6 +10835,41 @@ function patch(source, name, replacement) {
10834
10835
  };
10835
10836
  }
10836
10837
  }
10838
+ function describeNode(el) {
10839
+ const tag = el.tagName.toLowerCase();
10840
+ const id = el.id ? `#${el.id}` : "";
10841
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
10842
+ return `${tag}${id}${classes}`;
10843
+ }
10844
+ function getElementVisibility(el) {
10845
+ var _a2, _b2;
10846
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
10847
+ const rect = el.getBoundingClientRect();
10848
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
10849
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
10850
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
10851
+ const style = (_b2 = win.getComputedStyle) == null ? void 0 : _b2.call(win, el);
10852
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
10853
+ const isVisible = isStyleVisible2 && isRectVisible;
10854
+ let ratio = 0;
10855
+ if (isVisible) {
10856
+ const xOverlap = Math.max(
10857
+ 0,
10858
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
10859
+ );
10860
+ const yOverlap = Math.max(
10861
+ 0,
10862
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
10863
+ );
10864
+ const intersectionArea = xOverlap * yOverlap;
10865
+ const elementArea = rect.width * rect.height;
10866
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
10867
+ }
10868
+ return {
10869
+ isVisible,
10870
+ ratio
10871
+ };
10872
+ }
10837
10873
  const index = {
10838
10874
  childNodes,
10839
10875
  parentNode,
@@ -10847,7 +10883,9 @@ const index = {
10847
10883
  querySelector,
10848
10884
  querySelectorAll,
10849
10885
  mutationObserver: mutationObserverCtor,
10850
- patch
10886
+ patch,
10887
+ describeNode,
10888
+ getElementVisibility
10851
10889
  };
10852
10890
  function on(type, fn, target = document) {
10853
10891
  const options = { capture: true, passive: true };
@@ -11248,6 +11286,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
11248
11286
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
11249
11287
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
11250
11288
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
11289
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
11251
11290
  return IncrementalSource2;
11252
11291
  })(IncrementalSource || {});
11253
11292
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -11414,7 +11453,6 @@ class MutationBuffer {
11414
11453
  __publicField(this, "maskTextClass");
11415
11454
  __publicField(this, "maskTextSelector");
11416
11455
  __publicField(this, "excludeAttribute");
11417
- __publicField(this, "includeAttribute");
11418
11456
  __publicField(this, "inlineStylesheet");
11419
11457
  __publicField(this, "maskInputOptions");
11420
11458
  __publicField(this, "maskTextFn");
@@ -11430,6 +11468,7 @@ class MutationBuffer {
11430
11468
  __publicField(this, "stylesheetManager");
11431
11469
  __publicField(this, "shadowDomManager");
11432
11470
  __publicField(this, "canvasManager");
11471
+ __publicField(this, "visibilityManager");
11433
11472
  __publicField(this, "processedNodeManager");
11434
11473
  __publicField(this, "unattachedDoc");
11435
11474
  __publicField(this, "processMutations", (mutations) => {
@@ -11479,7 +11518,6 @@ class MutationBuffer {
11479
11518
  maskTextClass: this.maskTextClass,
11480
11519
  maskTextSelector: this.maskTextSelector,
11481
11520
  excludeAttribute: this.excludeAttribute,
11482
- includeAttribute: this.includeAttribute,
11483
11521
  skipChild: true,
11484
11522
  newlyAddedElement: true,
11485
11523
  inlineStylesheet: this.inlineStylesheet,
@@ -11525,7 +11563,8 @@ class MutationBuffer {
11525
11563
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
11526
11564
  }
11527
11565
  for (const n2 of this.movedSet) {
11528
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
11566
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11567
+ !this.movedSet.has(index.parentNode(n2))) {
11529
11568
  continue;
11530
11569
  }
11531
11570
  pushAdd(n2);
@@ -11681,12 +11720,60 @@ class MutationBuffer {
11681
11720
  const target = m.target;
11682
11721
  let attributeName = m.attributeName;
11683
11722
  let value = m.target.getAttribute(attributeName);
11684
- const propValue = target[attributeName];
11685
- const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
11723
+ const attrKey = attributeName;
11724
+ const propValue = target[attrKey];
11725
+ const isBooleanAttr = typeof propValue === "boolean";
11726
+ const inDOM = document.contains(target);
11727
+ const isVisible = isElementVisible(target);
11728
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
11729
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
11730
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
11731
+ m.oldValue !== null && // раньше он был
11732
+ (propValue === "" || // свойство = пустая строка
11733
+ propValue === null || // или null
11734
+ typeof propValue === "undefined");
11686
11735
  if (isPhantomAttributeMutation) {
11736
+ console.debug(
11737
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ phantom attribute mutation ignored`,
11738
+ {
11739
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11740
+ node: index.describeNode(target),
11741
+ tag: target.tagName,
11742
+ nodeType: target.nodeType,
11743
+ attribute: attributeName,
11744
+ value,
11745
+ oldValue: m.oldValue,
11746
+ excludeAttribute: this.excludeAttribute,
11747
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11748
+ propValue,
11749
+ isBooleanAttr,
11750
+ inDOM,
11751
+ isVisible,
11752
+ isExcludeAttributeName
11753
+ }
11754
+ );
11687
11755
  return;
11688
11756
  }
11689
11757
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
11758
+ console.debug(
11759
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ excluded attribute mutation ignored`,
11760
+ {
11761
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11762
+ node: index.describeNode(target),
11763
+ tag: target.tagName,
11764
+ nodeType: target.nodeType,
11765
+ attribute: attributeName,
11766
+ value,
11767
+ oldValue: m.oldValue,
11768
+ excludeAttribute: this.excludeAttribute,
11769
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11770
+ propValue,
11771
+ isBooleanAttr,
11772
+ inDOM,
11773
+ isVisible,
11774
+ isExcludeAttributeName
11775
+ }
11776
+ );
11690
11777
  return;
11691
11778
  }
11692
11779
  if (attributeName === "value") {
@@ -11731,9 +11818,35 @@ class MutationBuffer {
11731
11818
  toLowerCase(attributeName),
11732
11819
  value
11733
11820
  );
11734
- if (value === item.attributes[attributeName]) {
11735
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
11736
- return;
11821
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
11822
+ value === "" || // класс удалён
11823
+ value !== m.oldValue);
11824
+ if (isSuspiciousClassMutation) {
11825
+ console.debug(
11826
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⚠️ suspicious attribute mutation`,
11827
+ {
11828
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11829
+ reason: [
11830
+ value === m.oldValue ? "no change in value" : null,
11831
+ value === propValue ? "mirrored in DOM property" : null,
11832
+ value === item.attributes[attributeName] ? "redundant assignment" : null
11833
+ ].filter(Boolean).join(", ") || "uncategorized",
11834
+ node: index.describeNode(target),
11835
+ tag: target.tagName,
11836
+ nodeType: target.nodeType,
11837
+ attribute: attributeName,
11838
+ value,
11839
+ oldValue: m.oldValue,
11840
+ transformedValue: item.attributes[attributeName],
11841
+ excludeAttribute: this.excludeAttribute,
11842
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11843
+ propValue,
11844
+ isBooleanAttr,
11845
+ inDOM,
11846
+ isVisible,
11847
+ isExcludeAttributeName
11848
+ }
11849
+ );
11737
11850
  }
11738
11851
  if (attributeName === "style") {
11739
11852
  if (!this.unattachedDoc) {
@@ -11848,7 +11961,6 @@ class MutationBuffer {
11848
11961
  "maskTextClass",
11849
11962
  "maskTextSelector",
11850
11963
  "excludeAttribute",
11851
- "includeAttribute",
11852
11964
  "inlineStylesheet",
11853
11965
  "maskInputOptions",
11854
11966
  "maskTextFn",
@@ -11864,6 +11976,7 @@ class MutationBuffer {
11864
11976
  "stylesheetManager",
11865
11977
  "shadowDomManager",
11866
11978
  "canvasManager",
11979
+ "visibilityManager",
11867
11980
  "processedNodeManager"
11868
11981
  ].forEach((key) => {
11869
11982
  this[key] = options[key];
@@ -11872,10 +11985,12 @@ class MutationBuffer {
11872
11985
  freeze() {
11873
11986
  this.frozen = true;
11874
11987
  this.canvasManager.freeze();
11988
+ this.visibilityManager.freeze();
11875
11989
  }
11876
11990
  unfreeze() {
11877
11991
  this.frozen = false;
11878
11992
  this.canvasManager.unfreeze();
11993
+ this.visibilityManager.unfreeze();
11879
11994
  this.emit();
11880
11995
  }
11881
11996
  isFrozen() {
@@ -11884,15 +11999,18 @@ class MutationBuffer {
11884
11999
  lock() {
11885
12000
  this.locked = true;
11886
12001
  this.canvasManager.lock();
12002
+ this.visibilityManager.lock();
11887
12003
  }
11888
12004
  unlock() {
11889
12005
  this.locked = false;
11890
12006
  this.canvasManager.unlock();
12007
+ this.visibilityManager.unlock();
11891
12008
  this.emit();
11892
12009
  }
11893
12010
  reset() {
11894
12011
  this.shadowDomManager.reset();
11895
12012
  this.canvasManager.reset();
12013
+ this.visibilityManager.reset();
11896
12014
  }
11897
12015
  }
11898
12016
  function deepDelete(addsSet, n2) {
@@ -12826,6 +12944,7 @@ function mergeHooks(o2, hooks) {
12826
12944
  styleSheetRuleCb,
12827
12945
  styleDeclarationCb,
12828
12946
  canvasMutationCb,
12947
+ visibilityMutationCb,
12829
12948
  fontCb,
12830
12949
  selectionCb,
12831
12950
  customElementCb
@@ -12890,6 +13009,12 @@ function mergeHooks(o2, hooks) {
12890
13009
  }
12891
13010
  canvasMutationCb(...p);
12892
13011
  };
13012
+ o2.visibilityMutationCb = (...p) => {
13013
+ if (hooks.visibilityMutation) {
13014
+ hooks.visibilityMutation(...p);
13015
+ }
13016
+ visibilityMutationCb(...p);
13017
+ };
12893
13018
  o2.fontCb = (...p) => {
12894
13019
  if (hooks.font) {
12895
13020
  hooks.font(...p);
@@ -14023,11 +14148,249 @@ class ProcessedNodeManager {
14023
14148
  destroy() {
14024
14149
  }
14025
14150
  }
14151
+ function computeVisibility(elements, previous, options) {
14152
+ const root2 = (options == null ? void 0 : options.root) ?? null;
14153
+ const threshold = (options == null ? void 0 : options.threshold) ?? 0.5;
14154
+ const sensitivity = (options == null ? void 0 : options.sensitivity) ?? 0.05;
14155
+ const rootMarginFn = parseRootMargin((options == null ? void 0 : options.rootMargin) ?? "0px");
14156
+ const current = /* @__PURE__ */ new Map();
14157
+ const rootRect = getRootRect(root2);
14158
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
14159
+ for (const el of elements) {
14160
+ const elRect = el.getBoundingClientRect();
14161
+ let intersectionRect = emptyRect();
14162
+ let intersectionRatio = 0;
14163
+ if (elRect.width > 0 && elRect.height > 0) {
14164
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
14165
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
14166
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
14167
+ }
14168
+ const isStyle = isStyleVisible(el);
14169
+ const old = previous.get(el) ?? null;
14170
+ const prevRatio = (old == null ? void 0 : old.intersectionRatio) ?? 0;
14171
+ const currRatio = intersectionRatio;
14172
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
14173
+ const nowVisible = isStyle && currRatio > threshold;
14174
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
14175
+ if (changed) {
14176
+ current.set(el, {
14177
+ target: el,
14178
+ isVisible: nowVisible,
14179
+ isStyleVisible: isStyle,
14180
+ intersectionRatio: currRatio,
14181
+ intersectionRect,
14182
+ oldValue: old
14183
+ });
14184
+ } else {
14185
+ current.set(el, old);
14186
+ }
14187
+ }
14188
+ return current;
14189
+ }
14190
+ function parseRootMargin(marginStr) {
14191
+ const parts = marginStr.trim().split(/\s+/);
14192
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
14193
+ return function(rootRect) {
14194
+ const top = getValue(parts[0] || "0px", rootRect.height);
14195
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
14196
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
14197
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
14198
+ return { top, right, bottom, left, width: 0, height: 0 };
14199
+ };
14200
+ }
14201
+ function getRootRect(root2) {
14202
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
14203
+ }
14204
+ function expandRootRect(rect, marginFn) {
14205
+ const margin = marginFn(rect);
14206
+ return new DOMRect(
14207
+ rect.left - margin.left,
14208
+ rect.top - margin.top,
14209
+ rect.width + margin.left + margin.right,
14210
+ rect.height + margin.top + margin.bottom
14211
+ );
14212
+ }
14213
+ function computeIntersectionRect(a2, b) {
14214
+ const top = Math.max(a2.top, b.top);
14215
+ const left = Math.max(a2.left, b.left);
14216
+ const bottom = Math.min(a2.bottom, b.bottom);
14217
+ const right = Math.min(a2.right, b.right);
14218
+ const width = Math.max(0, right - left);
14219
+ const height = Math.max(0, bottom - top);
14220
+ return { top, left, bottom, right, width, height };
14221
+ }
14222
+ function computeIntersectionRatio(elRect, intersectionRect) {
14223
+ const elArea = elRect.width * elRect.height;
14224
+ const intArea = intersectionRect.width * intersectionRect.height;
14225
+ return elArea > 0 ? intArea / elArea : 0;
14226
+ }
14227
+ function emptyRect() {
14228
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
14229
+ }
14230
+ function isStyleVisible(el) {
14231
+ const style = getComputedStyle(el);
14232
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
14233
+ }
14234
+ class VisibilityManager {
14235
+ constructor(options) {
14236
+ __publicField(this, "frozen", false);
14237
+ __publicField(this, "locked", false);
14238
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
14239
+ __publicField(this, "mirror");
14240
+ __publicField(this, "mutationCb");
14241
+ __publicField(this, "rafId", null);
14242
+ __publicField(this, "rafThrottle");
14243
+ __publicField(this, "lastFlushTime", 0);
14244
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
14245
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
14246
+ __publicField(this, "root", null);
14247
+ __publicField(this, "threshold");
14248
+ __publicField(this, "sensitivity");
14249
+ __publicField(this, "rootMargin");
14250
+ __publicField(this, "hasInitialized", false);
14251
+ __publicField(this, "mode", "none");
14252
+ __publicField(this, "debounce", 50);
14253
+ __publicField(this, "throttle", 100);
14254
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
14255
+ __publicField(this, "debounceTimer", null);
14256
+ __publicField(this, "lastThrottleTime", 0);
14257
+ __publicField(this, "disabled", false);
14258
+ __publicField(this, "notifyActivity");
14259
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
14260
+ this.mirror = mirror2;
14261
+ this.mutationCb = mutationCb;
14262
+ this.notifyActivity = notifyActivity;
14263
+ this.rootMargin = "0px";
14264
+ if (sampling === false) {
14265
+ this.disabled = true;
14266
+ return;
14267
+ }
14268
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
14269
+ this.mode = (visibilitySampling == null ? void 0 : visibilitySampling.mode) ?? "none";
14270
+ this.debounce = Number((visibilitySampling == null ? void 0 : visibilitySampling.debounce) ?? 100);
14271
+ this.throttle = Number((visibilitySampling == null ? void 0 : visibilitySampling.throttle) ?? 100);
14272
+ this.threshold = Number((visibilitySampling == null ? void 0 : visibilitySampling.threshold) ?? 0.5);
14273
+ this.sensitivity = Number((visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) ?? 0.05);
14274
+ this.rafThrottle = Number((visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) ?? 100);
14275
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
14276
+ const mo = new MutationObserver((mutations) => {
14277
+ mutations.forEach((m) => {
14278
+ m.addedNodes.forEach((n2) => {
14279
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14280
+ this.observe(n2);
14281
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
14282
+ }
14283
+ });
14284
+ m.removedNodes.forEach((n2) => {
14285
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14286
+ this.unobserve(n2);
14287
+ }
14288
+ });
14289
+ });
14290
+ });
14291
+ mo.observe(doc.body, { childList: true, subtree: true });
14292
+ this.startPendingFlushLoop();
14293
+ }
14294
+ startPendingFlushLoop() {
14295
+ if (this.disabled) return;
14296
+ const loop = (timestamp) => {
14297
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
14298
+ this.lastFlushTime = timestamp;
14299
+ this.flushPendingVisibilityMutations();
14300
+ }
14301
+ this.rafId = requestAnimationFrame(loop);
14302
+ };
14303
+ this.rafId = requestAnimationFrame(loop);
14304
+ }
14305
+ flushPendingVisibilityMutations() {
14306
+ if (this.disabled) return;
14307
+ if (this.frozen || this.locked || this.elements.size === 0) return;
14308
+ const state = computeVisibility(this.elements, this.previousState, {
14309
+ root: this.root,
14310
+ threshold: this.threshold,
14311
+ sensitivity: this.sensitivity,
14312
+ rootMargin: this.rootMargin
14313
+ });
14314
+ for (const [el, entry] of state.entries()) {
14315
+ const old = this.previousState.get(el);
14316
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
14317
+ if (changed) {
14318
+ const id = this.mirror.getId(el);
14319
+ if (id !== -1) {
14320
+ this.buffer.set(el, {
14321
+ id,
14322
+ isVisible: entry.isVisible,
14323
+ ratio: entry.intersectionRatio
14324
+ });
14325
+ }
14326
+ this.previousState.set(el, entry);
14327
+ }
14328
+ }
14329
+ this.previousState = state;
14330
+ if (!this.hasInitialized) {
14331
+ this.hasInitialized = true;
14332
+ this.buffer.clear();
14333
+ return;
14334
+ }
14335
+ this.scheduleEmit();
14336
+ }
14337
+ scheduleEmit() {
14338
+ if (this.mode === "debounce") {
14339
+ clearTimeout(this.debounceTimer);
14340
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
14341
+ } else if (this.mode === "throttle") {
14342
+ const now = performance.now();
14343
+ if (now - this.lastThrottleTime >= this.throttle) {
14344
+ this.lastThrottleTime = now;
14345
+ this.flushBuffer();
14346
+ }
14347
+ } else {
14348
+ this.flushBuffer();
14349
+ }
14350
+ }
14351
+ flushBuffer() {
14352
+ var _a2;
14353
+ if (this.buffer.size === 0) return;
14354
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
14355
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
14356
+ this.buffer.clear();
14357
+ }
14358
+ observe(el) {
14359
+ if (this.disabled) return;
14360
+ this.elements.add(el);
14361
+ }
14362
+ unobserve(el) {
14363
+ if (this.disabled) return;
14364
+ this.elements.delete(el);
14365
+ this.previousState.delete(el);
14366
+ this.pending.delete(el);
14367
+ }
14368
+ freeze() {
14369
+ this.frozen = true;
14370
+ }
14371
+ unfreeze() {
14372
+ this.frozen = false;
14373
+ }
14374
+ lock() {
14375
+ this.locked = true;
14376
+ }
14377
+ unlock() {
14378
+ this.locked = false;
14379
+ }
14380
+ reset() {
14381
+ this.elements.clear();
14382
+ this.previousState.clear();
14383
+ this.pending.clear();
14384
+ if (this.rafId) cancelAnimationFrame(this.rafId);
14385
+ }
14386
+ }
14026
14387
  let wrappedEmit;
14027
14388
  let takeFullSnapshot$1;
14028
14389
  let canvasManager;
14390
+ let visibilityManager;
14029
14391
  let recording = false;
14030
- const preRecordingCustomEvents = [];
14392
+ const customEventQueue = [];
14393
+ let flushCustomEventQueue;
14031
14394
  try {
14032
14395
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
14033
14396
  const cleanFrame = document.createElement("iframe");
@@ -14044,12 +14407,12 @@ function record(options = {}) {
14044
14407
  emit,
14045
14408
  checkoutEveryNms,
14046
14409
  checkoutEveryNth,
14410
+ checkoutEveryNvm,
14047
14411
  blockClass = "rr-block",
14048
14412
  blockSelector = null,
14049
14413
  ignoreClass = "rr-ignore",
14050
14414
  ignoreSelector = null,
14051
14415
  excludeAttribute: _excludeAttribute,
14052
- includeAttribute: _includeAttribute,
14053
14416
  maskTextClass = "rr-mask",
14054
14417
  maskTextSelector = null,
14055
14418
  inlineStylesheet = true,
@@ -14067,7 +14430,7 @@ function record(options = {}) {
14067
14430
  recordCanvas = false,
14068
14431
  recordCrossOriginIframes = false,
14069
14432
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
14070
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
14433
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
14071
14434
  userTriggeredOnInput = false,
14072
14435
  collectFonts = false,
14073
14436
  inlineImages = false,
@@ -14099,8 +14462,7 @@ function record(options = {}) {
14099
14462
  sampling.mousemove = mousemoveWait;
14100
14463
  }
14101
14464
  mirror.reset();
14102
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
14103
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
14465
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
14104
14466
  const maskInputOptions = maskAllInputs === true ? {
14105
14467
  color: true,
14106
14468
  date: true,
@@ -14137,6 +14499,10 @@ function record(options = {}) {
14137
14499
  polyfill$1();
14138
14500
  let lastFullSnapshotEvent;
14139
14501
  let incrementalSnapshotCount = 0;
14502
+ let recentVisibilityChanges = 0;
14503
+ const onVisibilityActivity = (count) => {
14504
+ recentVisibilityChanges += count;
14505
+ };
14140
14506
  const eventProcessor = (e2) => {
14141
14507
  for (const plugin3 of plugins || []) {
14142
14508
  if (plugin3.eventProcessor) {
@@ -14177,7 +14543,11 @@ function record(options = {}) {
14177
14543
  incrementalSnapshotCount++;
14178
14544
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
14179
14545
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
14180
- if (exceedCount || exceedTime) {
14546
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
14547
+ if (exceedCount || exceedTime || exceedVisibility) {
14548
+ if (exceedVisibility) {
14549
+ recentVisibilityChanges = 0;
14550
+ }
14181
14551
  takeFullSnapshot$1(true);
14182
14552
  }
14183
14553
  }
@@ -14205,6 +14575,17 @@ function record(options = {}) {
14205
14575
  ...p
14206
14576
  }
14207
14577
  });
14578
+ const wrappedVisibilityMutationEmit = (p) => {
14579
+ var _a2;
14580
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
14581
+ wrappedEmit({
14582
+ type: EventType.IncrementalSnapshot,
14583
+ data: {
14584
+ source: IncrementalSource.VisibilityMutation,
14585
+ ...p
14586
+ }
14587
+ });
14588
+ };
14208
14589
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
14209
14590
  type: EventType.IncrementalSnapshot,
14210
14591
  data: {
@@ -14242,6 +14623,13 @@ function record(options = {}) {
14242
14623
  sampling: sampling.canvas,
14243
14624
  dataURLOptions
14244
14625
  });
14626
+ visibilityManager = new VisibilityManager({
14627
+ doc: window.document,
14628
+ mirror,
14629
+ sampling: sampling.visibility,
14630
+ mutationCb: wrappedVisibilityMutationEmit,
14631
+ notifyActivity: onVisibilityActivity
14632
+ });
14245
14633
  const shadowDomManager = new ShadowDomManager({
14246
14634
  mutationCb: wrappedMutationEmit,
14247
14635
  scrollCb: wrappedScrollEmit,
@@ -14251,7 +14639,6 @@ function record(options = {}) {
14251
14639
  maskTextClass,
14252
14640
  maskTextSelector,
14253
14641
  excludeAttribute,
14254
- includeAttribute,
14255
14642
  inlineStylesheet,
14256
14643
  maskInputOptions,
14257
14644
  dataURLOptions,
@@ -14264,6 +14651,7 @@ function record(options = {}) {
14264
14651
  iframeManager,
14265
14652
  stylesheetManager,
14266
14653
  canvasManager,
14654
+ visibilityManager,
14267
14655
  keepIframeSrcFn,
14268
14656
  processedNodeManager
14269
14657
  },
@@ -14294,7 +14682,6 @@ function record(options = {}) {
14294
14682
  maskTextClass,
14295
14683
  maskTextSelector,
14296
14684
  excludeAttribute,
14297
- includeAttribute,
14298
14685
  inlineStylesheet,
14299
14686
  maskAllInputs: maskInputOptions,
14300
14687
  maskTextFn,
@@ -14343,6 +14730,12 @@ function record(options = {}) {
14343
14730
  mirror.getId(document)
14344
14731
  );
14345
14732
  };
14733
+ flushCustomEventQueue = () => {
14734
+ for (const e2 of customEventQueue) {
14735
+ wrappedEmit(e2);
14736
+ }
14737
+ customEventQueue.length = 0;
14738
+ };
14346
14739
  try {
14347
14740
  const handlers = [];
14348
14741
  const observe = (doc) => {
@@ -14401,6 +14794,7 @@ function record(options = {}) {
14401
14794
  }
14402
14795
  }),
14403
14796
  canvasMutationCb: wrappedCanvasMutationEmit,
14797
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
14404
14798
  fontCb: (p) => wrappedEmit({
14405
14799
  type: EventType.IncrementalSnapshot,
14406
14800
  data: {
@@ -14432,7 +14826,6 @@ function record(options = {}) {
14432
14826
  maskTextClass,
14433
14827
  maskTextSelector,
14434
14828
  excludeAttribute,
14435
- includeAttribute,
14436
14829
  maskInputOptions,
14437
14830
  inlineStylesheet,
14438
14831
  sampling,
@@ -14454,6 +14847,7 @@ function record(options = {}) {
14454
14847
  shadowDomManager,
14455
14848
  processedNodeManager,
14456
14849
  canvasManager,
14850
+ visibilityManager,
14457
14851
  ignoreCSSAttributes,
14458
14852
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
14459
14853
  observer: p.observer,
@@ -14478,14 +14872,14 @@ function record(options = {}) {
14478
14872
  }
14479
14873
  });
14480
14874
  const init = () => {
14481
- if (flushCustomQueue === "before") {
14482
- flushPreRecordingEvents();
14875
+ if (flushCustomEvent === "before") {
14876
+ flushCustomEventQueue();
14483
14877
  }
14484
14878
  takeFullSnapshot$1();
14485
14879
  handlers.push(observe(document));
14486
14880
  recording = true;
14487
- if (flushCustomQueue === "after") {
14488
- flushPreRecordingEvents();
14881
+ if (flushCustomEvent === "after") {
14882
+ flushCustomEventQueue();
14489
14883
  }
14490
14884
  };
14491
14885
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -14515,7 +14909,7 @@ function record(options = {}) {
14515
14909
  );
14516
14910
  }
14517
14911
  return () => {
14518
- flushPreRecordingEvents();
14912
+ flushCustomEventQueue();
14519
14913
  handlers.forEach((h) => h());
14520
14914
  processedNodeManager.destroy();
14521
14915
  recording = false;
@@ -14525,12 +14919,10 @@ function record(options = {}) {
14525
14919
  console.warn(error);
14526
14920
  }
14527
14921
  }
14528
- function flushPreRecordingEvents() {
14529
- for (const e2 of preRecordingCustomEvents) {
14530
- wrappedEmit(e2);
14531
- }
14532
- preRecordingCustomEvents.length = 0;
14533
- }
14922
+ record.flushCustomEventQueue = () => {
14923
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
14924
+ flushCustomEventQueue();
14925
+ };
14534
14926
  record.addCustomEvent = (tag, payload) => {
14535
14927
  const customEvent = {
14536
14928
  type: EventType.Custom,
@@ -14540,8 +14932,8 @@ record.addCustomEvent = (tag, payload) => {
14540
14932
  }
14541
14933
  };
14542
14934
  if (!recording) {
14543
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
14544
- preRecordingCustomEvents.push(customEvent);
14935
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
14936
+ customEventQueue.push(customEvent);
14545
14937
  return;
14546
14938
  }
14547
14939
  wrappedEmit(customEvent);