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