@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.
package/dist/rrweb.js CHANGED
@@ -168,6 +168,41 @@ function patch$1(source, name, replacement) {
168
168
  };
169
169
  }
170
170
  }
171
+ function describeNode$1(el) {
172
+ const tag = el.tagName.toLowerCase();
173
+ const id = el.id ? `#${el.id}` : "";
174
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
175
+ return `${tag}${id}${classes}`;
176
+ }
177
+ function getElementVisibility$1(el) {
178
+ var _a2, _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;
@@ -567,71 +604,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
567
604
  function markCssSplits(cssText, style) {
568
605
  return splitCssText(cssText, style).join("/* rr_split */");
569
606
  }
570
- function getXPath(node2) {
571
- if (node2.nodeType === Node.DOCUMENT_NODE) {
572
- return "/";
607
+ function isSelectorUnique(selector, target) {
608
+ try {
609
+ const matches = document.querySelectorAll(selector);
610
+ return matches.length === 1 && matches[0] === target;
611
+ } catch {
612
+ return false;
573
613
  }
574
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
575
- return "/html/doctype";
614
+ }
615
+ function buildSelector(node2) {
616
+ if (!(node2 instanceof Element)) return null;
617
+ if (node2.id) {
618
+ return `#${CSS.escape(node2.id)}`;
576
619
  }
577
- if (node2.nodeType === Node.ELEMENT_NODE) {
578
- const element = node2;
579
- if (element.id) {
580
- return `//*[@id="${element.id}"]`;
581
- }
582
- if (element.tagName && element.tagName.toLowerCase() === "html") {
583
- return "/html";
584
- }
585
- if (element === document.head) {
586
- return "/html/head";
587
- }
588
- if (element === document.body) {
589
- return "/html/body";
590
- }
591
- const parentNode2 = element.parentNode;
592
- if (!parentNode2) {
593
- return "";
594
- }
595
- const siblings = Array.from(parentNode2.children).filter(
596
- (sibling) => sibling.tagName === element.tagName
597
- );
598
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
599
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
620
+ const parts = [];
621
+ const tag = node2.tagName.toLowerCase();
622
+ if (node2.classList.length) {
623
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
600
624
  }
601
- if (node2.nodeType === Node.TEXT_NODE) {
602
- const parent = node2.parentNode;
603
- if (!parent) {
604
- return "";
625
+ Array.from(node2.attributes).forEach((attr) => {
626
+ if (attr.name.startsWith("data-")) {
627
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
605
628
  }
606
- const textSiblings = Array.from(parent.childNodes).filter(
607
- (sibling) => sibling.nodeType === Node.TEXT_NODE
608
- );
609
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
610
- return `${getXPath(parent)}/text()${index2}`;
611
- }
612
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
613
- const parent = node2.parentNode;
614
- if (!parent) {
615
- return "";
629
+ });
630
+ const shortSelector = `${tag}${parts.join("")}`;
631
+ if (isSelectorUnique(shortSelector, node2)) {
632
+ return shortSelector;
633
+ }
634
+ const pathParts = [];
635
+ let current = node2;
636
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
637
+ const parent = current.parentElement;
638
+ const tagName = current.tagName.toLowerCase();
639
+ let nth = "";
640
+ if (parent) {
641
+ const siblings = Array.from(parent.children).filter(
642
+ (el) => el.tagName.toLowerCase() === tagName
643
+ );
644
+ if (siblings.length > 1) {
645
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
646
+ }
616
647
  }
617
- const cdataSiblings = Array.from(parent.childNodes).filter(
618
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
619
- );
620
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
621
- return `${getXPath(parent)}/text()${index2}`;
648
+ pathParts.unshift(`${tagName}${nth}`);
649
+ current = parent;
622
650
  }
623
- if (node2.nodeType === Node.COMMENT_NODE) {
624
- const parent = node2.parentNode;
625
- if (!parent) {
626
- return "";
651
+ return pathParts.join(" > ") || null;
652
+ }
653
+ function buildXPath(node2) {
654
+ switch (node2.nodeType) {
655
+ case Node.DOCUMENT_NODE:
656
+ return "/";
657
+ case Node.DOCUMENT_TYPE_NODE:
658
+ return "/html/doctype";
659
+ case Node.ELEMENT_NODE: {
660
+ const element = node2;
661
+ if (element.id) {
662
+ return `//*[@id="${CSS.escape(element.id)}"]`;
663
+ }
664
+ if (element.tagName.toLowerCase() === "html") return "/html";
665
+ if (element === document.head) return "/html/head";
666
+ if (element === document.body) return "/html/body";
667
+ const parent = element.parentNode;
668
+ if (!parent) return "";
669
+ const tag = element.tagName.toLowerCase();
670
+ const siblings = Array.from(parent.children).filter(
671
+ (el) => el.tagName.toLowerCase() === tag
672
+ );
673
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
674
+ return `${buildXPath(parent)}/${tag}${index2}`;
675
+ }
676
+ case Node.TEXT_NODE:
677
+ case Node.CDATA_SECTION_NODE:
678
+ case Node.COMMENT_NODE: {
679
+ const parent = node2.parentNode;
680
+ if (!parent) return "";
681
+ const typeMap = {
682
+ [Node.TEXT_NODE]: "text()",
683
+ [Node.CDATA_SECTION_NODE]: "text()",
684
+ // CDATA ≡ text() в XPath
685
+ [Node.COMMENT_NODE]: "comment()"
686
+ };
687
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
688
+ (sibling) => sibling.nodeType === node2.nodeType
689
+ );
690
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
691
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
627
692
  }
628
- const commentSiblings = Array.from(parent.childNodes).filter(
629
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
630
- );
631
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
632
- return `${getXPath(parent)}/comment()${index2}`;
693
+ default:
694
+ return "";
633
695
  }
634
- return "";
635
696
  }
636
697
  function isTextVisible(n2) {
637
698
  var _a2;
@@ -647,20 +708,9 @@ function isTextVisible(n2) {
647
708
  const textContent2 = (_a2 = n2.textContent) == null ? void 0 : _a2.trim();
648
709
  return textContent2 !== "";
649
710
  }
650
- function isElementVisible(n2) {
651
- var _a2;
652
- const win = ((_a2 = n2.ownerDocument) == null ? void 0 : _a2.defaultView) ?? null;
653
- const style = win ? win.getComputedStyle(n2) : null;
654
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
655
- const rect = n2.getBoundingClientRect();
656
- const result2 = isStyleVisible && isRectVisible(rect);
657
- return result2;
658
- }
659
- function isRectVisible(rect, win = window) {
660
- var _a2, _b, _c, _d;
661
- 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;
662
- const width = (win == null ? void 0 : win.innerWidth) ?? ((_d = (_c = win == null ? void 0 : win.document) == null ? void 0 : _c.documentElement) == null ? void 0 : _d.clientWidth) ?? 0;
663
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
711
+ function isElementVisible(el) {
712
+ const visibility = index$1.getElementVisibility(el);
713
+ return visibility.isVisible;
664
714
  }
665
715
  const interactiveEvents$1 = [
666
716
  "change",
@@ -893,9 +943,6 @@ function transformAttribute(doc, tagName, name, value) {
893
943
  function ignoreAttribute(tagName, name, _value) {
894
944
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
895
945
  }
896
- function isIncludeAttribute(name, include) {
897
- return typeof include === "string" ? name.includes(include) : include.test(name);
898
- }
899
946
  function isExcludeAttribute(name, exclude) {
900
947
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
901
948
  }
@@ -1029,7 +1076,6 @@ function serializeNode(n2, options) {
1029
1076
  blockClass,
1030
1077
  blockSelector,
1031
1078
  excludeAttribute,
1032
- includeAttribute,
1033
1079
  needsMask,
1034
1080
  inlineStylesheet,
1035
1081
  maskInputOptions = {},
@@ -1043,22 +1089,19 @@ function serializeNode(n2, options) {
1043
1089
  cssCaptured = false
1044
1090
  } = options;
1045
1091
  const rootId = getRootId(doc, mirror2);
1046
- const xPath = getXPath(n2);
1047
1092
  switch (n2.nodeType) {
1048
1093
  case n2.DOCUMENT_NODE:
1049
1094
  if (n2.compatMode !== "CSS1Compat") {
1050
1095
  return {
1051
1096
  type: NodeType$3.Document,
1052
1097
  childNodes: [],
1053
- xPath,
1054
1098
  compatMode: n2.compatMode
1055
1099
  // probably "BackCompat"
1056
1100
  };
1057
1101
  } else {
1058
1102
  return {
1059
1103
  type: NodeType$3.Document,
1060
- childNodes: [],
1061
- xPath
1104
+ childNodes: []
1062
1105
  };
1063
1106
  }
1064
1107
  case n2.DOCUMENT_TYPE_NODE:
@@ -1067,8 +1110,7 @@ function serializeNode(n2, options) {
1067
1110
  name: n2.name,
1068
1111
  publicId: n2.publicId,
1069
1112
  systemId: n2.systemId,
1070
- rootId,
1071
- xPath
1113
+ rootId
1072
1114
  };
1073
1115
  case n2.ELEMENT_NODE:
1074
1116
  return serializeElementNode(n2, {
@@ -1076,7 +1118,6 @@ function serializeNode(n2, options) {
1076
1118
  blockClass,
1077
1119
  blockSelector,
1078
1120
  excludeAttribute,
1079
- includeAttribute,
1080
1121
  inlineStylesheet,
1081
1122
  maskInputOptions,
1082
1123
  maskInputFn,
@@ -1085,8 +1126,7 @@ function serializeNode(n2, options) {
1085
1126
  recordCanvas,
1086
1127
  keepIframeSrcFn,
1087
1128
  newlyAddedElement,
1088
- rootId,
1089
- xPath
1129
+ rootId
1090
1130
  });
1091
1131
  case n2.TEXT_NODE:
1092
1132
  return serializeTextNode(n2, {
@@ -1094,22 +1134,19 @@ function serializeNode(n2, options) {
1094
1134
  needsMask,
1095
1135
  maskTextFn,
1096
1136
  rootId,
1097
- cssCaptured,
1098
- xPath
1137
+ cssCaptured
1099
1138
  });
1100
1139
  case n2.CDATA_SECTION_NODE:
1101
1140
  return {
1102
1141
  type: NodeType$3.CDATA,
1103
1142
  textContent: "",
1104
- rootId,
1105
- xPath
1143
+ rootId
1106
1144
  };
1107
1145
  case n2.COMMENT_NODE:
1108
1146
  return {
1109
1147
  type: NodeType$3.Comment,
1110
1148
  textContent: index$1.textContent(n2) || "",
1111
- rootId,
1112
- xPath
1149
+ rootId
1113
1150
  };
1114
1151
  default:
1115
1152
  return false;
@@ -1121,7 +1158,7 @@ function getRootId(doc, mirror2) {
1121
1158
  return docId === 1 ? void 0 : docId;
1122
1159
  }
1123
1160
  function serializeTextNode(n2, options) {
1124
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1161
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1125
1162
  const parent = index$1.parentNode(n2);
1126
1163
  const parentTagName = parent && parent.tagName;
1127
1164
  let textContent2 = "";
@@ -1138,15 +1175,10 @@ function serializeTextNode(n2, options) {
1138
1175
  if (!isStyle && !isScript && textContent2 && needsMask) {
1139
1176
  textContent2 = maskTextFn ? maskTextFn(textContent2, index$1.parentElement(n2)) : textContent2.replace(/[\S]/g, "*");
1140
1177
  }
1141
- const isVisible = isTextVisible(n2);
1142
- const isInteractive = isElementInteractive(n2);
1143
1178
  return {
1144
1179
  type: NodeType$3.Text,
1145
1180
  textContent: textContent2 || "",
1146
- rootId,
1147
- isVisible,
1148
- isInteractive,
1149
- xPath
1181
+ rootId
1150
1182
  };
1151
1183
  }
1152
1184
  function serializeElementNode(n2, options) {
@@ -1155,7 +1187,6 @@ function serializeElementNode(n2, options) {
1155
1187
  blockClass,
1156
1188
  blockSelector,
1157
1189
  excludeAttribute,
1158
- includeAttribute,
1159
1190
  inlineStylesheet,
1160
1191
  maskInputOptions = {},
1161
1192
  maskInputFn,
@@ -1164,8 +1195,7 @@ function serializeElementNode(n2, options) {
1164
1195
  recordCanvas,
1165
1196
  keepIframeSrcFn,
1166
1197
  newlyAddedElement = false,
1167
- rootId,
1168
- xPath
1198
+ rootId
1169
1199
  } = options;
1170
1200
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1171
1201
  const tagName = getValidTagName$1(n2);
@@ -1173,7 +1203,7 @@ function serializeElementNode(n2, options) {
1173
1203
  const len = n2.attributes.length;
1174
1204
  for (let i2 = 0; i2 < len; i2++) {
1175
1205
  const attr = n2.attributes[i2];
1176
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1206
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1177
1207
  continue;
1178
1208
  }
1179
1209
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1335,8 +1365,6 @@ function serializeElementNode(n2, options) {
1335
1365
  if (customElements.get(tagName)) isCustomElement = true;
1336
1366
  } catch (e2) {
1337
1367
  }
1338
- const isVisible = isElementVisible(n2);
1339
- const isInteractive = isElementInteractive(n2);
1340
1368
  return {
1341
1369
  type: NodeType$3.Element,
1342
1370
  tagName,
@@ -1345,10 +1373,7 @@ function serializeElementNode(n2, options) {
1345
1373
  isSVG: isSVGElement(n2) || void 0,
1346
1374
  needBlock,
1347
1375
  rootId,
1348
- isCustom: isCustomElement,
1349
- isVisible,
1350
- isInteractive,
1351
- xPath
1376
+ isCustom: isCustomElement
1352
1377
  };
1353
1378
  }
1354
1379
  function lowerIfExists(maybeAttr) {
@@ -1399,7 +1424,6 @@ function serializeNodeWithId(n2, options) {
1399
1424
  maskTextClass,
1400
1425
  maskTextSelector,
1401
1426
  excludeAttribute,
1402
- includeAttribute,
1403
1427
  skipChild = false,
1404
1428
  inlineStylesheet = true,
1405
1429
  maskInputOptions = {},
@@ -1435,7 +1459,6 @@ function serializeNodeWithId(n2, options) {
1435
1459
  blockClass,
1436
1460
  blockSelector,
1437
1461
  excludeAttribute,
1438
- includeAttribute,
1439
1462
  needsMask,
1440
1463
  inlineStylesheet,
1441
1464
  maskInputOptions,
@@ -1461,6 +1484,22 @@ function serializeNodeWithId(n2, options) {
1461
1484
  id = genId();
1462
1485
  }
1463
1486
  const serializedNode = Object.assign(_serializedNode, { id });
1487
+ if (isElement(n2) || n2.nodeType === Node.TEXT_NODE) {
1488
+ serializedNode.xpath = buildXPath(n2);
1489
+ if (isElement(n2)) {
1490
+ const selector = buildSelector(n2);
1491
+ if (selector) {
1492
+ serializedNode.selector = selector;
1493
+ }
1494
+ }
1495
+ if (n2.nodeType === Node.TEXT_NODE) {
1496
+ serializedNode.isVisible = isTextVisible(n2);
1497
+ }
1498
+ if (n2.nodeType === Node.ELEMENT_NODE) {
1499
+ serializedNode.isVisible = isElementVisible(n2);
1500
+ serializedNode.isInteractive = isElementInteractive(n2);
1501
+ }
1502
+ }
1464
1503
  mirror2.add(n2, serializedNode);
1465
1504
  if (id === IGNORED_NODE) {
1466
1505
  return null;
@@ -1489,7 +1528,6 @@ function serializeNodeWithId(n2, options) {
1489
1528
  maskTextClass,
1490
1529
  maskTextSelector,
1491
1530
  excludeAttribute,
1492
- includeAttribute,
1493
1531
  skipChild,
1494
1532
  inlineStylesheet,
1495
1533
  maskInputOptions,
@@ -1550,7 +1588,6 @@ function serializeNodeWithId(n2, options) {
1550
1588
  maskTextClass,
1551
1589
  maskTextSelector,
1552
1590
  excludeAttribute,
1553
- includeAttribute,
1554
1591
  skipChild: false,
1555
1592
  inlineStylesheet,
1556
1593
  maskInputOptions,
@@ -1593,7 +1630,6 @@ function serializeNodeWithId(n2, options) {
1593
1630
  maskTextClass,
1594
1631
  maskTextSelector,
1595
1632
  excludeAttribute,
1596
- includeAttribute,
1597
1633
  skipChild: false,
1598
1634
  inlineStylesheet,
1599
1635
  maskInputOptions,
@@ -1631,8 +1667,7 @@ function snapshot(n2, options) {
1631
1667
  blockSelector = null,
1632
1668
  maskTextClass = "rr-mask",
1633
1669
  maskTextSelector = null,
1634
- excludeAttribute = /^$a/,
1635
- includeAttribute = /.+/i,
1670
+ excludeAttribute = /.^/,
1636
1671
  inlineStylesheet = true,
1637
1672
  inlineImages = false,
1638
1673
  recordCanvas = false,
@@ -1693,7 +1728,6 @@ function snapshot(n2, options) {
1693
1728
  maskTextClass,
1694
1729
  maskTextSelector,
1695
1730
  excludeAttribute,
1696
- includeAttribute,
1697
1731
  skipChild: false,
1698
1732
  inlineStylesheet,
1699
1733
  maskInputOptions,
@@ -10832,6 +10866,41 @@ function patch(source, name, replacement) {
10832
10866
  };
10833
10867
  }
10834
10868
  }
10869
+ function describeNode(el) {
10870
+ const tag = el.tagName.toLowerCase();
10871
+ const id = el.id ? `#${el.id}` : "";
10872
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
10873
+ return `${tag}${id}${classes}`;
10874
+ }
10875
+ function getElementVisibility(el) {
10876
+ var _a2, _b;
10877
+ const win = ((_a2 = el.ownerDocument) == null ? void 0 : _a2.defaultView) ?? window;
10878
+ const rect = el.getBoundingClientRect();
10879
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
10880
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
10881
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
10882
+ const style = (_b = win.getComputedStyle) == null ? void 0 : _b.call(win, el);
10883
+ const isStyleVisible2 = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
10884
+ const isVisible = isStyleVisible2 && isRectVisible;
10885
+ let ratio = 0;
10886
+ if (isVisible) {
10887
+ const xOverlap = Math.max(
10888
+ 0,
10889
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
10890
+ );
10891
+ const yOverlap = Math.max(
10892
+ 0,
10893
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
10894
+ );
10895
+ const intersectionArea = xOverlap * yOverlap;
10896
+ const elementArea = rect.width * rect.height;
10897
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
10898
+ }
10899
+ return {
10900
+ isVisible,
10901
+ ratio
10902
+ };
10903
+ }
10835
10904
  const index = {
10836
10905
  childNodes,
10837
10906
  parentNode,
@@ -10845,7 +10914,9 @@ const index = {
10845
10914
  querySelector,
10846
10915
  querySelectorAll,
10847
10916
  mutationObserver: mutationObserverCtor,
10848
- patch
10917
+ patch,
10918
+ describeNode,
10919
+ getElementVisibility
10849
10920
  };
10850
10921
  function on(type, fn, target = document) {
10851
10922
  const options = { capture: true, passive: true };
@@ -11246,6 +11317,7 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
11246
11317
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
11247
11318
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
11248
11319
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
11320
+ IncrementalSource2[IncrementalSource2["VisibilityMutation"] = 17] = "VisibilityMutation";
11249
11321
  return IncrementalSource2;
11250
11322
  })(IncrementalSource || {});
11251
11323
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -11406,11 +11478,11 @@ class MutationBuffer {
11406
11478
  * the browser MutationObserver emits multiple mutations after
11407
11479
  * a delay for performance reasons, making tracing added nodes hard
11408
11480
  * in our `processMutations` callback function.
11409
- * For example, if we append an element el_1 into body, and then append
11481
+ * For example, if we append an element el_1 into body and then append
11410
11482
  * another element el_2 into el_1, these two mutations may be passed to the
11411
11483
  * callback function together when the two operations were done.
11412
- * Generally we need to trace child nodes of newly added nodes, but in this
11413
- * case if we count el_2 as el_1's child node in the first mutation record,
11484
+ * Generally, we need to trace child nodes of newly added nodes, but in this
11485
+ * case, if we count el_2 as el_1's child node in the first mutation record,
11414
11486
  * then we will count el_2 again in the second mutation record which was
11415
11487
  * duplicated.
11416
11488
  * To avoid of duplicate counting added nodes, we use a Set to store
@@ -11429,7 +11501,6 @@ class MutationBuffer {
11429
11501
  __publicField(this, "maskTextClass");
11430
11502
  __publicField(this, "maskTextSelector");
11431
11503
  __publicField(this, "excludeAttribute");
11432
- __publicField(this, "includeAttribute");
11433
11504
  __publicField(this, "inlineStylesheet");
11434
11505
  __publicField(this, "maskInputOptions");
11435
11506
  __publicField(this, "maskTextFn");
@@ -11445,6 +11516,7 @@ class MutationBuffer {
11445
11516
  __publicField(this, "stylesheetManager");
11446
11517
  __publicField(this, "shadowDomManager");
11447
11518
  __publicField(this, "canvasManager");
11519
+ __publicField(this, "visibilityManager");
11448
11520
  __publicField(this, "processedNodeManager");
11449
11521
  __publicField(this, "unattachedDoc");
11450
11522
  __publicField(this, "processMutations", (mutations) => {
@@ -11494,7 +11566,6 @@ class MutationBuffer {
11494
11566
  maskTextClass: this.maskTextClass,
11495
11567
  maskTextSelector: this.maskTextSelector,
11496
11568
  excludeAttribute: this.excludeAttribute,
11497
- includeAttribute: this.includeAttribute,
11498
11569
  skipChild: true,
11499
11570
  newlyAddedElement: true,
11500
11571
  inlineStylesheet: this.inlineStylesheet,
@@ -11540,7 +11611,8 @@ class MutationBuffer {
11540
11611
  this.mirror.removeNodeFromMap(this.mapRemoves.shift());
11541
11612
  }
11542
11613
  for (const n2 of this.movedSet) {
11543
- if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && !this.movedSet.has(index.parentNode(n2))) {
11614
+ if (isParentRemoved(this.removesSubTreeCache, n2, this.mirror) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11615
+ !this.movedSet.has(index.parentNode(n2))) {
11544
11616
  continue;
11545
11617
  }
11546
11618
  pushAdd(n2);
@@ -11696,12 +11768,60 @@ class MutationBuffer {
11696
11768
  const target = m.target;
11697
11769
  let attributeName = m.attributeName;
11698
11770
  let value = m.target.getAttribute(attributeName);
11699
- const propValue = target[attributeName];
11700
- const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
11771
+ const attrKey = attributeName;
11772
+ const propValue = target[attrKey];
11773
+ const isBooleanAttr = typeof propValue === "boolean";
11774
+ const inDOM = document.contains(target);
11775
+ const isVisible = isElementVisible(target);
11776
+ const isExcludeAttributeName = isExcludeAttribute(attributeName, this.excludeAttribute);
11777
+ const isPhantomAttributeMutation = value === null && // текущего атрибута нет
11778
+ !target.hasAttribute(attributeName) && // явно подтверждаем отсутствие
11779
+ m.oldValue !== null && // раньше он был
11780
+ (propValue === "" || // свойство = пустая строка
11781
+ propValue === null || // или null
11782
+ typeof propValue === "undefined");
11701
11783
  if (isPhantomAttributeMutation) {
11784
+ console.debug(
11785
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ phantom attribute mutation ignored`,
11786
+ {
11787
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11788
+ node: index.describeNode(target),
11789
+ tag: target.tagName,
11790
+ nodeType: target.nodeType,
11791
+ attribute: attributeName,
11792
+ value,
11793
+ oldValue: m.oldValue,
11794
+ excludeAttribute: this.excludeAttribute,
11795
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11796
+ propValue,
11797
+ isBooleanAttr,
11798
+ inDOM,
11799
+ isVisible,
11800
+ isExcludeAttributeName
11801
+ }
11802
+ );
11702
11803
  return;
11703
11804
  }
11704
11805
  if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
11806
+ console.debug(
11807
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⛔ excluded attribute mutation ignored`,
11808
+ {
11809
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11810
+ node: index.describeNode(target),
11811
+ tag: target.tagName,
11812
+ nodeType: target.nodeType,
11813
+ attribute: attributeName,
11814
+ value,
11815
+ oldValue: m.oldValue,
11816
+ excludeAttribute: this.excludeAttribute,
11817
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11818
+ propValue,
11819
+ isBooleanAttr,
11820
+ inDOM,
11821
+ isVisible,
11822
+ isExcludeAttributeName
11823
+ }
11824
+ );
11705
11825
  return;
11706
11826
  }
11707
11827
  if (attributeName === "value") {
@@ -11746,9 +11866,35 @@ class MutationBuffer {
11746
11866
  toLowerCase(attributeName),
11747
11867
  value
11748
11868
  );
11749
- if (value === item.attributes[attributeName]) {
11750
- console.debug("[rrweb-record] A questionable mutation that needs to be investigated in the future.");
11751
- return;
11869
+ const isSuspiciousClassMutation = attributeName !== "class" && (m.oldValue === null || // ранее не было класса
11870
+ value === "" || // класс удалён
11871
+ value !== m.oldValue);
11872
+ if (isSuspiciousClassMutation) {
11873
+ console.debug(
11874
+ `[${nowTimestamp()}] [rrweb:record/mutation] ⚠️ suspicious attribute mutation`,
11875
+ {
11876
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
11877
+ reason: [
11878
+ value === m.oldValue ? "no change in value" : null,
11879
+ value === propValue ? "mirrored in DOM property" : null,
11880
+ value === item.attributes[attributeName] ? "redundant assignment" : null
11881
+ ].filter(Boolean).join(", ") || "uncategorized",
11882
+ node: index.describeNode(target),
11883
+ tag: target.tagName,
11884
+ nodeType: target.nodeType,
11885
+ attribute: attributeName,
11886
+ value,
11887
+ oldValue: m.oldValue,
11888
+ transformedValue: item.attributes[attributeName],
11889
+ excludeAttribute: this.excludeAttribute,
11890
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11891
+ propValue,
11892
+ isBooleanAttr,
11893
+ inDOM,
11894
+ isVisible,
11895
+ isExcludeAttributeName
11896
+ }
11897
+ );
11752
11898
  }
11753
11899
  if (attributeName === "style") {
11754
11900
  if (!this.unattachedDoc) {
@@ -11866,7 +12012,6 @@ class MutationBuffer {
11866
12012
  "maskTextClass",
11867
12013
  "maskTextSelector",
11868
12014
  "excludeAttribute",
11869
- "includeAttribute",
11870
12015
  "inlineStylesheet",
11871
12016
  "maskInputOptions",
11872
12017
  "maskTextFn",
@@ -11882,6 +12027,7 @@ class MutationBuffer {
11882
12027
  "stylesheetManager",
11883
12028
  "shadowDomManager",
11884
12029
  "canvasManager",
12030
+ "visibilityManager",
11885
12031
  "processedNodeManager"
11886
12032
  ].forEach((key) => {
11887
12033
  this[key] = options[key];
@@ -11890,10 +12036,12 @@ class MutationBuffer {
11890
12036
  freeze() {
11891
12037
  this.frozen = true;
11892
12038
  this.canvasManager.freeze();
12039
+ this.visibilityManager.freeze();
11893
12040
  }
11894
12041
  unfreeze() {
11895
12042
  this.frozen = false;
11896
12043
  this.canvasManager.unfreeze();
12044
+ this.visibilityManager.unfreeze();
11897
12045
  this.emit();
11898
12046
  }
11899
12047
  isFrozen() {
@@ -11902,15 +12050,18 @@ class MutationBuffer {
11902
12050
  lock() {
11903
12051
  this.locked = true;
11904
12052
  this.canvasManager.lock();
12053
+ this.visibilityManager.lock();
11905
12054
  }
11906
12055
  unlock() {
11907
12056
  this.locked = false;
11908
12057
  this.canvasManager.unlock();
12058
+ this.visibilityManager.unlock();
11909
12059
  this.emit();
11910
12060
  }
11911
12061
  reset() {
11912
12062
  this.shadowDomManager.reset();
11913
12063
  this.canvasManager.reset();
12064
+ this.visibilityManager.reset();
11914
12065
  }
11915
12066
  }
11916
12067
  function deepDelete(addsSet, n2) {
@@ -12844,6 +12995,7 @@ function mergeHooks(o2, hooks) {
12844
12995
  styleSheetRuleCb,
12845
12996
  styleDeclarationCb,
12846
12997
  canvasMutationCb,
12998
+ visibilityMutationCb,
12847
12999
  fontCb,
12848
13000
  selectionCb,
12849
13001
  customElementCb
@@ -12908,6 +13060,12 @@ function mergeHooks(o2, hooks) {
12908
13060
  }
12909
13061
  canvasMutationCb(...p);
12910
13062
  };
13063
+ o2.visibilityMutationCb = (...p) => {
13064
+ if (hooks.visibilityMutation) {
13065
+ hooks.visibilityMutation(...p);
13066
+ }
13067
+ visibilityMutationCb(...p);
13068
+ };
12911
13069
  o2.fontCb = (...p) => {
12912
13070
  if (hooks.font) {
12913
13071
  hooks.font(...p);
@@ -14041,11 +14199,249 @@ class ProcessedNodeManager {
14041
14199
  destroy() {
14042
14200
  }
14043
14201
  }
14202
+ function computeVisibility(elements, previous, options) {
14203
+ const root2 = (options == null ? void 0 : options.root) ?? null;
14204
+ const threshold = (options == null ? void 0 : options.threshold) ?? 0.5;
14205
+ const sensitivity = (options == null ? void 0 : options.sensitivity) ?? 0.05;
14206
+ const rootMarginFn = parseRootMargin((options == null ? void 0 : options.rootMargin) ?? "0px");
14207
+ const current = /* @__PURE__ */ new Map();
14208
+ const rootRect = getRootRect(root2);
14209
+ const expandedRoot = expandRootRect(rootRect, rootMarginFn);
14210
+ for (const el of elements) {
14211
+ const elRect = el.getBoundingClientRect();
14212
+ let intersectionRect = emptyRect();
14213
+ let intersectionRatio = 0;
14214
+ if (elRect.width > 0 && elRect.height > 0) {
14215
+ intersectionRect = computeIntersectionRect(elRect, expandedRoot);
14216
+ intersectionRatio = computeIntersectionRatio(elRect, intersectionRect);
14217
+ intersectionRatio = Math.round(intersectionRatio * 100) / 100;
14218
+ }
14219
+ const isStyle = isStyleVisible(el);
14220
+ const old = previous.get(el) ?? null;
14221
+ const prevRatio = (old == null ? void 0 : old.intersectionRatio) ?? 0;
14222
+ const currRatio = intersectionRatio;
14223
+ const wasVisible = (old == null ? void 0 : old.isStyleVisible) && prevRatio > threshold;
14224
+ const nowVisible = isStyle && currRatio > threshold;
14225
+ const changed = !old || wasVisible !== nowVisible || wasVisible !== nowVisible && Math.abs(currRatio - prevRatio) > sensitivity;
14226
+ if (changed) {
14227
+ current.set(el, {
14228
+ target: el,
14229
+ isVisible: nowVisible,
14230
+ isStyleVisible: isStyle,
14231
+ intersectionRatio: currRatio,
14232
+ intersectionRect,
14233
+ oldValue: old
14234
+ });
14235
+ } else {
14236
+ current.set(el, old);
14237
+ }
14238
+ }
14239
+ return current;
14240
+ }
14241
+ function parseRootMargin(marginStr) {
14242
+ const parts = marginStr.trim().split(/\s+/);
14243
+ const getValue = (val, size) => val.endsWith("%") ? parseFloat(val) / 100 * size : parseFloat(val) || 0;
14244
+ return function(rootRect) {
14245
+ const top = getValue(parts[0] || "0px", rootRect.height);
14246
+ const right = getValue(parts[1] || parts[0] || "0px", rootRect.width);
14247
+ const bottom = getValue(parts[2] || parts[0] || "0px", rootRect.height);
14248
+ const left = getValue(parts[3] || parts[1] || parts[0] || "0px", rootRect.width);
14249
+ return { top, right, bottom, left, width: 0, height: 0 };
14250
+ };
14251
+ }
14252
+ function getRootRect(root2) {
14253
+ return root2 ? root2.getBoundingClientRect() : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
14254
+ }
14255
+ function expandRootRect(rect, marginFn) {
14256
+ const margin = marginFn(rect);
14257
+ return new DOMRect(
14258
+ rect.left - margin.left,
14259
+ rect.top - margin.top,
14260
+ rect.width + margin.left + margin.right,
14261
+ rect.height + margin.top + margin.bottom
14262
+ );
14263
+ }
14264
+ function computeIntersectionRect(a2, b) {
14265
+ const top = Math.max(a2.top, b.top);
14266
+ const left = Math.max(a2.left, b.left);
14267
+ const bottom = Math.min(a2.bottom, b.bottom);
14268
+ const right = Math.min(a2.right, b.right);
14269
+ const width = Math.max(0, right - left);
14270
+ const height = Math.max(0, bottom - top);
14271
+ return { top, left, bottom, right, width, height };
14272
+ }
14273
+ function computeIntersectionRatio(elRect, intersectionRect) {
14274
+ const elArea = elRect.width * elRect.height;
14275
+ const intArea = intersectionRect.width * intersectionRect.height;
14276
+ return elArea > 0 ? intArea / elArea : 0;
14277
+ }
14278
+ function emptyRect() {
14279
+ return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
14280
+ }
14281
+ function isStyleVisible(el) {
14282
+ const style = getComputedStyle(el);
14283
+ return style && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity || "1") > 0;
14284
+ }
14285
+ class VisibilityManager {
14286
+ constructor(options) {
14287
+ __publicField(this, "frozen", false);
14288
+ __publicField(this, "locked", false);
14289
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
14290
+ __publicField(this, "mirror");
14291
+ __publicField(this, "mutationCb");
14292
+ __publicField(this, "rafId", null);
14293
+ __publicField(this, "rafThrottle");
14294
+ __publicField(this, "lastFlushTime", 0);
14295
+ __publicField(this, "elements", /* @__PURE__ */ new Set());
14296
+ __publicField(this, "previousState", /* @__PURE__ */ new Map());
14297
+ __publicField(this, "root", null);
14298
+ __publicField(this, "threshold");
14299
+ __publicField(this, "sensitivity");
14300
+ __publicField(this, "rootMargin");
14301
+ __publicField(this, "hasInitialized", false);
14302
+ __publicField(this, "mode", "none");
14303
+ __publicField(this, "debounce", 50);
14304
+ __publicField(this, "throttle", 100);
14305
+ __publicField(this, "buffer", /* @__PURE__ */ new Map());
14306
+ __publicField(this, "debounceTimer", null);
14307
+ __publicField(this, "lastThrottleTime", 0);
14308
+ __publicField(this, "disabled", false);
14309
+ __publicField(this, "notifyActivity");
14310
+ const { doc, mirror: mirror2, sampling, mutationCb, notifyActivity } = options;
14311
+ this.mirror = mirror2;
14312
+ this.mutationCb = mutationCb;
14313
+ this.notifyActivity = notifyActivity;
14314
+ this.rootMargin = "0px";
14315
+ if (sampling === false) {
14316
+ this.disabled = true;
14317
+ return;
14318
+ }
14319
+ const visibilitySampling = typeof sampling === "object" && sampling !== null ? sampling : {};
14320
+ this.mode = (visibilitySampling == null ? void 0 : visibilitySampling.mode) ?? "none";
14321
+ this.debounce = Number((visibilitySampling == null ? void 0 : visibilitySampling.debounce) ?? 100);
14322
+ this.throttle = Number((visibilitySampling == null ? void 0 : visibilitySampling.throttle) ?? 100);
14323
+ this.threshold = Number((visibilitySampling == null ? void 0 : visibilitySampling.threshold) ?? 0.5);
14324
+ this.sensitivity = Number((visibilitySampling == null ? void 0 : visibilitySampling.sensitivity) ?? 0.05);
14325
+ this.rafThrottle = Number((visibilitySampling == null ? void 0 : visibilitySampling.rafThrottle) ?? 100);
14326
+ doc.querySelectorAll("*").forEach((el) => this.observe(el));
14327
+ const mo = new MutationObserver((mutations) => {
14328
+ mutations.forEach((m) => {
14329
+ m.addedNodes.forEach((n2) => {
14330
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14331
+ this.observe(n2);
14332
+ n2.querySelectorAll("*").forEach((el) => this.observe(el));
14333
+ }
14334
+ });
14335
+ m.removedNodes.forEach((n2) => {
14336
+ if (n2.nodeType === Node.ELEMENT_NODE) {
14337
+ this.unobserve(n2);
14338
+ }
14339
+ });
14340
+ });
14341
+ });
14342
+ mo.observe(doc.body, { childList: true, subtree: true });
14343
+ this.startPendingFlushLoop();
14344
+ }
14345
+ startPendingFlushLoop() {
14346
+ if (this.disabled) return;
14347
+ const loop = (timestamp) => {
14348
+ if (timestamp - this.lastFlushTime >= this.rafThrottle) {
14349
+ this.lastFlushTime = timestamp;
14350
+ this.flushPendingVisibilityMutations();
14351
+ }
14352
+ this.rafId = requestAnimationFrame(loop);
14353
+ };
14354
+ this.rafId = requestAnimationFrame(loop);
14355
+ }
14356
+ flushPendingVisibilityMutations() {
14357
+ if (this.disabled) return;
14358
+ if (this.frozen || this.locked || this.elements.size === 0) return;
14359
+ const state = computeVisibility(this.elements, this.previousState, {
14360
+ root: this.root,
14361
+ threshold: this.threshold,
14362
+ sensitivity: this.sensitivity,
14363
+ rootMargin: this.rootMargin
14364
+ });
14365
+ for (const [el, entry] of state.entries()) {
14366
+ const old = this.previousState.get(el);
14367
+ const changed = !old || old.isVisible !== entry.isVisible || Math.abs(old.intersectionRatio - entry.intersectionRatio) > this.sensitivity;
14368
+ if (changed) {
14369
+ const id = this.mirror.getId(el);
14370
+ if (id !== -1) {
14371
+ this.buffer.set(el, {
14372
+ id,
14373
+ isVisible: entry.isVisible,
14374
+ ratio: entry.intersectionRatio
14375
+ });
14376
+ }
14377
+ this.previousState.set(el, entry);
14378
+ }
14379
+ }
14380
+ this.previousState = state;
14381
+ if (!this.hasInitialized) {
14382
+ this.hasInitialized = true;
14383
+ this.buffer.clear();
14384
+ return;
14385
+ }
14386
+ this.scheduleEmit();
14387
+ }
14388
+ scheduleEmit() {
14389
+ if (this.mode === "debounce") {
14390
+ clearTimeout(this.debounceTimer);
14391
+ this.debounceTimer = setTimeout(() => this.flushBuffer(), this.debounce);
14392
+ } else if (this.mode === "throttle") {
14393
+ const now = performance.now();
14394
+ if (now - this.lastThrottleTime >= this.throttle) {
14395
+ this.lastThrottleTime = now;
14396
+ this.flushBuffer();
14397
+ }
14398
+ } else {
14399
+ this.flushBuffer();
14400
+ }
14401
+ }
14402
+ flushBuffer() {
14403
+ var _a2;
14404
+ if (this.buffer.size === 0) return;
14405
+ (_a2 = this.notifyActivity) == null ? void 0 : _a2.call(this, this.buffer.size);
14406
+ this.mutationCb({ mutations: Array.from(this.buffer.values()) });
14407
+ this.buffer.clear();
14408
+ }
14409
+ observe(el) {
14410
+ if (this.disabled) return;
14411
+ this.elements.add(el);
14412
+ }
14413
+ unobserve(el) {
14414
+ if (this.disabled) return;
14415
+ this.elements.delete(el);
14416
+ this.previousState.delete(el);
14417
+ this.pending.delete(el);
14418
+ }
14419
+ freeze() {
14420
+ this.frozen = true;
14421
+ }
14422
+ unfreeze() {
14423
+ this.frozen = false;
14424
+ }
14425
+ lock() {
14426
+ this.locked = true;
14427
+ }
14428
+ unlock() {
14429
+ this.locked = false;
14430
+ }
14431
+ reset() {
14432
+ this.elements.clear();
14433
+ this.previousState.clear();
14434
+ this.pending.clear();
14435
+ if (this.rafId) cancelAnimationFrame(this.rafId);
14436
+ }
14437
+ }
14044
14438
  let wrappedEmit;
14045
14439
  let takeFullSnapshot$1;
14046
14440
  let canvasManager;
14441
+ let visibilityManager;
14047
14442
  let recording = false;
14048
- const preRecordingCustomEvents = [];
14443
+ const customEventQueue = [];
14444
+ let flushCustomEventQueue;
14049
14445
  try {
14050
14446
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
14051
14447
  const cleanFrame = document.createElement("iframe");
@@ -14062,12 +14458,12 @@ function record(options = {}) {
14062
14458
  emit,
14063
14459
  checkoutEveryNms,
14064
14460
  checkoutEveryNth,
14461
+ checkoutEveryNvm,
14065
14462
  blockClass = "rr-block",
14066
14463
  blockSelector = null,
14067
14464
  ignoreClass = "rr-ignore",
14068
14465
  ignoreSelector = null,
14069
14466
  excludeAttribute: _excludeAttribute,
14070
- includeAttribute: _includeAttribute,
14071
14467
  maskTextClass = "rr-mask",
14072
14468
  maskTextSelector = null,
14073
14469
  inlineStylesheet = true,
@@ -14085,7 +14481,7 @@ function record(options = {}) {
14085
14481
  recordCanvas = false,
14086
14482
  recordCrossOriginIframes = false,
14087
14483
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
14088
- flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
14484
+ flushCustomEvent = options.flushCustomEvent !== void 0 ? options.flushCustomEvent : "after",
14089
14485
  userTriggeredOnInput = false,
14090
14486
  collectFonts = false,
14091
14487
  inlineImages = false,
@@ -14117,8 +14513,7 @@ function record(options = {}) {
14117
14513
  sampling.mousemove = mousemoveWait;
14118
14514
  }
14119
14515
  mirror.reset();
14120
- const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
14121
- const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
14516
+ const excludeAttribute = _excludeAttribute === void 0 ? /.^/ : _excludeAttribute;
14122
14517
  const maskInputOptions = maskAllInputs === true ? {
14123
14518
  color: true,
14124
14519
  date: true,
@@ -14155,6 +14550,10 @@ function record(options = {}) {
14155
14550
  polyfill$1();
14156
14551
  let lastFullSnapshotEvent;
14157
14552
  let incrementalSnapshotCount = 0;
14553
+ let recentVisibilityChanges = 0;
14554
+ const onVisibilityActivity = (count) => {
14555
+ recentVisibilityChanges += count;
14556
+ };
14158
14557
  const eventProcessor = (e2) => {
14159
14558
  for (const plugin3 of plugins || []) {
14160
14559
  if (plugin3.eventProcessor) {
@@ -14195,7 +14594,11 @@ function record(options = {}) {
14195
14594
  incrementalSnapshotCount++;
14196
14595
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
14197
14596
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
14198
- if (exceedCount || exceedTime) {
14597
+ const exceedVisibility = checkoutEveryNvm && recentVisibilityChanges >= checkoutEveryNvm;
14598
+ if (exceedCount || exceedTime || exceedVisibility) {
14599
+ if (exceedVisibility) {
14600
+ recentVisibilityChanges = 0;
14601
+ }
14199
14602
  takeFullSnapshot$1(true);
14200
14603
  }
14201
14604
  }
@@ -14223,6 +14626,17 @@ function record(options = {}) {
14223
14626
  ...p
14224
14627
  }
14225
14628
  });
14629
+ const wrappedVisibilityMutationEmit = (p) => {
14630
+ var _a2;
14631
+ (_a2 = hooks == null ? void 0 : hooks.visibilityMutation) == null ? void 0 : _a2.call(hooks, p);
14632
+ wrappedEmit({
14633
+ type: EventType.IncrementalSnapshot,
14634
+ data: {
14635
+ source: IncrementalSource.VisibilityMutation,
14636
+ ...p
14637
+ }
14638
+ });
14639
+ };
14226
14640
  const wrappedAdoptedStyleSheetEmit = (a2) => wrappedEmit({
14227
14641
  type: EventType.IncrementalSnapshot,
14228
14642
  data: {
@@ -14260,6 +14674,13 @@ function record(options = {}) {
14260
14674
  sampling: sampling.canvas,
14261
14675
  dataURLOptions
14262
14676
  });
14677
+ visibilityManager = new VisibilityManager({
14678
+ doc: window.document,
14679
+ mirror,
14680
+ sampling: sampling.visibility,
14681
+ mutationCb: wrappedVisibilityMutationEmit,
14682
+ notifyActivity: onVisibilityActivity
14683
+ });
14263
14684
  const shadowDomManager = new ShadowDomManager({
14264
14685
  mutationCb: wrappedMutationEmit,
14265
14686
  scrollCb: wrappedScrollEmit,
@@ -14269,7 +14690,6 @@ function record(options = {}) {
14269
14690
  maskTextClass,
14270
14691
  maskTextSelector,
14271
14692
  excludeAttribute,
14272
- includeAttribute,
14273
14693
  inlineStylesheet,
14274
14694
  maskInputOptions,
14275
14695
  dataURLOptions,
@@ -14282,6 +14702,7 @@ function record(options = {}) {
14282
14702
  iframeManager,
14283
14703
  stylesheetManager,
14284
14704
  canvasManager,
14705
+ visibilityManager,
14285
14706
  keepIframeSrcFn,
14286
14707
  processedNodeManager
14287
14708
  },
@@ -14312,7 +14733,6 @@ function record(options = {}) {
14312
14733
  maskTextClass,
14313
14734
  maskTextSelector,
14314
14735
  excludeAttribute,
14315
- includeAttribute,
14316
14736
  inlineStylesheet,
14317
14737
  maskAllInputs: maskInputOptions,
14318
14738
  maskTextFn,
@@ -14361,6 +14781,12 @@ function record(options = {}) {
14361
14781
  mirror.getId(document)
14362
14782
  );
14363
14783
  };
14784
+ flushCustomEventQueue = () => {
14785
+ for (const e2 of customEventQueue) {
14786
+ wrappedEmit(e2);
14787
+ }
14788
+ customEventQueue.length = 0;
14789
+ };
14364
14790
  try {
14365
14791
  const handlers = [];
14366
14792
  const observe = (doc) => {
@@ -14419,6 +14845,7 @@ function record(options = {}) {
14419
14845
  }
14420
14846
  }),
14421
14847
  canvasMutationCb: wrappedCanvasMutationEmit,
14848
+ visibilityMutationCb: wrappedVisibilityMutationEmit,
14422
14849
  fontCb: (p) => wrappedEmit({
14423
14850
  type: EventType.IncrementalSnapshot,
14424
14851
  data: {
@@ -14450,7 +14877,6 @@ function record(options = {}) {
14450
14877
  maskTextClass,
14451
14878
  maskTextSelector,
14452
14879
  excludeAttribute,
14453
- includeAttribute,
14454
14880
  maskInputOptions,
14455
14881
  inlineStylesheet,
14456
14882
  sampling,
@@ -14472,6 +14898,7 @@ function record(options = {}) {
14472
14898
  shadowDomManager,
14473
14899
  processedNodeManager,
14474
14900
  canvasManager,
14901
+ visibilityManager,
14475
14902
  ignoreCSSAttributes,
14476
14903
  plugins: ((_a2 = plugins == null ? void 0 : plugins.filter((p) => p.observer)) == null ? void 0 : _a2.map((p) => ({
14477
14904
  observer: p.observer,
@@ -14496,14 +14923,14 @@ function record(options = {}) {
14496
14923
  }
14497
14924
  });
14498
14925
  const init = () => {
14499
- if (flushCustomQueue === "before") {
14500
- flushPreRecordingEvents();
14926
+ if (flushCustomEvent === "before") {
14927
+ flushCustomEventQueue();
14501
14928
  }
14502
14929
  takeFullSnapshot$1();
14503
14930
  handlers.push(observe(document));
14504
14931
  recording = true;
14505
- if (flushCustomQueue === "after") {
14506
- flushPreRecordingEvents();
14932
+ if (flushCustomEvent === "after") {
14933
+ flushCustomEventQueue();
14507
14934
  }
14508
14935
  };
14509
14936
  if (document.readyState === "interactive" || document.readyState === "complete") {
@@ -14533,7 +14960,7 @@ function record(options = {}) {
14533
14960
  );
14534
14961
  }
14535
14962
  return () => {
14536
- flushPreRecordingEvents();
14963
+ flushCustomEventQueue();
14537
14964
  handlers.forEach((h) => h());
14538
14965
  processedNodeManager.destroy();
14539
14966
  recording = false;
@@ -14543,12 +14970,10 @@ function record(options = {}) {
14543
14970
  console.warn(error);
14544
14971
  }
14545
14972
  }
14546
- function flushPreRecordingEvents() {
14547
- for (const e2 of preRecordingCustomEvents) {
14548
- wrappedEmit(e2);
14549
- }
14550
- preRecordingCustomEvents.length = 0;
14551
- }
14973
+ record.flushCustomEventQueue = () => {
14974
+ console.warn(`[rrweb] CustomEvent flushing: ${customEventQueue.length} events`);
14975
+ flushCustomEventQueue();
14976
+ };
14552
14977
  record.addCustomEvent = (tag, payload) => {
14553
14978
  const customEvent = {
14554
14979
  type: EventType.Custom,
@@ -14558,8 +14983,8 @@ record.addCustomEvent = (tag, payload) => {
14558
14983
  }
14559
14984
  };
14560
14985
  if (!recording) {
14561
- console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
14562
- preRecordingCustomEvents.push(customEvent);
14986
+ console.warn(`[rrweb] CustomEvent buffered before/after recording start: ${tag}`);
14987
+ customEventQueue.push(customEvent);
14563
14988
  return;
14564
14989
  }
14565
14990
  wrappedEmit(customEvent);