@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.
package/dist/rrweb-all.js CHANGED
@@ -168,6 +168,41 @@ function patch$1(source, name, replacement) {
168
168
  };
169
169
  }
170
170
  }
171
+ function describeNode$1(el) {
172
+ const tag = el.tagName.toLowerCase();
173
+ const id = el.id ? `#${el.id}` : "";
174
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
175
+ return `${tag}${id}${classes}`;
176
+ }
177
+ function getElementVisibility$1(el) {
178
+ var _a2, _b2;
179
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
180
+ const rect = el.getBoundingClientRect();
181
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
182
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
183
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
184
+ const style = (_b2 = win.getComputedStyle) == null ? void 0 : _b2.call(win, el);
185
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
186
+ const isVisible = isStyleVisible2 && isRectVisible;
187
+ let ratio = 0;
188
+ if (isVisible) {
189
+ const xOverlap = Math.max(
190
+ 0,
191
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
192
+ );
193
+ const yOverlap = Math.max(
194
+ 0,
195
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
196
+ );
197
+ const intersectionArea = xOverlap * yOverlap;
198
+ const elementArea = rect.width * rect.height;
199
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
200
+ }
201
+ return {
202
+ isVisible,
203
+ ratio
204
+ };
205
+ }
171
206
  const index$1 = {
172
207
  childNodes: childNodes$1,
173
208
  parentNode: parentNode$1,
@@ -181,7 +216,9 @@ const index$1 = {
181
216
  querySelector: querySelector$1,
182
217
  querySelectorAll: querySelectorAll$1,
183
218
  mutationObserver: mutationObserverCtor$1,
184
- patch: patch$1
219
+ patch: patch$1,
220
+ describeNode: describeNode$1,
221
+ getElementVisibility: getElementVisibility$1
185
222
  };
186
223
  function isElement(n2) {
187
224
  return n2.nodeType === n2.ELEMENT_NODE;
@@ -567,71 +604,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
567
604
  function markCssSplits(cssText, style) {
568
605
  return splitCssText(cssText, style).join("/* rr_split */");
569
606
  }
570
- function getXPath(node2) {
571
- if (node2.nodeType === Node.DOCUMENT_NODE) {
572
- return "/";
607
+ function isSelectorUnique(selector, target) {
608
+ try {
609
+ const matches = document.querySelectorAll(selector);
610
+ return matches.length === 1 && matches[0] === target;
611
+ } catch {
612
+ return false;
573
613
  }
574
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
575
- return "/html/doctype";
614
+ }
615
+ function buildSelector(node2) {
616
+ if (!(node2 instanceof Element)) return null;
617
+ if (node2.id) {
618
+ return `#${CSS.escape(node2.id)}`;
576
619
  }
577
- if (node2.nodeType === Node.ELEMENT_NODE) {
578
- const element = node2;
579
- if (element.id) {
580
- return `//*[@id="${element.id}"]`;
581
- }
582
- if (element.tagName && element.tagName.toLowerCase() === "html") {
583
- return "/html";
584
- }
585
- if (element === document.head) {
586
- return "/html/head";
587
- }
588
- if (element === document.body) {
589
- return "/html/body";
590
- }
591
- const parentNode2 = element.parentNode;
592
- if (!parentNode2) {
593
- return "";
594
- }
595
- const siblings = Array.from(parentNode2.children).filter(
596
- (sibling) => sibling.tagName === element.tagName
597
- );
598
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
599
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
620
+ const parts = [];
621
+ const tag = node2.tagName.toLowerCase();
622
+ if (node2.classList.length) {
623
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
600
624
  }
601
- if (node2.nodeType === Node.TEXT_NODE) {
602
- const parent = node2.parentNode;
603
- if (!parent) {
604
- return "";
625
+ Array.from(node2.attributes).forEach((attr) => {
626
+ if (attr.name.startsWith("data-")) {
627
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
605
628
  }
606
- const textSiblings = Array.from(parent.childNodes).filter(
607
- (sibling) => sibling.nodeType === Node.TEXT_NODE
608
- );
609
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
610
- return `${getXPath(parent)}/text()${index2}`;
611
- }
612
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
613
- const parent = node2.parentNode;
614
- if (!parent) {
615
- return "";
629
+ });
630
+ const shortSelector = `${tag}${parts.join("")}`;
631
+ if (isSelectorUnique(shortSelector, node2)) {
632
+ return shortSelector;
633
+ }
634
+ const pathParts = [];
635
+ let current = node2;
636
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
637
+ const parent = current.parentElement;
638
+ const tagName = current.tagName.toLowerCase();
639
+ let nth = "";
640
+ if (parent) {
641
+ const siblings = Array.from(parent.children).filter(
642
+ (el) => el.tagName.toLowerCase() === tagName
643
+ );
644
+ if (siblings.length > 1) {
645
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
646
+ }
616
647
  }
617
- const cdataSiblings = Array.from(parent.childNodes).filter(
618
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
619
- );
620
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
621
- return `${getXPath(parent)}/text()${index2}`;
648
+ pathParts.unshift(`${tagName}${nth}`);
649
+ current = parent;
622
650
  }
623
- if (node2.nodeType === Node.COMMENT_NODE) {
624
- const parent = node2.parentNode;
625
- if (!parent) {
626
- return "";
651
+ return pathParts.join(" > ") || null;
652
+ }
653
+ function buildXPath(node2) {
654
+ switch (node2.nodeType) {
655
+ case Node.DOCUMENT_NODE:
656
+ return "/";
657
+ case Node.DOCUMENT_TYPE_NODE:
658
+ return "/html/doctype";
659
+ case Node.ELEMENT_NODE: {
660
+ const element = node2;
661
+ if (element.id) {
662
+ return `//*[@id="${CSS.escape(element.id)}"]`;
663
+ }
664
+ if (element.tagName.toLowerCase() === "html") return "/html";
665
+ if (element === document.head) return "/html/head";
666
+ if (element === document.body) return "/html/body";
667
+ const parent = element.parentNode;
668
+ if (!parent) return "";
669
+ const tag = element.tagName.toLowerCase();
670
+ const siblings = Array.from(parent.children).filter(
671
+ (el) => el.tagName.toLowerCase() === tag
672
+ );
673
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
674
+ return `${buildXPath(parent)}/${tag}${index2}`;
675
+ }
676
+ case Node.TEXT_NODE:
677
+ case Node.CDATA_SECTION_NODE:
678
+ case Node.COMMENT_NODE: {
679
+ const parent = node2.parentNode;
680
+ if (!parent) return "";
681
+ const typeMap = {
682
+ [Node.TEXT_NODE]: "text()",
683
+ [Node.CDATA_SECTION_NODE]: "text()",
684
+ // CDATA ≡ text() в XPath
685
+ [Node.COMMENT_NODE]: "comment()"
686
+ };
687
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
688
+ (sibling) => sibling.nodeType === node2.nodeType
689
+ );
690
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
691
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
627
692
  }
628
- const commentSiblings = Array.from(parent.childNodes).filter(
629
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
630
- );
631
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
632
- return `${getXPath(parent)}/comment()${index2}`;
693
+ default:
694
+ return "";
633
695
  }
634
- return "";
635
696
  }
636
697
  function isTextVisible(n2) {
637
698
  var _a2;
@@ -647,20 +708,9 @@ function isTextVisible(n2) {
647
708
  const textContent2 = (_a2 = n2.textContent) == null ? void 0 : _a2.trim();
648
709
  return textContent2 !== "";
649
710
  }
650
- function isElementVisible(n2) {
651
- var _a2;
652
- const win = ((_a2 = n2.ownerDocument) == null ? void 0 : _a2.defaultView) ?? null;
653
- const style = win ? win.getComputedStyle(n2) : null;
654
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
655
- const rect = n2.getBoundingClientRect();
656
- const result2 = isStyleVisible && isRectVisible(rect);
657
- return result2;
658
- }
659
- function isRectVisible(rect, win = window) {
660
- var _a2, _b2, _c, _d;
661
- 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;
662
- 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;
663
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
711
+ function isElementVisible(el) {
712
+ const visibility = index$1.getElementVisibility(el);
713
+ return visibility.isVisible;
664
714
  }
665
715
  const interactiveEvents$1 = [
666
716
  "change",
@@ -893,9 +943,6 @@ function transformAttribute(doc, tagName, name, value) {
893
943
  function ignoreAttribute(tagName, name, _value) {
894
944
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
895
945
  }
896
- function isIncludeAttribute(name, include) {
897
- return typeof include === "string" ? name.includes(include) : include.test(name);
898
- }
899
946
  function isExcludeAttribute(name, exclude) {
900
947
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
901
948
  }
@@ -1029,7 +1076,6 @@ function serializeNode(n2, options) {
1029
1076
  blockClass,
1030
1077
  blockSelector,
1031
1078
  excludeAttribute,
1032
- includeAttribute,
1033
1079
  needsMask,
1034
1080
  inlineStylesheet,
1035
1081
  maskInputOptions = {},
@@ -1043,22 +1089,19 @@ function serializeNode(n2, options) {
1043
1089
  cssCaptured = false
1044
1090
  } = options;
1045
1091
  const rootId = getRootId(doc, mirror2);
1046
- const xPath = getXPath(n2);
1047
1092
  switch (n2.nodeType) {
1048
1093
  case n2.DOCUMENT_NODE:
1049
1094
  if (n2.compatMode !== "CSS1Compat") {
1050
1095
  return {
1051
1096
  type: NodeType$3.Document,
1052
1097
  childNodes: [],
1053
- xPath,
1054
1098
  compatMode: n2.compatMode
1055
1099
  // probably "BackCompat"
1056
1100
  };
1057
1101
  } else {
1058
1102
  return {
1059
1103
  type: NodeType$3.Document,
1060
- childNodes: [],
1061
- xPath
1104
+ childNodes: []
1062
1105
  };
1063
1106
  }
1064
1107
  case n2.DOCUMENT_TYPE_NODE:
@@ -1067,8 +1110,7 @@ function serializeNode(n2, options) {
1067
1110
  name: n2.name,
1068
1111
  publicId: n2.publicId,
1069
1112
  systemId: n2.systemId,
1070
- rootId,
1071
- xPath
1113
+ rootId
1072
1114
  };
1073
1115
  case n2.ELEMENT_NODE:
1074
1116
  return serializeElementNode(n2, {
@@ -1076,7 +1118,6 @@ function serializeNode(n2, options) {
1076
1118
  blockClass,
1077
1119
  blockSelector,
1078
1120
  excludeAttribute,
1079
- includeAttribute,
1080
1121
  inlineStylesheet,
1081
1122
  maskInputOptions,
1082
1123
  maskInputFn,
@@ -1085,8 +1126,7 @@ function serializeNode(n2, options) {
1085
1126
  recordCanvas,
1086
1127
  keepIframeSrcFn,
1087
1128
  newlyAddedElement,
1088
- rootId,
1089
- xPath
1129
+ rootId
1090
1130
  });
1091
1131
  case n2.TEXT_NODE:
1092
1132
  return serializeTextNode(n2, {
@@ -1094,22 +1134,19 @@ function serializeNode(n2, options) {
1094
1134
  needsMask,
1095
1135
  maskTextFn,
1096
1136
  rootId,
1097
- cssCaptured,
1098
- xPath
1137
+ cssCaptured
1099
1138
  });
1100
1139
  case n2.CDATA_SECTION_NODE:
1101
1140
  return {
1102
1141
  type: NodeType$3.CDATA,
1103
1142
  textContent: "",
1104
- rootId,
1105
- xPath
1143
+ rootId
1106
1144
  };
1107
1145
  case n2.COMMENT_NODE:
1108
1146
  return {
1109
1147
  type: NodeType$3.Comment,
1110
1148
  textContent: index$1.textContent(n2) || "",
1111
- rootId,
1112
- xPath
1149
+ rootId
1113
1150
  };
1114
1151
  default:
1115
1152
  return false;
@@ -1121,7 +1158,7 @@ function getRootId(doc, mirror2) {
1121
1158
  return docId === 1 ? void 0 : docId;
1122
1159
  }
1123
1160
  function serializeTextNode(n2, options) {
1124
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1161
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1125
1162
  const parent = index$1.parentNode(n2);
1126
1163
  const parentTagName = parent && parent.tagName;
1127
1164
  let textContent2 = "";
@@ -1138,15 +1175,10 @@ function serializeTextNode(n2, options) {
1138
1175
  if (!isStyle && !isScript && textContent2 && needsMask) {
1139
1176
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1140
1177
  }
1141
- const isVisible = isTextVisible(n2);
1142
- const isInteractive = isElementInteractive(n2);
1143
1178
  return {
1144
1179
  type: NodeType$3.Text,
1145
1180
  textContent: textContent2 || "",
1146
- rootId,
1147
- isVisible,
1148
- isInteractive,
1149
- xPath
1181
+ rootId
1150
1182
  };
1151
1183
  }
1152
1184
  function serializeElementNode(n2, options) {
@@ -1155,7 +1187,6 @@ function serializeElementNode(n2, options) {
1155
1187
  blockClass,
1156
1188
  blockSelector,
1157
1189
  excludeAttribute,
1158
- includeAttribute,
1159
1190
  inlineStylesheet,
1160
1191
  maskInputOptions = {},
1161
1192
  maskInputFn,
@@ -1164,8 +1195,7 @@ function serializeElementNode(n2, options) {
1164
1195
  recordCanvas,
1165
1196
  keepIframeSrcFn,
1166
1197
  newlyAddedElement = false,
1167
- rootId,
1168
- xPath
1198
+ rootId
1169
1199
  } = options;
1170
1200
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1171
1201
  const tagName = getValidTagName$1(n2);
@@ -1173,7 +1203,7 @@ function serializeElementNode(n2, options) {
1173
1203
  const len = n2.attributes.length;
1174
1204
  for (let i2 = 0; i2 < len; i2++) {
1175
1205
  const attr = n2.attributes[i2];
1176
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1206
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1177
1207
  continue;
1178
1208
  }
1179
1209
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1335,8 +1365,6 @@ function serializeElementNode(n2, options) {
1335
1365
  if (customElements.get(tagName)) isCustomElement = true;
1336
1366
  } catch (e2) {
1337
1367
  }
1338
- const isVisible = isElementVisible(n2);
1339
- const isInteractive = isElementInteractive(n2);
1340
1368
  return {
1341
1369
  type: NodeType$3.Element,
1342
1370
  tagName,
@@ -1345,10 +1373,7 @@ function serializeElementNode(n2, options) {
1345
1373
  isSVG: isSVGElement(n2) || void 0,
1346
1374
  needBlock,
1347
1375
  rootId,
1348
- isCustom: isCustomElement,
1349
- isVisible,
1350
- isInteractive,
1351
- xPath
1376
+ isCustom: isCustomElement
1352
1377
  };
1353
1378
  }
1354
1379
  function lowerIfExists(maybeAttr) {
@@ -1399,7 +1424,6 @@ function serializeNodeWithId(n2, options) {
1399
1424
  maskTextClass,
1400
1425
  maskTextSelector,
1401
1426
  excludeAttribute,
1402
- includeAttribute,
1403
1427
  skipChild = false,
1404
1428
  inlineStylesheet = true,
1405
1429
  maskInputOptions = {},
@@ -1435,7 +1459,6 @@ function serializeNodeWithId(n2, options) {
1435
1459
  blockClass,
1436
1460
  blockSelector,
1437
1461
  excludeAttribute,
1438
- includeAttribute,
1439
1462
  needsMask,
1440
1463
  inlineStylesheet,
1441
1464
  maskInputOptions,
@@ -1461,6 +1484,22 @@ function serializeNodeWithId(n2, options) {
1461
1484
  id = genId();
1462
1485
  }
1463
1486
  const serializedNode = Object.assign(_serializedNode, { id });
1487
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1488
+ serializedNode.xpath = buildXPath(n2);
1489
+ if (isElement(n2)) {
1490
+ const selector = buildSelector(n2);
1491
+ if (selector) {
1492
+ serializedNode.selector = selector;
1493
+ }
1494
+ }
1495
+ if (n2.nodeType === Node.TEXT_NODE) {
1496
+ serializedNode.isVisible = isTextVisible(n2);
1497
+ }
1498
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1499
+ serializedNode.isVisible = isElementVisible(n2);
1500
+ serializedNode.isInteractive = isElementInteractive(n2);
1501
+ }
1502
+ }
1464
1503
  mirror2.add(n2, serializedNode);
1465
1504
  if (id === IGNORED_NODE) {
1466
1505
  return null;
@@ -1489,7 +1528,6 @@ function serializeNodeWithId(n2, options) {
1489
1528
  maskTextClass,
1490
1529
  maskTextSelector,
1491
1530
  excludeAttribute,
1492
- includeAttribute,
1493
1531
  skipChild,
1494
1532
  inlineStylesheet,
1495
1533
  maskInputOptions,
@@ -1550,7 +1588,6 @@ function serializeNodeWithId(n2, options) {
1550
1588
  maskTextClass,
1551
1589
  maskTextSelector,
1552
1590
  excludeAttribute,
1553
- includeAttribute,
1554
1591
  skipChild: false,
1555
1592
  inlineStylesheet,
1556
1593
  maskInputOptions,
@@ -1593,7 +1630,6 @@ function serializeNodeWithId(n2, options) {
1593
1630
  maskTextClass,
1594
1631
  maskTextSelector,
1595
1632
  excludeAttribute,
1596
- includeAttribute,
1597
1633
  skipChild: false,
1598
1634
  inlineStylesheet,
1599
1635
  maskInputOptions,
@@ -1631,8 +1667,7 @@ function snapshot(n2, options) {
1631
1667
  blockSelector = null,
1632
1668
  maskTextClass = "rr-mask",
1633
1669
  maskTextSelector = null,
1634
- excludeAttribute = /^$a/,
1635
- includeAttribute = /.+/i,
1670
+ excludeAttribute = /.^/,
1636
1671
  inlineStylesheet = true,
1637
1672
  inlineImages = false,
1638
1673
  recordCanvas = false,
@@ -1693,7 +1728,6 @@ function snapshot(n2, options) {
1693
1728
  maskTextClass,
1694
1729
  maskTextSelector,
1695
1730
  excludeAttribute,
1696
- includeAttribute,
1697
1731
  skipChild: false,
1698
1732
  inlineStylesheet,
1699
1733
  maskInputOptions,
@@ -10832,6 +10866,41 @@ function patch(source, name, replacement) {
10832
10866
  };
10833
10867
  }
10834
10868
  }
10869
+ function describeNode(el) {
10870
+ const tag = el.tagName.toLowerCase();
10871
+ const id = el.id ? `#${el.id}` : "";
10872
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
10873
+ return `${tag}${id}${classes}`;
10874
+ }
10875
+ function getElementVisibility(el) {
10876
+ var _a2, _b2;
10877
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
10878
+ const rect = el.getBoundingClientRect();
10879
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
10880
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
10881
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
10882
+ const style = (_b2 = win.getComputedStyle) == null ? void 0 : _b2.call(win, el);
10883
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
10884
+ const isVisible = isStyleVisible2 && isRectVisible;
10885
+ let ratio = 0;
10886
+ if (isVisible) {
10887
+ const xOverlap = Math.max(
10888
+ 0,
10889
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
10890
+ );
10891
+ const yOverlap = Math.max(
10892
+ 0,
10893
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
10894
+ );
10895
+ const intersectionArea = xOverlap * yOverlap;
10896
+ const elementArea = rect.width * rect.height;
10897
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
10898
+ }
10899
+ return {
10900
+ isVisible,
10901
+ ratio
10902
+ };
10903
+ }
10835
10904
  const index = {
10836
10905
  childNodes,
10837
10906
  parentNode,
@@ -10845,7 +10914,9 @@ const index = {
10845
10914
  querySelector,
10846
10915
  querySelectorAll,
10847
10916
  mutationObserver: mutationObserverCtor,
10848
- patch
10917
+ patch,
10918
+ describeNode,
10919
+ getElementVisibility
10849
10920
  };
10850
10921
  function on(type, fn, target = document) {
10851
10922
  const options = { capture: true, passive: true };
@@ -11246,6 +11317,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
11246
11317
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
11247
11318
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
11248
11319
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
11320
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
11249
11321
  return IncrementalSource2;
11250
11322
  })(IncrementalSource || {});
11251
11323
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -11412,7 +11484,6 @@ class MutationBuffer {
11412
11484
  __publicField(this, "maskTextClass");
11413
11485
  __publicField(this, "maskTextSelector");
11414
11486
  __publicField(this, "excludeAttribute");
11415
- __publicField(this, "includeAttribute");
11416
11487
  __publicField(this, "inlineStylesheet");
11417
11488
  __publicField(this, "maskInputOptions");
11418
11489
  __publicField(this, "maskTextFn");
@@ -11428,6 +11499,7 @@ class MutationBuffer {
11428
11499
  __publicField(this, "stylesheetManager");
11429
11500
  __publicField(this, "shadowDomManager");
11430
11501
  __publicField(this, "canvasManager");
11502
+ __publicField(this, "visibilityManager");
11431
11503
  __publicField(this, "processedNodeManager");
11432
11504
  __publicField(this, "unattachedDoc");
11433
11505
  __publicField(this, "processMutations", (mutations) => {
@@ -11477,7 +11549,6 @@ class MutationBuffer {
11477
11549
  maskTextClass: this.maskTextClass,
11478
11550
  maskTextSelector: this.maskTextSelector,
11479
11551
  excludeAttribute: this.excludeAttribute,
11480
- includeAttribute: this.includeAttribute,
11481
11552
  skipChild: true,
11482
11553
  newlyAddedElement: true,
11483
11554
  inlineStylesheet: this.inlineStylesheet,
@@ -11523,7 +11594,8 @@ class MutationBuffer {
11523
11594
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
11524
11595
  }
11525
11596
  for (const n2 of this.movedSet) {
11526
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
11597
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11598
+ !this.movedSet.has(index.parentNode(n2))) {
11527
11599
  continue;
11528
11600
  }
11529
11601
  pushAdd(n2);
@@ -11679,12 +11751,60 @@ class MutationBuffer {
11679
11751
  const target = m.target;
11680
11752
  let attributeName = m.attributeName;
11681
11753
  let value = m.target.getAttribute(attributeName);
11682
- const propValue = target[attributeName];
11683
- const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
11754
+ const attrKey = attributeName;
11755
+ const propValue = target[attrKey];
11756
+ const isBooleanAttr = typeof propValue === "boolean";
11757
+ const inDOM = document.contains(target);
11758
+ const isVisible = isElementVisible(target);
11759
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
11760
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
11761
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
11762
+ m.oldValue !== null && // раньше он был
11763
+ (propValue === "" || // свойство = пустая строка
11764
+ propValue === null || // или null
11765
+ typeof propValue === "undefined");
11684
11766
  if (isPhantomAttributeMutation) {
11767
+ console.debug(
11768
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ phantom attribute mutation ignored`,
11769
+ {
11770
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11771
+ node: index.describeNode(target),
11772
+ tag: target.tagName,
11773
+ nodeType: target.nodeType,
11774
+ attribute: attributeName,
11775
+ value,
11776
+ oldValue: m.oldValue,
11777
+ excludeAttribute: this.excludeAttribute,
11778
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11779
+ propValue,
11780
+ isBooleanAttr,
11781
+ inDOM,
11782
+ isVisible,
11783
+ isExcludeAttributeName
11784
+ }
11785
+ );
11685
11786
  return;
11686
11787
  }
11687
11788
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
11789
+ console.debug(
11790
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ excluded attribute mutation ignored`,
11791
+ {
11792
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11793
+ node: index.describeNode(target),
11794
+ tag: target.tagName,
11795
+ nodeType: target.nodeType,
11796
+ attribute: attributeName,
11797
+ value,
11798
+ oldValue: m.oldValue,
11799
+ excludeAttribute: this.excludeAttribute,
11800
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11801
+ propValue,
11802
+ isBooleanAttr,
11803
+ inDOM,
11804
+ isVisible,
11805
+ isExcludeAttributeName
11806
+ }
11807
+ );
11688
11808
  return;
11689
11809
  }
11690
11810
  if (attributeName === "value") {
@@ -11729,9 +11849,35 @@ class MutationBuffer {
11729
11849
  toLowerCase(attributeName),
11730
11850
  value
11731
11851
  );
11732
- if (value === item.attributes[attributeName]) {
11733
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
11734
- return;
11852
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
11853
+ value === "" || // класс удалён
11854
+ value !== m.oldValue);
11855
+ if (isSuspiciousClassMutation) {
11856
+ console.debug(
11857
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⚠️ suspicious attribute mutation`,
11858
+ {
11859
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11860
+ reason: [
11861
+ value === m.oldValue ? "no change in value" : null,
11862
+ value === propValue ? "mirrored in DOM property" : null,
11863
+ value === item.attributes[attributeName] ? "redundant assignment" : null
11864
+ ].filter(Boolean).join(", ") || "uncategorized",
11865
+ node: index.describeNode(target),
11866
+ tag: target.tagName,
11867
+ nodeType: target.nodeType,
11868
+ attribute: attributeName,
11869
+ value,
11870
+ oldValue: m.oldValue,
11871
+ transformedValue: item.attributes[attributeName],
11872
+ excludeAttribute: this.excludeAttribute,
11873
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11874
+ propValue,
11875
+ isBooleanAttr,
11876
+ inDOM,
11877
+ isVisible,
11878
+ isExcludeAttributeName
11879
+ }
11880
+ );
11735
11881
  }
11736
11882
  if (attributeName === "style") {
11737
11883
  if (!this.unattachedDoc) {
@@ -11846,7 +11992,6 @@ class MutationBuffer {
11846
11992
  "maskTextClass",
11847
11993
  "maskTextSelector",
11848
11994
  "excludeAttribute",
11849
- "includeAttribute",
11850
11995
  "inlineStylesheet",
11851
11996
  "maskInputOptions",
11852
11997
  "maskTextFn",
@@ -11862,6 +12007,7 @@ class MutationBuffer {
11862
12007
  "stylesheetManager",
11863
12008
  "shadowDomManager",
11864
12009
  "canvasManager",
12010
+ "visibilityManager",
11865
12011
  "processedNodeManager"
11866
12012
  ].forEach((key) => {
11867
12013
  this[key] = options[key];
@@ -11870,10 +12016,12 @@ class MutationBuffer {
11870
12016
  freeze() {
11871
12017
  this.frozen = true;
11872
12018
  this.canvasManager.freeze();
12019
+ this.visibilityManager.freeze();
11873
12020
  }
11874
12021
  unfreeze() {
11875
12022
  this.frozen = false;
11876
12023
  this.canvasManager.unfreeze();
12024
+ this.visibilityManager.unfreeze();
11877
12025
  this.emit();
11878
12026
  }
11879
12027
  isFrozen() {
@@ -11882,15 +12030,18 @@ class MutationBuffer {
11882
12030
  lock() {
11883
12031
  this.locked = true;
11884
12032
  this.canvasManager.lock();
12033
+ this.visibilityManager.lock();
11885
12034
  }
11886
12035
  unlock() {
11887
12036
  this.locked = false;
11888
12037
  this.canvasManager.unlock();
12038
+ this.visibilityManager.unlock();
11889
12039
  this.emit();
11890
12040
  }
11891
12041
  reset() {
11892
12042
  this.shadowDomManager.reset();
11893
12043
  this.canvasManager.reset();
12044
+ this.visibilityManager.reset();
11894
12045
  }
11895
12046
  }
11896
12047
  function deepDelete(addsSet, n2) {
@@ -12824,6 +12975,7 @@ function mergeHooks(o2, hooks) {
12824
12975
  styleSheetRuleCb,
12825
12976
  styleDeclarationCb,
12826
12977
  canvasMutationCb,
12978
+ visibilityMutationCb,
12827
12979
  fontCb,
12828
12980
  selectionCb,
12829
12981
  customElementCb
@@ -12888,6 +13040,12 @@ function mergeHooks(o2, hooks) {
12888
13040
  }
12889
13041
  canvasMutationCb(...p);
12890
13042
  };
13043
+ o2.visibilityMutationCb = (...p) => {
13044
+ if (hooks.visibilityMutation) {
13045
+ hooks.visibilityMutation(...p);
13046
+ }
13047
+ visibilityMutationCb(...p);
13048
+ };
12891
13049
  o2.fontCb = (...p) => {
12892
13050
  if (hooks.font) {
12893
13051
  hooks.font(...p);
@@ -14021,11 +14179,249 @@ class ProcessedNodeManager {
14021
14179
  destroy() {
14022
14180
  }
14023
14181
  }
14182
+ function computeVisibility(elements, previous, options) {
14183
+ const root2 = (options == null ? void 0 : options.root) ?? null;
14184
+ const threshold = (options == null ? void 0 : options.threshold) ?? 0.5;
14185
+ const sensitivity = (options == null ? void 0 : options.sensitivity) ?? 0.05;
14186
+ const rootMarginFn = parseRootMargin((options == null ? void 0 : options.rootMargin) ?? "0px");
14187
+ const current = /* @__PURE__ */ new Map();
14188
+ const rootRect = getRootRect(root2);
14189
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
14190
+ for (const el of elements) {
14191
+ const elRect = el.getBoundingClientRect();
14192
+ let intersectionRect = emptyRect();
14193
+ let intersectionRatio = 0;
14194
+ if (elRect.width > 0 && elRect.height > 0) {
14195
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
14196
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
14197
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
14198
+ }
14199
+ const isStyle = isStyleVisible(el);
14200
+ const old = previous.get(el) ?? null;
14201
+ const prevRatio = (old == null ? void 0 : old.intersectionRatio) ?? 0;
14202
+ const currRatio = intersectionRatio;
14203
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
14204
+ const nowVisible = isStyle && currRatio > threshold;
14205
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
14206
+ if (changed) {
14207
+ current.set(el, {
14208
+ target: el,
14209
+ isVisible: nowVisible,
14210
+ isStyleVisible: isStyle,
14211
+ intersectionRatio: currRatio,
14212
+ intersectionRect,
14213
+ oldValue: old
14214
+ });
14215
+ } else {
14216
+ current.set(el, old);
14217
+ }
14218
+ }
14219
+ return current;
14220
+ }
14221
+ function parseRootMargin(marginStr) {
14222
+ const parts = marginStr.trim().split(/\s+/);
14223
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
14224
+ return function(rootRect) {
14225
+ const top = getValue(parts[0] || "0px", rootRect.height);
14226
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
14227
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
14228
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
14229
+ return { top, right, bottom, left, width: 0, height: 0 };
14230
+ };
14231
+ }
14232
+ function getRootRect(root2) {
14233
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
14234
+ }
14235
+ function expandRootRect(rect, marginFn) {
14236
+ const margin = marginFn(rect);
14237
+ return new DOMRect(
14238
+ rect.left - margin.left,
14239
+ rect.top - margin.top,
14240
+ rect.width + margin.left + margin.right,
14241
+ rect.height + margin.top + margin.bottom
14242
+ );
14243
+ }
14244
+ function computeIntersectionRect(a2, b) {
14245
+ const top = Math.max(a2.top, b.top);
14246
+ const left = Math.max(a2.left, b.left);
14247
+ const bottom = Math.min(a2.bottom, b.bottom);
14248
+ const right = Math.min(a2.right, b.right);
14249
+ const width = Math.max(0, right - left);
14250
+ const height = Math.max(0, bottom - top);
14251
+ return { top, left, bottom, right, width, height };
14252
+ }
14253
+ function computeIntersectionRatio(elRect, intersectionRect) {
14254
+ const elArea = elRect.width * elRect.height;
14255
+ const intArea = intersectionRect.width * intersectionRect.height;
14256
+ return elArea > 0 ? intArea / elArea : 0;
14257
+ }
14258
+ function emptyRect() {
14259
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
14260
+ }
14261
+ function isStyleVisible(el) {
14262
+ const style = getComputedStyle(el);
14263
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
14264
+ }
14265
+ class VisibilityManager {
14266
+ constructor(options) {
14267
+ __publicField(this, "frozen", false);
14268
+ __publicField(this, "locked", false);
14269
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
14270
+ __publicField(this, "mirror");
14271
+ __publicField(this, "mutationCb");
14272
+ __publicField(this, "rafId", null);
14273
+ __publicField(this, "rafThrottle");
14274
+ __publicField(this, "lastFlushTime", 0);
14275
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
14276
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
14277
+ __publicField(this, "root", null);
14278
+ __publicField(this, "threshold");
14279
+ __publicField(this, "sensitivity");
14280
+ __publicField(this, "rootMargin");
14281
+ __publicField(this, "hasInitialized", false);
14282
+ __publicField(this, "mode", "none");
14283
+ __publicField(this, "debounce", 50);
14284
+ __publicField(this, "throttle", 100);
14285
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
14286
+ __publicField(this, "debounceTimer", null);
14287
+ __publicField(this, "lastThrottleTime", 0);
14288
+ __publicField(this, "disabled", false);
14289
+ __publicField(this, "notifyActivity");
14290
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
14291
+ this.mirror = mirror2;
14292
+ this.mutationCb = mutationCb;
14293
+ this.notifyActivity = notifyActivity;
14294
+ this.rootMargin = "0px";
14295
+ if (sampling === false) {
14296
+ this.disabled = true;
14297
+ return;
14298
+ }
14299
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
14300
+ this.mode = (visibilitySampling == null ? void 0 : visibilitySampling.mode) ?? "none";
14301
+ this.debounce = Number((visibilitySampling == null ? void 0 : visibilitySampling.debounce) ?? 100);
14302
+ this.throttle = Number((visibilitySampling == null ? void 0 : visibilitySampling.throttle) ?? 100);
14303
+ this.threshold = Number((visibilitySampling == null ? void 0 : visibilitySampling.threshold) ?? 0.5);
14304
+ this.sensitivity = Number((visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) ?? 0.05);
14305
+ this.rafThrottle = Number((visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) ?? 100);
14306
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
14307
+ const mo = new MutationObserver((mutations) => {
14308
+ mutations.forEach((m) => {
14309
+ m.addedNodes.forEach((n2) => {
14310
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14311
+ this.observe(n2);
14312
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
14313
+ }
14314
+ });
14315
+ m.removedNodes.forEach((n2) => {
14316
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14317
+ this.unobserve(n2);
14318
+ }
14319
+ });
14320
+ });
14321
+ });
14322
+ mo.observe(doc.body, { childList: true, subtree: true });
14323
+ this.startPendingFlushLoop();
14324
+ }
14325
+ startPendingFlushLoop() {
14326
+ if (this.disabled) return;
14327
+ const loop = (timestamp) => {
14328
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
14329
+ this.lastFlushTime = timestamp;
14330
+ this.flushPendingVisibilityMutations();
14331
+ }
14332
+ this.rafId = requestAnimationFrame(loop);
14333
+ };
14334
+ this.rafId = requestAnimationFrame(loop);
14335
+ }
14336
+ flushPendingVisibilityMutations() {
14337
+ if (this.disabled) return;
14338
+ if (this.frozen || this.locked || this.elements.size === 0) return;
14339
+ const state = computeVisibility(this.elements, this.previousState, {
14340
+ root: this.root,
14341
+ threshold: this.threshold,
14342
+ sensitivity: this.sensitivity,
14343
+ rootMargin: this.rootMargin
14344
+ });
14345
+ for (const [el, entry] of state.entries()) {
14346
+ const old = this.previousState.get(el);
14347
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
14348
+ if (changed) {
14349
+ const id = this.mirror.getId(el);
14350
+ if (id !== -1) {
14351
+ this.buffer.set(el, {
14352
+ id,
14353
+ isVisible: entry.isVisible,
14354
+ ratio: entry.intersectionRatio
14355
+ });
14356
+ }
14357
+ this.previousState.set(el, entry);
14358
+ }
14359
+ }
14360
+ this.previousState = state;
14361
+ if (!this.hasInitialized) {
14362
+ this.hasInitialized = true;
14363
+ this.buffer.clear();
14364
+ return;
14365
+ }
14366
+ this.scheduleEmit();
14367
+ }
14368
+ scheduleEmit() {
14369
+ if (this.mode === "debounce") {
14370
+ clearTimeout(this.debounceTimer);
14371
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
14372
+ } else if (this.mode === "throttle") {
14373
+ const now = performance.now();
14374
+ if (now - this.lastThrottleTime >= this.throttle) {
14375
+ this.lastThrottleTime = now;
14376
+ this.flushBuffer();
14377
+ }
14378
+ } else {
14379
+ this.flushBuffer();
14380
+ }
14381
+ }
14382
+ flushBuffer() {
14383
+ var _a2;
14384
+ if (this.buffer.size === 0) return;
14385
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
14386
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
14387
+ this.buffer.clear();
14388
+ }
14389
+ observe(el) {
14390
+ if (this.disabled) return;
14391
+ this.elements.add(el);
14392
+ }
14393
+ unobserve(el) {
14394
+ if (this.disabled) return;
14395
+ this.elements.delete(el);
14396
+ this.previousState.delete(el);
14397
+ this.pending.delete(el);
14398
+ }
14399
+ freeze() {
14400
+ this.frozen = true;
14401
+ }
14402
+ unfreeze() {
14403
+ this.frozen = false;
14404
+ }
14405
+ lock() {
14406
+ this.locked = true;
14407
+ }
14408
+ unlock() {
14409
+ this.locked = false;
14410
+ }
14411
+ reset() {
14412
+ this.elements.clear();
14413
+ this.previousState.clear();
14414
+ this.pending.clear();
14415
+ if (this.rafId) cancelAnimationFrame(this.rafId);
14416
+ }
14417
+ }
14024
14418
  let wrappedEmit;
14025
14419
  let takeFullSnapshot$1;
14026
14420
  let canvasManager;
14421
+ let visibilityManager;
14027
14422
  let recording = false;
14028
- const preRecordingCustomEvents = [];
14423
+ const customEventQueue = [];
14424
+ let flushCustomEventQueue;
14029
14425
  try {
14030
14426
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
14031
14427
  const cleanFrame = document.createElement("iframe");
@@ -14042,12 +14438,12 @@ function record(options = {}) {
14042
14438
  emit,
14043
14439
  checkoutEveryNms,
14044
14440
  checkoutEveryNth,
14441
+ checkoutEveryNvm,
14045
14442
  blockClass = "rr-block",
14046
14443
  blockSelector = null,
14047
14444
  ignoreClass = "rr-ignore",
14048
14445
  ignoreSelector = null,
14049
14446
  excludeAttribute: _excludeAttribute,
14050
- includeAttribute: _includeAttribute,
14051
14447
  maskTextClass = "rr-mask",
14052
14448
  maskTextSelector = null,
14053
14449
  inlineStylesheet = true,
@@ -14065,7 +14461,7 @@ function record(options = {}) {
14065
14461
  recordCanvas = false,
14066
14462
  recordCrossOriginIframes = false,
14067
14463
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
14068
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
14464
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
14069
14465
  userTriggeredOnInput = false,
14070
14466
  collectFonts = false,
14071
14467
  inlineImages = false,
@@ -14097,8 +14493,7 @@ function record(options = {}) {
14097
14493
  sampling.mousemove = mousemoveWait;
14098
14494
  }
14099
14495
  mirror.reset();
14100
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
14101
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
14496
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
14102
14497
  const maskInputOptions = maskAllInputs === true ? {
14103
14498
  color: true,
14104
14499
  date: true,
@@ -14135,6 +14530,10 @@ function record(options = {}) {
14135
14530
  polyfill$1();
14136
14531
  let lastFullSnapshotEvent;
14137
14532
  let incrementalSnapshotCount = 0;
14533
+ let recentVisibilityChanges = 0;
14534
+ const onVisibilityActivity = (count) => {
14535
+ recentVisibilityChanges += count;
14536
+ };
14138
14537
  const eventProcessor = (e2) => {
14139
14538
  for (const plugin3 of plugins || []) {
14140
14539
  if (plugin3.eventProcessor) {
@@ -14175,7 +14574,11 @@ function record(options = {}) {
14175
14574
  incrementalSnapshotCount++;
14176
14575
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
14177
14576
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
14178
- if (exceedCount || exceedTime) {
14577
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
14578
+ if (exceedCount || exceedTime || exceedVisibility) {
14579
+ if (exceedVisibility) {
14580
+ recentVisibilityChanges = 0;
14581
+ }
14179
14582
  takeFullSnapshot$1(true);
14180
14583
  }
14181
14584
  }
@@ -14203,6 +14606,17 @@ function record(options = {}) {
14203
14606
  ...p
14204
14607
  }
14205
14608
  });
14609
+ const wrappedVisibilityMutationEmit = (p) => {
14610
+ var _a2;
14611
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
14612
+ wrappedEmit({
14613
+ type: EventType.IncrementalSnapshot,
14614
+ data: {
14615
+ source: IncrementalSource.VisibilityMutation,
14616
+ ...p
14617
+ }
14618
+ });
14619
+ };
14206
14620
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
14207
14621
  type: EventType.IncrementalSnapshot,
14208
14622
  data: {
@@ -14240,6 +14654,13 @@ function record(options = {}) {
14240
14654
  sampling: sampling.canvas,
14241
14655
  dataURLOptions
14242
14656
  });
14657
+ visibilityManager = new VisibilityManager({
14658
+ doc: window.document,
14659
+ mirror,
14660
+ sampling: sampling.visibility,
14661
+ mutationCb: wrappedVisibilityMutationEmit,
14662
+ notifyActivity: onVisibilityActivity
14663
+ });
14243
14664
  const shadowDomManager = new ShadowDomManager({
14244
14665
  mutationCb: wrappedMutationEmit,
14245
14666
  scrollCb: wrappedScrollEmit,
@@ -14249,7 +14670,6 @@ function record(options = {}) {
14249
14670
  maskTextClass,
14250
14671
  maskTextSelector,
14251
14672
  excludeAttribute,
14252
- includeAttribute,
14253
14673
  inlineStylesheet,
14254
14674
  maskInputOptions,
14255
14675
  dataURLOptions,
@@ -14262,6 +14682,7 @@ function record(options = {}) {
14262
14682
  iframeManager,
14263
14683
  stylesheetManager,
14264
14684
  canvasManager,
14685
+ visibilityManager,
14265
14686
  keepIframeSrcFn,
14266
14687
  processedNodeManager
14267
14688
  },
@@ -14292,7 +14713,6 @@ function record(options = {}) {
14292
14713
  maskTextClass,
14293
14714
  maskTextSelector,
14294
14715
  excludeAttribute,
14295
- includeAttribute,
14296
14716
  inlineStylesheet,
14297
14717
  maskAllInputs: maskInputOptions,
14298
14718
  maskTextFn,
@@ -14341,6 +14761,12 @@ function record(options = {}) {
14341
14761
  mirror.getId(document)
14342
14762
  );
14343
14763
  };
14764
+ flushCustomEventQueue = () => {
14765
+ for (const e2 of customEventQueue) {
14766
+ wrappedEmit(e2);
14767
+ }
14768
+ customEventQueue.length = 0;
14769
+ };
14344
14770
  try {
14345
14771
  const handlers = [];
14346
14772
  const observe = (doc) => {
@@ -14399,6 +14825,7 @@ function record(options = {}) {
14399
14825
  }
14400
14826
  }),
14401
14827
  canvasMutationCb: wrappedCanvasMutationEmit,
14828
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
14402
14829
  fontCb: (p) => wrappedEmit({
14403
14830
  type: EventType.IncrementalSnapshot,
14404
14831
  data: {
@@ -14430,7 +14857,6 @@ function record(options = {}) {
14430
14857
  maskTextClass,
14431
14858
  maskTextSelector,
14432
14859
  excludeAttribute,
14433
- includeAttribute,
14434
14860
  maskInputOptions,
14435
14861
  inlineStylesheet,
14436
14862
  sampling,
@@ -14452,6 +14878,7 @@ function record(options = {}) {
14452
14878
  shadowDomManager,
14453
14879
  processedNodeManager,
14454
14880
  canvasManager,
14881
+ visibilityManager,
14455
14882
  ignoreCSSAttributes,
14456
14883
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
14457
14884
  observer: p.observer,
@@ -14476,14 +14903,14 @@ function record(options = {}) {
14476
14903
  }
14477
14904
  });
14478
14905
  const init = () => {
14479
- if (flushCustomQueue === "before") {
14480
- flushPreRecordingEvents();
14906
+ if (flushCustomEvent === "before") {
14907
+ flushCustomEventQueue();
14481
14908
  }
14482
14909
  takeFullSnapshot$1();
14483
14910
  handlers.push(observe(document));
14484
14911
  recording = true;
14485
- if (flushCustomQueue === "after") {
14486
- flushPreRecordingEvents();
14912
+ if (flushCustomEvent === "after") {
14913
+ flushCustomEventQueue();
14487
14914
  }
14488
14915
  };
14489
14916
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -14513,7 +14940,7 @@ function record(options = {}) {
14513
14940
  );
14514
14941
  }
14515
14942
  return () => {
14516
- flushPreRecordingEvents();
14943
+ flushCustomEventQueue();
14517
14944
  handlers.forEach((h) => h());
14518
14945
  processedNodeManager.destroy();
14519
14946
  recording = false;
@@ -14523,12 +14950,10 @@ function record(options = {}) {
14523
14950
  console.warn(error);
14524
14951
  }
14525
14952
  }
14526
- function flushPreRecordingEvents() {
14527
- for (const e2 of preRecordingCustomEvents) {
14528
- wrappedEmit(e2);
14529
- }
14530
- preRecordingCustomEvents.length = 0;
14531
- }
14953
+ record.flushCustomEventQueue = () => {
14954
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
14955
+ flushCustomEventQueue();
14956
+ };
14532
14957
  record.addCustomEvent = (tag, payload) => {
14533
14958
  const customEvent = {
14534
14959
  type: EventType.Custom,
@@ -14538,8 +14963,8 @@ record.addCustomEvent = (tag, payload) => {
14538
14963
  }
14539
14964
  };
14540
14965
  if (!recording) {
14541
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
14542
- preRecordingCustomEvents.push(customEvent);
14966
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
14967
+ customEventQueue.push(customEvent);
14543
14968
  return;
14544
14969
  }
14545
14970
  wrappedEmit(customEvent);