@appsurify-testmap/rrweb-record 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, _b;
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 = (_b = win.getComputedStyle) == null ? void 0 : _b.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;
@@ -557,71 +594,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
557
594
  function markCssSplits(cssText, style) {
558
595
  return splitCssText(cssText, style).join("/* rr_split */");
559
596
  }
560
- function getXPath(node2) {
561
- if (node2.nodeType === Node.DOCUMENT_NODE) {
562
- return "/";
597
+ function isSelectorUnique(selector, target) {
598
+ try {
599
+ const matches = document.querySelectorAll(selector);
600
+ return matches.length === 1 && matches[0] === target;
601
+ } catch {
602
+ return false;
563
603
  }
564
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
565
- return "/html/doctype";
604
+ }
605
+ function buildSelector(node2) {
606
+ if (!(node2 instanceof Element)) return null;
607
+ if (node2.id) {
608
+ return `#${CSS.escape(node2.id)}`;
566
609
  }
567
- if (node2.nodeType === Node.ELEMENT_NODE) {
568
- const element = node2;
569
- if (element.id) {
570
- return `//*[@id="${element.id}"]`;
571
- }
572
- if (element.tagName && element.tagName.toLowerCase() === "html") {
573
- return "/html";
574
- }
575
- if (element === document.head) {
576
- return "/html/head";
577
- }
578
- if (element === document.body) {
579
- return "/html/body";
580
- }
581
- const parentNode2 = element.parentNode;
582
- if (!parentNode2) {
583
- return "";
584
- }
585
- const siblings = Array.from(parentNode2.children).filter(
586
- (sibling) => sibling.tagName === element.tagName
587
- );
588
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
589
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
610
+ const parts = [];
611
+ const tag = node2.tagName.toLowerCase();
612
+ if (node2.classList.length) {
613
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
590
614
  }
591
- if (node2.nodeType === Node.TEXT_NODE) {
592
- const parent = node2.parentNode;
593
- if (!parent) {
594
- return "";
615
+ Array.from(node2.attributes).forEach((attr) => {
616
+ if (attr.name.startsWith("data-")) {
617
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
595
618
  }
596
- const textSiblings = Array.from(parent.childNodes).filter(
597
- (sibling) => sibling.nodeType === Node.TEXT_NODE
598
- );
599
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
600
- return `${getXPath(parent)}/text()${index2}`;
601
- }
602
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
603
- const parent = node2.parentNode;
604
- if (!parent) {
605
- return "";
619
+ });
620
+ const shortSelector = `${tag}${parts.join("")}`;
621
+ if (isSelectorUnique(shortSelector, node2)) {
622
+ return shortSelector;
623
+ }
624
+ const pathParts = [];
625
+ let current = node2;
626
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
627
+ const parent = current.parentElement;
628
+ const tagName = current.tagName.toLowerCase();
629
+ let nth = "";
630
+ if (parent) {
631
+ const siblings = Array.from(parent.children).filter(
632
+ (el) => el.tagName.toLowerCase() === tagName
633
+ );
634
+ if (siblings.length > 1) {
635
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
636
+ }
637
+ }
638
+ pathParts.unshift(`${tagName}${nth}`);
639
+ current = parent;
640
+ }
641
+ return pathParts.join(" > ") || null;
642
+ }
643
+ function buildXPath(node2) {
644
+ switch (node2.nodeType) {
645
+ case Node.DOCUMENT_NODE:
646
+ return "/";
647
+ case Node.DOCUMENT_TYPE_NODE:
648
+ return "/html/doctype";
649
+ case Node.ELEMENT_NODE: {
650
+ const element = node2;
651
+ if (element.id) {
652
+ return `//*[@id="${CSS.escape(element.id)}"]`;
653
+ }
654
+ if (element.tagName.toLowerCase() === "html") return "/html";
655
+ if (element === document.head) return "/html/head";
656
+ if (element === document.body) return "/html/body";
657
+ const parent = element.parentNode;
658
+ if (!parent) return "";
659
+ const tag = element.tagName.toLowerCase();
660
+ const siblings = Array.from(parent.children).filter(
661
+ (el) => el.tagName.toLowerCase() === tag
662
+ );
663
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
664
+ return `${buildXPath(parent)}/${tag}${index2}`;
665
+ }
666
+ case Node.TEXT_NODE:
667
+ case Node.CDATA_SECTION_NODE:
668
+ case Node.COMMENT_NODE: {
669
+ const parent = node2.parentNode;
670
+ if (!parent) return "";
671
+ const typeMap = {
672
+ [Node.TEXT_NODE]: "text()",
673
+ [Node.CDATA_SECTION_NODE]: "text()",
674
+ // CDATA ≡ text() в XPath
675
+ [Node.COMMENT_NODE]: "comment()"
676
+ };
677
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
678
+ (sibling) => sibling.nodeType === node2.nodeType
679
+ );
680
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
681
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
606
682
  }
607
- const cdataSiblings = Array.from(parent.childNodes).filter(
608
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
609
- );
610
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
611
- return `${getXPath(parent)}/text()${index2}`;
612
- }
613
- if (node2.nodeType === Node.COMMENT_NODE) {
614
- const parent = node2.parentNode;
615
- if (!parent) {
683
+ default:
616
684
  return "";
617
- }
618
- const commentSiblings = Array.from(parent.childNodes).filter(
619
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
620
- );
621
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
622
- return `${getXPath(parent)}/comment()${index2}`;
623
685
  }
624
- return "";
625
686
  }
626
687
  function isTextVisible(n2) {
627
688
  var _a2;
@@ -637,20 +698,9 @@ function isTextVisible(n2) {
637
698
  const textContent2 = (_a2 = n2.textContent) == null ? void 0 : _a2.trim();
638
699
  return textContent2 !== "";
639
700
  }
640
- function isElementVisible(n2) {
641
- var _a2;
642
- const win = ((_a2 = n2.ownerDocument) == null ? void 0 : _a2.defaultView) ?? null;
643
- const style = win ? win.getComputedStyle(n2) : null;
644
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
645
- const rect = n2.getBoundingClientRect();
646
- const result2 = isStyleVisible && isRectVisible(rect);
647
- return result2;
648
- }
649
- function isRectVisible(rect, win = window) {
650
- var _a2, _b, _c, _d;
651
- const height = (win == null ? void 0 : win.innerHeight) ?? ((_b = (_a2 = win == null ? void 0 : win.document) == null ? void 0 : _a2.documentElement) == null ? void 0 : _b.clientHeight) ?? 0;
652
- 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;
653
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
701
+ function isElementVisible(el) {
702
+ const visibility = index$1.getElementVisibility(el);
703
+ return visibility.isVisible;
654
704
  }
655
705
  const interactiveEvents$1 = [
656
706
  "change",
@@ -677,19 +727,6 @@ const interactiveEvents$1 = [
677
727
  "touchend",
678
728
  "touchcancel"
679
729
  ];
680
- const interactiveTags = [
681
- "a",
682
- "button",
683
- "input",
684
- "select",
685
- "textarea",
686
- "label",
687
- "details",
688
- "summary",
689
- "dialog",
690
- "video",
691
- "audio"
692
- ];
693
730
  const inlineEventAttributes$1 = [
694
731
  "onclick",
695
732
  "ondblclick",
@@ -729,25 +766,6 @@ const originalRemoveEventListener$1 = EventTarget.prototype.removeEventListener;
729
766
  EventTarget.prototype.removeEventListener = function(type, listener, options) {
730
767
  originalRemoveEventListener$1.call(this, type, listener, options);
731
768
  };
732
- function hasEventListeners(n2) {
733
- return n2 instanceof Element && interactiveElementsRegistry$1.has(n2);
734
- }
735
- function isElementInteractive(n2) {
736
- if (n2.nodeType === Node.ELEMENT_NODE) {
737
- const element = n2;
738
- const tagName = element.tagName.toLowerCase();
739
- if (interactiveTags.includes(tagName)) {
740
- return true;
741
- }
742
- const hasTabIndex = element.hasAttribute("tabindex") && element.getAttribute("tabindex") !== "-1";
743
- const hasRoleInteractive = ["button", "link", "checkbox", "switch", "menuitem"].includes(
744
- element.getAttribute("role") || ""
745
- );
746
- const result2 = hasEventListeners(element) || hasTabIndex || hasRoleInteractive || element instanceof HTMLAnchorElement && element.hasAttribute("href") || element instanceof HTMLButtonElement && !element.disabled;
747
- return result2;
748
- }
749
- return false;
750
- }
751
769
  function inspectInlineEventHandlers$1() {
752
770
  const allElements = document.querySelectorAll("*");
753
771
  allElements.forEach((el) => {
@@ -883,9 +901,6 @@ function transformAttribute(doc, tagName, name, value) {
883
901
  function ignoreAttribute(tagName, name, _value) {
884
902
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
885
903
  }
886
- function isIncludeAttribute(name, include) {
887
- return typeof include === "string" ? name.includes(include) : include.test(name);
888
- }
889
904
  function isExcludeAttribute(name, exclude) {
890
905
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
891
906
  }
@@ -1019,7 +1034,6 @@ function serializeNode(n2, options) {
1019
1034
  blockClass,
1020
1035
  blockSelector,
1021
1036
  excludeAttribute,
1022
- includeAttribute,
1023
1037
  needsMask,
1024
1038
  inlineStylesheet,
1025
1039
  maskInputOptions = {},
@@ -1033,22 +1047,19 @@ function serializeNode(n2, options) {
1033
1047
  cssCaptured = false
1034
1048
  } = options;
1035
1049
  const rootId = getRootId(doc, mirror2);
1036
- const xPath = getXPath(n2);
1037
1050
  switch (n2.nodeType) {
1038
1051
  case n2.DOCUMENT_NODE:
1039
1052
  if (n2.compatMode !== "CSS1Compat") {
1040
1053
  return {
1041
1054
  type: NodeType$3.Document,
1042
1055
  childNodes: [],
1043
- xPath,
1044
1056
  compatMode: n2.compatMode
1045
1057
  // probably "BackCompat"
1046
1058
  };
1047
1059
  } else {
1048
1060
  return {
1049
1061
  type: NodeType$3.Document,
1050
- childNodes: [],
1051
- xPath
1062
+ childNodes: []
1052
1063
  };
1053
1064
  }
1054
1065
  case n2.DOCUMENT_TYPE_NODE:
@@ -1057,8 +1068,7 @@ function serializeNode(n2, options) {
1057
1068
  name: n2.name,
1058
1069
  publicId: n2.publicId,
1059
1070
  systemId: n2.systemId,
1060
- rootId,
1061
- xPath
1071
+ rootId
1062
1072
  };
1063
1073
  case n2.ELEMENT_NODE:
1064
1074
  return serializeElementNode(n2, {
@@ -1066,7 +1076,6 @@ function serializeNode(n2, options) {
1066
1076
  blockClass,
1067
1077
  blockSelector,
1068
1078
  excludeAttribute,
1069
- includeAttribute,
1070
1079
  inlineStylesheet,
1071
1080
  maskInputOptions,
1072
1081
  maskInputFn,
@@ -1075,8 +1084,7 @@ function serializeNode(n2, options) {
1075
1084
  recordCanvas,
1076
1085
  keepIframeSrcFn,
1077
1086
  newlyAddedElement,
1078
- rootId,
1079
- xPath
1087
+ rootId
1080
1088
  });
1081
1089
  case n2.TEXT_NODE:
1082
1090
  return serializeTextNode(n2, {
@@ -1084,22 +1092,19 @@ function serializeNode(n2, options) {
1084
1092
  needsMask,
1085
1093
  maskTextFn,
1086
1094
  rootId,
1087
- cssCaptured,
1088
- xPath
1095
+ cssCaptured
1089
1096
  });
1090
1097
  case n2.CDATA_SECTION_NODE:
1091
1098
  return {
1092
1099
  type: NodeType$3.CDATA,
1093
1100
  textContent: "",
1094
- rootId,
1095
- xPath
1101
+ rootId
1096
1102
  };
1097
1103
  case n2.COMMENT_NODE:
1098
1104
  return {
1099
1105
  type: NodeType$3.Comment,
1100
1106
  textContent: index$1.textContent(n2) || "",
1101
- rootId,
1102
- xPath
1107
+ rootId
1103
1108
  };
1104
1109
  default:
1105
1110
  return false;
@@ -1111,7 +1116,7 @@ function getRootId(doc, mirror2) {
1111
1116
  return docId === 1 ? void 0 : docId;
1112
1117
  }
1113
1118
  function serializeTextNode(n2, options) {
1114
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1119
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1115
1120
  const parent = index$1.parentNode(n2);
1116
1121
  const parentTagName = parent && parent.tagName;
1117
1122
  let textContent2 = "";
@@ -1128,15 +1133,10 @@ function serializeTextNode(n2, options) {
1128
1133
  if (!isStyle && !isScript && textContent2 && needsMask) {
1129
1134
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1130
1135
  }
1131
- const isVisible = isTextVisible(n2);
1132
- const isInteractive = isElementInteractive(n2);
1133
1136
  return {
1134
1137
  type: NodeType$3.Text,
1135
1138
  textContent: textContent2 || "",
1136
- rootId,
1137
- isVisible,
1138
- isInteractive,
1139
- xPath
1139
+ rootId
1140
1140
  };
1141
1141
  }
1142
1142
  function serializeElementNode(n2, options) {
@@ -1145,7 +1145,6 @@ function serializeElementNode(n2, options) {
1145
1145
  blockClass,
1146
1146
  blockSelector,
1147
1147
  excludeAttribute,
1148
- includeAttribute,
1149
1148
  inlineStylesheet,
1150
1149
  maskInputOptions = {},
1151
1150
  maskInputFn,
@@ -1154,8 +1153,7 @@ function serializeElementNode(n2, options) {
1154
1153
  recordCanvas,
1155
1154
  keepIframeSrcFn,
1156
1155
  newlyAddedElement = false,
1157
- rootId,
1158
- xPath
1156
+ rootId
1159
1157
  } = options;
1160
1158
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1161
1159
  const tagName = getValidTagName$1(n2);
@@ -1163,7 +1161,7 @@ function serializeElementNode(n2, options) {
1163
1161
  const len = n2.attributes.length;
1164
1162
  for (let i2 = 0; i2 < len; i2++) {
1165
1163
  const attr = n2.attributes[i2];
1166
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1164
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1167
1165
  continue;
1168
1166
  }
1169
1167
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1325,8 +1323,6 @@ function serializeElementNode(n2, options) {
1325
1323
  if (customElements.get(tagName)) isCustomElement = true;
1326
1324
  } catch (e2) {
1327
1325
  }
1328
- const isVisible = isElementVisible(n2);
1329
- const isInteractive = isElementInteractive(n2);
1330
1326
  return {
1331
1327
  type: NodeType$3.Element,
1332
1328
  tagName,
@@ -1335,10 +1331,7 @@ function serializeElementNode(n2, options) {
1335
1331
  isSVG: isSVGElement(n2) || void 0,
1336
1332
  needBlock,
1337
1333
  rootId,
1338
- isCustom: isCustomElement,
1339
- isVisible,
1340
- isInteractive,
1341
- xPath
1334
+ isCustom: isCustomElement
1342
1335
  };
1343
1336
  }
1344
1337
  function lowerIfExists(maybeAttr) {
@@ -1389,7 +1382,6 @@ function serializeNodeWithId(n2, options) {
1389
1382
  maskTextClass,
1390
1383
  maskTextSelector,
1391
1384
  excludeAttribute,
1392
- includeAttribute,
1393
1385
  skipChild = false,
1394
1386
  inlineStylesheet = true,
1395
1387
  maskInputOptions = {},
@@ -1425,7 +1417,6 @@ function serializeNodeWithId(n2, options) {
1425
1417
  blockClass,
1426
1418
  blockSelector,
1427
1419
  excludeAttribute,
1428
- includeAttribute,
1429
1420
  needsMask,
1430
1421
  inlineStylesheet,
1431
1422
  maskInputOptions,
@@ -1451,6 +1442,21 @@ function serializeNodeWithId(n2, options) {
1451
1442
  id = genId();
1452
1443
  }
1453
1444
  const serializedNode = Object.assign(_serializedNode, { id });
1445
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1446
+ serializedNode.xpath = buildXPath(n2);
1447
+ if (isElement(n2)) {
1448
+ const selector = buildSelector(n2);
1449
+ if (selector) {
1450
+ serializedNode.selector = selector;
1451
+ }
1452
+ }
1453
+ if (n2.nodeType === Node.TEXT_NODE) {
1454
+ serializedNode.isVisible = isTextVisible(n2);
1455
+ }
1456
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1457
+ serializedNode.isVisible = isElementVisible(n2);
1458
+ }
1459
+ }
1454
1460
  mirror2.add(n2, serializedNode);
1455
1461
  if (id === IGNORED_NODE) {
1456
1462
  return null;
@@ -1479,7 +1485,6 @@ function serializeNodeWithId(n2, options) {
1479
1485
  maskTextClass,
1480
1486
  maskTextSelector,
1481
1487
  excludeAttribute,
1482
- includeAttribute,
1483
1488
  skipChild,
1484
1489
  inlineStylesheet,
1485
1490
  maskInputOptions,
@@ -1540,7 +1545,6 @@ function serializeNodeWithId(n2, options) {
1540
1545
  maskTextClass,
1541
1546
  maskTextSelector,
1542
1547
  excludeAttribute,
1543
- includeAttribute,
1544
1548
  skipChild: false,
1545
1549
  inlineStylesheet,
1546
1550
  maskInputOptions,
@@ -1583,7 +1587,6 @@ function serializeNodeWithId(n2, options) {
1583
1587
  maskTextClass,
1584
1588
  maskTextSelector,
1585
1589
  excludeAttribute,
1586
- includeAttribute,
1587
1590
  skipChild: false,
1588
1591
  inlineStylesheet,
1589
1592
  maskInputOptions,
@@ -1621,8 +1624,7 @@ function snapshot(n2, options) {
1621
1624
  blockSelector = null,
1622
1625
  maskTextClass = "rr-mask",
1623
1626
  maskTextSelector = null,
1624
- excludeAttribute = /^$a/,
1625
- includeAttribute = /.+/i,
1627
+ excludeAttribute = /.^/,
1626
1628
  inlineStylesheet = true,
1627
1629
  inlineImages = false,
1628
1630
  recordCanvas = false,
@@ -1683,7 +1685,6 @@ function snapshot(n2, options) {
1683
1685
  maskTextClass,
1684
1686
  maskTextSelector,
1685
1687
  excludeAttribute,
1686
- includeAttribute,
1687
1688
  skipChild: false,
1688
1689
  inlineStylesheet,
1689
1690
  maskInputOptions,
@@ -9094,6 +9095,41 @@ function patch(source, name, replacement) {
9094
9095
  };
9095
9096
  }
9096
9097
  }
9098
+ function describeNode(el) {
9099
+ const tag = el.tagName.toLowerCase();
9100
+ const id = el.id ? `#${el.id}` : "";
9101
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
9102
+ return `${tag}${id}${classes}`;
9103
+ }
9104
+ function getElementVisibility(el) {
9105
+ var _a2, _b;
9106
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
9107
+ const rect = el.getBoundingClientRect();
9108
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
9109
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
9110
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
9111
+ const style = (_b = win.getComputedStyle) == null ? void 0 : _b.call(win, el);
9112
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
9113
+ const isVisible = isStyleVisible2 && isRectVisible;
9114
+ let ratio = 0;
9115
+ if (isVisible) {
9116
+ const xOverlap = Math.max(
9117
+ 0,
9118
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
9119
+ );
9120
+ const yOverlap = Math.max(
9121
+ 0,
9122
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
9123
+ );
9124
+ const intersectionArea = xOverlap * yOverlap;
9125
+ const elementArea = rect.width * rect.height;
9126
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
9127
+ }
9128
+ return {
9129
+ isVisible,
9130
+ ratio
9131
+ };
9132
+ }
9097
9133
  const index = {
9098
9134
  childNodes,
9099
9135
  parentNode,
@@ -9107,7 +9143,9 @@ const index = {
9107
9143
  querySelector,
9108
9144
  querySelectorAll,
9109
9145
  mutationObserver: mutationObserverCtor,
9110
- patch
9146
+ patch,
9147
+ describeNode,
9148
+ getElementVisibility
9111
9149
  };
9112
9150
  function on(type, fn, target = document) {
9113
9151
  const options = { capture: true, passive: true };
@@ -9381,6 +9419,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
9381
9419
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
9382
9420
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
9383
9421
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
9422
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
9384
9423
  return IncrementalSource2;
9385
9424
  })(IncrementalSource || {});
9386
9425
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -9527,7 +9566,6 @@ class MutationBuffer {
9527
9566
  __publicField(this, "maskTextClass");
9528
9567
  __publicField(this, "maskTextSelector");
9529
9568
  __publicField(this, "excludeAttribute");
9530
- __publicField(this, "includeAttribute");
9531
9569
  __publicField(this, "inlineStylesheet");
9532
9570
  __publicField(this, "maskInputOptions");
9533
9571
  __publicField(this, "maskTextFn");
@@ -9543,6 +9581,7 @@ class MutationBuffer {
9543
9581
  __publicField(this, "stylesheetManager");
9544
9582
  __publicField(this, "shadowDomManager");
9545
9583
  __publicField(this, "canvasManager");
9584
+ __publicField(this, "visibilityManager");
9546
9585
  __publicField(this, "processedNodeManager");
9547
9586
  __publicField(this, "unattachedDoc");
9548
9587
  __publicField(this, "processMutations", (mutations) => {
@@ -9592,7 +9631,6 @@ class MutationBuffer {
9592
9631
  maskTextClass: this.maskTextClass,
9593
9632
  maskTextSelector: this.maskTextSelector,
9594
9633
  excludeAttribute: this.excludeAttribute,
9595
- includeAttribute: this.includeAttribute,
9596
9634
  skipChild: true,
9597
9635
  newlyAddedElement: true,
9598
9636
  inlineStylesheet: this.inlineStylesheet,
@@ -9638,7 +9676,8 @@ class MutationBuffer {
9638
9676
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
9639
9677
  }
9640
9678
  for (const n2 of this.movedSet) {
9641
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
9679
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9680
+ !this.movedSet.has(index.parentNode(n2))) {
9642
9681
  continue;
9643
9682
  }
9644
9683
  pushAdd(n2);
@@ -9794,12 +9833,60 @@ class MutationBuffer {
9794
9833
  const target = m.target;
9795
9834
  let attributeName = m.attributeName;
9796
9835
  let value = m.target.getAttribute(attributeName);
9797
- const propValue = target[attributeName];
9798
- const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
9836
+ const attrKey = attributeName;
9837
+ const propValue = target[attrKey];
9838
+ const isBooleanAttr = typeof propValue === "boolean";
9839
+ const inDOM = document.contains(target);
9840
+ const isVisible = isElementVisible(target);
9841
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
9842
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
9843
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
9844
+ m.oldValue !== null && // раньше он был
9845
+ (propValue === "" || // свойство = пустая строка
9846
+ propValue === null || // или null
9847
+ typeof propValue === "undefined");
9799
9848
  if (isPhantomAttributeMutation) {
9849
+ console.debug(
9850
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ phantom attribute mutation ignored`,
9851
+ {
9852
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
9853
+ node: index.describeNode(target),
9854
+ tag: target.tagName,
9855
+ nodeType: target.nodeType,
9856
+ attribute: attributeName,
9857
+ value,
9858
+ oldValue: m.oldValue,
9859
+ excludeAttribute: this.excludeAttribute,
9860
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9861
+ propValue,
9862
+ isBooleanAttr,
9863
+ inDOM,
9864
+ isVisible,
9865
+ isExcludeAttributeName
9866
+ }
9867
+ );
9800
9868
  return;
9801
9869
  }
9802
9870
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
9871
+ console.debug(
9872
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ excluded attribute mutation ignored`,
9873
+ {
9874
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
9875
+ node: index.describeNode(target),
9876
+ tag: target.tagName,
9877
+ nodeType: target.nodeType,
9878
+ attribute: attributeName,
9879
+ value,
9880
+ oldValue: m.oldValue,
9881
+ excludeAttribute: this.excludeAttribute,
9882
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9883
+ propValue,
9884
+ isBooleanAttr,
9885
+ inDOM,
9886
+ isVisible,
9887
+ isExcludeAttributeName
9888
+ }
9889
+ );
9803
9890
  return;
9804
9891
  }
9805
9892
  if (attributeName === "value") {
@@ -9844,9 +9931,35 @@ class MutationBuffer {
9844
9931
  toLowerCase(attributeName),
9845
9932
  value
9846
9933
  );
9847
- if (value === item.attributes[attributeName]) {
9848
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
9849
- return;
9934
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
9935
+ value === "" || // класс удалён
9936
+ value !== m.oldValue);
9937
+ if (isSuspiciousClassMutation) {
9938
+ console.debug(
9939
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⚠️ suspicious attribute mutation`,
9940
+ {
9941
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
9942
+ reason: [
9943
+ value === m.oldValue ? "no change in value" : null,
9944
+ value === propValue ? "mirrored in DOM property" : null,
9945
+ value === item.attributes[attributeName] ? "redundant assignment" : null
9946
+ ].filter(Boolean).join(", ") || "uncategorized",
9947
+ node: index.describeNode(target),
9948
+ tag: target.tagName,
9949
+ nodeType: target.nodeType,
9950
+ attribute: attributeName,
9951
+ value,
9952
+ oldValue: m.oldValue,
9953
+ transformedValue: item.attributes[attributeName],
9954
+ excludeAttribute: this.excludeAttribute,
9955
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9956
+ propValue,
9957
+ isBooleanAttr,
9958
+ inDOM,
9959
+ isVisible,
9960
+ isExcludeAttributeName
9961
+ }
9962
+ );
9850
9963
  }
9851
9964
  if (attributeName === "style") {
9852
9965
  if (!this.unattachedDoc) {
@@ -9961,7 +10074,6 @@ class MutationBuffer {
9961
10074
  "maskTextClass",
9962
10075
  "maskTextSelector",
9963
10076
  "excludeAttribute",
9964
- "includeAttribute",
9965
10077
  "inlineStylesheet",
9966
10078
  "maskInputOptions",
9967
10079
  "maskTextFn",
@@ -9977,6 +10089,7 @@ class MutationBuffer {
9977
10089
  "stylesheetManager",
9978
10090
  "shadowDomManager",
9979
10091
  "canvasManager",
10092
+ "visibilityManager",
9980
10093
  "processedNodeManager"
9981
10094
  ].forEach((key) => {
9982
10095
  this[key] = options[key];
@@ -9985,10 +10098,12 @@ class MutationBuffer {
9985
10098
  freeze() {
9986
10099
  this.frozen = true;
9987
10100
  this.canvasManager.freeze();
10101
+ this.visibilityManager.freeze();
9988
10102
  }
9989
10103
  unfreeze() {
9990
10104
  this.frozen = false;
9991
10105
  this.canvasManager.unfreeze();
10106
+ this.visibilityManager.unfreeze();
9992
10107
  this.emit();
9993
10108
  }
9994
10109
  isFrozen() {
@@ -9997,15 +10112,18 @@ class MutationBuffer {
9997
10112
  lock() {
9998
10113
  this.locked = true;
9999
10114
  this.canvasManager.lock();
10115
+ this.visibilityManager.lock();
10000
10116
  }
10001
10117
  unlock() {
10002
10118
  this.locked = false;
10003
10119
  this.canvasManager.unlock();
10120
+ this.visibilityManager.unlock();
10004
10121
  this.emit();
10005
10122
  }
10006
10123
  reset() {
10007
10124
  this.shadowDomManager.reset();
10008
10125
  this.canvasManager.reset();
10126
+ this.visibilityManager.reset();
10009
10127
  }
10010
10128
  }
10011
10129
  function deepDelete(addsSet, n2) {
@@ -10939,6 +11057,7 @@ function mergeHooks(o2, hooks) {
10939
11057
  styleSheetRuleCb,
10940
11058
  styleDeclarationCb,
10941
11059
  canvasMutationCb,
11060
+ visibilityMutationCb,
10942
11061
  fontCb,
10943
11062
  selectionCb,
10944
11063
  customElementCb
@@ -11003,6 +11122,12 @@ function mergeHooks(o2, hooks) {
11003
11122
  }
11004
11123
  canvasMutationCb(...p);
11005
11124
  };
11125
+ o2.visibilityMutationCb = (...p) => {
11126
+ if (hooks.visibilityMutation) {
11127
+ hooks.visibilityMutation(...p);
11128
+ }
11129
+ visibilityMutationCb(...p);
11130
+ };
11006
11131
  o2.fontCb = (...p) => {
11007
11132
  if (hooks.font) {
11008
11133
  hooks.font(...p);
@@ -12116,11 +12241,249 @@ class ProcessedNodeManager {
12116
12241
  destroy() {
12117
12242
  }
12118
12243
  }
12244
+ function computeVisibility(elements, previous, options) {
12245
+ const root2 = (options == null ? void 0 : options.root) ?? null;
12246
+ const threshold = (options == null ? void 0 : options.threshold) ?? 0.5;
12247
+ const sensitivity = (options == null ? void 0 : options.sensitivity) ?? 0.05;
12248
+ const rootMarginFn = parseRootMargin((options == null ? void 0 : options.rootMargin) ?? "0px");
12249
+ const current = /* @__PURE__ */ new Map();
12250
+ const rootRect = getRootRect(root2);
12251
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
12252
+ for (const el of elements) {
12253
+ const elRect = el.getBoundingClientRect();
12254
+ let intersectionRect = emptyRect();
12255
+ let intersectionRatio = 0;
12256
+ if (elRect.width > 0 && elRect.height > 0) {
12257
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
12258
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
12259
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
12260
+ }
12261
+ const isStyle = isStyleVisible(el);
12262
+ const old = previous.get(el) ?? null;
12263
+ const prevRatio = (old == null ? void 0 : old.intersectionRatio) ?? 0;
12264
+ const currRatio = intersectionRatio;
12265
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
12266
+ const nowVisible = isStyle && currRatio > threshold;
12267
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
12268
+ if (changed) {
12269
+ current.set(el, {
12270
+ target: el,
12271
+ isVisible: nowVisible,
12272
+ isStyleVisible: isStyle,
12273
+ intersectionRatio: currRatio,
12274
+ intersectionRect,
12275
+ oldValue: old
12276
+ });
12277
+ } else {
12278
+ current.set(el, old);
12279
+ }
12280
+ }
12281
+ return current;
12282
+ }
12283
+ function parseRootMargin(marginStr) {
12284
+ const parts = marginStr.trim().split(/\s+/);
12285
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
12286
+ return function(rootRect) {
12287
+ const top = getValue(parts[0] || "0px", rootRect.height);
12288
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
12289
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
12290
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
12291
+ return { top, right, bottom, left, width: 0, height: 0 };
12292
+ };
12293
+ }
12294
+ function getRootRect(root2) {
12295
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
12296
+ }
12297
+ function expandRootRect(rect, marginFn) {
12298
+ const margin = marginFn(rect);
12299
+ return new DOMRect(
12300
+ rect.left - margin.left,
12301
+ rect.top - margin.top,
12302
+ rect.width + margin.left + margin.right,
12303
+ rect.height + margin.top + margin.bottom
12304
+ );
12305
+ }
12306
+ function computeIntersectionRect(a2, b) {
12307
+ const top = Math.max(a2.top, b.top);
12308
+ const left = Math.max(a2.left, b.left);
12309
+ const bottom = Math.min(a2.bottom, b.bottom);
12310
+ const right = Math.min(a2.right, b.right);
12311
+ const width = Math.max(0, right - left);
12312
+ const height = Math.max(0, bottom - top);
12313
+ return { top, left, bottom, right, width, height };
12314
+ }
12315
+ function computeIntersectionRatio(elRect, intersectionRect) {
12316
+ const elArea = elRect.width * elRect.height;
12317
+ const intArea = intersectionRect.width * intersectionRect.height;
12318
+ return elArea > 0 ? intArea / elArea : 0;
12319
+ }
12320
+ function emptyRect() {
12321
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
12322
+ }
12323
+ function isStyleVisible(el) {
12324
+ const style = getComputedStyle(el);
12325
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
12326
+ }
12327
+ class VisibilityManager {
12328
+ constructor(options) {
12329
+ __publicField(this, "frozen", false);
12330
+ __publicField(this, "locked", false);
12331
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
12332
+ __publicField(this, "mirror");
12333
+ __publicField(this, "mutationCb");
12334
+ __publicField(this, "rafId", null);
12335
+ __publicField(this, "rafThrottle");
12336
+ __publicField(this, "lastFlushTime", 0);
12337
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
12338
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
12339
+ __publicField(this, "root", null);
12340
+ __publicField(this, "threshold");
12341
+ __publicField(this, "sensitivity");
12342
+ __publicField(this, "rootMargin");
12343
+ __publicField(this, "hasInitialized", false);
12344
+ __publicField(this, "mode", "none");
12345
+ __publicField(this, "debounce", 50);
12346
+ __publicField(this, "throttle", 100);
12347
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
12348
+ __publicField(this, "debounceTimer", null);
12349
+ __publicField(this, "lastThrottleTime", 0);
12350
+ __publicField(this, "disabled", false);
12351
+ __publicField(this, "notifyActivity");
12352
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
12353
+ this.mirror = mirror2;
12354
+ this.mutationCb = mutationCb;
12355
+ this.notifyActivity = notifyActivity;
12356
+ this.rootMargin = "0px";
12357
+ if (sampling === false) {
12358
+ this.disabled = true;
12359
+ return;
12360
+ }
12361
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
12362
+ this.mode = (visibilitySampling == null ? void 0 : visibilitySampling.mode) ?? "none";
12363
+ this.debounce = Number((visibilitySampling == null ? void 0 : visibilitySampling.debounce) ?? 100);
12364
+ this.throttle = Number((visibilitySampling == null ? void 0 : visibilitySampling.throttle) ?? 100);
12365
+ this.threshold = Number((visibilitySampling == null ? void 0 : visibilitySampling.threshold) ?? 0.5);
12366
+ this.sensitivity = Number((visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) ?? 0.05);
12367
+ this.rafThrottle = Number((visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) ?? 100);
12368
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
12369
+ const mo = new MutationObserver((mutations) => {
12370
+ mutations.forEach((m) => {
12371
+ m.addedNodes.forEach((n2) => {
12372
+ if (n2.nodeType === Node.ELEMENT_NODE) {
12373
+ this.observe(n2);
12374
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
12375
+ }
12376
+ });
12377
+ m.removedNodes.forEach((n2) => {
12378
+ if (n2.nodeType === Node.ELEMENT_NODE) {
12379
+ this.unobserve(n2);
12380
+ }
12381
+ });
12382
+ });
12383
+ });
12384
+ mo.observe(doc.body, { childList: true, subtree: true });
12385
+ this.startPendingFlushLoop();
12386
+ }
12387
+ startPendingFlushLoop() {
12388
+ if (this.disabled) return;
12389
+ const loop = (timestamp) => {
12390
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
12391
+ this.lastFlushTime = timestamp;
12392
+ this.flushPendingVisibilityMutations();
12393
+ }
12394
+ this.rafId = requestAnimationFrame(loop);
12395
+ };
12396
+ this.rafId = requestAnimationFrame(loop);
12397
+ }
12398
+ flushPendingVisibilityMutations() {
12399
+ if (this.disabled) return;
12400
+ if (this.frozen || this.locked || this.elements.size === 0) return;
12401
+ const state = computeVisibility(this.elements, this.previousState, {
12402
+ root: this.root,
12403
+ threshold: this.threshold,
12404
+ sensitivity: this.sensitivity,
12405
+ rootMargin: this.rootMargin
12406
+ });
12407
+ for (const [el, entry] of state.entries()) {
12408
+ const old = this.previousState.get(el);
12409
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
12410
+ if (changed) {
12411
+ const id = this.mirror.getId(el);
12412
+ if (id !== -1) {
12413
+ this.buffer.set(el, {
12414
+ id,
12415
+ isVisible: entry.isVisible,
12416
+ ratio: entry.intersectionRatio
12417
+ });
12418
+ }
12419
+ this.previousState.set(el, entry);
12420
+ }
12421
+ }
12422
+ this.previousState = state;
12423
+ if (!this.hasInitialized) {
12424
+ this.hasInitialized = true;
12425
+ this.buffer.clear();
12426
+ return;
12427
+ }
12428
+ this.scheduleEmit();
12429
+ }
12430
+ scheduleEmit() {
12431
+ if (this.mode === "debounce") {
12432
+ clearTimeout(this.debounceTimer);
12433
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
12434
+ } else if (this.mode === "throttle") {
12435
+ const now = performance.now();
12436
+ if (now - this.lastThrottleTime >= this.throttle) {
12437
+ this.lastThrottleTime = now;
12438
+ this.flushBuffer();
12439
+ }
12440
+ } else {
12441
+ this.flushBuffer();
12442
+ }
12443
+ }
12444
+ flushBuffer() {
12445
+ var _a2;
12446
+ if (this.buffer.size === 0) return;
12447
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
12448
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
12449
+ this.buffer.clear();
12450
+ }
12451
+ observe(el) {
12452
+ if (this.disabled) return;
12453
+ this.elements.add(el);
12454
+ }
12455
+ unobserve(el) {
12456
+ if (this.disabled) return;
12457
+ this.elements.delete(el);
12458
+ this.previousState.delete(el);
12459
+ this.pending.delete(el);
12460
+ }
12461
+ freeze() {
12462
+ this.frozen = true;
12463
+ }
12464
+ unfreeze() {
12465
+ this.frozen = false;
12466
+ }
12467
+ lock() {
12468
+ this.locked = true;
12469
+ }
12470
+ unlock() {
12471
+ this.locked = false;
12472
+ }
12473
+ reset() {
12474
+ this.elements.clear();
12475
+ this.previousState.clear();
12476
+ this.pending.clear();
12477
+ if (this.rafId) cancelAnimationFrame(this.rafId);
12478
+ }
12479
+ }
12119
12480
  let wrappedEmit;
12120
12481
  let takeFullSnapshot$1;
12121
12482
  let canvasManager;
12483
+ let visibilityManager;
12122
12484
  let recording = false;
12123
- const preRecordingCustomEvents = [];
12485
+ const customEventQueue = [];
12486
+ let flushCustomEventQueue;
12124
12487
  try {
12125
12488
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
12126
12489
  const cleanFrame = document.createElement("iframe");
@@ -12137,12 +12500,12 @@ function record(options = {}) {
12137
12500
  emit,
12138
12501
  checkoutEveryNms,
12139
12502
  checkoutEveryNth,
12503
+ checkoutEveryNvm,
12140
12504
  blockClass = "rr-block",
12141
12505
  blockSelector = null,
12142
12506
  ignoreClass = "rr-ignore",
12143
12507
  ignoreSelector = null,
12144
12508
  excludeAttribute: _excludeAttribute,
12145
- includeAttribute: _includeAttribute,
12146
12509
  maskTextClass = "rr-mask",
12147
12510
  maskTextSelector = null,
12148
12511
  inlineStylesheet = true,
@@ -12160,7 +12523,7 @@ function record(options = {}) {
12160
12523
  recordCanvas = false,
12161
12524
  recordCrossOriginIframes = false,
12162
12525
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
12163
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
12526
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
12164
12527
  userTriggeredOnInput = false,
12165
12528
  collectFonts = false,
12166
12529
  inlineImages = false,
@@ -12192,8 +12555,7 @@ function record(options = {}) {
12192
12555
  sampling.mousemove = mousemoveWait;
12193
12556
  }
12194
12557
  mirror.reset();
12195
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
12196
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
12558
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
12197
12559
  const maskInputOptions = maskAllInputs === true ? {
12198
12560
  color: true,
12199
12561
  date: true,
@@ -12230,6 +12592,10 @@ function record(options = {}) {
12230
12592
  polyfill$1();
12231
12593
  let lastFullSnapshotEvent;
12232
12594
  let incrementalSnapshotCount = 0;
12595
+ let recentVisibilityChanges = 0;
12596
+ const onVisibilityActivity = (count) => {
12597
+ recentVisibilityChanges += count;
12598
+ };
12233
12599
  const eventProcessor = (e2) => {
12234
12600
  for (const plugin3 of plugins || []) {
12235
12601
  if (plugin3.eventProcessor) {
@@ -12270,7 +12636,11 @@ function record(options = {}) {
12270
12636
  incrementalSnapshotCount++;
12271
12637
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
12272
12638
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
12273
- if (exceedCount || exceedTime) {
12639
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
12640
+ if (exceedCount || exceedTime || exceedVisibility) {
12641
+ if (exceedVisibility) {
12642
+ recentVisibilityChanges = 0;
12643
+ }
12274
12644
  takeFullSnapshot$1(true);
12275
12645
  }
12276
12646
  }
@@ -12298,6 +12668,17 @@ function record(options = {}) {
12298
12668
  ...p
12299
12669
  }
12300
12670
  });
12671
+ const wrappedVisibilityMutationEmit = (p) => {
12672
+ var _a2;
12673
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
12674
+ wrappedEmit({
12675
+ type: EventType.IncrementalSnapshot,
12676
+ data: {
12677
+ source: IncrementalSource.VisibilityMutation,
12678
+ ...p
12679
+ }
12680
+ });
12681
+ };
12301
12682
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
12302
12683
  type: EventType.IncrementalSnapshot,
12303
12684
  data: {
@@ -12335,6 +12716,13 @@ function record(options = {}) {
12335
12716
  sampling: sampling.canvas,
12336
12717
  dataURLOptions
12337
12718
  });
12719
+ visibilityManager = new VisibilityManager({
12720
+ doc: window.document,
12721
+ mirror,
12722
+ sampling: sampling.visibility,
12723
+ mutationCb: wrappedVisibilityMutationEmit,
12724
+ notifyActivity: onVisibilityActivity
12725
+ });
12338
12726
  const shadowDomManager = new ShadowDomManager({
12339
12727
  mutationCb: wrappedMutationEmit,
12340
12728
  scrollCb: wrappedScrollEmit,
@@ -12344,7 +12732,6 @@ function record(options = {}) {
12344
12732
  maskTextClass,
12345
12733
  maskTextSelector,
12346
12734
  excludeAttribute,
12347
- includeAttribute,
12348
12735
  inlineStylesheet,
12349
12736
  maskInputOptions,
12350
12737
  dataURLOptions,
@@ -12357,6 +12744,7 @@ function record(options = {}) {
12357
12744
  iframeManager,
12358
12745
  stylesheetManager,
12359
12746
  canvasManager,
12747
+ visibilityManager,
12360
12748
  keepIframeSrcFn,
12361
12749
  processedNodeManager
12362
12750
  },
@@ -12387,7 +12775,6 @@ function record(options = {}) {
12387
12775
  maskTextClass,
12388
12776
  maskTextSelector,
12389
12777
  excludeAttribute,
12390
- includeAttribute,
12391
12778
  inlineStylesheet,
12392
12779
  maskAllInputs: maskInputOptions,
12393
12780
  maskTextFn,
@@ -12436,6 +12823,12 @@ function record(options = {}) {
12436
12823
  mirror.getId(document)
12437
12824
  );
12438
12825
  };
12826
+ flushCustomEventQueue = () => {
12827
+ for (const e2 of customEventQueue) {
12828
+ wrappedEmit(e2);
12829
+ }
12830
+ customEventQueue.length = 0;
12831
+ };
12439
12832
  try {
12440
12833
  const handlers = [];
12441
12834
  const observe = (doc) => {
@@ -12494,6 +12887,7 @@ function record(options = {}) {
12494
12887
  }
12495
12888
  }),
12496
12889
  canvasMutationCb: wrappedCanvasMutationEmit,
12890
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
12497
12891
  fontCb: (p) => wrappedEmit({
12498
12892
  type: EventType.IncrementalSnapshot,
12499
12893
  data: {
@@ -12525,7 +12919,6 @@ function record(options = {}) {
12525
12919
  maskTextClass,
12526
12920
  maskTextSelector,
12527
12921
  excludeAttribute,
12528
- includeAttribute,
12529
12922
  maskInputOptions,
12530
12923
  inlineStylesheet,
12531
12924
  sampling,
@@ -12547,6 +12940,7 @@ function record(options = {}) {
12547
12940
  shadowDomManager,
12548
12941
  processedNodeManager,
12549
12942
  canvasManager,
12943
+ visibilityManager,
12550
12944
  ignoreCSSAttributes,
12551
12945
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
12552
12946
  observer: p.observer,
@@ -12571,14 +12965,14 @@ function record(options = {}) {
12571
12965
  }
12572
12966
  });
12573
12967
  const init = () => {
12574
- if (flushCustomQueue === "before") {
12575
- flushPreRecordingEvents();
12968
+ if (flushCustomEvent === "before") {
12969
+ flushCustomEventQueue();
12576
12970
  }
12577
12971
  takeFullSnapshot$1();
12578
12972
  handlers.push(observe(document));
12579
12973
  recording = true;
12580
- if (flushCustomQueue === "after") {
12581
- flushPreRecordingEvents();
12974
+ if (flushCustomEvent === "after") {
12975
+ flushCustomEventQueue();
12582
12976
  }
12583
12977
  };
12584
12978
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -12608,7 +13002,7 @@ function record(options = {}) {
12608
13002
  );
12609
13003
  }
12610
13004
  return () => {
12611
- flushPreRecordingEvents();
13005
+ flushCustomEventQueue();
12612
13006
  handlers.forEach((h) => h());
12613
13007
  processedNodeManager.destroy();
12614
13008
  recording = false;
@@ -12618,12 +13012,10 @@ function record(options = {}) {
12618
13012
  console.warn(error);
12619
13013
  }
12620
13014
  }
12621
- function flushPreRecordingEvents() {
12622
- for (const e2 of preRecordingCustomEvents) {
12623
- wrappedEmit(e2);
12624
- }
12625
- preRecordingCustomEvents.length = 0;
12626
- }
13015
+ record.flushCustomEventQueue = () => {
13016
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
13017
+ flushCustomEventQueue();
13018
+ };
12627
13019
  record.addCustomEvent = (tag, payload) => {
12628
13020
  const customEvent = {
12629
13021
  type: EventType.Custom,
@@ -12633,8 +13025,8 @@ record.addCustomEvent = (tag, payload) => {
12633
13025
  }
12634
13026
  };
12635
13027
  if (!recording) {
12636
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
12637
- preRecordingCustomEvents.push(customEvent);
13028
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
13029
+ customEventQueue.push(customEvent);
12638
13030
  return;
12639
13031
  }
12640
13032
  wrappedEmit(customEvent);