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

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",
@@ -895,9 +945,6 @@ function transformAttribute(doc, tagName, name, value) {
895
945
  function ignoreAttribute(tagName, name, _value) {
896
946
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
897
947
  }
898
- function isIncludeAttribute(name, include) {
899
- return typeof include === "string" ? name.includes(include) : include.test(name);
900
- }
901
948
  function isExcludeAttribute(name, exclude) {
902
949
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
903
950
  }
@@ -1031,7 +1078,6 @@ function serializeNode(n2, options) {
1031
1078
  blockClass,
1032
1079
  blockSelector,
1033
1080
  excludeAttribute,
1034
- includeAttribute,
1035
1081
  needsMask,
1036
1082
  inlineStylesheet,
1037
1083
  maskInputOptions = {},
@@ -1045,22 +1091,19 @@ function serializeNode(n2, options) {
1045
1091
  cssCaptured = false
1046
1092
  } = options;
1047
1093
  const rootId = getRootId(doc, mirror2);
1048
- const xPath = getXPath(n2);
1049
1094
  switch (n2.nodeType) {
1050
1095
  case n2.DOCUMENT_NODE:
1051
1096
  if (n2.compatMode !== "CSS1Compat") {
1052
1097
  return {
1053
1098
  type: NodeType$3.Document,
1054
1099
  childNodes: [],
1055
- xPath,
1056
1100
  compatMode: n2.compatMode
1057
1101
  // probably "BackCompat"
1058
1102
  };
1059
1103
  } else {
1060
1104
  return {
1061
1105
  type: NodeType$3.Document,
1062
- childNodes: [],
1063
- xPath
1106
+ childNodes: []
1064
1107
  };
1065
1108
  }
1066
1109
  case n2.DOCUMENT_TYPE_NODE:
@@ -1069,8 +1112,7 @@ function serializeNode(n2, options) {
1069
1112
  name: n2.name,
1070
1113
  publicId: n2.publicId,
1071
1114
  systemId: n2.systemId,
1072
- rootId,
1073
- xPath
1115
+ rootId
1074
1116
  };
1075
1117
  case n2.ELEMENT_NODE:
1076
1118
  return serializeElementNode(n2, {
@@ -1078,7 +1120,6 @@ function serializeNode(n2, options) {
1078
1120
  blockClass,
1079
1121
  blockSelector,
1080
1122
  excludeAttribute,
1081
- includeAttribute,
1082
1123
  inlineStylesheet,
1083
1124
  maskInputOptions,
1084
1125
  maskInputFn,
@@ -1087,8 +1128,7 @@ function serializeNode(n2, options) {
1087
1128
  recordCanvas,
1088
1129
  keepIframeSrcFn,
1089
1130
  newlyAddedElement,
1090
- rootId,
1091
- xPath
1131
+ rootId
1092
1132
  });
1093
1133
  case n2.TEXT_NODE:
1094
1134
  return serializeTextNode(n2, {
@@ -1096,22 +1136,19 @@ function serializeNode(n2, options) {
1096
1136
  needsMask,
1097
1137
  maskTextFn,
1098
1138
  rootId,
1099
- cssCaptured,
1100
- xPath
1139
+ cssCaptured
1101
1140
  });
1102
1141
  case n2.CDATA_SECTION_NODE:
1103
1142
  return {
1104
1143
  type: NodeType$3.CDATA,
1105
1144
  textContent: "",
1106
- rootId,
1107
- xPath
1145
+ rootId
1108
1146
  };
1109
1147
  case n2.COMMENT_NODE:
1110
1148
  return {
1111
1149
  type: NodeType$3.Comment,
1112
1150
  textContent: index$1.textContent(n2) || "",
1113
- rootId,
1114
- xPath
1151
+ rootId
1115
1152
  };
1116
1153
  default:
1117
1154
  return false;
@@ -1123,7 +1160,7 @@ function getRootId(doc, mirror2) {
1123
1160
  return docId === 1 ? void 0 : docId;
1124
1161
  }
1125
1162
  function serializeTextNode(n2, options) {
1126
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1163
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1127
1164
  const parent = index$1.parentNode(n2);
1128
1165
  const parentTagName = parent && parent.tagName;
1129
1166
  let textContent2 = "";
@@ -1140,15 +1177,10 @@ function serializeTextNode(n2, options) {
1140
1177
  if (!isStyle && !isScript && textContent2 && needsMask) {
1141
1178
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1142
1179
  }
1143
- const isVisible = isTextVisible(n2);
1144
- const isInteractive = isElementInteractive(n2);
1145
1180
  return {
1146
1181
  type: NodeType$3.Text,
1147
1182
  textContent: textContent2 || "",
1148
- rootId,
1149
- isVisible,
1150
- isInteractive,
1151
- xPath
1183
+ rootId
1152
1184
  };
1153
1185
  }
1154
1186
  function serializeElementNode(n2, options) {
@@ -1157,7 +1189,6 @@ function serializeElementNode(n2, options) {
1157
1189
  blockClass,
1158
1190
  blockSelector,
1159
1191
  excludeAttribute,
1160
- includeAttribute,
1161
1192
  inlineStylesheet,
1162
1193
  maskInputOptions = {},
1163
1194
  maskInputFn,
@@ -1166,8 +1197,7 @@ function serializeElementNode(n2, options) {
1166
1197
  recordCanvas,
1167
1198
  keepIframeSrcFn,
1168
1199
  newlyAddedElement = false,
1169
- rootId,
1170
- xPath
1200
+ rootId
1171
1201
  } = options;
1172
1202
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1173
1203
  const tagName = getValidTagName$1(n2);
@@ -1175,7 +1205,7 @@ function serializeElementNode(n2, options) {
1175
1205
  const len = n2.attributes.length;
1176
1206
  for (let i2 = 0; i2 < len; i2++) {
1177
1207
  const attr = n2.attributes[i2];
1178
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1208
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1179
1209
  continue;
1180
1210
  }
1181
1211
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1337,8 +1367,6 @@ function serializeElementNode(n2, options) {
1337
1367
  if (customElements.get(tagName)) isCustomElement = true;
1338
1368
  } catch (e2) {
1339
1369
  }
1340
- const isVisible = isElementVisible(n2);
1341
- const isInteractive = isElementInteractive(n2);
1342
1370
  return {
1343
1371
  type: NodeType$3.Element,
1344
1372
  tagName,
@@ -1347,10 +1375,7 @@ function serializeElementNode(n2, options) {
1347
1375
  isSVG: isSVGElement(n2) || void 0,
1348
1376
  needBlock,
1349
1377
  rootId,
1350
- isCustom: isCustomElement,
1351
- isVisible,
1352
- isInteractive,
1353
- xPath
1378
+ isCustom: isCustomElement
1354
1379
  };
1355
1380
  }
1356
1381
  function lowerIfExists(maybeAttr) {
@@ -1401,7 +1426,6 @@ function serializeNodeWithId(n2, options) {
1401
1426
  maskTextClass,
1402
1427
  maskTextSelector,
1403
1428
  excludeAttribute,
1404
- includeAttribute,
1405
1429
  skipChild = false,
1406
1430
  inlineStylesheet = true,
1407
1431
  maskInputOptions = {},
@@ -1437,7 +1461,6 @@ function serializeNodeWithId(n2, options) {
1437
1461
  blockClass,
1438
1462
  blockSelector,
1439
1463
  excludeAttribute,
1440
- includeAttribute,
1441
1464
  needsMask,
1442
1465
  inlineStylesheet,
1443
1466
  maskInputOptions,
@@ -1463,6 +1486,22 @@ function serializeNodeWithId(n2, options) {
1463
1486
  id = genId();
1464
1487
  }
1465
1488
  const serializedNode = Object.assign(_serializedNode, { id });
1489
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1490
+ serializedNode.xpath = buildXPath(n2);
1491
+ if (isElement(n2)) {
1492
+ const selector = buildSelector(n2);
1493
+ if (selector) {
1494
+ serializedNode.selector = selector;
1495
+ }
1496
+ }
1497
+ if (n2.nodeType === Node.TEXT_NODE) {
1498
+ serializedNode.isVisible = isTextVisible(n2);
1499
+ }
1500
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1501
+ serializedNode.isVisible = isElementVisible(n2);
1502
+ serializedNode.isInteractive = isElementInteractive(n2);
1503
+ }
1504
+ }
1466
1505
  mirror2.add(n2, serializedNode);
1467
1506
  if (id === IGNORED_NODE) {
1468
1507
  return null;
@@ -1491,7 +1530,6 @@ function serializeNodeWithId(n2, options) {
1491
1530
  maskTextClass,
1492
1531
  maskTextSelector,
1493
1532
  excludeAttribute,
1494
- includeAttribute,
1495
1533
  skipChild,
1496
1534
  inlineStylesheet,
1497
1535
  maskInputOptions,
@@ -1552,7 +1590,6 @@ function serializeNodeWithId(n2, options) {
1552
1590
  maskTextClass,
1553
1591
  maskTextSelector,
1554
1592
  excludeAttribute,
1555
- includeAttribute,
1556
1593
  skipChild: false,
1557
1594
  inlineStylesheet,
1558
1595
  maskInputOptions,
@@ -1595,7 +1632,6 @@ function serializeNodeWithId(n2, options) {
1595
1632
  maskTextClass,
1596
1633
  maskTextSelector,
1597
1634
  excludeAttribute,
1598
- includeAttribute,
1599
1635
  skipChild: false,
1600
1636
  inlineStylesheet,
1601
1637
  maskInputOptions,
@@ -1633,8 +1669,7 @@ function snapshot(n2, options) {
1633
1669
  blockSelector = null,
1634
1670
  maskTextClass = "rr-mask",
1635
1671
  maskTextSelector = null,
1636
- excludeAttribute = /^$a/,
1637
- includeAttribute = /.+/i,
1672
+ excludeAttribute = /.^/,
1638
1673
  inlineStylesheet = true,
1639
1674
  inlineImages = false,
1640
1675
  recordCanvas = false,
@@ -1695,7 +1730,6 @@ function snapshot(n2, options) {
1695
1730
  maskTextClass,
1696
1731
  maskTextSelector,
1697
1732
  excludeAttribute,
1698
- includeAttribute,
1699
1733
  skipChild: false,
1700
1734
  inlineStylesheet,
1701
1735
  maskInputOptions,
@@ -10834,6 +10868,41 @@ function patch(source, name, replacement) {
10834
10868
  };
10835
10869
  }
10836
10870
  }
10871
+ function describeNode(el) {
10872
+ const tag = el.tagName.toLowerCase();
10873
+ const id = el.id ? `#${el.id}` : "";
10874
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
10875
+ return `${tag}${id}${classes}`;
10876
+ }
10877
+ function getElementVisibility(el) {
10878
+ var _a2, _b2;
10879
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
10880
+ const rect = el.getBoundingClientRect();
10881
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
10882
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
10883
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
10884
+ const style = (_b2 = win.getComputedStyle) == null ? void 0 : _b2.call(win, el);
10885
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
10886
+ const isVisible = isStyleVisible2 && isRectVisible;
10887
+ let ratio = 0;
10888
+ if (isVisible) {
10889
+ const xOverlap = Math.max(
10890
+ 0,
10891
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
10892
+ );
10893
+ const yOverlap = Math.max(
10894
+ 0,
10895
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
10896
+ );
10897
+ const intersectionArea = xOverlap * yOverlap;
10898
+ const elementArea = rect.width * rect.height;
10899
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
10900
+ }
10901
+ return {
10902
+ isVisible,
10903
+ ratio
10904
+ };
10905
+ }
10837
10906
  const index = {
10838
10907
  childNodes,
10839
10908
  parentNode,
@@ -10847,7 +10916,9 @@ const index = {
10847
10916
  querySelector,
10848
10917
  querySelectorAll,
10849
10918
  mutationObserver: mutationObserverCtor,
10850
- patch
10919
+ patch,
10920
+ describeNode,
10921
+ getElementVisibility
10851
10922
  };
10852
10923
  function on(type, fn, target = document) {
10853
10924
  const options = { capture: true, passive: true };
@@ -11248,6 +11319,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
11248
11319
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
11249
11320
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
11250
11321
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
11322
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
11251
11323
  return IncrementalSource2;
11252
11324
  })(IncrementalSource || {});
11253
11325
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -11414,7 +11486,6 @@ class MutationBuffer {
11414
11486
  __publicField(this, "maskTextClass");
11415
11487
  __publicField(this, "maskTextSelector");
11416
11488
  __publicField(this, "excludeAttribute");
11417
- __publicField(this, "includeAttribute");
11418
11489
  __publicField(this, "inlineStylesheet");
11419
11490
  __publicField(this, "maskInputOptions");
11420
11491
  __publicField(this, "maskTextFn");
@@ -11430,6 +11501,7 @@ class MutationBuffer {
11430
11501
  __publicField(this, "stylesheetManager");
11431
11502
  __publicField(this, "shadowDomManager");
11432
11503
  __publicField(this, "canvasManager");
11504
+ __publicField(this, "visibilityManager");
11433
11505
  __publicField(this, "processedNodeManager");
11434
11506
  __publicField(this, "unattachedDoc");
11435
11507
  __publicField(this, "processMutations", (mutations) => {
@@ -11479,7 +11551,6 @@ class MutationBuffer {
11479
11551
  maskTextClass: this.maskTextClass,
11480
11552
  maskTextSelector: this.maskTextSelector,
11481
11553
  excludeAttribute: this.excludeAttribute,
11482
- includeAttribute: this.includeAttribute,
11483
11554
  skipChild: true,
11484
11555
  newlyAddedElement: true,
11485
11556
  inlineStylesheet: this.inlineStylesheet,
@@ -11525,7 +11596,8 @@ class MutationBuffer {
11525
11596
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
11526
11597
  }
11527
11598
  for (const n2 of this.movedSet) {
11528
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
11599
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11600
+ !this.movedSet.has(index.parentNode(n2))) {
11529
11601
  continue;
11530
11602
  }
11531
11603
  pushAdd(n2);
@@ -11681,12 +11753,60 @@ class MutationBuffer {
11681
11753
  const target = m.target;
11682
11754
  let attributeName = m.attributeName;
11683
11755
  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");
11756
+ const attrKey = attributeName;
11757
+ const propValue = target[attrKey];
11758
+ const isBooleanAttr = typeof propValue === "boolean";
11759
+ const inDOM = document.contains(target);
11760
+ const isVisible = isElementVisible(target);
11761
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
11762
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
11763
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
11764
+ m.oldValue !== null && // раньше он был
11765
+ (propValue === "" || // свойство = пустая строка
11766
+ propValue === null || // или null
11767
+ typeof propValue === "undefined");
11686
11768
  if (isPhantomAttributeMutation) {
11769
+ console.debug(
11770
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ phantom attribute mutation ignored`,
11771
+ {
11772
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11773
+ node: index.describeNode(target),
11774
+ tag: target.tagName,
11775
+ nodeType: target.nodeType,
11776
+ attribute: attributeName,
11777
+ value,
11778
+ oldValue: m.oldValue,
11779
+ excludeAttribute: this.excludeAttribute,
11780
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11781
+ propValue,
11782
+ isBooleanAttr,
11783
+ inDOM,
11784
+ isVisible,
11785
+ isExcludeAttributeName
11786
+ }
11787
+ );
11687
11788
  return;
11688
11789
  }
11689
11790
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
11791
+ console.debug(
11792
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ excluded attribute mutation ignored`,
11793
+ {
11794
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11795
+ node: index.describeNode(target),
11796
+ tag: target.tagName,
11797
+ nodeType: target.nodeType,
11798
+ attribute: attributeName,
11799
+ value,
11800
+ oldValue: m.oldValue,
11801
+ excludeAttribute: this.excludeAttribute,
11802
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11803
+ propValue,
11804
+ isBooleanAttr,
11805
+ inDOM,
11806
+ isVisible,
11807
+ isExcludeAttributeName
11808
+ }
11809
+ );
11690
11810
  return;
11691
11811
  }
11692
11812
  if (attributeName === "value") {
@@ -11731,9 +11851,35 @@ class MutationBuffer {
11731
11851
  toLowerCase(attributeName),
11732
11852
  value
11733
11853
  );
11734
- if (value === item.attributes[attributeName]) {
11735
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
11736
- return;
11854
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
11855
+ value === "" || // класс удалён
11856
+ value !== m.oldValue);
11857
+ if (isSuspiciousClassMutation) {
11858
+ console.debug(
11859
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⚠️ suspicious attribute mutation`,
11860
+ {
11861
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11862
+ reason: [
11863
+ value === m.oldValue ? "no change in value" : null,
11864
+ value === propValue ? "mirrored in DOM property" : null,
11865
+ value === item.attributes[attributeName] ? "redundant assignment" : null
11866
+ ].filter(Boolean).join(", ") || "uncategorized",
11867
+ node: index.describeNode(target),
11868
+ tag: target.tagName,
11869
+ nodeType: target.nodeType,
11870
+ attribute: attributeName,
11871
+ value,
11872
+ oldValue: m.oldValue,
11873
+ transformedValue: item.attributes[attributeName],
11874
+ excludeAttribute: this.excludeAttribute,
11875
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11876
+ propValue,
11877
+ isBooleanAttr,
11878
+ inDOM,
11879
+ isVisible,
11880
+ isExcludeAttributeName
11881
+ }
11882
+ );
11737
11883
  }
11738
11884
  if (attributeName === "style") {
11739
11885
  if (!this.unattachedDoc) {
@@ -11848,7 +11994,6 @@ class MutationBuffer {
11848
11994
  "maskTextClass",
11849
11995
  "maskTextSelector",
11850
11996
  "excludeAttribute",
11851
- "includeAttribute",
11852
11997
  "inlineStylesheet",
11853
11998
  "maskInputOptions",
11854
11999
  "maskTextFn",
@@ -11864,6 +12009,7 @@ class MutationBuffer {
11864
12009
  "stylesheetManager",
11865
12010
  "shadowDomManager",
11866
12011
  "canvasManager",
12012
+ "visibilityManager",
11867
12013
  "processedNodeManager"
11868
12014
  ].forEach((key) => {
11869
12015
  this[key] = options[key];
@@ -11872,10 +12018,12 @@ class MutationBuffer {
11872
12018
  freeze() {
11873
12019
  this.frozen = true;
11874
12020
  this.canvasManager.freeze();
12021
+ this.visibilityManager.freeze();
11875
12022
  }
11876
12023
  unfreeze() {
11877
12024
  this.frozen = false;
11878
12025
  this.canvasManager.unfreeze();
12026
+ this.visibilityManager.unfreeze();
11879
12027
  this.emit();
11880
12028
  }
11881
12029
  isFrozen() {
@@ -11884,15 +12032,18 @@ class MutationBuffer {
11884
12032
  lock() {
11885
12033
  this.locked = true;
11886
12034
  this.canvasManager.lock();
12035
+ this.visibilityManager.lock();
11887
12036
  }
11888
12037
  unlock() {
11889
12038
  this.locked = false;
11890
12039
  this.canvasManager.unlock();
12040
+ this.visibilityManager.unlock();
11891
12041
  this.emit();
11892
12042
  }
11893
12043
  reset() {
11894
12044
  this.shadowDomManager.reset();
11895
12045
  this.canvasManager.reset();
12046
+ this.visibilityManager.reset();
11896
12047
  }
11897
12048
  }
11898
12049
  function deepDelete(addsSet, n2) {
@@ -12826,6 +12977,7 @@ function mergeHooks(o2, hooks) {
12826
12977
  styleSheetRuleCb,
12827
12978
  styleDeclarationCb,
12828
12979
  canvasMutationCb,
12980
+ visibilityMutationCb,
12829
12981
  fontCb,
12830
12982
  selectionCb,
12831
12983
  customElementCb
@@ -12890,6 +13042,12 @@ function mergeHooks(o2, hooks) {
12890
13042
  }
12891
13043
  canvasMutationCb(...p);
12892
13044
  };
13045
+ o2.visibilityMutationCb = (...p) => {
13046
+ if (hooks.visibilityMutation) {
13047
+ hooks.visibilityMutation(...p);
13048
+ }
13049
+ visibilityMutationCb(...p);
13050
+ };
12893
13051
  o2.fontCb = (...p) => {
12894
13052
  if (hooks.font) {
12895
13053
  hooks.font(...p);
@@ -14023,11 +14181,249 @@ class ProcessedNodeManager {
14023
14181
  destroy() {
14024
14182
  }
14025
14183
  }
14184
+ function computeVisibility(elements, previous, options) {
14185
+ const root2 = (options == null ? void 0 : options.root) ?? null;
14186
+ const threshold = (options == null ? void 0 : options.threshold) ?? 0.5;
14187
+ const sensitivity = (options == null ? void 0 : options.sensitivity) ?? 0.05;
14188
+ const rootMarginFn = parseRootMargin((options == null ? void 0 : options.rootMargin) ?? "0px");
14189
+ const current = /* @__PURE__ */ new Map();
14190
+ const rootRect = getRootRect(root2);
14191
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
14192
+ for (const el of elements) {
14193
+ const elRect = el.getBoundingClientRect();
14194
+ let intersectionRect = emptyRect();
14195
+ let intersectionRatio = 0;
14196
+ if (elRect.width > 0 && elRect.height > 0) {
14197
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
14198
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
14199
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
14200
+ }
14201
+ const isStyle = isStyleVisible(el);
14202
+ const old = previous.get(el) ?? null;
14203
+ const prevRatio = (old == null ? void 0 : old.intersectionRatio) ?? 0;
14204
+ const currRatio = intersectionRatio;
14205
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
14206
+ const nowVisible = isStyle && currRatio > threshold;
14207
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
14208
+ if (changed) {
14209
+ current.set(el, {
14210
+ target: el,
14211
+ isVisible: nowVisible,
14212
+ isStyleVisible: isStyle,
14213
+ intersectionRatio: currRatio,
14214
+ intersectionRect,
14215
+ oldValue: old
14216
+ });
14217
+ } else {
14218
+ current.set(el, old);
14219
+ }
14220
+ }
14221
+ return current;
14222
+ }
14223
+ function parseRootMargin(marginStr) {
14224
+ const parts = marginStr.trim().split(/\s+/);
14225
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
14226
+ return function(rootRect) {
14227
+ const top = getValue(parts[0] || "0px", rootRect.height);
14228
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
14229
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
14230
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
14231
+ return { top, right, bottom, left, width: 0, height: 0 };
14232
+ };
14233
+ }
14234
+ function getRootRect(root2) {
14235
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
14236
+ }
14237
+ function expandRootRect(rect, marginFn) {
14238
+ const margin = marginFn(rect);
14239
+ return new DOMRect(
14240
+ rect.left - margin.left,
14241
+ rect.top - margin.top,
14242
+ rect.width + margin.left + margin.right,
14243
+ rect.height + margin.top + margin.bottom
14244
+ );
14245
+ }
14246
+ function computeIntersectionRect(a2, b) {
14247
+ const top = Math.max(a2.top, b.top);
14248
+ const left = Math.max(a2.left, b.left);
14249
+ const bottom = Math.min(a2.bottom, b.bottom);
14250
+ const right = Math.min(a2.right, b.right);
14251
+ const width = Math.max(0, right - left);
14252
+ const height = Math.max(0, bottom - top);
14253
+ return { top, left, bottom, right, width, height };
14254
+ }
14255
+ function computeIntersectionRatio(elRect, intersectionRect) {
14256
+ const elArea = elRect.width * elRect.height;
14257
+ const intArea = intersectionRect.width * intersectionRect.height;
14258
+ return elArea > 0 ? intArea / elArea : 0;
14259
+ }
14260
+ function emptyRect() {
14261
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
14262
+ }
14263
+ function isStyleVisible(el) {
14264
+ const style = getComputedStyle(el);
14265
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
14266
+ }
14267
+ class VisibilityManager {
14268
+ constructor(options) {
14269
+ __publicField(this, "frozen", false);
14270
+ __publicField(this, "locked", false);
14271
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
14272
+ __publicField(this, "mirror");
14273
+ __publicField(this, "mutationCb");
14274
+ __publicField(this, "rafId", null);
14275
+ __publicField(this, "rafThrottle");
14276
+ __publicField(this, "lastFlushTime", 0);
14277
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
14278
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
14279
+ __publicField(this, "root", null);
14280
+ __publicField(this, "threshold");
14281
+ __publicField(this, "sensitivity");
14282
+ __publicField(this, "rootMargin");
14283
+ __publicField(this, "hasInitialized", false);
14284
+ __publicField(this, "mode", "none");
14285
+ __publicField(this, "debounce", 50);
14286
+ __publicField(this, "throttle", 100);
14287
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
14288
+ __publicField(this, "debounceTimer", null);
14289
+ __publicField(this, "lastThrottleTime", 0);
14290
+ __publicField(this, "disabled", false);
14291
+ __publicField(this, "notifyActivity");
14292
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
14293
+ this.mirror = mirror2;
14294
+ this.mutationCb = mutationCb;
14295
+ this.notifyActivity = notifyActivity;
14296
+ this.rootMargin = "0px";
14297
+ if (sampling === false) {
14298
+ this.disabled = true;
14299
+ return;
14300
+ }
14301
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
14302
+ this.mode = (visibilitySampling == null ? void 0 : visibilitySampling.mode) ?? "none";
14303
+ this.debounce = Number((visibilitySampling == null ? void 0 : visibilitySampling.debounce) ?? 100);
14304
+ this.throttle = Number((visibilitySampling == null ? void 0 : visibilitySampling.throttle) ?? 100);
14305
+ this.threshold = Number((visibilitySampling == null ? void 0 : visibilitySampling.threshold) ?? 0.5);
14306
+ this.sensitivity = Number((visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) ?? 0.05);
14307
+ this.rafThrottle = Number((visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) ?? 100);
14308
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
14309
+ const mo = new MutationObserver((mutations) => {
14310
+ mutations.forEach((m) => {
14311
+ m.addedNodes.forEach((n2) => {
14312
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14313
+ this.observe(n2);
14314
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
14315
+ }
14316
+ });
14317
+ m.removedNodes.forEach((n2) => {
14318
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14319
+ this.unobserve(n2);
14320
+ }
14321
+ });
14322
+ });
14323
+ });
14324
+ mo.observe(doc.body, { childList: true, subtree: true });
14325
+ this.startPendingFlushLoop();
14326
+ }
14327
+ startPendingFlushLoop() {
14328
+ if (this.disabled) return;
14329
+ const loop = (timestamp) => {
14330
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
14331
+ this.lastFlushTime = timestamp;
14332
+ this.flushPendingVisibilityMutations();
14333
+ }
14334
+ this.rafId = requestAnimationFrame(loop);
14335
+ };
14336
+ this.rafId = requestAnimationFrame(loop);
14337
+ }
14338
+ flushPendingVisibilityMutations() {
14339
+ if (this.disabled) return;
14340
+ if (this.frozen || this.locked || this.elements.size === 0) return;
14341
+ const state = computeVisibility(this.elements, this.previousState, {
14342
+ root: this.root,
14343
+ threshold: this.threshold,
14344
+ sensitivity: this.sensitivity,
14345
+ rootMargin: this.rootMargin
14346
+ });
14347
+ for (const [el, entry] of state.entries()) {
14348
+ const old = this.previousState.get(el);
14349
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
14350
+ if (changed) {
14351
+ const id = this.mirror.getId(el);
14352
+ if (id !== -1) {
14353
+ this.buffer.set(el, {
14354
+ id,
14355
+ isVisible: entry.isVisible,
14356
+ ratio: entry.intersectionRatio
14357
+ });
14358
+ }
14359
+ this.previousState.set(el, entry);
14360
+ }
14361
+ }
14362
+ this.previousState = state;
14363
+ if (!this.hasInitialized) {
14364
+ this.hasInitialized = true;
14365
+ this.buffer.clear();
14366
+ return;
14367
+ }
14368
+ this.scheduleEmit();
14369
+ }
14370
+ scheduleEmit() {
14371
+ if (this.mode === "debounce") {
14372
+ clearTimeout(this.debounceTimer);
14373
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
14374
+ } else if (this.mode === "throttle") {
14375
+ const now = performance.now();
14376
+ if (now - this.lastThrottleTime >= this.throttle) {
14377
+ this.lastThrottleTime = now;
14378
+ this.flushBuffer();
14379
+ }
14380
+ } else {
14381
+ this.flushBuffer();
14382
+ }
14383
+ }
14384
+ flushBuffer() {
14385
+ var _a2;
14386
+ if (this.buffer.size === 0) return;
14387
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
14388
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
14389
+ this.buffer.clear();
14390
+ }
14391
+ observe(el) {
14392
+ if (this.disabled) return;
14393
+ this.elements.add(el);
14394
+ }
14395
+ unobserve(el) {
14396
+ if (this.disabled) return;
14397
+ this.elements.delete(el);
14398
+ this.previousState.delete(el);
14399
+ this.pending.delete(el);
14400
+ }
14401
+ freeze() {
14402
+ this.frozen = true;
14403
+ }
14404
+ unfreeze() {
14405
+ this.frozen = false;
14406
+ }
14407
+ lock() {
14408
+ this.locked = true;
14409
+ }
14410
+ unlock() {
14411
+ this.locked = false;
14412
+ }
14413
+ reset() {
14414
+ this.elements.clear();
14415
+ this.previousState.clear();
14416
+ this.pending.clear();
14417
+ if (this.rafId) cancelAnimationFrame(this.rafId);
14418
+ }
14419
+ }
14026
14420
  let wrappedEmit;
14027
14421
  let takeFullSnapshot$1;
14028
14422
  let canvasManager;
14423
+ let visibilityManager;
14029
14424
  let recording = false;
14030
- const preRecordingCustomEvents = [];
14425
+ const customEventQueue = [];
14426
+ let flushCustomEventQueue;
14031
14427
  try {
14032
14428
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
14033
14429
  const cleanFrame = document.createElement("iframe");
@@ -14044,12 +14440,12 @@ function record(options = {}) {
14044
14440
  emit,
14045
14441
  checkoutEveryNms,
14046
14442
  checkoutEveryNth,
14443
+ checkoutEveryNvm,
14047
14444
  blockClass = "rr-block",
14048
14445
  blockSelector = null,
14049
14446
  ignoreClass = "rr-ignore",
14050
14447
  ignoreSelector = null,
14051
14448
  excludeAttribute: _excludeAttribute,
14052
- includeAttribute: _includeAttribute,
14053
14449
  maskTextClass = "rr-mask",
14054
14450
  maskTextSelector = null,
14055
14451
  inlineStylesheet = true,
@@ -14067,7 +14463,7 @@ function record(options = {}) {
14067
14463
  recordCanvas = false,
14068
14464
  recordCrossOriginIframes = false,
14069
14465
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
14070
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
14466
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
14071
14467
  userTriggeredOnInput = false,
14072
14468
  collectFonts = false,
14073
14469
  inlineImages = false,
@@ -14099,8 +14495,7 @@ function record(options = {}) {
14099
14495
  sampling.mousemove = mousemoveWait;
14100
14496
  }
14101
14497
  mirror.reset();
14102
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
14103
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
14498
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
14104
14499
  const maskInputOptions = maskAllInputs === true ? {
14105
14500
  color: true,
14106
14501
  date: true,
@@ -14137,6 +14532,10 @@ function record(options = {}) {
14137
14532
  polyfill$1();
14138
14533
  let lastFullSnapshotEvent;
14139
14534
  let incrementalSnapshotCount = 0;
14535
+ let recentVisibilityChanges = 0;
14536
+ const onVisibilityActivity = (count) => {
14537
+ recentVisibilityChanges += count;
14538
+ };
14140
14539
  const eventProcessor = (e2) => {
14141
14540
  for (const plugin3 of plugins || []) {
14142
14541
  if (plugin3.eventProcessor) {
@@ -14177,7 +14576,11 @@ function record(options = {}) {
14177
14576
  incrementalSnapshotCount++;
14178
14577
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
14179
14578
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
14180
- if (exceedCount || exceedTime) {
14579
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
14580
+ if (exceedCount || exceedTime || exceedVisibility) {
14581
+ if (exceedVisibility) {
14582
+ recentVisibilityChanges = 0;
14583
+ }
14181
14584
  takeFullSnapshot$1(true);
14182
14585
  }
14183
14586
  }
@@ -14205,6 +14608,17 @@ function record(options = {}) {
14205
14608
  ...p
14206
14609
  }
14207
14610
  });
14611
+ const wrappedVisibilityMutationEmit = (p) => {
14612
+ var _a2;
14613
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
14614
+ wrappedEmit({
14615
+ type: EventType.IncrementalSnapshot,
14616
+ data: {
14617
+ source: IncrementalSource.VisibilityMutation,
14618
+ ...p
14619
+ }
14620
+ });
14621
+ };
14208
14622
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
14209
14623
  type: EventType.IncrementalSnapshot,
14210
14624
  data: {
@@ -14242,6 +14656,13 @@ function record(options = {}) {
14242
14656
  sampling: sampling.canvas,
14243
14657
  dataURLOptions
14244
14658
  });
14659
+ visibilityManager = new VisibilityManager({
14660
+ doc: window.document,
14661
+ mirror,
14662
+ sampling: sampling.visibility,
14663
+ mutationCb: wrappedVisibilityMutationEmit,
14664
+ notifyActivity: onVisibilityActivity
14665
+ });
14245
14666
  const shadowDomManager = new ShadowDomManager({
14246
14667
  mutationCb: wrappedMutationEmit,
14247
14668
  scrollCb: wrappedScrollEmit,
@@ -14251,7 +14672,6 @@ function record(options = {}) {
14251
14672
  maskTextClass,
14252
14673
  maskTextSelector,
14253
14674
  excludeAttribute,
14254
- includeAttribute,
14255
14675
  inlineStylesheet,
14256
14676
  maskInputOptions,
14257
14677
  dataURLOptions,
@@ -14264,6 +14684,7 @@ function record(options = {}) {
14264
14684
  iframeManager,
14265
14685
  stylesheetManager,
14266
14686
  canvasManager,
14687
+ visibilityManager,
14267
14688
  keepIframeSrcFn,
14268
14689
  processedNodeManager
14269
14690
  },
@@ -14294,7 +14715,6 @@ function record(options = {}) {
14294
14715
  maskTextClass,
14295
14716
  maskTextSelector,
14296
14717
  excludeAttribute,
14297
- includeAttribute,
14298
14718
  inlineStylesheet,
14299
14719
  maskAllInputs: maskInputOptions,
14300
14720
  maskTextFn,
@@ -14343,6 +14763,12 @@ function record(options = {}) {
14343
14763
  mirror.getId(document)
14344
14764
  );
14345
14765
  };
14766
+ flushCustomEventQueue = () => {
14767
+ for (const e2 of customEventQueue) {
14768
+ wrappedEmit(e2);
14769
+ }
14770
+ customEventQueue.length = 0;
14771
+ };
14346
14772
  try {
14347
14773
  const handlers = [];
14348
14774
  const observe = (doc) => {
@@ -14401,6 +14827,7 @@ function record(options = {}) {
14401
14827
  }
14402
14828
  }),
14403
14829
  canvasMutationCb: wrappedCanvasMutationEmit,
14830
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
14404
14831
  fontCb: (p) => wrappedEmit({
14405
14832
  type: EventType.IncrementalSnapshot,
14406
14833
  data: {
@@ -14432,7 +14859,6 @@ function record(options = {}) {
14432
14859
  maskTextClass,
14433
14860
  maskTextSelector,
14434
14861
  excludeAttribute,
14435
- includeAttribute,
14436
14862
  maskInputOptions,
14437
14863
  inlineStylesheet,
14438
14864
  sampling,
@@ -14454,6 +14880,7 @@ function record(options = {}) {
14454
14880
  shadowDomManager,
14455
14881
  processedNodeManager,
14456
14882
  canvasManager,
14883
+ visibilityManager,
14457
14884
  ignoreCSSAttributes,
14458
14885
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
14459
14886
  observer: p.observer,
@@ -14478,14 +14905,14 @@ function record(options = {}) {
14478
14905
  }
14479
14906
  });
14480
14907
  const init = () => {
14481
- if (flushCustomQueue === "before") {
14482
- flushPreRecordingEvents();
14908
+ if (flushCustomEvent === "before") {
14909
+ flushCustomEventQueue();
14483
14910
  }
14484
14911
  takeFullSnapshot$1();
14485
14912
  handlers.push(observe(document));
14486
14913
  recording = true;
14487
- if (flushCustomQueue === "after") {
14488
- flushPreRecordingEvents();
14914
+ if (flushCustomEvent === "after") {
14915
+ flushCustomEventQueue();
14489
14916
  }
14490
14917
  };
14491
14918
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -14515,7 +14942,7 @@ function record(options = {}) {
14515
14942
  );
14516
14943
  }
14517
14944
  return () => {
14518
- flushPreRecordingEvents();
14945
+ flushCustomEventQueue();
14519
14946
  handlers.forEach((h) => h());
14520
14947
  processedNodeManager.destroy();
14521
14948
  recording = false;
@@ -14525,12 +14952,10 @@ function record(options = {}) {
14525
14952
  console.warn(error);
14526
14953
  }
14527
14954
  }
14528
- function flushPreRecordingEvents() {
14529
- for (const e2 of preRecordingCustomEvents) {
14530
- wrappedEmit(e2);
14531
- }
14532
- preRecordingCustomEvents.length = 0;
14533
- }
14955
+ record.flushCustomEventQueue = () => {
14956
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
14957
+ flushCustomEventQueue();
14958
+ };
14534
14959
  record.addCustomEvent = (tag, payload) => {
14535
14960
  const customEvent = {
14536
14961
  type: EventType.Custom,
@@ -14540,8 +14965,8 @@ record.addCustomEvent = (tag, payload) => {
14540
14965
  }
14541
14966
  };
14542
14967
  if (!recording) {
14543
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
14544
- preRecordingCustomEvents.push(customEvent);
14968
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
14969
+ customEventQueue.push(customEvent);
14545
14970
  return;
14546
14971
  }
14547
14972
  wrappedEmit(customEvent);