@appsurify-testmap/rrweb-record 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.
@@ -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, _b;
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 = (_b = win.getComputedStyle) == null ? void 0 : _b.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;
@@ -555,71 +592,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
555
592
  function markCssSplits(cssText, style) {
556
593
  return splitCssText(cssText, style).join("/* rr_split */");
557
594
  }
558
- function getXPath(node2) {
559
- if (node2.nodeType === Node.DOCUMENT_NODE) {
560
- return "/";
595
+ function isSelectorUnique(selector, target) {
596
+ try {
597
+ const matches = document.querySelectorAll(selector);
598
+ return matches.length === 1 && matches[0] === target;
599
+ } catch {
600
+ return false;
561
601
  }
562
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
563
- return "/html/doctype";
602
+ }
603
+ function buildSelector(node2) {
604
+ if (!(node2 instanceof Element)) return null;
605
+ if (node2.id) {
606
+ return `#${CSS.escape(node2.id)}`;
564
607
  }
565
- if (node2.nodeType === Node.ELEMENT_NODE) {
566
- const element = node2;
567
- if (element.id) {
568
- return `//*[@id="${element.id}"]`;
569
- }
570
- if (element.tagName && element.tagName.toLowerCase() === "html") {
571
- return "/html";
572
- }
573
- if (element === document.head) {
574
- return "/html/head";
575
- }
576
- if (element === document.body) {
577
- return "/html/body";
578
- }
579
- const parentNode2 = element.parentNode;
580
- if (!parentNode2) {
581
- return "";
582
- }
583
- const siblings = Array.from(parentNode2.children).filter(
584
- (sibling) => sibling.tagName === element.tagName
585
- );
586
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
587
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
608
+ const parts = [];
609
+ const tag = node2.tagName.toLowerCase();
610
+ if (node2.classList.length) {
611
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
588
612
  }
589
- if (node2.nodeType === Node.TEXT_NODE) {
590
- const parent = node2.parentNode;
591
- if (!parent) {
592
- return "";
613
+ Array.from(node2.attributes).forEach((attr) => {
614
+ if (attr.name.startsWith("data-")) {
615
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
593
616
  }
594
- const textSiblings = Array.from(parent.childNodes).filter(
595
- (sibling) => sibling.nodeType === Node.TEXT_NODE
596
- );
597
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
598
- return `${getXPath(parent)}/text()${index2}`;
599
- }
600
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
601
- const parent = node2.parentNode;
602
- if (!parent) {
603
- return "";
617
+ });
618
+ const shortSelector = `${tag}${parts.join("")}`;
619
+ if (isSelectorUnique(shortSelector, node2)) {
620
+ return shortSelector;
621
+ }
622
+ const pathParts = [];
623
+ let current = node2;
624
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
625
+ const parent = current.parentElement;
626
+ const tagName = current.tagName.toLowerCase();
627
+ let nth = "";
628
+ if (parent) {
629
+ const siblings = Array.from(parent.children).filter(
630
+ (el) => el.tagName.toLowerCase() === tagName
631
+ );
632
+ if (siblings.length > 1) {
633
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
634
+ }
635
+ }
636
+ pathParts.unshift(`${tagName}${nth}`);
637
+ current = parent;
638
+ }
639
+ return pathParts.join(" > ") || null;
640
+ }
641
+ function buildXPath(node2) {
642
+ switch (node2.nodeType) {
643
+ case Node.DOCUMENT_NODE:
644
+ return "/";
645
+ case Node.DOCUMENT_TYPE_NODE:
646
+ return "/html/doctype";
647
+ case Node.ELEMENT_NODE: {
648
+ const element = node2;
649
+ if (element.id) {
650
+ return `//*[@id="${CSS.escape(element.id)}"]`;
651
+ }
652
+ if (element.tagName.toLowerCase() === "html") return "/html";
653
+ if (element === document.head) return "/html/head";
654
+ if (element === document.body) return "/html/body";
655
+ const parent = element.parentNode;
656
+ if (!parent) return "";
657
+ const tag = element.tagName.toLowerCase();
658
+ const siblings = Array.from(parent.children).filter(
659
+ (el) => el.tagName.toLowerCase() === tag
660
+ );
661
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
662
+ return `${buildXPath(parent)}/${tag}${index2}`;
663
+ }
664
+ case Node.TEXT_NODE:
665
+ case Node.CDATA_SECTION_NODE:
666
+ case Node.COMMENT_NODE: {
667
+ const parent = node2.parentNode;
668
+ if (!parent) return "";
669
+ const typeMap = {
670
+ [Node.TEXT_NODE]: "text()",
671
+ [Node.CDATA_SECTION_NODE]: "text()",
672
+ // CDATA ≡ text() в XPath
673
+ [Node.COMMENT_NODE]: "comment()"
674
+ };
675
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
676
+ (sibling) => sibling.nodeType === node2.nodeType
677
+ );
678
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
679
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
604
680
  }
605
- const cdataSiblings = Array.from(parent.childNodes).filter(
606
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
607
- );
608
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
609
- return `${getXPath(parent)}/text()${index2}`;
610
- }
611
- if (node2.nodeType === Node.COMMENT_NODE) {
612
- const parent = node2.parentNode;
613
- if (!parent) {
681
+ default:
614
682
  return "";
615
- }
616
- const commentSiblings = Array.from(parent.childNodes).filter(
617
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
618
- );
619
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
620
- return `${getXPath(parent)}/comment()${index2}`;
621
683
  }
622
- return "";
623
684
  }
624
685
  function isTextVisible(n2) {
625
686
  var _a2;
@@ -635,20 +696,9 @@ function isTextVisible(n2) {
635
696
  const textContent2 = (_a2 = n2.textContent) == null ? void 0 : _a2.trim();
636
697
  return textContent2 !== "";
637
698
  }
638
- function isElementVisible(n2) {
639
- var _a2;
640
- const win = ((_a2 = n2.ownerDocument) == null ? void 0 : _a2.defaultView) ?? null;
641
- const style = win ? win.getComputedStyle(n2) : null;
642
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
643
- const rect = n2.getBoundingClientRect();
644
- const result2 = isStyleVisible && isRectVisible(rect);
645
- return result2;
646
- }
647
- function isRectVisible(rect, win = window) {
648
- var _a2, _b, _c, _d;
649
- const height = (win == null ? void 0 : win.innerHeight) ?? ((_b = (_a2 = win == null ? void 0 : win.document) == null ? void 0 : _a2.documentElement) == null ? void 0 : _b.clientHeight) ?? 0;
650
- 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;
651
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
699
+ function isElementVisible(el) {
700
+ const visibility = index$1.getElementVisibility(el);
701
+ return visibility.isVisible;
652
702
  }
653
703
  const interactiveEvents$1 = [
654
704
  "change",
@@ -881,9 +931,6 @@ function transformAttribute(doc, tagName, name, value) {
881
931
  function ignoreAttribute(tagName, name, _value) {
882
932
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
883
933
  }
884
- function isIncludeAttribute(name, include) {
885
- return typeof include === "string" ? name.includes(include) : include.test(name);
886
- }
887
934
  function isExcludeAttribute(name, exclude) {
888
935
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
889
936
  }
@@ -1017,7 +1064,6 @@ function serializeNode(n2, options) {
1017
1064
  blockClass,
1018
1065
  blockSelector,
1019
1066
  excludeAttribute,
1020
- includeAttribute,
1021
1067
  needsMask,
1022
1068
  inlineStylesheet,
1023
1069
  maskInputOptions = {},
@@ -1031,22 +1077,19 @@ function serializeNode(n2, options) {
1031
1077
  cssCaptured = false
1032
1078
  } = options;
1033
1079
  const rootId = getRootId(doc, mirror2);
1034
- const xPath = getXPath(n2);
1035
1080
  switch (n2.nodeType) {
1036
1081
  case n2.DOCUMENT_NODE:
1037
1082
  if (n2.compatMode !== "CSS1Compat") {
1038
1083
  return {
1039
1084
  type: NodeType$3.Document,
1040
1085
  childNodes: [],
1041
- xPath,
1042
1086
  compatMode: n2.compatMode
1043
1087
  // probably "BackCompat"
1044
1088
  };
1045
1089
  } else {
1046
1090
  return {
1047
1091
  type: NodeType$3.Document,
1048
- childNodes: [],
1049
- xPath
1092
+ childNodes: []
1050
1093
  };
1051
1094
  }
1052
1095
  case n2.DOCUMENT_TYPE_NODE:
@@ -1055,8 +1098,7 @@ function serializeNode(n2, options) {
1055
1098
  name: n2.name,
1056
1099
  publicId: n2.publicId,
1057
1100
  systemId: n2.systemId,
1058
- rootId,
1059
- xPath
1101
+ rootId
1060
1102
  };
1061
1103
  case n2.ELEMENT_NODE:
1062
1104
  return serializeElementNode(n2, {
@@ -1064,7 +1106,6 @@ function serializeNode(n2, options) {
1064
1106
  blockClass,
1065
1107
  blockSelector,
1066
1108
  excludeAttribute,
1067
- includeAttribute,
1068
1109
  inlineStylesheet,
1069
1110
  maskInputOptions,
1070
1111
  maskInputFn,
@@ -1073,8 +1114,7 @@ function serializeNode(n2, options) {
1073
1114
  recordCanvas,
1074
1115
  keepIframeSrcFn,
1075
1116
  newlyAddedElement,
1076
- rootId,
1077
- xPath
1117
+ rootId
1078
1118
  });
1079
1119
  case n2.TEXT_NODE:
1080
1120
  return serializeTextNode(n2, {
@@ -1082,22 +1122,19 @@ function serializeNode(n2, options) {
1082
1122
  needsMask,
1083
1123
  maskTextFn,
1084
1124
  rootId,
1085
- cssCaptured,
1086
- xPath
1125
+ cssCaptured
1087
1126
  });
1088
1127
  case n2.CDATA_SECTION_NODE:
1089
1128
  return {
1090
1129
  type: NodeType$3.CDATA,
1091
1130
  textContent: "",
1092
- rootId,
1093
- xPath
1131
+ rootId
1094
1132
  };
1095
1133
  case n2.COMMENT_NODE:
1096
1134
  return {
1097
1135
  type: NodeType$3.Comment,
1098
1136
  textContent: index$1.textContent(n2) || "",
1099
- rootId,
1100
- xPath
1137
+ rootId
1101
1138
  };
1102
1139
  default:
1103
1140
  return false;
@@ -1109,7 +1146,7 @@ function getRootId(doc, mirror2) {
1109
1146
  return docId === 1 ? void 0 : docId;
1110
1147
  }
1111
1148
  function serializeTextNode(n2, options) {
1112
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1149
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1113
1150
  const parent = index$1.parentNode(n2);
1114
1151
  const parentTagName = parent && parent.tagName;
1115
1152
  let textContent2 = "";
@@ -1126,15 +1163,10 @@ function serializeTextNode(n2, options) {
1126
1163
  if (!isStyle && !isScript && textContent2 && needsMask) {
1127
1164
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1128
1165
  }
1129
- const isVisible = isTextVisible(n2);
1130
- const isInteractive = isElementInteractive(n2);
1131
1166
  return {
1132
1167
  type: NodeType$3.Text,
1133
1168
  textContent: textContent2 || "",
1134
- rootId,
1135
- isVisible,
1136
- isInteractive,
1137
- xPath
1169
+ rootId
1138
1170
  };
1139
1171
  }
1140
1172
  function serializeElementNode(n2, options) {
@@ -1143,7 +1175,6 @@ function serializeElementNode(n2, options) {
1143
1175
  blockClass,
1144
1176
  blockSelector,
1145
1177
  excludeAttribute,
1146
- includeAttribute,
1147
1178
  inlineStylesheet,
1148
1179
  maskInputOptions = {},
1149
1180
  maskInputFn,
@@ -1152,8 +1183,7 @@ function serializeElementNode(n2, options) {
1152
1183
  recordCanvas,
1153
1184
  keepIframeSrcFn,
1154
1185
  newlyAddedElement = false,
1155
- rootId,
1156
- xPath
1186
+ rootId
1157
1187
  } = options;
1158
1188
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1159
1189
  const tagName = getValidTagName$1(n2);
@@ -1161,7 +1191,7 @@ function serializeElementNode(n2, options) {
1161
1191
  const len = n2.attributes.length;
1162
1192
  for (let i2 = 0; i2 < len; i2++) {
1163
1193
  const attr = n2.attributes[i2];
1164
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1194
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1165
1195
  continue;
1166
1196
  }
1167
1197
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1323,8 +1353,6 @@ function serializeElementNode(n2, options) {
1323
1353
  if (customElements.get(tagName)) isCustomElement = true;
1324
1354
  } catch (e2) {
1325
1355
  }
1326
- const isVisible = isElementVisible(n2);
1327
- const isInteractive = isElementInteractive(n2);
1328
1356
  return {
1329
1357
  type: NodeType$3.Element,
1330
1358
  tagName,
@@ -1333,10 +1361,7 @@ function serializeElementNode(n2, options) {
1333
1361
  isSVG: isSVGElement(n2) || void 0,
1334
1362
  needBlock,
1335
1363
  rootId,
1336
- isCustom: isCustomElement,
1337
- isVisible,
1338
- isInteractive,
1339
- xPath
1364
+ isCustom: isCustomElement
1340
1365
  };
1341
1366
  }
1342
1367
  function lowerIfExists(maybeAttr) {
@@ -1387,7 +1412,6 @@ function serializeNodeWithId(n2, options) {
1387
1412
  maskTextClass,
1388
1413
  maskTextSelector,
1389
1414
  excludeAttribute,
1390
- includeAttribute,
1391
1415
  skipChild = false,
1392
1416
  inlineStylesheet = true,
1393
1417
  maskInputOptions = {},
@@ -1423,7 +1447,6 @@ function serializeNodeWithId(n2, options) {
1423
1447
  blockClass,
1424
1448
  blockSelector,
1425
1449
  excludeAttribute,
1426
- includeAttribute,
1427
1450
  needsMask,
1428
1451
  inlineStylesheet,
1429
1452
  maskInputOptions,
@@ -1449,6 +1472,22 @@ function serializeNodeWithId(n2, options) {
1449
1472
  id = genId();
1450
1473
  }
1451
1474
  const serializedNode = Object.assign(_serializedNode, { id });
1475
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1476
+ serializedNode.xpath = buildXPath(n2);
1477
+ if (isElement(n2)) {
1478
+ const selector = buildSelector(n2);
1479
+ if (selector) {
1480
+ serializedNode.selector = selector;
1481
+ }
1482
+ }
1483
+ if (n2.nodeType === Node.TEXT_NODE) {
1484
+ serializedNode.isVisible = isTextVisible(n2);
1485
+ }
1486
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1487
+ serializedNode.isVisible = isElementVisible(n2);
1488
+ serializedNode.isInteractive = isElementInteractive(n2);
1489
+ }
1490
+ }
1452
1491
  mirror2.add(n2, serializedNode);
1453
1492
  if (id === IGNORED_NODE) {
1454
1493
  return null;
@@ -1477,7 +1516,6 @@ function serializeNodeWithId(n2, options) {
1477
1516
  maskTextClass,
1478
1517
  maskTextSelector,
1479
1518
  excludeAttribute,
1480
- includeAttribute,
1481
1519
  skipChild,
1482
1520
  inlineStylesheet,
1483
1521
  maskInputOptions,
@@ -1538,7 +1576,6 @@ function serializeNodeWithId(n2, options) {
1538
1576
  maskTextClass,
1539
1577
  maskTextSelector,
1540
1578
  excludeAttribute,
1541
- includeAttribute,
1542
1579
  skipChild: false,
1543
1580
  inlineStylesheet,
1544
1581
  maskInputOptions,
@@ -1581,7 +1618,6 @@ function serializeNodeWithId(n2, options) {
1581
1618
  maskTextClass,
1582
1619
  maskTextSelector,
1583
1620
  excludeAttribute,
1584
- includeAttribute,
1585
1621
  skipChild: false,
1586
1622
  inlineStylesheet,
1587
1623
  maskInputOptions,
@@ -1619,8 +1655,7 @@ function snapshot(n2, options) {
1619
1655
  blockSelector = null,
1620
1656
  maskTextClass = "rr-mask",
1621
1657
  maskTextSelector = null,
1622
- excludeAttribute = /^$a/,
1623
- includeAttribute = /.+/i,
1658
+ excludeAttribute = /.^/,
1624
1659
  inlineStylesheet = true,
1625
1660
  inlineImages = false,
1626
1661
  recordCanvas = false,
@@ -1681,7 +1716,6 @@ function snapshot(n2, options) {
1681
1716
  maskTextClass,
1682
1717
  maskTextSelector,
1683
1718
  excludeAttribute,
1684
- includeAttribute,
1685
1719
  skipChild: false,
1686
1720
  inlineStylesheet,
1687
1721
  maskInputOptions,
@@ -9092,6 +9126,41 @@ function patch(source, name, replacement) {
9092
9126
  };
9093
9127
  }
9094
9128
  }
9129
+ function describeNode(el) {
9130
+ const tag = el.tagName.toLowerCase();
9131
+ const id = el.id ? `#${el.id}` : "";
9132
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
9133
+ return `${tag}${id}${classes}`;
9134
+ }
9135
+ function getElementVisibility(el) {
9136
+ var _a2, _b;
9137
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
9138
+ const rect = el.getBoundingClientRect();
9139
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
9140
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
9141
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
9142
+ const style = (_b = win.getComputedStyle) == null ? void 0 : _b.call(win, el);
9143
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
9144
+ const isVisible = isStyleVisible2 && isRectVisible;
9145
+ let ratio = 0;
9146
+ if (isVisible) {
9147
+ const xOverlap = Math.max(
9148
+ 0,
9149
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
9150
+ );
9151
+ const yOverlap = Math.max(
9152
+ 0,
9153
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
9154
+ );
9155
+ const intersectionArea = xOverlap * yOverlap;
9156
+ const elementArea = rect.width * rect.height;
9157
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
9158
+ }
9159
+ return {
9160
+ isVisible,
9161
+ ratio
9162
+ };
9163
+ }
9095
9164
  const index = {
9096
9165
  childNodes,
9097
9166
  parentNode,
@@ -9105,7 +9174,9 @@ const index = {
9105
9174
  querySelector,
9106
9175
  querySelectorAll,
9107
9176
  mutationObserver: mutationObserverCtor,
9108
- patch
9177
+ patch,
9178
+ describeNode,
9179
+ getElementVisibility
9109
9180
  };
9110
9181
  function on(type, fn, target = document) {
9111
9182
  const options = { capture: true, passive: true };
@@ -9379,6 +9450,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
9379
9450
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
9380
9451
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
9381
9452
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
9453
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
9382
9454
  return IncrementalSource2;
9383
9455
  })(IncrementalSource || {});
9384
9456
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -9525,7 +9597,6 @@ class MutationBuffer {
9525
9597
  __publicField(this, "maskTextClass");
9526
9598
  __publicField(this, "maskTextSelector");
9527
9599
  __publicField(this, "excludeAttribute");
9528
- __publicField(this, "includeAttribute");
9529
9600
  __publicField(this, "inlineStylesheet");
9530
9601
  __publicField(this, "maskInputOptions");
9531
9602
  __publicField(this, "maskTextFn");
@@ -9541,6 +9612,7 @@ class MutationBuffer {
9541
9612
  __publicField(this, "stylesheetManager");
9542
9613
  __publicField(this, "shadowDomManager");
9543
9614
  __publicField(this, "canvasManager");
9615
+ __publicField(this, "visibilityManager");
9544
9616
  __publicField(this, "processedNodeManager");
9545
9617
  __publicField(this, "unattachedDoc");
9546
9618
  __publicField(this, "processMutations", (mutations) => {
@@ -9590,7 +9662,6 @@ class MutationBuffer {
9590
9662
  maskTextClass: this.maskTextClass,
9591
9663
  maskTextSelector: this.maskTextSelector,
9592
9664
  excludeAttribute: this.excludeAttribute,
9593
- includeAttribute: this.includeAttribute,
9594
9665
  skipChild: true,
9595
9666
  newlyAddedElement: true,
9596
9667
  inlineStylesheet: this.inlineStylesheet,
@@ -9636,7 +9707,8 @@ class MutationBuffer {
9636
9707
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
9637
9708
  }
9638
9709
  for (const n2 of this.movedSet) {
9639
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
9710
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9711
+ !this.movedSet.has(index.parentNode(n2))) {
9640
9712
  continue;
9641
9713
  }
9642
9714
  pushAdd(n2);
@@ -9792,12 +9864,60 @@ class MutationBuffer {
9792
9864
  const target = m.target;
9793
9865
  let attributeName = m.attributeName;
9794
9866
  let value = m.target.getAttribute(attributeName);
9795
- const propValue = target[attributeName];
9796
- const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
9867
+ const attrKey = attributeName;
9868
+ const propValue = target[attrKey];
9869
+ const isBooleanAttr = typeof propValue === "boolean";
9870
+ const inDOM = document.contains(target);
9871
+ const isVisible = isElementVisible(target);
9872
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
9873
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
9874
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
9875
+ m.oldValue !== null && // раньше он был
9876
+ (propValue === "" || // свойство = пустая строка
9877
+ propValue === null || // или null
9878
+ typeof propValue === "undefined");
9797
9879
  if (isPhantomAttributeMutation) {
9880
+ console.debug(
9881
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ phantom attribute mutation ignored`,
9882
+ {
9883
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
9884
+ node: index.describeNode(target),
9885
+ tag: target.tagName,
9886
+ nodeType: target.nodeType,
9887
+ attribute: attributeName,
9888
+ value,
9889
+ oldValue: m.oldValue,
9890
+ excludeAttribute: this.excludeAttribute,
9891
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9892
+ propValue,
9893
+ isBooleanAttr,
9894
+ inDOM,
9895
+ isVisible,
9896
+ isExcludeAttributeName
9897
+ }
9898
+ );
9798
9899
  return;
9799
9900
  }
9800
9901
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
9902
+ console.debug(
9903
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ excluded attribute mutation ignored`,
9904
+ {
9905
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
9906
+ node: index.describeNode(target),
9907
+ tag: target.tagName,
9908
+ nodeType: target.nodeType,
9909
+ attribute: attributeName,
9910
+ value,
9911
+ oldValue: m.oldValue,
9912
+ excludeAttribute: this.excludeAttribute,
9913
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9914
+ propValue,
9915
+ isBooleanAttr,
9916
+ inDOM,
9917
+ isVisible,
9918
+ isExcludeAttributeName
9919
+ }
9920
+ );
9801
9921
  return;
9802
9922
  }
9803
9923
  if (attributeName === "value") {
@@ -9842,9 +9962,35 @@ class MutationBuffer {
9842
9962
  toLowerCase(attributeName),
9843
9963
  value
9844
9964
  );
9845
- if (value === item.attributes[attributeName]) {
9846
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
9847
- return;
9965
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
9966
+ value === "" || // класс удалён
9967
+ value !== m.oldValue);
9968
+ if (isSuspiciousClassMutation) {
9969
+ console.debug(
9970
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⚠️ suspicious attribute mutation`,
9971
+ {
9972
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
9973
+ reason: [
9974
+ value === m.oldValue ? "no change in value" : null,
9975
+ value === propValue ? "mirrored in DOM property" : null,
9976
+ value === item.attributes[attributeName] ? "redundant assignment" : null
9977
+ ].filter(Boolean).join(", ") || "uncategorized",
9978
+ node: index.describeNode(target),
9979
+ tag: target.tagName,
9980
+ nodeType: target.nodeType,
9981
+ attribute: attributeName,
9982
+ value,
9983
+ oldValue: m.oldValue,
9984
+ transformedValue: item.attributes[attributeName],
9985
+ excludeAttribute: this.excludeAttribute,
9986
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9987
+ propValue,
9988
+ isBooleanAttr,
9989
+ inDOM,
9990
+ isVisible,
9991
+ isExcludeAttributeName
9992
+ }
9993
+ );
9848
9994
  }
9849
9995
  if (attributeName === "style") {
9850
9996
  if (!this.unattachedDoc) {
@@ -9959,7 +10105,6 @@ class MutationBuffer {
9959
10105
  "maskTextClass",
9960
10106
  "maskTextSelector",
9961
10107
  "excludeAttribute",
9962
- "includeAttribute",
9963
10108
  "inlineStylesheet",
9964
10109
  "maskInputOptions",
9965
10110
  "maskTextFn",
@@ -9975,6 +10120,7 @@ class MutationBuffer {
9975
10120
  "stylesheetManager",
9976
10121
  "shadowDomManager",
9977
10122
  "canvasManager",
10123
+ "visibilityManager",
9978
10124
  "processedNodeManager"
9979
10125
  ].forEach((key) => {
9980
10126
  this[key] = options[key];
@@ -9983,10 +10129,12 @@ class MutationBuffer {
9983
10129
  freeze() {
9984
10130
  this.frozen = true;
9985
10131
  this.canvasManager.freeze();
10132
+ this.visibilityManager.freeze();
9986
10133
  }
9987
10134
  unfreeze() {
9988
10135
  this.frozen = false;
9989
10136
  this.canvasManager.unfreeze();
10137
+ this.visibilityManager.unfreeze();
9990
10138
  this.emit();
9991
10139
  }
9992
10140
  isFrozen() {
@@ -9995,15 +10143,18 @@ class MutationBuffer {
9995
10143
  lock() {
9996
10144
  this.locked = true;
9997
10145
  this.canvasManager.lock();
10146
+ this.visibilityManager.lock();
9998
10147
  }
9999
10148
  unlock() {
10000
10149
  this.locked = false;
10001
10150
  this.canvasManager.unlock();
10151
+ this.visibilityManager.unlock();
10002
10152
  this.emit();
10003
10153
  }
10004
10154
  reset() {
10005
10155
  this.shadowDomManager.reset();
10006
10156
  this.canvasManager.reset();
10157
+ this.visibilityManager.reset();
10007
10158
  }
10008
10159
  }
10009
10160
  function deepDelete(addsSet, n2) {
@@ -10937,6 +11088,7 @@ function mergeHooks(o2, hooks) {
10937
11088
  styleSheetRuleCb,
10938
11089
  styleDeclarationCb,
10939
11090
  canvasMutationCb,
11091
+ visibilityMutationCb,
10940
11092
  fontCb,
10941
11093
  selectionCb,
10942
11094
  customElementCb
@@ -11001,6 +11153,12 @@ function mergeHooks(o2, hooks) {
11001
11153
  }
11002
11154
  canvasMutationCb(...p);
11003
11155
  };
11156
+ o2.visibilityMutationCb = (...p) => {
11157
+ if (hooks.visibilityMutation) {
11158
+ hooks.visibilityMutation(...p);
11159
+ }
11160
+ visibilityMutationCb(...p);
11161
+ };
11004
11162
  o2.fontCb = (...p) => {
11005
11163
  if (hooks.font) {
11006
11164
  hooks.font(...p);
@@ -12114,11 +12272,249 @@ class ProcessedNodeManager {
12114
12272
  destroy() {
12115
12273
  }
12116
12274
  }
12275
+ function computeVisibility(elements, previous, options) {
12276
+ const root2 = (options == null ? void 0 : options.root) ?? null;
12277
+ const threshold = (options == null ? void 0 : options.threshold) ?? 0.5;
12278
+ const sensitivity = (options == null ? void 0 : options.sensitivity) ?? 0.05;
12279
+ const rootMarginFn = parseRootMargin((options == null ? void 0 : options.rootMargin) ?? "0px");
12280
+ const current = /* @__PURE__ */ new Map();
12281
+ const rootRect = getRootRect(root2);
12282
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
12283
+ for (const el of elements) {
12284
+ const elRect = el.getBoundingClientRect();
12285
+ let intersectionRect = emptyRect();
12286
+ let intersectionRatio = 0;
12287
+ if (elRect.width > 0 && elRect.height > 0) {
12288
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
12289
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
12290
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
12291
+ }
12292
+ const isStyle = isStyleVisible(el);
12293
+ const old = previous.get(el) ?? null;
12294
+ const prevRatio = (old == null ? void 0 : old.intersectionRatio) ?? 0;
12295
+ const currRatio = intersectionRatio;
12296
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
12297
+ const nowVisible = isStyle && currRatio > threshold;
12298
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
12299
+ if (changed) {
12300
+ current.set(el, {
12301
+ target: el,
12302
+ isVisible: nowVisible,
12303
+ isStyleVisible: isStyle,
12304
+ intersectionRatio: currRatio,
12305
+ intersectionRect,
12306
+ oldValue: old
12307
+ });
12308
+ } else {
12309
+ current.set(el, old);
12310
+ }
12311
+ }
12312
+ return current;
12313
+ }
12314
+ function parseRootMargin(marginStr) {
12315
+ const parts = marginStr.trim().split(/\s+/);
12316
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
12317
+ return function(rootRect) {
12318
+ const top = getValue(parts[0] || "0px", rootRect.height);
12319
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
12320
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
12321
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
12322
+ return { top, right, bottom, left, width: 0, height: 0 };
12323
+ };
12324
+ }
12325
+ function getRootRect(root2) {
12326
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
12327
+ }
12328
+ function expandRootRect(rect, marginFn) {
12329
+ const margin = marginFn(rect);
12330
+ return new DOMRect(
12331
+ rect.left - margin.left,
12332
+ rect.top - margin.top,
12333
+ rect.width + margin.left + margin.right,
12334
+ rect.height + margin.top + margin.bottom
12335
+ );
12336
+ }
12337
+ function computeIntersectionRect(a2, b) {
12338
+ const top = Math.max(a2.top, b.top);
12339
+ const left = Math.max(a2.left, b.left);
12340
+ const bottom = Math.min(a2.bottom, b.bottom);
12341
+ const right = Math.min(a2.right, b.right);
12342
+ const width = Math.max(0, right - left);
12343
+ const height = Math.max(0, bottom - top);
12344
+ return { top, left, bottom, right, width, height };
12345
+ }
12346
+ function computeIntersectionRatio(elRect, intersectionRect) {
12347
+ const elArea = elRect.width * elRect.height;
12348
+ const intArea = intersectionRect.width * intersectionRect.height;
12349
+ return elArea > 0 ? intArea / elArea : 0;
12350
+ }
12351
+ function emptyRect() {
12352
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
12353
+ }
12354
+ function isStyleVisible(el) {
12355
+ const style = getComputedStyle(el);
12356
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
12357
+ }
12358
+ class VisibilityManager {
12359
+ constructor(options) {
12360
+ __publicField(this, "frozen", false);
12361
+ __publicField(this, "locked", false);
12362
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
12363
+ __publicField(this, "mirror");
12364
+ __publicField(this, "mutationCb");
12365
+ __publicField(this, "rafId", null);
12366
+ __publicField(this, "rafThrottle");
12367
+ __publicField(this, "lastFlushTime", 0);
12368
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
12369
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
12370
+ __publicField(this, "root", null);
12371
+ __publicField(this, "threshold");
12372
+ __publicField(this, "sensitivity");
12373
+ __publicField(this, "rootMargin");
12374
+ __publicField(this, "hasInitialized", false);
12375
+ __publicField(this, "mode", "none");
12376
+ __publicField(this, "debounce", 50);
12377
+ __publicField(this, "throttle", 100);
12378
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
12379
+ __publicField(this, "debounceTimer", null);
12380
+ __publicField(this, "lastThrottleTime", 0);
12381
+ __publicField(this, "disabled", false);
12382
+ __publicField(this, "notifyActivity");
12383
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
12384
+ this.mirror = mirror2;
12385
+ this.mutationCb = mutationCb;
12386
+ this.notifyActivity = notifyActivity;
12387
+ this.rootMargin = "0px";
12388
+ if (sampling === false) {
12389
+ this.disabled = true;
12390
+ return;
12391
+ }
12392
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
12393
+ this.mode = (visibilitySampling == null ? void 0 : visibilitySampling.mode) ?? "none";
12394
+ this.debounce = Number((visibilitySampling == null ? void 0 : visibilitySampling.debounce) ?? 100);
12395
+ this.throttle = Number((visibilitySampling == null ? void 0 : visibilitySampling.throttle) ?? 100);
12396
+ this.threshold = Number((visibilitySampling == null ? void 0 : visibilitySampling.threshold) ?? 0.5);
12397
+ this.sensitivity = Number((visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) ?? 0.05);
12398
+ this.rafThrottle = Number((visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) ?? 100);
12399
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
12400
+ const mo = new MutationObserver((mutations) => {
12401
+ mutations.forEach((m) => {
12402
+ m.addedNodes.forEach((n2) => {
12403
+ if (n2.nodeType === Node.ELEMENT_NODE) {
12404
+ this.observe(n2);
12405
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
12406
+ }
12407
+ });
12408
+ m.removedNodes.forEach((n2) => {
12409
+ if (n2.nodeType === Node.ELEMENT_NODE) {
12410
+ this.unobserve(n2);
12411
+ }
12412
+ });
12413
+ });
12414
+ });
12415
+ mo.observe(doc.body, { childList: true, subtree: true });
12416
+ this.startPendingFlushLoop();
12417
+ }
12418
+ startPendingFlushLoop() {
12419
+ if (this.disabled) return;
12420
+ const loop = (timestamp) => {
12421
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
12422
+ this.lastFlushTime = timestamp;
12423
+ this.flushPendingVisibilityMutations();
12424
+ }
12425
+ this.rafId = requestAnimationFrame(loop);
12426
+ };
12427
+ this.rafId = requestAnimationFrame(loop);
12428
+ }
12429
+ flushPendingVisibilityMutations() {
12430
+ if (this.disabled) return;
12431
+ if (this.frozen || this.locked || this.elements.size === 0) return;
12432
+ const state = computeVisibility(this.elements, this.previousState, {
12433
+ root: this.root,
12434
+ threshold: this.threshold,
12435
+ sensitivity: this.sensitivity,
12436
+ rootMargin: this.rootMargin
12437
+ });
12438
+ for (const [el, entry] of state.entries()) {
12439
+ const old = this.previousState.get(el);
12440
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
12441
+ if (changed) {
12442
+ const id = this.mirror.getId(el);
12443
+ if (id !== -1) {
12444
+ this.buffer.set(el, {
12445
+ id,
12446
+ isVisible: entry.isVisible,
12447
+ ratio: entry.intersectionRatio
12448
+ });
12449
+ }
12450
+ this.previousState.set(el, entry);
12451
+ }
12452
+ }
12453
+ this.previousState = state;
12454
+ if (!this.hasInitialized) {
12455
+ this.hasInitialized = true;
12456
+ this.buffer.clear();
12457
+ return;
12458
+ }
12459
+ this.scheduleEmit();
12460
+ }
12461
+ scheduleEmit() {
12462
+ if (this.mode === "debounce") {
12463
+ clearTimeout(this.debounceTimer);
12464
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
12465
+ } else if (this.mode === "throttle") {
12466
+ const now = performance.now();
12467
+ if (now - this.lastThrottleTime >= this.throttle) {
12468
+ this.lastThrottleTime = now;
12469
+ this.flushBuffer();
12470
+ }
12471
+ } else {
12472
+ this.flushBuffer();
12473
+ }
12474
+ }
12475
+ flushBuffer() {
12476
+ var _a2;
12477
+ if (this.buffer.size === 0) return;
12478
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
12479
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
12480
+ this.buffer.clear();
12481
+ }
12482
+ observe(el) {
12483
+ if (this.disabled) return;
12484
+ this.elements.add(el);
12485
+ }
12486
+ unobserve(el) {
12487
+ if (this.disabled) return;
12488
+ this.elements.delete(el);
12489
+ this.previousState.delete(el);
12490
+ this.pending.delete(el);
12491
+ }
12492
+ freeze() {
12493
+ this.frozen = true;
12494
+ }
12495
+ unfreeze() {
12496
+ this.frozen = false;
12497
+ }
12498
+ lock() {
12499
+ this.locked = true;
12500
+ }
12501
+ unlock() {
12502
+ this.locked = false;
12503
+ }
12504
+ reset() {
12505
+ this.elements.clear();
12506
+ this.previousState.clear();
12507
+ this.pending.clear();
12508
+ if (this.rafId) cancelAnimationFrame(this.rafId);
12509
+ }
12510
+ }
12117
12511
  let wrappedEmit;
12118
12512
  let takeFullSnapshot$1;
12119
12513
  let canvasManager;
12514
+ let visibilityManager;
12120
12515
  let recording = false;
12121
- const preRecordingCustomEvents = [];
12516
+ const customEventQueue = [];
12517
+ let flushCustomEventQueue;
12122
12518
  try {
12123
12519
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
12124
12520
  const cleanFrame = document.createElement("iframe");
@@ -12135,12 +12531,12 @@ function record(options = {}) {
12135
12531
  emit,
12136
12532
  checkoutEveryNms,
12137
12533
  checkoutEveryNth,
12534
+ checkoutEveryNvm,
12138
12535
  blockClass = "rr-block",
12139
12536
  blockSelector = null,
12140
12537
  ignoreClass = "rr-ignore",
12141
12538
  ignoreSelector = null,
12142
12539
  excludeAttribute: _excludeAttribute,
12143
- includeAttribute: _includeAttribute,
12144
12540
  maskTextClass = "rr-mask",
12145
12541
  maskTextSelector = null,
12146
12542
  inlineStylesheet = true,
@@ -12158,7 +12554,7 @@ function record(options = {}) {
12158
12554
  recordCanvas = false,
12159
12555
  recordCrossOriginIframes = false,
12160
12556
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
12161
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
12557
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
12162
12558
  userTriggeredOnInput = false,
12163
12559
  collectFonts = false,
12164
12560
  inlineImages = false,
@@ -12190,8 +12586,7 @@ function record(options = {}) {
12190
12586
  sampling.mousemove = mousemoveWait;
12191
12587
  }
12192
12588
  mirror.reset();
12193
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
12194
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
12589
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
12195
12590
  const maskInputOptions = maskAllInputs === true ? {
12196
12591
  color: true,
12197
12592
  date: true,
@@ -12228,6 +12623,10 @@ function record(options = {}) {
12228
12623
  polyfill$1();
12229
12624
  let lastFullSnapshotEvent;
12230
12625
  let incrementalSnapshotCount = 0;
12626
+ let recentVisibilityChanges = 0;
12627
+ const onVisibilityActivity = (count) => {
12628
+ recentVisibilityChanges += count;
12629
+ };
12231
12630
  const eventProcessor = (e2) => {
12232
12631
  for (const plugin3 of plugins || []) {
12233
12632
  if (plugin3.eventProcessor) {
@@ -12268,7 +12667,11 @@ function record(options = {}) {
12268
12667
  incrementalSnapshotCount++;
12269
12668
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
12270
12669
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
12271
- if (exceedCount || exceedTime) {
12670
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
12671
+ if (exceedCount || exceedTime || exceedVisibility) {
12672
+ if (exceedVisibility) {
12673
+ recentVisibilityChanges = 0;
12674
+ }
12272
12675
  takeFullSnapshot$1(true);
12273
12676
  }
12274
12677
  }
@@ -12296,6 +12699,17 @@ function record(options = {}) {
12296
12699
  ...p
12297
12700
  }
12298
12701
  });
12702
+ const wrappedVisibilityMutationEmit = (p) => {
12703
+ var _a2;
12704
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
12705
+ wrappedEmit({
12706
+ type: EventType.IncrementalSnapshot,
12707
+ data: {
12708
+ source: IncrementalSource.VisibilityMutation,
12709
+ ...p
12710
+ }
12711
+ });
12712
+ };
12299
12713
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
12300
12714
  type: EventType.IncrementalSnapshot,
12301
12715
  data: {
@@ -12333,6 +12747,13 @@ function record(options = {}) {
12333
12747
  sampling: sampling.canvas,
12334
12748
  dataURLOptions
12335
12749
  });
12750
+ visibilityManager = new VisibilityManager({
12751
+ doc: window.document,
12752
+ mirror,
12753
+ sampling: sampling.visibility,
12754
+ mutationCb: wrappedVisibilityMutationEmit,
12755
+ notifyActivity: onVisibilityActivity
12756
+ });
12336
12757
  const shadowDomManager = new ShadowDomManager({
12337
12758
  mutationCb: wrappedMutationEmit,
12338
12759
  scrollCb: wrappedScrollEmit,
@@ -12342,7 +12763,6 @@ function record(options = {}) {
12342
12763
  maskTextClass,
12343
12764
  maskTextSelector,
12344
12765
  excludeAttribute,
12345
- includeAttribute,
12346
12766
  inlineStylesheet,
12347
12767
  maskInputOptions,
12348
12768
  dataURLOptions,
@@ -12355,6 +12775,7 @@ function record(options = {}) {
12355
12775
  iframeManager,
12356
12776
  stylesheetManager,
12357
12777
  canvasManager,
12778
+ visibilityManager,
12358
12779
  keepIframeSrcFn,
12359
12780
  processedNodeManager
12360
12781
  },
@@ -12385,7 +12806,6 @@ function record(options = {}) {
12385
12806
  maskTextClass,
12386
12807
  maskTextSelector,
12387
12808
  excludeAttribute,
12388
- includeAttribute,
12389
12809
  inlineStylesheet,
12390
12810
  maskAllInputs: maskInputOptions,
12391
12811
  maskTextFn,
@@ -12434,6 +12854,12 @@ function record(options = {}) {
12434
12854
  mirror.getId(document)
12435
12855
  );
12436
12856
  };
12857
+ flushCustomEventQueue = () => {
12858
+ for (const e2 of customEventQueue) {
12859
+ wrappedEmit(e2);
12860
+ }
12861
+ customEventQueue.length = 0;
12862
+ };
12437
12863
  try {
12438
12864
  const handlers = [];
12439
12865
  const observe = (doc) => {
@@ -12492,6 +12918,7 @@ function record(options = {}) {
12492
12918
  }
12493
12919
  }),
12494
12920
  canvasMutationCb: wrappedCanvasMutationEmit,
12921
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
12495
12922
  fontCb: (p) => wrappedEmit({
12496
12923
  type: EventType.IncrementalSnapshot,
12497
12924
  data: {
@@ -12523,7 +12950,6 @@ function record(options = {}) {
12523
12950
  maskTextClass,
12524
12951
  maskTextSelector,
12525
12952
  excludeAttribute,
12526
- includeAttribute,
12527
12953
  maskInputOptions,
12528
12954
  inlineStylesheet,
12529
12955
  sampling,
@@ -12545,6 +12971,7 @@ function record(options = {}) {
12545
12971
  shadowDomManager,
12546
12972
  processedNodeManager,
12547
12973
  canvasManager,
12974
+ visibilityManager,
12548
12975
  ignoreCSSAttributes,
12549
12976
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
12550
12977
  observer: p.observer,
@@ -12569,14 +12996,14 @@ function record(options = {}) {
12569
12996
  }
12570
12997
  });
12571
12998
  const init = () => {
12572
- if (flushCustomQueue === "before") {
12573
- flushPreRecordingEvents();
12999
+ if (flushCustomEvent === "before") {
13000
+ flushCustomEventQueue();
12574
13001
  }
12575
13002
  takeFullSnapshot$1();
12576
13003
  handlers.push(observe(document));
12577
13004
  recording = true;
12578
- if (flushCustomQueue === "after") {
12579
- flushPreRecordingEvents();
13005
+ if (flushCustomEvent === "after") {
13006
+ flushCustomEventQueue();
12580
13007
  }
12581
13008
  };
12582
13009
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -12606,7 +13033,7 @@ function record(options = {}) {
12606
13033
  );
12607
13034
  }
12608
13035
  return () => {
12609
- flushPreRecordingEvents();
13036
+ flushCustomEventQueue();
12610
13037
  handlers.forEach((h) => h());
12611
13038
  processedNodeManager.destroy();
12612
13039
  recording = false;
@@ -12616,12 +13043,10 @@ function record(options = {}) {
12616
13043
  console.warn(error);
12617
13044
  }
12618
13045
  }
12619
- function flushPreRecordingEvents() {
12620
- for (const e2 of preRecordingCustomEvents) {
12621
- wrappedEmit(e2);
12622
- }
12623
- preRecordingCustomEvents.length = 0;
12624
- }
13046
+ record.flushCustomEventQueue = () => {
13047
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
13048
+ flushCustomEventQueue();
13049
+ };
12625
13050
  record.addCustomEvent = (tag, payload) => {
12626
13051
  const customEvent = {
12627
13052
  type: EventType.Custom,
@@ -12631,8 +13056,8 @@ record.addCustomEvent = (tag, payload) => {
12631
13056
  }
12632
13057
  };
12633
13058
  if (!recording) {
12634
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
12635
- preRecordingCustomEvents.push(customEvent);
13059
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
13060
+ customEventQueue.push(customEvent);
12636
13061
  return;
12637
13062
  }
12638
13063
  wrappedEmit(customEvent);