@appsurify-testmap/rrweb 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.
@@ -214,6 +214,42 @@ function patch$1(source, name, replacement) {
214
214
  };
215
215
  }
216
216
  }
217
+ function describeNode$1(el) {
218
+ const tag = el.tagName.toLowerCase();
219
+ const id = el.id ? `#${el.id}` : "";
220
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
221
+ return `${tag}${id}${classes}`;
222
+ }
223
+ function getElementVisibility$1(el) {
224
+ var _a3;
225
+ var _a2, _b;
226
+ const win = (_a3 = (_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) != null ? _a3 : window;
227
+ const rect = el.getBoundingClientRect();
228
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
229
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
230
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
231
+ const style = (_b = win.getComputedStyle) == null ? void 0 : _b.call(win, el);
232
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
233
+ const isVisible = isStyleVisible2 && isRectVisible;
234
+ let ratio = 0;
235
+ if (isVisible) {
236
+ const xOverlap = Math.max(
237
+ 0,
238
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
239
+ );
240
+ const yOverlap = Math.max(
241
+ 0,
242
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
243
+ );
244
+ const intersectionArea = xOverlap * yOverlap;
245
+ const elementArea = rect.width * rect.height;
246
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
247
+ }
248
+ return {
249
+ isVisible,
250
+ ratio
251
+ };
252
+ }
217
253
  const index$1 = {
218
254
  childNodes: childNodes$1,
219
255
  parentNode: parentNode$1,
@@ -227,7 +263,9 @@ const index$1 = {
227
263
  querySelector: querySelector$1,
228
264
  querySelectorAll: querySelectorAll$1,
229
265
  mutationObserver: mutationObserverCtor$1,
230
- patch: patch$1
266
+ patch: patch$1,
267
+ describeNode: describeNode$1,
268
+ getElementVisibility: getElementVisibility$1
231
269
  };
232
270
  function isElement(n2) {
233
271
  return n2.nodeType === n2.ELEMENT_NODE;
@@ -614,71 +652,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
614
652
  function markCssSplits(cssText, style) {
615
653
  return splitCssText(cssText, style).join("/* rr_split */");
616
654
  }
617
- function getXPath(node2) {
618
- if (node2.nodeType === Node.DOCUMENT_NODE) {
619
- return "/";
655
+ function isSelectorUnique(selector, target) {
656
+ try {
657
+ const matches = document.querySelectorAll(selector);
658
+ return matches.length === 1 && matches[0] === target;
659
+ } catch (e2) {
660
+ return false;
620
661
  }
621
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
622
- return "/html/doctype";
662
+ }
663
+ function buildSelector(node2) {
664
+ if (!(node2 instanceof Element)) return null;
665
+ if (node2.id) {
666
+ return `#${CSS.escape(node2.id)}`;
623
667
  }
624
- if (node2.nodeType === Node.ELEMENT_NODE) {
625
- const element = node2;
626
- if (element.id) {
627
- return `//*[@id="${element.id}"]`;
628
- }
629
- if (element.tagName && element.tagName.toLowerCase() === "html") {
630
- return "/html";
631
- }
632
- if (element === document.head) {
633
- return "/html/head";
634
- }
635
- if (element === document.body) {
636
- return "/html/body";
637
- }
638
- const parentNode2 = element.parentNode;
639
- if (!parentNode2) {
640
- return "";
641
- }
642
- const siblings = Array.from(parentNode2.children).filter(
643
- (sibling) => sibling.tagName === element.tagName
644
- );
645
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
646
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
668
+ const parts = [];
669
+ const tag = node2.tagName.toLowerCase();
670
+ if (node2.classList.length) {
671
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
647
672
  }
648
- if (node2.nodeType === Node.TEXT_NODE) {
649
- const parent = node2.parentNode;
650
- if (!parent) {
651
- return "";
673
+ Array.from(node2.attributes).forEach((attr) => {
674
+ if (attr.name.startsWith("data-")) {
675
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
652
676
  }
653
- const textSiblings = Array.from(parent.childNodes).filter(
654
- (sibling) => sibling.nodeType === Node.TEXT_NODE
655
- );
656
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
657
- return `${getXPath(parent)}/text()${index2}`;
658
- }
659
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
660
- const parent = node2.parentNode;
661
- if (!parent) {
662
- return "";
677
+ });
678
+ const shortSelector = `${tag}${parts.join("")}`;
679
+ if (isSelectorUnique(shortSelector, node2)) {
680
+ return shortSelector;
681
+ }
682
+ const pathParts = [];
683
+ let current = node2;
684
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
685
+ const parent = current.parentElement;
686
+ const tagName = current.tagName.toLowerCase();
687
+ let nth = "";
688
+ if (parent) {
689
+ const siblings = Array.from(parent.children).filter(
690
+ (el) => el.tagName.toLowerCase() === tagName
691
+ );
692
+ if (siblings.length > 1) {
693
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
694
+ }
663
695
  }
664
- const cdataSiblings = Array.from(parent.childNodes).filter(
665
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
666
- );
667
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
668
- return `${getXPath(parent)}/text()${index2}`;
696
+ pathParts.unshift(`${tagName}${nth}`);
697
+ current = parent;
669
698
  }
670
- if (node2.nodeType === Node.COMMENT_NODE) {
671
- const parent = node2.parentNode;
672
- if (!parent) {
673
- return "";
699
+ return pathParts.join(" > ") || null;
700
+ }
701
+ function buildXPath(node2) {
702
+ switch (node2.nodeType) {
703
+ case Node.DOCUMENT_NODE:
704
+ return "/";
705
+ case Node.DOCUMENT_TYPE_NODE:
706
+ return "/html/doctype";
707
+ case Node.ELEMENT_NODE: {
708
+ const element = node2;
709
+ if (element.id) {
710
+ return `//*[@id="${CSS.escape(element.id)}"]`;
711
+ }
712
+ if (element.tagName.toLowerCase() === "html") return "/html";
713
+ if (element === document.head) return "/html/head";
714
+ if (element === document.body) return "/html/body";
715
+ const parent = element.parentNode;
716
+ if (!parent) return "";
717
+ const tag = element.tagName.toLowerCase();
718
+ const siblings = Array.from(parent.children).filter(
719
+ (el) => el.tagName.toLowerCase() === tag
720
+ );
721
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
722
+ return `${buildXPath(parent)}/${tag}${index2}`;
723
+ }
724
+ case Node.TEXT_NODE:
725
+ case Node.CDATA_SECTION_NODE:
726
+ case Node.COMMENT_NODE: {
727
+ const parent = node2.parentNode;
728
+ if (!parent) return "";
729
+ const typeMap = {
730
+ [Node.TEXT_NODE]: "text()",
731
+ [Node.CDATA_SECTION_NODE]: "text()",
732
+ // CDATA ≡ text() в XPath
733
+ [Node.COMMENT_NODE]: "comment()"
734
+ };
735
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
736
+ (sibling) => sibling.nodeType === node2.nodeType
737
+ );
738
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
739
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
674
740
  }
675
- const commentSiblings = Array.from(parent.childNodes).filter(
676
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
677
- );
678
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
679
- return `${getXPath(parent)}/comment()${index2}`;
741
+ default:
742
+ return "";
680
743
  }
681
- return "";
682
744
  }
683
745
  function isTextVisible(n2) {
684
746
  var _a2;
@@ -694,22 +756,9 @@ function isTextVisible(n2) {
694
756
  const textContent2 = (_a2 = n2.textContent) == null ? void 0 : _a2.trim();
695
757
  return textContent2 !== "";
696
758
  }
697
- function isElementVisible(n2) {
698
- var _a3;
699
- var _a2;
700
- const win = (_a3 = (_a2 = n2.ownerDocument) == null ? void 0 : _a2.defaultView) != null ? _a3 : null;
701
- const style = win ? win.getComputedStyle(n2) : null;
702
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
703
- const rect = n2.getBoundingClientRect();
704
- const result2 = isStyleVisible && isRectVisible(rect);
705
- return result2;
706
- }
707
- function isRectVisible(rect, win = window) {
708
- var _a3, _b2, _c2, _d2;
709
- var _a2, _b, _c, _d;
710
- const height = (_b2 = (_a3 = win == null ? void 0 : win.innerHeight) != null ? _a3 : (_b = (_a2 = win == null ? void 0 : win.document) == null ? void 0 : _a2.documentElement) == null ? void 0 : _b.clientHeight) != null ? _b2 : 0;
711
- const width = (_d2 = (_c2 = win == null ? void 0 : win.innerWidth) != null ? _c2 : (_d = (_c = win == null ? void 0 : win.document) == null ? void 0 : _c.documentElement) == null ? void 0 : _d.clientWidth) != null ? _d2 : 0;
712
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
759
+ function isElementVisible(el) {
760
+ const visibility = index$1.getElementVisibility(el);
761
+ return visibility.isVisible;
713
762
  }
714
763
  const interactiveEvents$1 = [
715
764
  "change",
@@ -942,9 +991,6 @@ function transformAttribute(doc, tagName, name, value) {
942
991
  function ignoreAttribute(tagName, name, _value) {
943
992
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
944
993
  }
945
- function isIncludeAttribute(name, include) {
946
- return typeof include === "string" ? name.includes(include) : include.test(name);
947
- }
948
994
  function isExcludeAttribute(name, exclude) {
949
995
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
950
996
  }
@@ -1078,7 +1124,6 @@ function serializeNode(n2, options) {
1078
1124
  blockClass,
1079
1125
  blockSelector,
1080
1126
  excludeAttribute,
1081
- includeAttribute,
1082
1127
  needsMask,
1083
1128
  inlineStylesheet,
1084
1129
  maskInputOptions = {},
@@ -1092,22 +1137,19 @@ function serializeNode(n2, options) {
1092
1137
  cssCaptured = false
1093
1138
  } = options;
1094
1139
  const rootId = getRootId(doc, mirror2);
1095
- const xPath = getXPath(n2);
1096
1140
  switch (n2.nodeType) {
1097
1141
  case n2.DOCUMENT_NODE:
1098
1142
  if (n2.compatMode !== "CSS1Compat") {
1099
1143
  return {
1100
1144
  type: NodeType$3.Document,
1101
1145
  childNodes: [],
1102
- xPath,
1103
1146
  compatMode: n2.compatMode
1104
1147
  // probably "BackCompat"
1105
1148
  };
1106
1149
  } else {
1107
1150
  return {
1108
1151
  type: NodeType$3.Document,
1109
- childNodes: [],
1110
- xPath
1152
+ childNodes: []
1111
1153
  };
1112
1154
  }
1113
1155
  case n2.DOCUMENT_TYPE_NODE:
@@ -1116,8 +1158,7 @@ function serializeNode(n2, options) {
1116
1158
  name: n2.name,
1117
1159
  publicId: n2.publicId,
1118
1160
  systemId: n2.systemId,
1119
- rootId,
1120
- xPath
1161
+ rootId
1121
1162
  };
1122
1163
  case n2.ELEMENT_NODE:
1123
1164
  return serializeElementNode(n2, {
@@ -1125,7 +1166,6 @@ function serializeNode(n2, options) {
1125
1166
  blockClass,
1126
1167
  blockSelector,
1127
1168
  excludeAttribute,
1128
- includeAttribute,
1129
1169
  inlineStylesheet,
1130
1170
  maskInputOptions,
1131
1171
  maskInputFn,
@@ -1134,8 +1174,7 @@ function serializeNode(n2, options) {
1134
1174
  recordCanvas,
1135
1175
  keepIframeSrcFn,
1136
1176
  newlyAddedElement,
1137
- rootId,
1138
- xPath
1177
+ rootId
1139
1178
  });
1140
1179
  case n2.TEXT_NODE:
1141
1180
  return serializeTextNode(n2, {
@@ -1143,22 +1182,19 @@ function serializeNode(n2, options) {
1143
1182
  needsMask,
1144
1183
  maskTextFn,
1145
1184
  rootId,
1146
- cssCaptured,
1147
- xPath
1185
+ cssCaptured
1148
1186
  });
1149
1187
  case n2.CDATA_SECTION_NODE:
1150
1188
  return {
1151
1189
  type: NodeType$3.CDATA,
1152
1190
  textContent: "",
1153
- rootId,
1154
- xPath
1191
+ rootId
1155
1192
  };
1156
1193
  case n2.COMMENT_NODE:
1157
1194
  return {
1158
1195
  type: NodeType$3.Comment,
1159
1196
  textContent: index$1.textContent(n2) || "",
1160
- rootId,
1161
- xPath
1197
+ rootId
1162
1198
  };
1163
1199
  default:
1164
1200
  return false;
@@ -1170,7 +1206,7 @@ function getRootId(doc, mirror2) {
1170
1206
  return docId === 1 ? void 0 : docId;
1171
1207
  }
1172
1208
  function serializeTextNode(n2, options) {
1173
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1209
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1174
1210
  const parent = index$1.parentNode(n2);
1175
1211
  const parentTagName = parent && parent.tagName;
1176
1212
  let textContent2 = "";
@@ -1187,15 +1223,10 @@ function serializeTextNode(n2, options) {
1187
1223
  if (!isStyle && !isScript && textContent2 && needsMask) {
1188
1224
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1189
1225
  }
1190
- const isVisible = isTextVisible(n2);
1191
- const isInteractive = isElementInteractive(n2);
1192
1226
  return {
1193
1227
  type: NodeType$3.Text,
1194
1228
  textContent: textContent2 || "",
1195
- rootId,
1196
- isVisible,
1197
- isInteractive,
1198
- xPath
1229
+ rootId
1199
1230
  };
1200
1231
  }
1201
1232
  function serializeElementNode(n2, options) {
@@ -1204,7 +1235,6 @@ function serializeElementNode(n2, options) {
1204
1235
  blockClass,
1205
1236
  blockSelector,
1206
1237
  excludeAttribute,
1207
- includeAttribute,
1208
1238
  inlineStylesheet,
1209
1239
  maskInputOptions = {},
1210
1240
  maskInputFn,
@@ -1213,8 +1243,7 @@ function serializeElementNode(n2, options) {
1213
1243
  recordCanvas,
1214
1244
  keepIframeSrcFn,
1215
1245
  newlyAddedElement = false,
1216
- rootId,
1217
- xPath
1246
+ rootId
1218
1247
  } = options;
1219
1248
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1220
1249
  const tagName = getValidTagName$1(n2);
@@ -1222,7 +1251,7 @@ function serializeElementNode(n2, options) {
1222
1251
  const len = n2.attributes.length;
1223
1252
  for (let i2 = 0; i2 < len; i2++) {
1224
1253
  const attr = n2.attributes[i2];
1225
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1254
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1226
1255
  continue;
1227
1256
  }
1228
1257
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1384,8 +1413,6 @@ function serializeElementNode(n2, options) {
1384
1413
  if (customElements.get(tagName)) isCustomElement = true;
1385
1414
  } catch (e2) {
1386
1415
  }
1387
- const isVisible = isElementVisible(n2);
1388
- const isInteractive = isElementInteractive(n2);
1389
1416
  return {
1390
1417
  type: NodeType$3.Element,
1391
1418
  tagName,
@@ -1394,10 +1421,7 @@ function serializeElementNode(n2, options) {
1394
1421
  isSVG: isSVGElement(n2) || void 0,
1395
1422
  needBlock,
1396
1423
  rootId,
1397
- isCustom: isCustomElement,
1398
- isVisible,
1399
- isInteractive,
1400
- xPath
1424
+ isCustom: isCustomElement
1401
1425
  };
1402
1426
  }
1403
1427
  function lowerIfExists(maybeAttr) {
@@ -1448,7 +1472,6 @@ function serializeNodeWithId(n2, options) {
1448
1472
  maskTextClass,
1449
1473
  maskTextSelector,
1450
1474
  excludeAttribute,
1451
- includeAttribute,
1452
1475
  skipChild = false,
1453
1476
  inlineStylesheet = true,
1454
1477
  maskInputOptions = {},
@@ -1484,7 +1507,6 @@ function serializeNodeWithId(n2, options) {
1484
1507
  blockClass,
1485
1508
  blockSelector,
1486
1509
  excludeAttribute,
1487
- includeAttribute,
1488
1510
  needsMask,
1489
1511
  inlineStylesheet,
1490
1512
  maskInputOptions,
@@ -1510,6 +1532,22 @@ function serializeNodeWithId(n2, options) {
1510
1532
  id = genId();
1511
1533
  }
1512
1534
  const serializedNode = Object.assign(_serializedNode, { id });
1535
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1536
+ serializedNode.xpath = buildXPath(n2);
1537
+ if (isElement(n2)) {
1538
+ const selector = buildSelector(n2);
1539
+ if (selector) {
1540
+ serializedNode.selector = selector;
1541
+ }
1542
+ }
1543
+ if (n2.nodeType === Node.TEXT_NODE) {
1544
+ serializedNode.isVisible = isTextVisible(n2);
1545
+ }
1546
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1547
+ serializedNode.isVisible = isElementVisible(n2);
1548
+ serializedNode.isInteractive = isElementInteractive(n2);
1549
+ }
1550
+ }
1513
1551
  mirror2.add(n2, serializedNode);
1514
1552
  if (id === IGNORED_NODE) {
1515
1553
  return null;
@@ -1538,7 +1576,6 @@ function serializeNodeWithId(n2, options) {
1538
1576
  maskTextClass,
1539
1577
  maskTextSelector,
1540
1578
  excludeAttribute,
1541
- includeAttribute,
1542
1579
  skipChild,
1543
1580
  inlineStylesheet,
1544
1581
  maskInputOptions,
@@ -1599,7 +1636,6 @@ function serializeNodeWithId(n2, options) {
1599
1636
  maskTextClass,
1600
1637
  maskTextSelector,
1601
1638
  excludeAttribute,
1602
- includeAttribute,
1603
1639
  skipChild: false,
1604
1640
  inlineStylesheet,
1605
1641
  maskInputOptions,
@@ -1642,7 +1678,6 @@ function serializeNodeWithId(n2, options) {
1642
1678
  maskTextClass,
1643
1679
  maskTextSelector,
1644
1680
  excludeAttribute,
1645
- includeAttribute,
1646
1681
  skipChild: false,
1647
1682
  inlineStylesheet,
1648
1683
  maskInputOptions,
@@ -1680,8 +1715,7 @@ function snapshot(n2, options) {
1680
1715
  blockSelector = null,
1681
1716
  maskTextClass = "rr-mask",
1682
1717
  maskTextSelector = null,
1683
- excludeAttribute = /^$a/,
1684
- includeAttribute = /.+/i,
1718
+ excludeAttribute = /.^/,
1685
1719
  inlineStylesheet = true,
1686
1720
  inlineImages = false,
1687
1721
  recordCanvas = false,
@@ -1742,7 +1776,6 @@ function snapshot(n2, options) {
1742
1776
  maskTextClass,
1743
1777
  maskTextSelector,
1744
1778
  excludeAttribute,
1745
- includeAttribute,
1746
1779
  skipChild: false,
1747
1780
  inlineStylesheet,
1748
1781
  maskInputOptions,
@@ -10889,6 +10922,42 @@ function patch(source, name, replacement) {
10889
10922
  };
10890
10923
  }
10891
10924
  }
10925
+ function describeNode(el) {
10926
+ const tag = el.tagName.toLowerCase();
10927
+ const id = el.id ? `#${el.id}` : "";
10928
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
10929
+ return `${tag}${id}${classes}`;
10930
+ }
10931
+ function getElementVisibility(el) {
10932
+ var _a3;
10933
+ var _a2, _b;
10934
+ const win = (_a3 = (_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) != null ? _a3 : window;
10935
+ const rect = el.getBoundingClientRect();
10936
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
10937
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
10938
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
10939
+ const style = (_b = win.getComputedStyle) == null ? void 0 : _b.call(win, el);
10940
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
10941
+ const isVisible = isStyleVisible2 && isRectVisible;
10942
+ let ratio = 0;
10943
+ if (isVisible) {
10944
+ const xOverlap = Math.max(
10945
+ 0,
10946
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
10947
+ );
10948
+ const yOverlap = Math.max(
10949
+ 0,
10950
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
10951
+ );
10952
+ const intersectionArea = xOverlap * yOverlap;
10953
+ const elementArea = rect.width * rect.height;
10954
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
10955
+ }
10956
+ return {
10957
+ isVisible,
10958
+ ratio
10959
+ };
10960
+ }
10892
10961
  const index = {
10893
10962
  childNodes,
10894
10963
  parentNode,
@@ -10902,7 +10971,9 @@ const index = {
10902
10971
  querySelector,
10903
10972
  querySelectorAll,
10904
10973
  mutationObserver: mutationObserverCtor,
10905
- patch
10974
+ patch,
10975
+ describeNode,
10976
+ getElementVisibility
10906
10977
  };
10907
10978
  function on(type, fn, target = document) {
10908
10979
  const options = { capture: true, passive: true };
@@ -11304,6 +11375,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
11304
11375
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
11305
11376
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
11306
11377
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
11378
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
11307
11379
  return IncrementalSource2;
11308
11380
  })(IncrementalSource || {});
11309
11381
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -11470,7 +11542,6 @@ class MutationBuffer {
11470
11542
  __publicField(this, "maskTextClass");
11471
11543
  __publicField(this, "maskTextSelector");
11472
11544
  __publicField(this, "excludeAttribute");
11473
- __publicField(this, "includeAttribute");
11474
11545
  __publicField(this, "inlineStylesheet");
11475
11546
  __publicField(this, "maskInputOptions");
11476
11547
  __publicField(this, "maskTextFn");
@@ -11486,6 +11557,7 @@ class MutationBuffer {
11486
11557
  __publicField(this, "stylesheetManager");
11487
11558
  __publicField(this, "shadowDomManager");
11488
11559
  __publicField(this, "canvasManager");
11560
+ __publicField(this, "visibilityManager");
11489
11561
  __publicField(this, "processedNodeManager");
11490
11562
  __publicField(this, "unattachedDoc");
11491
11563
  __publicField(this, "processMutations", (mutations) => {
@@ -11535,7 +11607,6 @@ class MutationBuffer {
11535
11607
  maskTextClass: this.maskTextClass,
11536
11608
  maskTextSelector: this.maskTextSelector,
11537
11609
  excludeAttribute: this.excludeAttribute,
11538
- includeAttribute: this.includeAttribute,
11539
11610
  skipChild: true,
11540
11611
  newlyAddedElement: true,
11541
11612
  inlineStylesheet: this.inlineStylesheet,
@@ -11581,7 +11652,8 @@ class MutationBuffer {
11581
11652
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
11582
11653
  }
11583
11654
  for (const n2 of this.movedSet) {
11584
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
11655
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11656
+ !this.movedSet.has(index.parentNode(n2))) {
11585
11657
  continue;
11586
11658
  }
11587
11659
  pushAdd(n2);
@@ -11737,12 +11809,60 @@ class MutationBuffer {
11737
11809
  const target = m.target;
11738
11810
  let attributeName = m.attributeName;
11739
11811
  let value = m.target.getAttribute(attributeName);
11740
- const propValue = target[attributeName];
11741
- const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
11812
+ const attrKey = attributeName;
11813
+ const propValue = target[attrKey];
11814
+ const isBooleanAttr = typeof propValue === "boolean";
11815
+ const inDOM = document.contains(target);
11816
+ const isVisible = isElementVisible(target);
11817
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
11818
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
11819
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
11820
+ m.oldValue !== null && // раньше он был
11821
+ (propValue === "" || // свойство = пустая строка
11822
+ propValue === null || // или null
11823
+ typeof propValue === "undefined");
11742
11824
  if (isPhantomAttributeMutation) {
11825
+ console.debug(
11826
+ `[${nowTimestamp()}] [rrweb:record/mutation] \u26D4 phantom attribute mutation ignored`,
11827
+ {
11828
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11829
+ node: index.describeNode(target),
11830
+ tag: target.tagName,
11831
+ nodeType: target.nodeType,
11832
+ attribute: attributeName,
11833
+ value,
11834
+ oldValue: m.oldValue,
11835
+ excludeAttribute: this.excludeAttribute,
11836
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11837
+ propValue,
11838
+ isBooleanAttr,
11839
+ inDOM,
11840
+ isVisible,
11841
+ isExcludeAttributeName
11842
+ }
11843
+ );
11743
11844
  return;
11744
11845
  }
11745
11846
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
11847
+ console.debug(
11848
+ `[${nowTimestamp()}] [rrweb:record/mutation] \u26D4 excluded attribute mutation ignored`,
11849
+ {
11850
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11851
+ node: index.describeNode(target),
11852
+ tag: target.tagName,
11853
+ nodeType: target.nodeType,
11854
+ attribute: attributeName,
11855
+ value,
11856
+ oldValue: m.oldValue,
11857
+ excludeAttribute: this.excludeAttribute,
11858
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11859
+ propValue,
11860
+ isBooleanAttr,
11861
+ inDOM,
11862
+ isVisible,
11863
+ isExcludeAttributeName
11864
+ }
11865
+ );
11746
11866
  return;
11747
11867
  }
11748
11868
  if (attributeName === "value") {
@@ -11787,9 +11907,35 @@ class MutationBuffer {
11787
11907
  toLowerCase(attributeName),
11788
11908
  value
11789
11909
  );
11790
- if (value === item.attributes[attributeName]) {
11791
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
11792
- return;
11910
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
11911
+ value === "" || // класс удалён
11912
+ value !== m.oldValue);
11913
+ if (isSuspiciousClassMutation) {
11914
+ console.debug(
11915
+ `[${nowTimestamp()}] [rrweb:record/mutation] \u26A0\uFE0F suspicious attribute mutation`,
11916
+ {
11917
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11918
+ reason: [
11919
+ value === m.oldValue ? "no change in value" : null,
11920
+ value === propValue ? "mirrored in DOM property" : null,
11921
+ value === item.attributes[attributeName] ? "redundant assignment" : null
11922
+ ].filter(Boolean).join(", ") || "uncategorized",
11923
+ node: index.describeNode(target),
11924
+ tag: target.tagName,
11925
+ nodeType: target.nodeType,
11926
+ attribute: attributeName,
11927
+ value,
11928
+ oldValue: m.oldValue,
11929
+ transformedValue: item.attributes[attributeName],
11930
+ excludeAttribute: this.excludeAttribute,
11931
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11932
+ propValue,
11933
+ isBooleanAttr,
11934
+ inDOM,
11935
+ isVisible,
11936
+ isExcludeAttributeName
11937
+ }
11938
+ );
11793
11939
  }
11794
11940
  if (attributeName === "style") {
11795
11941
  if (!this.unattachedDoc) {
@@ -11904,7 +12050,6 @@ class MutationBuffer {
11904
12050
  "maskTextClass",
11905
12051
  "maskTextSelector",
11906
12052
  "excludeAttribute",
11907
- "includeAttribute",
11908
12053
  "inlineStylesheet",
11909
12054
  "maskInputOptions",
11910
12055
  "maskTextFn",
@@ -11920,6 +12065,7 @@ class MutationBuffer {
11920
12065
  "stylesheetManager",
11921
12066
  "shadowDomManager",
11922
12067
  "canvasManager",
12068
+ "visibilityManager",
11923
12069
  "processedNodeManager"
11924
12070
  ].forEach((key) => {
11925
12071
  this[key] = options[key];
@@ -11928,10 +12074,12 @@ class MutationBuffer {
11928
12074
  freeze() {
11929
12075
  this.frozen = true;
11930
12076
  this.canvasManager.freeze();
12077
+ this.visibilityManager.freeze();
11931
12078
  }
11932
12079
  unfreeze() {
11933
12080
  this.frozen = false;
11934
12081
  this.canvasManager.unfreeze();
12082
+ this.visibilityManager.unfreeze();
11935
12083
  this.emit();
11936
12084
  }
11937
12085
  isFrozen() {
@@ -11940,15 +12088,18 @@ class MutationBuffer {
11940
12088
  lock() {
11941
12089
  this.locked = true;
11942
12090
  this.canvasManager.lock();
12091
+ this.visibilityManager.lock();
11943
12092
  }
11944
12093
  unlock() {
11945
12094
  this.locked = false;
11946
12095
  this.canvasManager.unlock();
12096
+ this.visibilityManager.unlock();
11947
12097
  this.emit();
11948
12098
  }
11949
12099
  reset() {
11950
12100
  this.shadowDomManager.reset();
11951
12101
  this.canvasManager.reset();
12102
+ this.visibilityManager.reset();
11952
12103
  }
11953
12104
  }
11954
12105
  function deepDelete(addsSet, n2) {
@@ -12880,6 +13031,7 @@ function mergeHooks(o2, hooks) {
12880
13031
  styleSheetRuleCb,
12881
13032
  styleDeclarationCb,
12882
13033
  canvasMutationCb,
13034
+ visibilityMutationCb,
12883
13035
  fontCb,
12884
13036
  selectionCb,
12885
13037
  customElementCb
@@ -12944,6 +13096,12 @@ function mergeHooks(o2, hooks) {
12944
13096
  }
12945
13097
  canvasMutationCb(...p);
12946
13098
  };
13099
+ o2.visibilityMutationCb = (...p) => {
13100
+ if (hooks.visibilityMutation) {
13101
+ hooks.visibilityMutation(...p);
13102
+ }
13103
+ visibilityMutationCb(...p);
13104
+ };
12947
13105
  o2.fontCb = (...p) => {
12948
13106
  if (hooks.font) {
12949
13107
  hooks.font(...p);
@@ -14075,11 +14233,251 @@ class ProcessedNodeManager {
14075
14233
  destroy() {
14076
14234
  }
14077
14235
  }
14236
+ function computeVisibility(elements, previous, options) {
14237
+ var _a2, _b, _c, _d, _e, _f;
14238
+ const root2 = (_a2 = options == null ? void 0 : options.root) != null ? _a2 : null;
14239
+ const threshold = (_b = options == null ? void 0 : options.threshold) != null ? _b : 0.5;
14240
+ const sensitivity = (_c = options == null ? void 0 : options.sensitivity) != null ? _c : 0.05;
14241
+ const rootMarginFn = parseRootMargin((_d = options == null ? void 0 : options.rootMargin) != null ? _d : "0px");
14242
+ const current = /* @__PURE__ */ new Map();
14243
+ const rootRect = getRootRect(root2);
14244
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
14245
+ for (const el of elements) {
14246
+ const elRect = el.getBoundingClientRect();
14247
+ let intersectionRect = emptyRect();
14248
+ let intersectionRatio = 0;
14249
+ if (elRect.width > 0 && elRect.height > 0) {
14250
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
14251
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
14252
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
14253
+ }
14254
+ const isStyle = isStyleVisible(el);
14255
+ const old = (_e = previous.get(el)) != null ? _e : null;
14256
+ const prevRatio = (_f = old == null ? void 0 : old.intersectionRatio) != null ? _f : 0;
14257
+ const currRatio = intersectionRatio;
14258
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
14259
+ const nowVisible = isStyle && currRatio > threshold;
14260
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
14261
+ if (changed) {
14262
+ current.set(el, {
14263
+ target: el,
14264
+ isVisible: nowVisible,
14265
+ isStyleVisible: isStyle,
14266
+ intersectionRatio: currRatio,
14267
+ intersectionRect,
14268
+ oldValue: old
14269
+ });
14270
+ } else {
14271
+ current.set(el, old);
14272
+ }
14273
+ }
14274
+ return current;
14275
+ }
14276
+ function parseRootMargin(marginStr) {
14277
+ const parts = marginStr.trim().split(/\s+/);
14278
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
14279
+ return function(rootRect) {
14280
+ const top = getValue(parts[0] || "0px", rootRect.height);
14281
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
14282
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
14283
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
14284
+ return { top, right, bottom, left, width: 0, height: 0 };
14285
+ };
14286
+ }
14287
+ function getRootRect(root2) {
14288
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
14289
+ }
14290
+ function expandRootRect(rect, marginFn) {
14291
+ const margin = marginFn(rect);
14292
+ return new DOMRect(
14293
+ rect.left - margin.left,
14294
+ rect.top - margin.top,
14295
+ rect.width + margin.left + margin.right,
14296
+ rect.height + margin.top + margin.bottom
14297
+ );
14298
+ }
14299
+ function computeIntersectionRect(a2, b) {
14300
+ const top = Math.max(a2.top, b.top);
14301
+ const left = Math.max(a2.left, b.left);
14302
+ const bottom = Math.min(a2.bottom, b.bottom);
14303
+ const right = Math.min(a2.right, b.right);
14304
+ const width = Math.max(0, right - left);
14305
+ const height = Math.max(0, bottom - top);
14306
+ return { top, left, bottom, right, width, height };
14307
+ }
14308
+ function computeIntersectionRatio(elRect, intersectionRect) {
14309
+ const elArea = elRect.width * elRect.height;
14310
+ const intArea = intersectionRect.width * intersectionRect.height;
14311
+ return elArea > 0 ? intArea / elArea : 0;
14312
+ }
14313
+ function emptyRect() {
14314
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
14315
+ }
14316
+ function isStyleVisible(el) {
14317
+ const style = getComputedStyle(el);
14318
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
14319
+ }
14320
+ class VisibilityManager {
14321
+ constructor(options) {
14322
+ var _a2, _b, _c, _d, _e, _f;
14323
+ __publicField(this, "frozen", false);
14324
+ __publicField(this, "locked", false);
14325
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
14326
+ __publicField(this, "mirror");
14327
+ __publicField(this, "mutationCb");
14328
+ __publicField(this, "rafId", null);
14329
+ __publicField(this, "rafThrottle");
14330
+ __publicField(this, "lastFlushTime", 0);
14331
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
14332
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
14333
+ __publicField(this, "root", null);
14334
+ __publicField(this, "threshold");
14335
+ __publicField(this, "sensitivity");
14336
+ __publicField(this, "rootMargin");
14337
+ __publicField(this, "hasInitialized", false);
14338
+ __publicField(this, "mode", "none");
14339
+ __publicField(this, "debounce", 50);
14340
+ __publicField(this, "throttle", 100);
14341
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
14342
+ __publicField(this, "debounceTimer", null);
14343
+ __publicField(this, "lastThrottleTime", 0);
14344
+ __publicField(this, "disabled", false);
14345
+ __publicField(this, "notifyActivity");
14346
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
14347
+ this.mirror = mirror2;
14348
+ this.mutationCb = mutationCb;
14349
+ this.notifyActivity = notifyActivity;
14350
+ this.rootMargin = "0px";
14351
+ if (sampling === false) {
14352
+ this.disabled = true;
14353
+ return;
14354
+ }
14355
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
14356
+ this.mode = (_a2 = visibilitySampling == null ? void 0 : visibilitySampling.mode) != null ? _a2 : "none";
14357
+ this.debounce = Number((_b = visibilitySampling == null ? void 0 : visibilitySampling.debounce) != null ? _b : 100);
14358
+ this.throttle = Number((_c = visibilitySampling == null ? void 0 : visibilitySampling.throttle) != null ? _c : 100);
14359
+ this.threshold = Number((_d = visibilitySampling == null ? void 0 : visibilitySampling.threshold) != null ? _d : 0.5);
14360
+ this.sensitivity = Number((_e = visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) != null ? _e : 0.05);
14361
+ this.rafThrottle = Number((_f = visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) != null ? _f : 100);
14362
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
14363
+ const mo = new MutationObserver((mutations) => {
14364
+ mutations.forEach((m) => {
14365
+ m.addedNodes.forEach((n2) => {
14366
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14367
+ this.observe(n2);
14368
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
14369
+ }
14370
+ });
14371
+ m.removedNodes.forEach((n2) => {
14372
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14373
+ this.unobserve(n2);
14374
+ }
14375
+ });
14376
+ });
14377
+ });
14378
+ mo.observe(doc.body, { childList: true, subtree: true });
14379
+ this.startPendingFlushLoop();
14380
+ }
14381
+ startPendingFlushLoop() {
14382
+ if (this.disabled) return;
14383
+ const loop = (timestamp) => {
14384
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
14385
+ this.lastFlushTime = timestamp;
14386
+ this.flushPendingVisibilityMutations();
14387
+ }
14388
+ this.rafId = requestAnimationFrame(loop);
14389
+ };
14390
+ this.rafId = requestAnimationFrame(loop);
14391
+ }
14392
+ flushPendingVisibilityMutations() {
14393
+ if (this.disabled) return;
14394
+ if (this.frozen || this.locked || this.elements.size === 0) return;
14395
+ const state = computeVisibility(this.elements, this.previousState, {
14396
+ root: this.root,
14397
+ threshold: this.threshold,
14398
+ sensitivity: this.sensitivity,
14399
+ rootMargin: this.rootMargin
14400
+ });
14401
+ for (const [el, entry] of state.entries()) {
14402
+ const old = this.previousState.get(el);
14403
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
14404
+ if (changed) {
14405
+ const id = this.mirror.getId(el);
14406
+ if (id !== -1) {
14407
+ this.buffer.set(el, {
14408
+ id,
14409
+ isVisible: entry.isVisible,
14410
+ ratio: entry.intersectionRatio
14411
+ });
14412
+ }
14413
+ this.previousState.set(el, entry);
14414
+ }
14415
+ }
14416
+ this.previousState = state;
14417
+ if (!this.hasInitialized) {
14418
+ this.hasInitialized = true;
14419
+ this.buffer.clear();
14420
+ return;
14421
+ }
14422
+ this.scheduleEmit();
14423
+ }
14424
+ scheduleEmit() {
14425
+ if (this.mode === "debounce") {
14426
+ clearTimeout(this.debounceTimer);
14427
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
14428
+ } else if (this.mode === "throttle") {
14429
+ const now = performance.now();
14430
+ if (now - this.lastThrottleTime >= this.throttle) {
14431
+ this.lastThrottleTime = now;
14432
+ this.flushBuffer();
14433
+ }
14434
+ } else {
14435
+ this.flushBuffer();
14436
+ }
14437
+ }
14438
+ flushBuffer() {
14439
+ var _a2;
14440
+ if (this.buffer.size === 0) return;
14441
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
14442
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
14443
+ this.buffer.clear();
14444
+ }
14445
+ observe(el) {
14446
+ if (this.disabled) return;
14447
+ this.elements.add(el);
14448
+ }
14449
+ unobserve(el) {
14450
+ if (this.disabled) return;
14451
+ this.elements.delete(el);
14452
+ this.previousState.delete(el);
14453
+ this.pending.delete(el);
14454
+ }
14455
+ freeze() {
14456
+ this.frozen = true;
14457
+ }
14458
+ unfreeze() {
14459
+ this.frozen = false;
14460
+ }
14461
+ lock() {
14462
+ this.locked = true;
14463
+ }
14464
+ unlock() {
14465
+ this.locked = false;
14466
+ }
14467
+ reset() {
14468
+ this.elements.clear();
14469
+ this.previousState.clear();
14470
+ this.pending.clear();
14471
+ if (this.rafId) cancelAnimationFrame(this.rafId);
14472
+ }
14473
+ }
14078
14474
  let wrappedEmit;
14079
14475
  let takeFullSnapshot$1;
14080
14476
  let canvasManager;
14477
+ let visibilityManager;
14081
14478
  let recording = false;
14082
- const preRecordingCustomEvents = [];
14479
+ const customEventQueue = [];
14480
+ let flushCustomEventQueue;
14083
14481
  try {
14084
14482
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
14085
14483
  const cleanFrame = document.createElement("iframe");
@@ -14096,12 +14494,12 @@ function record(options = {}) {
14096
14494
  emit,
14097
14495
  checkoutEveryNms,
14098
14496
  checkoutEveryNth,
14497
+ checkoutEveryNvm,
14099
14498
  blockClass = "rr-block",
14100
14499
  blockSelector = null,
14101
14500
  ignoreClass = "rr-ignore",
14102
14501
  ignoreSelector = null,
14103
14502
  excludeAttribute: _excludeAttribute,
14104
- includeAttribute: _includeAttribute,
14105
14503
  maskTextClass = "rr-mask",
14106
14504
  maskTextSelector = null,
14107
14505
  inlineStylesheet = true,
@@ -14119,7 +14517,7 @@ function record(options = {}) {
14119
14517
  recordCanvas = false,
14120
14518
  recordCrossOriginIframes = false,
14121
14519
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
14122
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
14520
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
14123
14521
  userTriggeredOnInput = false,
14124
14522
  collectFonts = false,
14125
14523
  inlineImages = false,
@@ -14151,8 +14549,7 @@ function record(options = {}) {
14151
14549
  sampling.mousemove = mousemoveWait;
14152
14550
  }
14153
14551
  mirror.reset();
14154
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
14155
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
14552
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
14156
14553
  const maskInputOptions = maskAllInputs === true ? {
14157
14554
  color: true,
14158
14555
  date: true,
@@ -14189,6 +14586,10 @@ function record(options = {}) {
14189
14586
  polyfill$1();
14190
14587
  let lastFullSnapshotEvent;
14191
14588
  let incrementalSnapshotCount = 0;
14589
+ let recentVisibilityChanges = 0;
14590
+ const onVisibilityActivity = (count) => {
14591
+ recentVisibilityChanges += count;
14592
+ };
14192
14593
  const eventProcessor = (e2) => {
14193
14594
  for (const plugin3 of plugins || []) {
14194
14595
  if (plugin3.eventProcessor) {
@@ -14229,7 +14630,11 @@ function record(options = {}) {
14229
14630
  incrementalSnapshotCount++;
14230
14631
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
14231
14632
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
14232
- if (exceedCount || exceedTime) {
14633
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
14634
+ if (exceedCount || exceedTime || exceedVisibility) {
14635
+ if (exceedVisibility) {
14636
+ recentVisibilityChanges = 0;
14637
+ }
14233
14638
  takeFullSnapshot$1(true);
14234
14639
  }
14235
14640
  }
@@ -14254,6 +14659,16 @@ function record(options = {}) {
14254
14659
  source: IncrementalSource.CanvasMutation
14255
14660
  }, p)
14256
14661
  });
14662
+ const wrappedVisibilityMutationEmit = (p) => {
14663
+ var _a2;
14664
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
14665
+ wrappedEmit({
14666
+ type: EventType.IncrementalSnapshot,
14667
+ data: __spreadValues({
14668
+ source: IncrementalSource.VisibilityMutation
14669
+ }, p)
14670
+ });
14671
+ };
14257
14672
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
14258
14673
  type: EventType.IncrementalSnapshot,
14259
14674
  data: __spreadValues({
@@ -14290,6 +14705,13 @@ function record(options = {}) {
14290
14705
  sampling: sampling.canvas,
14291
14706
  dataURLOptions
14292
14707
  });
14708
+ visibilityManager = new VisibilityManager({
14709
+ doc: window.document,
14710
+ mirror,
14711
+ sampling: sampling.visibility,
14712
+ mutationCb: wrappedVisibilityMutationEmit,
14713
+ notifyActivity: onVisibilityActivity
14714
+ });
14293
14715
  const shadowDomManager = new ShadowDomManager({
14294
14716
  mutationCb: wrappedMutationEmit,
14295
14717
  scrollCb: wrappedScrollEmit,
@@ -14299,7 +14721,6 @@ function record(options = {}) {
14299
14721
  maskTextClass,
14300
14722
  maskTextSelector,
14301
14723
  excludeAttribute,
14302
- includeAttribute,
14303
14724
  inlineStylesheet,
14304
14725
  maskInputOptions,
14305
14726
  dataURLOptions,
@@ -14312,6 +14733,7 @@ function record(options = {}) {
14312
14733
  iframeManager,
14313
14734
  stylesheetManager,
14314
14735
  canvasManager,
14736
+ visibilityManager,
14315
14737
  keepIframeSrcFn,
14316
14738
  processedNodeManager
14317
14739
  },
@@ -14342,7 +14764,6 @@ function record(options = {}) {
14342
14764
  maskTextClass,
14343
14765
  maskTextSelector,
14344
14766
  excludeAttribute,
14345
- includeAttribute,
14346
14767
  inlineStylesheet,
14347
14768
  maskAllInputs: maskInputOptions,
14348
14769
  maskTextFn,
@@ -14391,6 +14812,12 @@ function record(options = {}) {
14391
14812
  mirror.getId(document)
14392
14813
  );
14393
14814
  };
14815
+ flushCustomEventQueue = () => {
14816
+ for (const e2 of customEventQueue) {
14817
+ wrappedEmit(e2);
14818
+ }
14819
+ customEventQueue.length = 0;
14820
+ };
14394
14821
  try {
14395
14822
  const handlers = [];
14396
14823
  const observe = (doc) => {
@@ -14443,6 +14870,7 @@ function record(options = {}) {
14443
14870
  }, r2)
14444
14871
  }),
14445
14872
  canvasMutationCb: wrappedCanvasMutationEmit,
14873
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
14446
14874
  fontCb: (p) => wrappedEmit({
14447
14875
  type: EventType.IncrementalSnapshot,
14448
14876
  data: __spreadValues({
@@ -14471,7 +14899,6 @@ function record(options = {}) {
14471
14899
  maskTextClass,
14472
14900
  maskTextSelector,
14473
14901
  excludeAttribute,
14474
- includeAttribute,
14475
14902
  maskInputOptions,
14476
14903
  inlineStylesheet,
14477
14904
  sampling,
@@ -14493,6 +14920,7 @@ function record(options = {}) {
14493
14920
  shadowDomManager,
14494
14921
  processedNodeManager,
14495
14922
  canvasManager,
14923
+ visibilityManager,
14496
14924
  ignoreCSSAttributes,
14497
14925
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
14498
14926
  observer: p.observer,
@@ -14517,14 +14945,14 @@ function record(options = {}) {
14517
14945
  }
14518
14946
  });
14519
14947
  const init = () => {
14520
- if (flushCustomQueue === "before") {
14521
- flushPreRecordingEvents();
14948
+ if (flushCustomEvent === "before") {
14949
+ flushCustomEventQueue();
14522
14950
  }
14523
14951
  takeFullSnapshot$1();
14524
14952
  handlers.push(observe(document));
14525
14953
  recording = true;
14526
- if (flushCustomQueue === "after") {
14527
- flushPreRecordingEvents();
14954
+ if (flushCustomEvent === "after") {
14955
+ flushCustomEventQueue();
14528
14956
  }
14529
14957
  };
14530
14958
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -14554,7 +14982,7 @@ function record(options = {}) {
14554
14982
  );
14555
14983
  }
14556
14984
  return () => {
14557
- flushPreRecordingEvents();
14985
+ flushCustomEventQueue();
14558
14986
  handlers.forEach((h) => h());
14559
14987
  processedNodeManager.destroy();
14560
14988
  recording = false;
@@ -14564,12 +14992,10 @@ function record(options = {}) {
14564
14992
  console.warn(error);
14565
14993
  }
14566
14994
  }
14567
- function flushPreRecordingEvents() {
14568
- for (const e2 of preRecordingCustomEvents) {
14569
- wrappedEmit(e2);
14570
- }
14571
- preRecordingCustomEvents.length = 0;
14572
- }
14995
+ record.flushCustomEventQueue = () => {
14996
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
14997
+ flushCustomEventQueue();
14998
+ };
14573
14999
  record.addCustomEvent = (tag, payload) => {
14574
15000
  const customEvent = {
14575
15001
  type: EventType.Custom,
@@ -14579,8 +15005,8 @@ record.addCustomEvent = (tag, payload) => {
14579
15005
  }
14580
15006
  };
14581
15007
  if (!recording) {
14582
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
14583
- preRecordingCustomEvents.push(customEvent);
15008
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
15009
+ customEventQueue.push(customEvent);
14584
15010
  return;
14585
15011
  }
14586
15012
  wrappedEmit(customEvent);