clarity-js 0.7.70 → 0.8.1

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/build/clarity.js CHANGED
@@ -1,5 +1,25 @@
1
1
  'use strict';
2
2
 
3
+ var dom = /*#__PURE__*/Object.freeze({
4
+ __proto__: null,
5
+ get add () { return add; },
6
+ get get () { return get; },
7
+ get getId () { return getId; },
8
+ get getNode () { return getNode; },
9
+ get getValue () { return getValue; },
10
+ get has () { return has$1; },
11
+ get hashText () { return hashText; },
12
+ get iframe () { return iframe; },
13
+ get iframeContent () { return iframeContent; },
14
+ get lookup () { return lookup; },
15
+ get parse () { return parse$1; },
16
+ get removeIFrame () { return removeIFrame; },
17
+ get sameorigin () { return sameorigin; },
18
+ get start () { return start$i; },
19
+ get stop () { return stop$g; },
20
+ get update () { return update$1; },
21
+ get updates () { return updates$2; }
22
+ });
3
23
  var upload$1 = /*#__PURE__*/Object.freeze({
4
24
  __proto__: null,
5
25
  get queue () { return queue; },
@@ -169,7 +189,7 @@ function stop$F() {
169
189
  startTime = 0;
170
190
  }
171
191
 
172
- var version$1 = "0.7.70";
192
+ var version$1 = "0.8.1";
173
193
 
174
194
  // tslint:disable: no-bitwise
175
195
  function hash (input, precision) {
@@ -432,19 +452,19 @@ function reset$s() {
432
452
  pointerY: 0,
433
453
  activityTime: 0,
434
454
  scrollTime: 0,
435
- pointerTime: 0,
436
- moveX: 0,
437
- moveY: 0,
438
- moveTime: 0,
439
- downX: 0,
440
- downY: 0,
441
- downTime: 0,
442
- upX: 0,
443
- upY: 0,
444
- upTime: 0,
445
- pointerPrevX: 0,
446
- pointerPrevY: 0,
447
- pointerPrevTime: 0,
455
+ pointerTime: undefined,
456
+ moveX: undefined,
457
+ moveY: undefined,
458
+ moveTime: undefined,
459
+ downX: undefined,
460
+ downY: undefined,
461
+ downTime: undefined,
462
+ upX: undefined,
463
+ upY: undefined,
464
+ upTime: undefined,
465
+ pointerPrevX: undefined,
466
+ pointerPrevY: undefined,
467
+ pointerPrevTime: undefined,
448
468
  };
449
469
  }
450
470
  function track$8(event, x, y, time) {
@@ -1030,1440 +1050,1355 @@ function check$4(id, target, input) {
1030
1050
  }
1031
1051
  }
1032
1052
 
1033
- var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
1034
- var selectorMap = {};
1053
+ // Track the start time to be able to compute duration at the end of the task
1054
+ var idleTimeout = 5000;
1055
+ var tracker = {};
1056
+ var queuedTasks = [];
1057
+ var activeTask = null;
1058
+ var pauseTask = null;
1059
+ var resumeResolve = null;
1060
+ function pause$1() {
1061
+ if (pauseTask === null) {
1062
+ pauseTask = new Promise(function (resolve) {
1063
+ resumeResolve = resolve;
1064
+ });
1065
+ }
1066
+ }
1067
+ function resume$1() {
1068
+ if (pauseTask) {
1069
+ resumeResolve();
1070
+ pauseTask = null;
1071
+ if (activeTask === null) {
1072
+ run();
1073
+ }
1074
+ }
1075
+ }
1035
1076
  function reset$n() {
1036
- selectorMap = {};
1077
+ tracker = {};
1078
+ queuedTasks = [];
1079
+ activeTask = null;
1080
+ pauseTask = null;
1037
1081
  }
1038
- function get$1(input, type) {
1039
- var a = input.attributes;
1040
- var prefix = input.prefix ? input.prefix[type] : null;
1041
- var suffix = type === 0 /* Selector.Alpha */ ? "".concat("~" /* Constant.Tilde */).concat(input.position - 1) : ":nth-of-type(".concat(input.position, ")");
1042
- switch (input.tag) {
1043
- case "STYLE":
1044
- case "TITLE":
1045
- case "LINK":
1046
- case "META":
1047
- case "*T" /* Constant.TextTag */:
1048
- case "*D" /* Constant.DocumentTag */:
1049
- return "" /* Constant.Empty */;
1050
- case "HTML":
1051
- return "HTML" /* Constant.HTML */;
1052
- default:
1053
- if (prefix === null) {
1054
- return "" /* Constant.Empty */;
1055
- }
1056
- prefix = "".concat(prefix).concat(">" /* Constant.Separator */);
1057
- input.tag = input.tag.indexOf("svg:" /* Constant.SvgPrefix */) === 0 ? input.tag.substr("svg:" /* Constant.SvgPrefix */.length) : input.tag;
1058
- var selector = "".concat(prefix).concat(input.tag).concat(suffix);
1059
- var id = "id" /* Constant.Id */ in a && a["id" /* Constant.Id */].length > 0 ? a["id" /* Constant.Id */] : null;
1060
- var classes = input.tag !== "BODY" /* Constant.BodyTag */ && "class" /* Constant.Class */ in a && a["class" /* Constant.Class */].length > 0 ? a["class" /* Constant.Class */].trim().split(/\s+/).filter(function (c) { return filter(c); }).join("." /* Constant.Period */) : null;
1061
- if (classes && classes.length > 0) {
1062
- if (type === 0 /* Selector.Alpha */) {
1063
- // In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
1064
- // If the node has valid class name(s) then drop relative positioning within the parent path to keep things simple.
1065
- var key = "".concat(getDomPath(prefix)).concat(input.tag).concat("." /* Constant.Dot */).concat(classes);
1066
- if (!(key in selectorMap)) {
1067
- selectorMap[key] = [];
1068
- }
1069
- if (selectorMap[key].indexOf(input.id) < 0) {
1070
- selectorMap[key].push(input.id);
1071
- }
1072
- selector = "".concat(key).concat("~" /* Constant.Tilde */).concat(selectorMap[key].indexOf(input.id));
1073
- }
1074
- else {
1075
- // In Beta mode, we continue to look at query selectors in context of the full page
1076
- selector = "".concat(prefix).concat(input.tag, ".").concat(classes).concat(suffix);
1082
+ function schedule$1(task, priority) {
1083
+ if (priority === void 0) { priority = 0 /* Priority.Normal */; }
1084
+ return __awaiter(this, void 0, void 0, function () {
1085
+ var _i, queuedTasks_1, q, promise;
1086
+ return __generator(this, function (_a) {
1087
+ // If this task is already scheduled, skip it
1088
+ for (_i = 0, queuedTasks_1 = queuedTasks; _i < queuedTasks_1.length; _i++) {
1089
+ q = queuedTasks_1[_i];
1090
+ if (q.task === task) {
1091
+ return [2 /*return*/];
1077
1092
  }
1078
1093
  }
1079
- // Update selector to use "id" field when available. There are two exceptions:
1080
- // (1) if "id" appears to be an auto generated string token, e.g. guid or a random id containing digits
1081
- // (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
1082
- selector = id && filter(id) ? "".concat(getDomPrefix(prefix)).concat("#" /* Constant.Hash */).concat(id) : selector;
1083
- return selector;
1084
- }
1094
+ promise = new Promise(function (resolve) {
1095
+ var insert = priority === 1 /* Priority.High */ ? "unshift" : "push";
1096
+ // Queue this task for asynchronous execution later
1097
+ // We also store a unique page identifier (id) along with the task to ensure
1098
+ // ensure that we do not accidentally execute this task in context of a different page
1099
+ queuedTasks[insert]({ task: task, resolve: resolve, id: id() });
1100
+ });
1101
+ // If there is no active task running, and Clarity is not in pause state,
1102
+ // invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
1103
+ if (activeTask === null && pauseTask === null) {
1104
+ run();
1105
+ }
1106
+ return [2 /*return*/, promise];
1107
+ });
1108
+ });
1085
1109
  }
1086
- function getDomPrefix(prefix) {
1087
- var shadowDomStart = prefix.lastIndexOf("*S" /* Constant.ShadowDomTag */);
1088
- var iframeDomStart = prefix.lastIndexOf("".concat("iframe:" /* Constant.IFramePrefix */).concat("HTML" /* Constant.HTML */));
1089
- var domStart = Math.max(shadowDomStart, iframeDomStart);
1090
- if (domStart < 0) {
1091
- return "" /* Constant.Empty */;
1110
+ function run() {
1111
+ var entry = queuedTasks.shift();
1112
+ if (entry) {
1113
+ activeTask = entry;
1114
+ entry.task().then(function () {
1115
+ // Bail out if the context in which this task was operating is different from the current page
1116
+ // An example scenario where task could span across pages is Single Page Applications (SPA)
1117
+ // A task that started on page #1, but completes on page #2
1118
+ if (entry.id !== id()) {
1119
+ return;
1120
+ }
1121
+ entry.resolve();
1122
+ activeTask = null; // Reset active task back to null now that the promise is resolved
1123
+ run();
1124
+ }).catch(function (error) {
1125
+ // If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
1126
+ if (entry.id !== id()) {
1127
+ return;
1128
+ }
1129
+ if (error) {
1130
+ log$1(0 /* Code.RunTask */, 1 /* Severity.Warning */, error.name, error.message, error.stack);
1131
+ }
1132
+ activeTask = null;
1133
+ run();
1134
+ });
1092
1135
  }
1093
- return prefix.substring(0, prefix.indexOf(">" /* Constant.Separator */, domStart) + 1);
1094
1136
  }
1095
- function getDomPath(input) {
1096
- var parts = input.split(">" /* Constant.Separator */);
1097
- for (var i = 0; i < parts.length; i++) {
1098
- var tIndex = parts[i].indexOf("~" /* Constant.Tilde */);
1099
- var dIndex = parts[i].indexOf("." /* Constant.Dot */);
1100
- parts[i] = parts[i].substring(0, dIndex > 0 ? dIndex : (tIndex > 0 ? tIndex : parts[i].length));
1137
+ function state$a(timer) {
1138
+ var id = key(timer);
1139
+ if (id in tracker) {
1140
+ var elapsed = performance.now() - tracker[id].start;
1141
+ return (elapsed > tracker[id].yield) ? 0 /* Task.Wait */ : 1 /* Task.Run */;
1101
1142
  }
1102
- return parts.join(">" /* Constant.Separator */);
1143
+ // If this task is no longer being tracked, send stop message to the caller
1144
+ return 2 /* Task.Stop */;
1103
1145
  }
1104
- // Check if the given input string has digits or excluded class names
1105
- function filter(value) {
1106
- if (!value) {
1107
- return false;
1108
- } // Do not process empty strings
1109
- if (excludeClassNames.some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
1110
- return false;
1111
- }
1112
- for (var i = 0; i < value.length; i++) {
1113
- var c = value.charCodeAt(i);
1114
- if (c >= 48 /* Character.Zero */ && c <= 57 /* Character.Nine */) {
1115
- return false;
1116
- }
1146
+ function start$z(timer) {
1147
+ tracker[key(timer)] = { start: performance.now(), calls: 0, yield: 30 /* Setting.LongTask */ };
1148
+ }
1149
+ function restart$2(timer) {
1150
+ var id = key(timer);
1151
+ if (tracker && tracker[id]) {
1152
+ var c = tracker[id].calls;
1153
+ var y = tracker[id].yield;
1154
+ start$z(timer);
1155
+ tracker[id].calls = c + 1;
1156
+ tracker[id].yield = y;
1117
1157
  }
1118
- return true;
1119
1158
  }
1120
-
1121
- var selector = /*#__PURE__*/Object.freeze({
1122
- __proto__: null,
1123
- get: get$1,
1124
- reset: reset$n
1125
- });
1126
-
1127
- var index = 1;
1128
- var nodesMap = null; // Maps id => node to retrieve further node details using id.
1129
- var values = [];
1130
- var updateMap = [];
1131
- var hashMap = {};
1132
- var override = [];
1133
- var unmask = [];
1134
- var maskText = [];
1135
- var maskExclude = [];
1136
- var maskDisable = [];
1137
- var maskTags = [];
1138
- // The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
1139
- var idMap = null; // Maps node => id.
1140
- var iframeMap = null; // Maps iframe's contentDocument => parent iframe element
1141
- var privacyMap = null; // Maps node => Privacy (enum)
1142
- var fraudMap = null; // Maps node => FraudId (number)
1143
- function start$z() {
1144
- reset$m();
1145
- parse$1(document, true);
1159
+ function stop$x(timer) {
1160
+ var end = performance.now();
1161
+ var id = key(timer);
1162
+ var duration = end - tracker[id].start;
1163
+ sum(timer.cost, duration);
1164
+ count$1(5 /* Metric.InvokeCount */);
1165
+ // For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
1166
+ // However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
1167
+ if (tracker[id].calls > 0) {
1168
+ sum(4 /* Metric.TotalCost */, duration);
1169
+ }
1146
1170
  }
1147
- function stop$x() {
1148
- reset$m();
1171
+ function suspend$1(timer) {
1172
+ return __awaiter(this, void 0, void 0, function () {
1173
+ var id, _a;
1174
+ return __generator(this, function (_b) {
1175
+ switch (_b.label) {
1176
+ case 0:
1177
+ id = key(timer);
1178
+ if (!(id in tracker)) return [3 /*break*/, 2];
1179
+ stop$x(timer);
1180
+ _a = tracker[id];
1181
+ return [4 /*yield*/, wait()];
1182
+ case 1:
1183
+ _a.yield = (_b.sent()).timeRemaining();
1184
+ restart$2(timer);
1185
+ _b.label = 2;
1186
+ case 2:
1187
+ // After we are done with suspending task, ensure that we are still operating in the right context
1188
+ // If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
1189
+ return [2 /*return*/, id in tracker ? 1 /* Task.Run */ : 2 /* Task.Stop */];
1190
+ }
1191
+ });
1192
+ });
1149
1193
  }
1150
- function reset$m() {
1151
- index = 1;
1152
- values = [];
1153
- updateMap = [];
1154
- hashMap = {};
1155
- override = [];
1156
- unmask = [];
1157
- maskText = "address,password,contact" /* Mask.Text */.split("," /* Constant.Comma */);
1158
- maskExclude = "password,secret,pass,social,ssn,code,hidden" /* Mask.Exclude */.split("," /* Constant.Comma */);
1159
- maskDisable = "radio,checkbox,range,button,reset,submit" /* Mask.Disable */.split("," /* Constant.Comma */);
1160
- maskTags = "INPUT,SELECT,TEXTAREA" /* Mask.Tags */.split("," /* Constant.Comma */);
1161
- nodesMap = new Map();
1162
- idMap = new WeakMap();
1163
- iframeMap = new WeakMap();
1164
- privacyMap = new WeakMap();
1165
- fraudMap = new WeakMap();
1166
- reset$n();
1194
+ function key(timer) {
1195
+ return "".concat(timer.id, ".").concat(timer.cost);
1167
1196
  }
1168
- // We parse new root nodes for any regions or masked nodes in the beginning (document) and
1169
- // later whenever there are new additions or modifications to DOM (mutations)
1170
- function parse$1(root, init) {
1171
- if (init === void 0) { init = false; }
1172
- // Wrap selectors in a try / catch block.
1173
- // It's possible for script to receive invalid selectors, e.g. "'#id'" with extra quotes, and cause the code below to fail
1174
- try {
1175
- // Parse unmask configuration into separate query selectors and override tokens as part of initialization
1176
- if (init) {
1177
- config$2.unmask.forEach(function (x) { return x.indexOf("!" /* Constant.Bang */) < 0 ? unmask.push(x) : override.push(x.substr(1)); });
1197
+ function wait() {
1198
+ return __awaiter(this, void 0, void 0, function () {
1199
+ return __generator(this, function (_a) {
1200
+ switch (_a.label) {
1201
+ case 0:
1202
+ if (!pauseTask) return [3 /*break*/, 2];
1203
+ return [4 /*yield*/, pauseTask];
1204
+ case 1:
1205
+ _a.sent();
1206
+ _a.label = 2;
1207
+ case 2: return [2 /*return*/, new Promise(function (resolve) {
1208
+ requestIdleCallback(resolve, { timeout: idleTimeout });
1209
+ })];
1210
+ }
1211
+ });
1212
+ });
1213
+ }
1214
+ // Use native implementation of requestIdleCallback if it exists.
1215
+ // Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
1216
+ // While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
1217
+ // Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
1218
+ // This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
1219
+ // For non-blocking behavior, We need to know when browser has finished painting. This can be accomplished in two different ways (hacks):
1220
+ // (1) Use MessageChannel to pass the message, and browser will receive the message right after paint event has occured.
1221
+ // (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
1222
+ // Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
1223
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
1224
+ function requestIdleCallbackPolyfill(callback, options) {
1225
+ var startTime = performance.now();
1226
+ var channel = new MessageChannel();
1227
+ var incoming = channel.port1;
1228
+ var outgoing = channel.port2;
1229
+ incoming.onmessage = function (event) {
1230
+ var currentTime = performance.now();
1231
+ var elapsed = currentTime - startTime;
1232
+ var duration = currentTime - event.data;
1233
+ if (duration > 30 /* Setting.LongTask */ && elapsed < options.timeout) {
1234
+ requestAnimationFrame(function () { outgoing.postMessage(currentTime); });
1178
1235
  }
1179
- // Since mutations may happen on leaf nodes too, e.g. text nodes, which may not support all selector APIs.
1180
- // We ensure that the root note supports querySelectorAll API before executing the code below to identify new regions.
1181
- if ("querySelectorAll" in root) {
1182
- config$2.regions.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return observe$1(e, "".concat(x[0])); }); }); // Regions
1183
- config$2.mask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 3 /* Privacy.TextImage */); }); }); // Masked Elements
1184
- config$2.checksum.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return fraudMap.set(e, x[0]); }); }); // Fraud Checksum Check
1185
- unmask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 0 /* Privacy.None */); }); }); // Unmasked Elements
1236
+ else {
1237
+ var didTimeout_1 = elapsed > options.timeout;
1238
+ callback({
1239
+ didTimeout: didTimeout_1,
1240
+ timeRemaining: function () { return didTimeout_1 ? 30 /* Setting.LongTask */ : Math.max(0, 30 /* Setting.LongTask */ - duration); }
1241
+ });
1186
1242
  }
1187
- }
1188
- catch (e) {
1189
- log$1(5 /* Code.Selector */, 1 /* Severity.Warning */, e ? e.name : null);
1190
- }
1243
+ };
1244
+ requestAnimationFrame(function () { outgoing.postMessage(performance.now()); });
1191
1245
  }
1192
- function getId(node, autogen) {
1193
- if (autogen === void 0) { autogen = false; }
1194
- if (node === null) {
1195
- return null;
1246
+ var requestIdleCallback = window["requestIdleCallback"] || requestIdleCallbackPolyfill;
1247
+
1248
+ var state$9 = [];
1249
+ function start$y() {
1250
+ reset$m();
1251
+ }
1252
+ function observe$c(root) {
1253
+ bind(root, "change", recompute$8, true);
1254
+ }
1255
+ function recompute$8(evt) {
1256
+ recompute$8.dn = 5 /* FunctionNames.ChangeRecompute */;
1257
+ var element = target(evt);
1258
+ if (element) {
1259
+ var value = element.value;
1260
+ var checksum = value && value.length >= 5 /* Setting.WordLength */ && config$2.fraud && "password,secret,pass,social,ssn,code,hidden" /* Mask.Exclude */.indexOf(element.type) === -1 ? hash(value, 28 /* Setting.ChecksumPrecision */) : "" /* Constant.Empty */;
1261
+ state$9.push({ time: time(evt), event: 42 /* Event.Change */, data: { target: target(evt), type: element.type, value: value, checksum: checksum } });
1262
+ schedule$1(encode$3.bind(this, 42 /* Event.Change */));
1196
1263
  }
1197
- var id = idMap.get(node);
1198
- if (!id && autogen) {
1199
- id = index++;
1200
- idMap.set(node, id);
1264
+ }
1265
+ function reset$m() {
1266
+ state$9 = [];
1267
+ }
1268
+ function stop$w() {
1269
+ reset$m();
1270
+ }
1271
+
1272
+ function offset(element) {
1273
+ var output = { x: 0, y: 0 };
1274
+ // Walk up the chain to ensure we compute offset distance correctly
1275
+ // In case where we may have nested IFRAMEs, we keep walking up until we get to the top most parent page
1276
+ if (element && element.offsetParent) {
1277
+ do {
1278
+ var parent_1 = element.offsetParent;
1279
+ var frame = parent_1 === null ? iframe(element.ownerDocument) : null;
1280
+ output.x += element.offsetLeft;
1281
+ output.y += element.offsetTop;
1282
+ element = frame ? frame : parent_1;
1283
+ } while (element);
1201
1284
  }
1202
- return id ? id : null;
1285
+ return output;
1203
1286
  }
1204
- function add(node, parent, data, source) {
1205
- var parentId = parent ? getId(parent) : null;
1206
- // Do not add detached nodes
1207
- if ((!parent || !parentId) && node.host == null && node.nodeType !== Node.DOCUMENT_TYPE_NODE) {
1208
- return;
1287
+
1288
+ var UserInputTags = ["input", "textarea", "radio", "button", "canvas"];
1289
+ var state$8 = [];
1290
+ function start$x() {
1291
+ reset$l();
1292
+ }
1293
+ function observe$b(root) {
1294
+ bind(root, "click", handler$3.bind(this, 9 /* Event.Click */, root), true);
1295
+ }
1296
+ function handler$3(event, root, evt) {
1297
+ handler$3.dn = 6 /* FunctionNames.ClickHandler */;
1298
+ var frame = iframe(root);
1299
+ var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1300
+ var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
1301
+ var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
1302
+ // In case of iframe, we adjust (x,y) to be relative to top parent's origin
1303
+ if (frame) {
1304
+ var distance = offset(frame);
1305
+ x = x ? x + Math.round(distance.x) : x;
1306
+ y = y ? y + Math.round(distance.y) : y;
1209
1307
  }
1210
- var id = getId(node, true);
1211
- var previousId = getPreviousId(node);
1212
- var parentValue = null;
1213
- var regionId = exists(node) ? id : null;
1214
- var fraudId = fraudMap.has(node) ? fraudMap.get(node) : null;
1215
- var privacyId = config$2.content ? 1 /* Privacy.Sensitive */ : 3 /* Privacy.TextImage */;
1216
- if (parentId >= 0 && values[parentId]) {
1217
- parentValue = values[parentId];
1218
- parentValue.children.push(id);
1219
- regionId = regionId === null ? parentValue.region : regionId;
1220
- fraudId = fraudId === null ? parentValue.metadata.fraud : fraudId;
1221
- privacyId = parentValue.metadata.privacy;
1308
+ var t = target(evt);
1309
+ // Find nearest anchor tag (<a/>) parent if current target node is part of one
1310
+ // If present, we use the returned link element to populate text and link properties below
1311
+ var a = link(t);
1312
+ // Get layout rectangle for the target element
1313
+ var l = layout$1(t);
1314
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
1315
+ // This property helps differentiate between a keyboard navigation vs. pointer click
1316
+ // In case of a keyboard navigation, we use center of target element as (x,y)
1317
+ if (evt.detail === 0 && l) {
1318
+ x = Math.round(l.x + (l.w / 2));
1319
+ y = Math.round(l.y + (l.h / 2));
1222
1320
  }
1223
- // If there's an explicit region attribute set on the element, use it to mark a region on the page
1224
- if (data.attributes && "data-clarity-region" /* Constant.RegionData */ in data.attributes) {
1225
- observe$1(node, data.attributes["data-clarity-region" /* Constant.RegionData */]);
1226
- regionId = id;
1321
+ var eX = l ? Math.max(Math.floor(((x - l.x) / l.w) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1322
+ var eY = l ? Math.max(Math.floor(((y - l.y) / l.h) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1323
+ // Check for null values before processing this event
1324
+ if (x !== null && y !== null) {
1325
+ var textInfo = text(t);
1326
+ state$8.push({
1327
+ time: time(evt),
1328
+ event: event,
1329
+ data: {
1330
+ target: t,
1331
+ x: x,
1332
+ y: y,
1333
+ eX: eX,
1334
+ eY: eY,
1335
+ button: evt.button,
1336
+ reaction: reaction(t),
1337
+ context: context(a),
1338
+ text: textInfo.text,
1339
+ link: a ? a.href : null,
1340
+ hash: null,
1341
+ trust: evt.isTrusted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */,
1342
+ isFullText: textInfo.isFullText,
1343
+ }
1344
+ });
1345
+ schedule$1(encode$3.bind(this, event));
1227
1346
  }
1228
- nodesMap.set(id, node);
1229
- values[id] = {
1230
- id: id,
1231
- parent: parentId,
1232
- previous: previousId,
1233
- children: [],
1234
- data: data,
1235
- selector: null,
1236
- hash: null,
1237
- region: regionId,
1238
- metadata: { active: true, suspend: false, privacy: privacyId, position: null, fraud: fraudId, size: null },
1239
- };
1240
- privacy(node, values[id], parentValue);
1241
- updateSelector(values[id]);
1242
- updateImageSize(values[id]);
1243
- track$6(id, source);
1244
1347
  }
1245
- function update$1(node, parent, data, source) {
1246
- var id = getId(node);
1247
- var parentId = parent ? getId(parent) : null;
1248
- var previousId = getPreviousId(node);
1249
- var changed = false;
1250
- var parentChanged = false;
1251
- if (id in values) {
1252
- var value = values[id];
1253
- value.metadata.active = true;
1254
- // Handle case where internal ordering may have changed
1255
- if (value.previous !== previousId) {
1256
- changed = true;
1257
- value.previous = previousId;
1258
- }
1259
- // Handle case where parent might have been updated
1260
- if (value.parent !== parentId) {
1261
- changed = true;
1262
- var oldParentId = value.parent;
1263
- value.parent = parentId;
1264
- // Move this node to the right location under new parent
1265
- if (parentId !== null && parentId >= 0) {
1266
- var childIndex = previousId === null ? 0 : values[parentId].children.indexOf(previousId) + 1;
1267
- values[parentId].children.splice(childIndex, 0, id);
1268
- // Update region after the move
1269
- value.region = exists(node) ? id : values[parentId].region;
1270
- }
1271
- else {
1272
- // Mark this element as deleted if the parent has been updated to null
1273
- remove(id, source);
1274
- }
1275
- // Remove reference to this node from the old parent
1276
- if (oldParentId !== null && oldParentId >= 0) {
1277
- var nodeIndex = values[oldParentId].children.indexOf(id);
1278
- if (nodeIndex >= 0) {
1279
- values[oldParentId].children.splice(nodeIndex, 1);
1280
- }
1281
- }
1282
- parentChanged = true;
1283
- }
1284
- // Update data
1285
- for (var key in data) {
1286
- if (diff(value["data"], data, key)) {
1287
- changed = true;
1288
- value["data"][key] = data[key];
1348
+ function link(node) {
1349
+ while (node && node !== document) {
1350
+ if (node.nodeType === Node.ELEMENT_NODE) {
1351
+ var element = node;
1352
+ if (element.tagName === "A") {
1353
+ return element;
1289
1354
  }
1290
1355
  }
1291
- // Update selector
1292
- updateSelector(value);
1293
- track$6(id, source, changed, parentChanged);
1356
+ node = node.parentNode;
1294
1357
  }
1358
+ return null;
1295
1359
  }
1296
- function sameorigin(node) {
1297
- var output = false;
1298
- if (node.nodeType === Node.ELEMENT_NODE && node.tagName === "IFRAME" /* Constant.IFrameTag */) {
1299
- var frame = node;
1300
- // To determine if the iframe is same-origin or not, we try accessing it's contentDocument.
1301
- // If the browser throws an exception, we assume it's cross-origin and move on.
1302
- // However, if we do a get a valid document object back, we assume the contents are accessible and iframe is same-origin.
1303
- try {
1304
- var doc = frame.contentDocument;
1305
- if (doc) {
1306
- iframeMap.set(frame.contentDocument, frame);
1307
- output = true;
1308
- }
1360
+ function text(element) {
1361
+ var output = null;
1362
+ var isFullText = false;
1363
+ if (element) {
1364
+ // Grab text using "textContent" for most HTMLElements, however, use "value" for HTMLInputElements and "alt" for HTMLImageElement.
1365
+ var t = element.textContent || String(element.value || '') || element.alt;
1366
+ if (t) {
1367
+ // Replace multiple occurrence of space characters with a single white space
1368
+ // Also, trim any spaces at the beginning or at the end of string
1369
+ var trimmedText = t.replace(/\s+/g, " " /* Constant.Space */).trim();
1370
+ // Finally, send only first few characters as specified by the Setting
1371
+ output = trimmedText.substring(0, 25 /* Setting.ClickText */);
1372
+ isFullText = output.length === trimmedText.length;
1309
1373
  }
1310
- catch ( /* do nothing */_a) { /* do nothing */ }
1311
- }
1312
- return output;
1313
- }
1314
- function iframe(node) {
1315
- var doc = node.nodeType === Node.DOCUMENT_NODE ? node : null;
1316
- return doc && iframeMap.has(doc) ? iframeMap.get(doc) : null;
1317
- }
1318
- function privacy(node, value, parent) {
1319
- var _a;
1320
- var data = value.data;
1321
- var metadata = value.metadata;
1322
- var current = metadata.privacy;
1323
- var attributes = data.attributes || {};
1324
- var tag = data.tag.toUpperCase();
1325
- switch (true) {
1326
- case maskTags.indexOf(tag) >= 0:
1327
- var type = attributes["type" /* Constant.Type */];
1328
- var meta_1 = "" /* Constant.Empty */;
1329
- var excludedPrivacyAttributes_1 = ["class" /* Constant.Class */, "style" /* Constant.Style */];
1330
- Object.keys(attributes)
1331
- .filter(function (x) { return !excludedPrivacyAttributes_1.includes(x); })
1332
- .forEach(function (x) { return (meta_1 += attributes[x].toLowerCase()); });
1333
- var exclude = maskExclude.some(function (x) { return meta_1.indexOf(x) >= 0; });
1334
- // Regardless of privacy mode, always mask off user input from input boxes or drop downs with two exceptions:
1335
- // (1) The node is detected to be one of the excluded fields, in which case we drop everything
1336
- // (2) The node's type is one of the allowed types (like checkboxes)
1337
- metadata.privacy = tag === "INPUT" /* Constant.InputTag */ && maskDisable.indexOf(type) >= 0 ? current : (exclude ? 4 /* Privacy.Exclude */ : 2 /* Privacy.Text */);
1338
- break;
1339
- case "data-clarity-mask" /* Constant.MaskData */ in attributes:
1340
- metadata.privacy = 3 /* Privacy.TextImage */;
1341
- break;
1342
- case "data-clarity-unmask" /* Constant.UnmaskData */ in attributes:
1343
- metadata.privacy = 0 /* Privacy.None */;
1344
- break;
1345
- case privacyMap.has(node):
1346
- // If this node was explicitly configured to contain sensitive content, honor that privacy setting
1347
- metadata.privacy = privacyMap.get(node);
1348
- break;
1349
- case fraudMap.has(node):
1350
- // If this node was explicitly configured to be evaluated for fraud, then also mask content
1351
- metadata.privacy = 2 /* Privacy.Text */;
1352
- break;
1353
- case tag === "*T" /* Constant.TextTag */:
1354
- // If it's a text node belonging to a STYLE or TITLE tag or one of scrub exceptions, then capture content
1355
- var pTag = parent && parent.data ? parent.data.tag : "" /* Constant.Empty */;
1356
- var pSelector_1 = parent && parent.selector ? parent.selector[1 /* Selector.Default */] : "" /* Constant.Empty */;
1357
- var tags = ["STYLE" /* Constant.StyleTag */, "TITLE" /* Constant.TitleTag */, "svg:style" /* Constant.SvgStyle */];
1358
- metadata.privacy = tags.includes(pTag) || override.some(function (x) { return pSelector_1.indexOf(x) >= 0; }) ? 0 /* Privacy.None */ : current;
1359
- break;
1360
- case current === 1 /* Privacy.Sensitive */:
1361
- // In a mode where we mask sensitive information by default, look through class names to aggressively mask content
1362
- metadata.privacy = inspect(attributes["class" /* Constant.Class */], maskText, metadata);
1363
- break;
1364
- case tag === "IMG" /* Constant.ImageTag */:
1365
- // Mask images with blob src as it is not publicly available anyway.
1366
- if ((_a = attributes.src) === null || _a === void 0 ? void 0 : _a.startsWith('blob:')) {
1367
- metadata.privacy = 3 /* Privacy.TextImage */;
1368
- }
1369
- break;
1370
1374
  }
1375
+ return { text: output, isFullText: isFullText ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */ };
1371
1376
  }
1372
- function inspect(input, lookup, metadata) {
1373
- if (input && lookup.some(function (x) { return input.indexOf(x) >= 0; })) {
1374
- return 2 /* Privacy.Text */;
1377
+ function reaction(element) {
1378
+ if (element.nodeType === Node.ELEMENT_NODE) {
1379
+ var tag = element.tagName.toLowerCase();
1380
+ if (UserInputTags.indexOf(tag) >= 0) {
1381
+ return 0 /* BooleanFlag.False */;
1382
+ }
1375
1383
  }
1376
- return metadata.privacy;
1384
+ return 1 /* BooleanFlag.True */;
1377
1385
  }
1378
- function diff(a, b, field) {
1379
- if (typeof a[field] === "object" && typeof b[field] === "object") {
1380
- for (var key in a[field]) {
1381
- if (a[field][key] !== b[field][key]) {
1382
- return true;
1383
- }
1384
- }
1385
- for (var key in b[field]) {
1386
- if (b[field][key] !== a[field][key]) {
1387
- return true;
1388
- }
1386
+ function layout$1(element) {
1387
+ var box = null;
1388
+ var de = document.documentElement;
1389
+ if (typeof element.getBoundingClientRect === "function") {
1390
+ // getBoundingClientRect returns rectangle relative positioning to viewport
1391
+ var rect = element.getBoundingClientRect();
1392
+ if (rect && rect.width > 0 && rect.height > 0) {
1393
+ // Add viewport's scroll position to rectangle to get position relative to document origin
1394
+ // Also: using Math.floor() instead of Math.round() because in Edge,
1395
+ // getBoundingClientRect returns partial pixel values (e.g. 162.5px) and Chrome already
1396
+ // floors the value (e.g. 162px). This keeps consistent behavior across browsers.
1397
+ box = {
1398
+ x: Math.floor(rect.left + ("pageXOffset" in window ? window.pageXOffset : de.scrollLeft)),
1399
+ y: Math.floor(rect.top + ("pageYOffset" in window ? window.pageYOffset : de.scrollTop)),
1400
+ w: Math.floor(rect.width),
1401
+ h: Math.floor(rect.height)
1402
+ };
1389
1403
  }
1390
- return false;
1391
1404
  }
1392
- return a[field] !== b[field];
1405
+ return box;
1393
1406
  }
1394
- function position(parent, child) {
1395
- child.metadata.position = 1;
1396
- var idx = parent ? parent.children.indexOf(child.id) : -1;
1397
- while (idx-- > 0) {
1398
- var sibling = values[parent.children[idx]];
1399
- if (child.data.tag === sibling.data.tag) {
1400
- child.metadata.position = sibling.metadata.position + 1;
1401
- break;
1407
+ function context(a) {
1408
+ if (a && a.hasAttribute("target" /* Constant.Target */)) {
1409
+ switch (a.getAttribute("target" /* Constant.Target */)) {
1410
+ case "_blank" /* Constant.Blank */: return 1 /* BrowsingContext.Blank */;
1411
+ case "_parent" /* Constant.Parent */: return 2 /* BrowsingContext.Parent */;
1412
+ case "_top" /* Constant.Top */: return 3 /* BrowsingContext.Top */;
1402
1413
  }
1403
1414
  }
1404
- return child.metadata.position;
1415
+ return 0 /* BrowsingContext.Self */;
1405
1416
  }
1406
- function updateSelector(value) {
1407
- var parent = value.parent && value.parent in values ? values[value.parent] : null;
1408
- var prefix = parent ? parent.selector : null;
1409
- var d = value.data;
1410
- var p = position(parent, value);
1411
- var s = { id: value.id, tag: d.tag, prefix: prefix, position: p, attributes: d.attributes };
1412
- value.selector = [get$1(s, 0 /* Selector.Alpha */), get$1(s, 1 /* Selector.Beta */)];
1413
- value.hash = value.selector.map(function (x) { return x ? hash(x) : null; });
1414
- value.hash.forEach(function (h) { return hashMap[h] = value.id; });
1417
+ function reset$l() {
1418
+ state$8 = [];
1415
1419
  }
1416
- function hashText(hash) {
1417
- var id = lookup(hash);
1418
- var node = getNode(id);
1419
- return node !== null && node.textContent !== null ? node.textContent.substr(0, 25 /* Setting.ClickText */) : '';
1420
+ function stop$v() {
1421
+ reset$l();
1420
1422
  }
1421
- function getNode(id) {
1422
- return nodesMap.has(id) ? nodesMap.get(id) : null;
1423
+
1424
+ var state$7 = [];
1425
+ function start$w() {
1426
+ reset$k();
1423
1427
  }
1424
- function getValue(id) {
1425
- if (id in values) {
1426
- return values[id];
1427
- }
1428
- return null;
1428
+ function observe$a(root) {
1429
+ bind(root, "cut", recompute$7.bind(this, 0 /* Clipboard.Cut */), true);
1430
+ bind(root, "copy", recompute$7.bind(this, 1 /* Clipboard.Copy */), true);
1431
+ bind(root, "paste", recompute$7.bind(this, 2 /* Clipboard.Paste */), true);
1429
1432
  }
1430
- function get(node) {
1431
- var id = getId(node);
1432
- return id in values ? values[id] : null;
1433
+ function recompute$7(action, evt) {
1434
+ recompute$7.dn = 7 /* FunctionNames.ClipboardRecompute */;
1435
+ state$7.push({ time: time(evt), event: 38 /* Event.Clipboard */, data: { target: target(evt), action: action } });
1436
+ schedule$1(encode$3.bind(this, 38 /* Event.Clipboard */));
1433
1437
  }
1434
- function lookup(hash) {
1435
- return hash in hashMap ? hashMap[hash] : null;
1438
+ function reset$k() {
1439
+ state$7 = [];
1436
1440
  }
1437
- function has(node) {
1438
- return nodesMap.has(getId(node));
1441
+ function stop$u() {
1442
+ reset$k();
1439
1443
  }
1440
- function updates$2() {
1441
- var output = [];
1442
- for (var _i = 0, updateMap_1 = updateMap; _i < updateMap_1.length; _i++) {
1443
- var id = updateMap_1[_i];
1444
- if (id in values) {
1445
- output.push(values[id]);
1446
- }
1447
- }
1448
- updateMap = [];
1449
- return output;
1444
+
1445
+ var timeout$6 = null;
1446
+ var state$6 = [];
1447
+ function start$v() {
1448
+ reset$j();
1450
1449
  }
1451
- function remove(id, source) {
1452
- if (id in values) {
1453
- var value = values[id];
1454
- value.metadata.active = false;
1455
- value.parent = null;
1456
- track$6(id, source);
1457
- // Clean up node references for removed nodes
1458
- removeNodeFromNodesMap(id);
1459
- }
1450
+ function observe$9(root) {
1451
+ bind(root, "input", recompute$6, true);
1460
1452
  }
1461
- function removeNodeFromNodesMap(id) {
1462
- // Shadow dom roots shouldn't be deleted,
1463
- // we should keep listening to the mutations there even they're not rendered in the DOM.
1464
- if (nodesMap.get(id).nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
1465
- return;
1466
- }
1467
- nodesMap.delete(id);
1468
- var value = id in values ? values[id] : null;
1469
- if (value && value.children) {
1470
- for (var _i = 0, _a = value.children; _i < _a.length; _i++) {
1471
- var childId = _a[_i];
1472
- removeNodeFromNodesMap(childId);
1453
+ function recompute$6(evt) {
1454
+ recompute$6.dn = 9 /* FunctionNames.InputRecompute */;
1455
+ var input = target(evt);
1456
+ var value = get(input);
1457
+ if (input && input.type && value) {
1458
+ var v = input.value;
1459
+ var t = input.type;
1460
+ switch (input.type) {
1461
+ case "radio":
1462
+ case "checkbox":
1463
+ v = input.checked ? "true" : "false";
1464
+ break;
1473
1465
  }
1474
- }
1475
- }
1476
- function updateImageSize(value) {
1477
- // If this element is a image node, and is masked, then track box model for the current element
1478
- if (value.data.tag === "IMG" /* Constant.ImageTag */ && value.metadata.privacy === 3 /* Privacy.TextImage */) {
1479
- var img_1 = getNode(value.id);
1480
- // We will not capture the natural image dimensions until it loads.
1481
- if (img_1 && (!img_1.complete || img_1.naturalWidth === 0)) {
1482
- // This will trigger mutation to update the original width and height after image loads.
1483
- bind(img_1, 'load', function () {
1484
- img_1.setAttribute('data-clarity-loaded', "".concat(shortid()));
1485
- });
1466
+ var data = { target: input, value: v, type: t };
1467
+ // If last entry in the queue is for the same target node as the current one, remove it so we can later swap it with current data.
1468
+ if (state$6.length > 0 && (state$6[state$6.length - 1].data.target === data.target)) {
1469
+ state$6.pop();
1486
1470
  }
1487
- value.metadata.size = [];
1471
+ state$6.push({ time: time(evt), event: 27 /* Event.Input */, data: data });
1472
+ clearTimeout(timeout$6);
1473
+ timeout$6 = setTimeout(process$7, 1000 /* Setting.InputLookAhead */, 27 /* Event.Input */);
1488
1474
  }
1489
1475
  }
1490
- function getPreviousId(node) {
1491
- var id = null;
1492
- // Some nodes may not have an ID by design since Clarity skips over tags like SCRIPT, NOSCRIPT, META, COMMENTS, etc..
1493
- // In that case, we keep going back and check for their sibling until we find a sibling with ID or no more sibling nodes are left.
1494
- while (id === null && node.previousSibling) {
1495
- id = getId(node.previousSibling);
1496
- node = node.previousSibling;
1497
- }
1498
- return id;
1476
+ function process$7(event) {
1477
+ schedule$1(encode$3.bind(this, event));
1499
1478
  }
1500
- function track$6(id, source, changed, parentChanged) {
1501
- if (changed === void 0) { changed = true; }
1502
- if (parentChanged === void 0) { parentChanged = false; }
1503
- // Keep track of the order in which mutations happened, they may not be sequential
1504
- // Edge case: If an element is added later on, and pre-discovered element is moved as a child.
1505
- // In that case, we need to reorder the pre-discovered element in the update list to keep visualization consistent.
1506
- var uIndex = updateMap.indexOf(id);
1507
- if (uIndex >= 0 && source === 1 /* Source.ChildListAdd */ && parentChanged) {
1508
- updateMap.splice(uIndex, 1);
1509
- updateMap.push(id);
1510
- }
1511
- else if (uIndex === -1 && changed) {
1512
- updateMap.push(id);
1513
- }
1479
+ function reset$j() {
1480
+ state$6 = [];
1514
1481
  }
1515
-
1516
- var dom = /*#__PURE__*/Object.freeze({
1517
- __proto__: null,
1518
- add: add,
1519
- get: get,
1520
- getId: getId,
1521
- getNode: getNode,
1522
- getValue: getValue,
1523
- has: has,
1524
- hashText: hashText,
1525
- iframe: iframe,
1526
- lookup: lookup,
1527
- parse: parse$1,
1528
- sameorigin: sameorigin,
1529
- start: start$z,
1530
- stop: stop$x,
1531
- update: update$1,
1532
- updates: updates$2
1533
- });
1534
-
1535
- // Track the start time to be able to compute duration at the end of the task
1536
- var idleTimeout = 5000;
1537
- var tracker = {};
1538
- var queuedTasks = [];
1539
- var activeTask = null;
1540
- var pauseTask = null;
1541
- var resumeResolve = null;
1542
- function pause$1() {
1543
- if (pauseTask === null) {
1544
- pauseTask = new Promise(function (resolve) {
1545
- resumeResolve = resolve;
1546
- });
1547
- }
1482
+ function stop$t() {
1483
+ clearTimeout(timeout$6);
1484
+ reset$j();
1548
1485
  }
1549
- function resume$1() {
1550
- if (pauseTask) {
1551
- resumeResolve();
1552
- pauseTask = null;
1553
- if (activeTask === null) {
1554
- run();
1555
- }
1556
- }
1486
+
1487
+ var state$5 = [];
1488
+ var timeout$5 = null;
1489
+ var hasPrimaryTouch = false;
1490
+ var primaryTouchId = 0;
1491
+ var activeTouchPointIds = new Set();
1492
+ function start$u() {
1493
+ reset$i();
1557
1494
  }
1558
- function reset$l() {
1559
- tracker = {};
1560
- queuedTasks = [];
1561
- activeTask = null;
1562
- pauseTask = null;
1495
+ function observe$8(root) {
1496
+ bind(root, "mousedown", mouse.bind(this, 13 /* Event.MouseDown */, root), true);
1497
+ bind(root, "mouseup", mouse.bind(this, 14 /* Event.MouseUp */, root), true);
1498
+ bind(root, "mousemove", mouse.bind(this, 12 /* Event.MouseMove */, root), true);
1499
+ bind(root, "wheel", mouse.bind(this, 15 /* Event.MouseWheel */, root), true);
1500
+ bind(root, "dblclick", mouse.bind(this, 16 /* Event.DoubleClick */, root), true);
1501
+ bind(root, "touchstart", touch.bind(this, 17 /* Event.TouchStart */, root), true);
1502
+ bind(root, "touchend", touch.bind(this, 18 /* Event.TouchEnd */, root), true);
1503
+ bind(root, "touchmove", touch.bind(this, 19 /* Event.TouchMove */, root), true);
1504
+ bind(root, "touchcancel", touch.bind(this, 20 /* Event.TouchCancel */, root), true);
1563
1505
  }
1564
- function schedule$1(task, priority) {
1565
- if (priority === void 0) { priority = 0 /* Priority.Normal */; }
1566
- return __awaiter(this, void 0, void 0, function () {
1567
- var _i, queuedTasks_1, q, promise;
1568
- return __generator(this, function (_a) {
1569
- // If this task is already scheduled, skip it
1570
- for (_i = 0, queuedTasks_1 = queuedTasks; _i < queuedTasks_1.length; _i++) {
1571
- q = queuedTasks_1[_i];
1572
- if (q.task === task) {
1573
- return [2 /*return*/];
1574
- }
1575
- }
1576
- promise = new Promise(function (resolve) {
1577
- var insert = priority === 1 /* Priority.High */ ? "unshift" : "push";
1578
- // Queue this task for asynchronous execution later
1579
- // We also store a unique page identifier (id) along with the task to ensure
1580
- // ensure that we do not accidentally execute this task in context of a different page
1581
- queuedTasks[insert]({ task: task, resolve: resolve, id: id() });
1582
- });
1583
- // If there is no active task running, and Clarity is not in pause state,
1584
- // invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
1585
- if (activeTask === null && pauseTask === null) {
1586
- run();
1587
- }
1588
- return [2 /*return*/, promise];
1589
- });
1590
- });
1506
+ function mouse(event, root, evt) {
1507
+ mouse.dn = 10 /* FunctionNames.PointerMouse */;
1508
+ var frame = iframe(root);
1509
+ var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1510
+ var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
1511
+ var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
1512
+ // In case of iframe, we adjust (x,y) to be relative to top parent's origin
1513
+ if (frame) {
1514
+ var distance = offset(frame);
1515
+ x = x ? x + Math.round(distance.x) : x;
1516
+ y = y ? y + Math.round(distance.y) : y;
1517
+ }
1518
+ // Check for null values before processing this event
1519
+ if (x !== null && y !== null) {
1520
+ handler$2({ time: time(evt), event: event, data: { target: target(evt), x: x, y: y } });
1521
+ }
1591
1522
  }
1592
- function run() {
1593
- var entry = queuedTasks.shift();
1594
- if (entry) {
1595
- activeTask = entry;
1596
- entry.task().then(function () {
1597
- // Bail out if the context in which this task was operating is different from the current page
1598
- // An example scenario where task could span across pages is Single Page Applications (SPA)
1599
- // A task that started on page #1, but completes on page #2
1600
- if (entry.id !== id()) {
1601
- return;
1523
+ function touch(event, root, evt) {
1524
+ touch.dn = 11 /* FunctionNames.PointerTouch */;
1525
+ var frame = iframe(root);
1526
+ var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1527
+ var touches = evt.changedTouches;
1528
+ var t = time(evt);
1529
+ if (touches) {
1530
+ for (var i = 0; i < touches.length; i++) {
1531
+ var entry = touches[i];
1532
+ var x = "clientX" in entry ? Math.round(entry["clientX"] + d.scrollLeft) : null;
1533
+ var y = "clientY" in entry ? Math.round(entry["clientY"] + d.scrollTop) : null;
1534
+ x = x && frame ? x + Math.round(frame.offsetLeft) : x;
1535
+ y = y && frame ? y + Math.round(frame.offsetTop) : y;
1536
+ // We cannot rely on identifier to determine primary touch as its value doesn't always start with 0.
1537
+ // Safari/Webkit uses the address of the UITouch object as the identifier value for each touch point.
1538
+ var id = "identifier" in entry ? entry["identifier"] : undefined;
1539
+ switch (event) {
1540
+ case 17 /* Event.TouchStart */:
1541
+ if (activeTouchPointIds.size === 0) {
1542
+ // Track presence of primary touch separately to handle scenarios when same id is repeated
1543
+ hasPrimaryTouch = true;
1544
+ primaryTouchId = id;
1545
+ }
1546
+ activeTouchPointIds.add(id);
1547
+ break;
1548
+ case 18 /* Event.TouchEnd */:
1549
+ case 20 /* Event.TouchCancel */:
1550
+ activeTouchPointIds.delete(id);
1551
+ break;
1602
1552
  }
1603
- entry.resolve();
1604
- activeTask = null; // Reset active task back to null now that the promise is resolved
1605
- run();
1606
- }).catch(function (error) {
1607
- // If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
1608
- if (entry.id !== id()) {
1609
- return;
1553
+ var isPrimary = hasPrimaryTouch && primaryTouchId === id;
1554
+ // Check for null values before processing this event
1555
+ if (x !== null && y !== null) {
1556
+ handler$2({ time: t, event: event, data: { target: target(evt), x: x, y: y, id: id, isPrimary: isPrimary } });
1610
1557
  }
1611
- if (error) {
1612
- log$1(0 /* Code.RunTask */, 1 /* Severity.Warning */, error.name, error.message, error.stack);
1558
+ // Reset primary touch point id once touch event ends
1559
+ if (event === 20 /* Event.TouchCancel */ || event === 18 /* Event.TouchEnd */) {
1560
+ if (primaryTouchId === id) {
1561
+ hasPrimaryTouch = false;
1562
+ }
1613
1563
  }
1614
- activeTask = null;
1615
- run();
1616
- });
1564
+ }
1617
1565
  }
1618
1566
  }
1619
- function state$a(timer) {
1620
- var id = key(timer);
1621
- if (id in tracker) {
1622
- var elapsed = performance.now() - tracker[id].start;
1623
- return (elapsed > tracker[id].yield) ? 0 /* Task.Wait */ : 1 /* Task.Run */;
1567
+ function handler$2(current) {
1568
+ switch (current.event) {
1569
+ case 12 /* Event.MouseMove */:
1570
+ case 15 /* Event.MouseWheel */:
1571
+ case 19 /* Event.TouchMove */:
1572
+ var length_1 = state$5.length;
1573
+ var last = length_1 > 1 ? state$5[length_1 - 2] : null;
1574
+ if (last && similar$1(last, current)) {
1575
+ state$5.pop();
1576
+ }
1577
+ state$5.push(current);
1578
+ clearTimeout(timeout$5);
1579
+ timeout$5 = setTimeout(process$6, 500 /* Setting.LookAhead */, current.event);
1580
+ break;
1581
+ default:
1582
+ state$5.push(current);
1583
+ process$6(current.event);
1584
+ break;
1624
1585
  }
1625
- // If this task is no longer being tracked, send stop message to the caller
1626
- return 2 /* Task.Stop */;
1627
1586
  }
1628
- function start$y(timer) {
1629
- tracker[key(timer)] = { start: performance.now(), calls: 0, yield: 30 /* Setting.LongTask */ };
1587
+ function process$6(event) {
1588
+ schedule$1(encode$3.bind(this, event));
1630
1589
  }
1631
- function restart$2(timer) {
1632
- var id = key(timer);
1633
- if (tracker && tracker[id]) {
1634
- var c = tracker[id].calls;
1635
- var y = tracker[id].yield;
1636
- start$y(timer);
1637
- tracker[id].calls = c + 1;
1638
- tracker[id].yield = y;
1639
- }
1590
+ function reset$i() {
1591
+ state$5 = [];
1640
1592
  }
1641
- function stop$w(timer) {
1642
- var end = performance.now();
1643
- var id = key(timer);
1644
- var duration = end - tracker[id].start;
1645
- sum(timer.cost, duration);
1646
- count$1(5 /* Metric.InvokeCount */);
1647
- // For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
1648
- // However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
1649
- if (tracker[id].calls > 0) {
1650
- sum(4 /* Metric.TotalCost */, duration);
1593
+ function similar$1(last, current) {
1594
+ var dx = last.data.x - current.data.x;
1595
+ var dy = last.data.y - current.data.y;
1596
+ var distance = Math.sqrt(dx * dx + dy * dy);
1597
+ var gap = current.time - last.time;
1598
+ var match = current.data.target === last.data.target;
1599
+ return current.event === last.event && match && distance < 20 /* Setting.Distance */ && gap < 25 /* Setting.Interval */;
1600
+ }
1601
+ function stop$s() {
1602
+ clearTimeout(timeout$5);
1603
+ // Send out any pending pointer events in the pipeline
1604
+ if (state$5.length > 0) {
1605
+ process$6(state$5[state$5.length - 1].event);
1651
1606
  }
1652
1607
  }
1653
- function suspend$1(timer) {
1654
- return __awaiter(this, void 0, void 0, function () {
1655
- var id, _a;
1656
- return __generator(this, function (_b) {
1657
- switch (_b.label) {
1658
- case 0:
1659
- id = key(timer);
1660
- if (!(id in tracker)) return [3 /*break*/, 2];
1661
- stop$w(timer);
1662
- _a = tracker[id];
1663
- return [4 /*yield*/, wait()];
1664
- case 1:
1665
- _a.yield = (_b.sent()).timeRemaining();
1666
- restart$2(timer);
1667
- _b.label = 2;
1668
- case 2:
1669
- // After we are done with suspending task, ensure that we are still operating in the right context
1670
- // If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
1671
- return [2 /*return*/, id in tracker ? 1 /* Task.Run */ : 2 /* Task.Stop */];
1672
- }
1673
- });
1674
- });
1608
+
1609
+ var data$c;
1610
+ var timeout$4 = null;
1611
+ var initialStateLogged = false;
1612
+ function start$t() {
1613
+ initialStateLogged = false;
1614
+ bind(window, "resize", recompute$5);
1615
+ recompute$5();
1675
1616
  }
1676
- function key(timer) {
1677
- return "".concat(timer.id, ".").concat(timer.cost);
1617
+ function recompute$5() {
1618
+ recompute$5.dn = 12 /* FunctionNames.ResizeRecompute */;
1619
+ var de = document.documentElement;
1620
+ // window.innerWidth includes width of the scrollbar and is not a true representation of the viewport width.
1621
+ // Therefore, when possible, use documentElement's clientWidth property.
1622
+ data$c = {
1623
+ width: de && "clientWidth" in de ? Math.min(de.clientWidth, window.innerWidth) : window.innerWidth,
1624
+ height: de && "clientHeight" in de ? Math.min(de.clientHeight, window.innerHeight) : window.innerHeight,
1625
+ };
1626
+ if (initialStateLogged) {
1627
+ clearTimeout(timeout$4);
1628
+ timeout$4 = setTimeout(process$5, 500 /* Setting.LookAhead */, 11 /* Event.Resize */);
1629
+ }
1630
+ else {
1631
+ encode$3(11 /* Event.Resize */);
1632
+ initialStateLogged = true;
1633
+ }
1678
1634
  }
1679
- function wait() {
1680
- return __awaiter(this, void 0, void 0, function () {
1681
- return __generator(this, function (_a) {
1682
- switch (_a.label) {
1683
- case 0:
1684
- if (!pauseTask) return [3 /*break*/, 2];
1685
- return [4 /*yield*/, pauseTask];
1686
- case 1:
1687
- _a.sent();
1688
- _a.label = 2;
1689
- case 2: return [2 /*return*/, new Promise(function (resolve) {
1690
- requestIdleCallback(resolve, { timeout: idleTimeout });
1691
- })];
1692
- }
1693
- });
1694
- });
1635
+ function process$5(event) {
1636
+ schedule$1(encode$3.bind(this, event));
1695
1637
  }
1696
- // Use native implementation of requestIdleCallback if it exists.
1697
- // Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
1698
- // While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
1699
- // Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
1700
- // This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
1701
- // For non-blocking behavior, We need to know when browser has finished painting. This can be accomplished in two different ways (hacks):
1702
- // (1) Use MessageChannel to pass the message, and browser will receive the message right after paint event has occured.
1703
- // (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
1704
- // Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
1705
- // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
1706
- function requestIdleCallbackPolyfill(callback, options) {
1707
- var startTime = performance.now();
1708
- var channel = new MessageChannel();
1709
- var incoming = channel.port1;
1710
- var outgoing = channel.port2;
1711
- incoming.onmessage = function (event) {
1712
- var currentTime = performance.now();
1713
- var elapsed = currentTime - startTime;
1714
- var duration = currentTime - event.data;
1715
- if (duration > 30 /* Setting.LongTask */ && elapsed < options.timeout) {
1716
- requestAnimationFrame(function () { outgoing.postMessage(currentTime); });
1717
- }
1718
- else {
1719
- var didTimeout_1 = elapsed > options.timeout;
1720
- callback({
1721
- didTimeout: didTimeout_1,
1722
- timeRemaining: function () { return didTimeout_1 ? 30 /* Setting.LongTask */ : Math.max(0, 30 /* Setting.LongTask */ - duration); }
1723
- });
1724
- }
1725
- };
1726
- requestAnimationFrame(function () { outgoing.postMessage(performance.now()); });
1638
+ function reset$h() {
1639
+ data$c = null;
1640
+ clearTimeout(timeout$4);
1727
1641
  }
1728
- var requestIdleCallback = window["requestIdleCallback"] || requestIdleCallbackPolyfill;
1729
-
1730
- // Following code takes an array of tokens and transforms it to optimize for repeating tokens and make it efficient to send over the wire
1731
- // The way it works is that it iterate over all tokens and checks if the current token was already seen in the tokens array so far
1732
- // If so, it replaces the token with its reference (index). This helps us save bytes by not repeating the same value twice.
1733
- // E.g. If tokens array is: ["hello", "world", "coding", "language", "world", "language", "example"]
1734
- // Then the resulting tokens array after following code execution would be: ["hello", "world", "coding", "language", [1, 3], "example"]
1735
- // Where [1,3] points to tokens[1] => "world" and tokens[3] => "language"
1736
- function tokenize (tokens) {
1737
- var output = [];
1738
- var lookup = {};
1739
- var pointer = 0;
1740
- var reference = null;
1741
- for (var i = 0; i < tokens.length; i++) {
1742
- // Only optimize for string values
1743
- if (typeof tokens[i] === "string" /* Constant.String */) {
1744
- var token = tokens[i];
1745
- var index = lookup[token] || -1;
1746
- if (index >= 0) {
1747
- if (reference) {
1748
- reference.push(index);
1749
- }
1750
- else {
1751
- reference = [index];
1752
- output.push(reference);
1753
- pointer++;
1754
- }
1755
- }
1756
- else {
1757
- reference = null;
1758
- output.push(token);
1759
- lookup[token] = pointer++;
1760
- }
1761
- }
1762
- else {
1763
- // If the value is anything other than string, append it as it is to the output array
1764
- // And, also increment the pointer to stay in sync with output array
1765
- reference = null;
1766
- output.push(tokens[i]);
1767
- pointer++;
1768
- }
1769
- }
1770
- return output;
1642
+ function stop$r() {
1643
+ reset$h();
1771
1644
  }
1772
1645
 
1773
- var data$c;
1774
- function reset$k() {
1775
- data$c = null;
1646
+ var state$4 = [];
1647
+ var initialTop = null;
1648
+ var initialBottom = null;
1649
+ var timeout$3 = null;
1650
+ function start$s() {
1651
+ state$4 = [];
1652
+ recompute$4();
1776
1653
  }
1777
- function start$x() {
1778
- reset$k();
1779
- compute$9();
1654
+ function observe$7(root) {
1655
+ var frame = iframe(root);
1656
+ var node = frame ? frame.contentWindow : (root === document ? window : root);
1657
+ bind(node, "scroll", recompute$4, true);
1780
1658
  }
1781
- function compute$9() {
1782
- compute$9.dn = 19 /* FunctionNames.DocumentCompute */;
1783
- var body = document.body;
1784
- var d = document.documentElement;
1785
- var bodyClientWidth = body ? body.clientWidth : null;
1786
- var bodyScrollWidth = body ? body.scrollWidth : null;
1787
- var bodyOffsetWidth = body ? body.offsetWidth : null;
1788
- var documentClientWidth = d ? d.clientWidth : null;
1789
- var documentScrollWidth = d ? d.scrollWidth : null;
1790
- var documentOffsetWidth = d ? d.offsetWidth : null;
1791
- var width = Math.max(bodyClientWidth, bodyScrollWidth, bodyOffsetWidth, documentClientWidth, documentScrollWidth, documentOffsetWidth);
1792
- var bodyClientHeight = body ? body.clientHeight : null;
1793
- var bodyScrollHeight = body ? body.scrollHeight : null;
1794
- var bodyOffsetHeight = body ? body.offsetHeight : null;
1795
- var documentClientHeight = d ? d.clientHeight : null;
1796
- var documentScrollHeight = d ? d.scrollHeight : null;
1797
- var documentOffsetHeight = d ? d.offsetHeight : null;
1798
- var height = Math.max(bodyClientHeight, bodyScrollHeight, bodyOffsetHeight, documentClientHeight, documentScrollHeight, documentOffsetHeight);
1799
- // Check that width or height has changed from before, and also that width & height are not null values
1800
- if ((data$c === null || width !== data$c.width || height !== data$c.height) && width !== null && height !== null) {
1801
- data$c = { width: width, height: height };
1802
- encode$4(8 /* Event.Document */);
1659
+ function recompute$4(event) {
1660
+ if (event === void 0) { event = null; }
1661
+ recompute$4.dn = 13 /* FunctionNames.ScrollRecompute */;
1662
+ var w = window;
1663
+ var de = document.documentElement;
1664
+ var element = event ? target(event) : de;
1665
+ // If the target is a Document node, then identify corresponding documentElement and window for this document
1666
+ if (element && element.nodeType === Node.DOCUMENT_NODE) {
1667
+ var frame = iframe(element);
1668
+ w = frame ? frame.contentWindow : w;
1669
+ element = de = element.documentElement;
1670
+ }
1671
+ // Edge doesn't support scrollTop position on document.documentElement.
1672
+ // For cross browser compatibility, looking up pageYOffset on window if the scroll is on document.
1673
+ // And, if for some reason that is not available, fall back to looking up scrollTop on document.documentElement.
1674
+ var x = element === de && "pageXOffset" in w ? Math.round(w.pageXOffset) : Math.round(element.scrollLeft);
1675
+ var y = element === de && "pageYOffset" in w ? Math.round(w.pageYOffset) : Math.round(element.scrollTop);
1676
+ var width = window.innerWidth;
1677
+ var height = window.innerHeight;
1678
+ var xPosition = width / 3;
1679
+ var yOffset = width > height ? height * 0.15 : height * 0.2;
1680
+ var startYPosition = yOffset;
1681
+ var endYPosition = height - yOffset;
1682
+ var top = getPositionNode(xPosition, startYPosition);
1683
+ var bottom = getPositionNode(xPosition, endYPosition);
1684
+ var current = { time: time(event), event: 10 /* Event.Scroll */, data: { target: element, x: x, y: y, top: top, bottom: bottom } };
1685
+ // We don't send any scroll events if this is the first event and the current position is top (0,0)
1686
+ if ((event === null && x === 0 && y === 0) || (x === null || y === null)) {
1687
+ initialTop = top;
1688
+ initialBottom = bottom;
1689
+ return;
1690
+ }
1691
+ var length = state$4.length;
1692
+ var last = length > 1 ? state$4[length - 2] : null;
1693
+ if (last && similar(last, current)) {
1694
+ state$4.pop();
1803
1695
  }
1696
+ state$4.push(current);
1697
+ clearTimeout(timeout$3);
1698
+ timeout$3 = setTimeout(process$4, 500 /* Setting.LookAhead */, 10 /* Event.Scroll */);
1804
1699
  }
1805
- function stop$v() {
1806
- reset$k();
1700
+ function getPositionNode(x, y) {
1701
+ var _a, _b;
1702
+ var node;
1703
+ if ("caretPositionFromPoint" in document) {
1704
+ node = (_a = document.caretPositionFromPoint(x, y)) === null || _a === void 0 ? void 0 : _a.offsetNode;
1705
+ }
1706
+ else if ("caretRangeFromPoint" in document) {
1707
+ node = (_b = document.caretRangeFromPoint(x, y)) === null || _b === void 0 ? void 0 : _b.startContainer;
1708
+ }
1709
+ if (!node) {
1710
+ node = document.elementFromPoint(x, y);
1711
+ }
1712
+ if (node && node.nodeType === Node.TEXT_NODE) {
1713
+ node = node.parentNode;
1714
+ }
1715
+ return node;
1807
1716
  }
1808
-
1809
- var state$9 = [];
1810
- function start$w() {
1811
- reset$j();
1717
+ function reset$g() {
1718
+ state$4 = [];
1719
+ initialTop = null;
1720
+ initialBottom = null;
1812
1721
  }
1813
- function observe$c(root) {
1814
- bind(root, "change", recompute$8, true);
1722
+ function process$4(event) {
1723
+ schedule$1(encode$3.bind(this, event));
1815
1724
  }
1816
- function recompute$8(evt) {
1817
- recompute$8.dn = 5 /* FunctionNames.ChangeRecompute */;
1818
- var element = target(evt);
1819
- if (element) {
1820
- var value = element.value;
1821
- var checksum = value && value.length >= 5 /* Setting.WordLength */ && config$2.fraud && "password,secret,pass,social,ssn,code,hidden" /* Mask.Exclude */.indexOf(element.type) === -1 ? hash(value, 28 /* Setting.ChecksumPrecision */) : "" /* Constant.Empty */;
1822
- state$9.push({ time: time(evt), event: 42 /* Event.Change */, data: { target: target(evt), type: element.type, value: value, checksum: checksum } });
1823
- schedule$1(encode$3.bind(this, 42 /* Event.Change */));
1725
+ function similar(last, current) {
1726
+ var dx = last.data.x - current.data.x;
1727
+ var dy = last.data.y - current.data.y;
1728
+ return (dx * dx + dy * dy < 20 /* Setting.Distance */ * 20 /* Setting.Distance */) && (current.time - last.time < 25 /* Setting.Interval */);
1729
+ }
1730
+ function compute$9() {
1731
+ var _a, _b;
1732
+ compute$9.dn = 14 /* FunctionNames.ScrollCompute */;
1733
+ if (initialTop) {
1734
+ var top_1 = metadata$2(initialTop, null);
1735
+ log(31 /* Dimension.InitialScrollTop */, (_a = top_1 === null || top_1 === void 0 ? void 0 : top_1.hash) === null || _a === void 0 ? void 0 : _a.join("." /* Constant.Dot */));
1736
+ }
1737
+ if (initialBottom) {
1738
+ var bottom = metadata$2(initialBottom, null);
1739
+ log(32 /* Dimension.InitialScrollBottom */, (_b = bottom === null || bottom === void 0 ? void 0 : bottom.hash) === null || _b === void 0 ? void 0 : _b.join("." /* Constant.Dot */));
1824
1740
  }
1825
1741
  }
1826
- function reset$j() {
1827
- state$9 = [];
1828
- }
1829
- function stop$u() {
1830
- reset$j();
1831
- }
1832
-
1833
- function offset(element) {
1834
- var output = { x: 0, y: 0 };
1835
- // Walk up the chain to ensure we compute offset distance correctly
1836
- // In case where we may have nested IFRAMEs, we keep walking up until we get to the top most parent page
1837
- if (element && element.offsetParent) {
1838
- do {
1839
- var parent_1 = element.offsetParent;
1840
- var frame = parent_1 === null ? iframe(element.ownerDocument) : null;
1841
- output.x += element.offsetLeft;
1842
- output.y += element.offsetTop;
1843
- element = frame ? frame : parent_1;
1844
- } while (element);
1845
- }
1846
- return output;
1742
+ function stop$q() {
1743
+ clearTimeout(timeout$3);
1744
+ state$4 = [];
1745
+ initialTop = null;
1746
+ initialBottom = null;
1847
1747
  }
1848
1748
 
1849
- var UserInputTags = ["input", "textarea", "radio", "button", "canvas"];
1850
- var state$8 = [];
1851
- function start$v() {
1852
- reset$i();
1749
+ var data$b = null;
1750
+ var previous = null;
1751
+ var timeout$2 = null;
1752
+ function start$r() {
1753
+ reset$f();
1853
1754
  }
1854
- function observe$b(root) {
1855
- bind(root, "click", handler$3.bind(this, 9 /* Event.Click */, root), true);
1755
+ function observe$6(root) {
1756
+ bind(root, "selectstart", recompute$3.bind(this, root), true);
1757
+ bind(root, "selectionchange", recompute$3.bind(this, root), true);
1856
1758
  }
1857
- function handler$3(event, root, evt) {
1858
- handler$3.dn = 6 /* FunctionNames.ClickHandler */;
1859
- var frame = iframe(root);
1860
- var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1861
- var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
1862
- var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
1863
- // In case of iframe, we adjust (x,y) to be relative to top parent's origin
1864
- if (frame) {
1865
- var distance = offset(frame);
1866
- x = x ? x + Math.round(distance.x) : x;
1867
- y = y ? y + Math.round(distance.y) : y;
1759
+ function recompute$3(root) {
1760
+ recompute$3.dn = 15 /* FunctionNames.SelectionRecompute */;
1761
+ var doc = root.nodeType === Node.DOCUMENT_NODE ? root : document;
1762
+ var current = doc.getSelection();
1763
+ // Bail out if we don't have a valid selection
1764
+ if (current === null) {
1765
+ return;
1868
1766
  }
1869
- var t = target(evt);
1870
- // Find nearest anchor tag (<a/>) parent if current target node is part of one
1871
- // If present, we use the returned link element to populate text and link properties below
1872
- var a = link(t);
1873
- // Get layout rectangle for the target element
1874
- var l = layout$1(t);
1875
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
1876
- // This property helps differentiate between a keyboard navigation vs. pointer click
1877
- // In case of a keyboard navigation, we use center of target element as (x,y)
1878
- if (evt.detail === 0 && l) {
1879
- x = Math.round(l.x + (l.w / 2));
1880
- y = Math.round(l.y + (l.h / 2));
1767
+ // Bail out if we got a valid selection but not valid nodes
1768
+ // In Edge, selectionchange gets fired even on interactions like right clicks and
1769
+ // can result in null anchorNode and focusNode if there was no previous selection on page
1770
+ // Also, ignore any selections that start and end at the exact same point
1771
+ if ((current.anchorNode === null && current.focusNode === null) ||
1772
+ (current.anchorNode === current.focusNode && current.anchorOffset === current.focusOffset)) {
1773
+ return;
1881
1774
  }
1882
- var eX = l ? Math.max(Math.floor(((x - l.x) / l.w) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1883
- var eY = l ? Math.max(Math.floor(((y - l.y) / l.h) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1884
- // Check for null values before processing this event
1885
- if (x !== null && y !== null) {
1886
- var textInfo = text(t);
1887
- state$8.push({
1888
- time: time(evt),
1889
- event: event,
1890
- data: {
1891
- target: t,
1892
- x: x,
1893
- y: y,
1894
- eX: eX,
1895
- eY: eY,
1896
- button: evt.button,
1897
- reaction: reaction(t),
1898
- context: context(a),
1899
- text: textInfo.text,
1900
- link: a ? a.href : null,
1901
- hash: null,
1902
- trust: evt.isTrusted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */,
1903
- isFullText: textInfo.isFullText,
1904
- }
1905
- });
1906
- schedule$1(encode$3.bind(this, event));
1775
+ var startNode = data$b.start ? data$b.start : null;
1776
+ if (previous !== null && data$b.start !== null && startNode !== current.anchorNode) {
1777
+ clearTimeout(timeout$2);
1778
+ process$3(21 /* Event.Selection */);
1907
1779
  }
1780
+ data$b = {
1781
+ start: current.anchorNode,
1782
+ startOffset: current.anchorOffset,
1783
+ end: current.focusNode,
1784
+ endOffset: current.focusOffset
1785
+ };
1786
+ previous = current;
1787
+ clearTimeout(timeout$2);
1788
+ timeout$2 = setTimeout(process$3, 500 /* Setting.LookAhead */, 21 /* Event.Selection */);
1908
1789
  }
1909
- function link(node) {
1910
- while (node && node !== document) {
1911
- if (node.nodeType === Node.ELEMENT_NODE) {
1912
- var element = node;
1913
- if (element.tagName === "A") {
1914
- return element;
1915
- }
1916
- }
1917
- node = node.parentNode;
1918
- }
1919
- return null;
1790
+ function process$3(event) {
1791
+ schedule$1(encode$3.bind(this, event));
1920
1792
  }
1921
- function text(element) {
1922
- var output = null;
1923
- var isFullText = false;
1924
- if (element) {
1925
- // Grab text using "textContent" for most HTMLElements, however, use "value" for HTMLInputElements and "alt" for HTMLImageElement.
1926
- var t = element.textContent || String(element.value || '') || element.alt;
1927
- if (t) {
1928
- // Replace multiple occurrence of space characters with a single white space
1929
- // Also, trim any spaces at the beginning or at the end of string
1930
- var trimmedText = t.replace(/\s+/g, " " /* Constant.Space */).trim();
1931
- // Finally, send only first few characters as specified by the Setting
1932
- output = trimmedText.substring(0, 25 /* Setting.ClickText */);
1933
- isFullText = output.length === trimmedText.length;
1934
- }
1935
- }
1936
- return { text: output, isFullText: isFullText ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */ };
1793
+ function reset$f() {
1794
+ previous = null;
1795
+ data$b = { start: 0, startOffset: 0, end: 0, endOffset: 0 };
1937
1796
  }
1938
- function reaction(element) {
1939
- if (element.nodeType === Node.ELEMENT_NODE) {
1940
- var tag = element.tagName.toLowerCase();
1941
- if (UserInputTags.indexOf(tag) >= 0) {
1942
- return 0 /* BooleanFlag.False */;
1943
- }
1944
- }
1945
- return 1 /* BooleanFlag.True */;
1797
+ function stop$p() {
1798
+ reset$f();
1799
+ clearTimeout(timeout$2);
1946
1800
  }
1947
- function layout$1(element) {
1948
- var box = null;
1949
- var de = document.documentElement;
1950
- if (typeof element.getBoundingClientRect === "function") {
1951
- // getBoundingClientRect returns rectangle relative positioning to viewport
1952
- var rect = element.getBoundingClientRect();
1953
- if (rect && rect.width > 0 && rect.height > 0) {
1954
- // Add viewport's scroll position to rectangle to get position relative to document origin
1955
- // Also: using Math.floor() instead of Math.round() because in Edge,
1956
- // getBoundingClientRect returns partial pixel values (e.g. 162.5px) and Chrome already
1957
- // floors the value (e.g. 162px). This keeps consistent behavior across browsers.
1958
- box = {
1959
- x: Math.floor(rect.left + ("pageXOffset" in window ? window.pageXOffset : de.scrollLeft)),
1960
- y: Math.floor(rect.top + ("pageYOffset" in window ? window.pageYOffset : de.scrollTop)),
1961
- w: Math.floor(rect.width),
1962
- h: Math.floor(rect.height)
1963
- };
1964
- }
1965
- }
1966
- return box;
1801
+
1802
+ var state$3 = [];
1803
+ function start$q() {
1804
+ reset$e();
1967
1805
  }
1968
- function context(a) {
1969
- if (a && a.hasAttribute("target" /* Constant.Target */)) {
1970
- switch (a.getAttribute("target" /* Constant.Target */)) {
1971
- case "_blank" /* Constant.Blank */: return 1 /* BrowsingContext.Blank */;
1972
- case "_parent" /* Constant.Parent */: return 2 /* BrowsingContext.Parent */;
1973
- case "_top" /* Constant.Top */: return 3 /* BrowsingContext.Top */;
1974
- }
1975
- }
1976
- return 0 /* BrowsingContext.Self */;
1806
+ function observe$5(root) {
1807
+ bind(root, "submit", recompute$2, true);
1977
1808
  }
1978
- function reset$i() {
1979
- state$8 = [];
1809
+ function recompute$2(evt) {
1810
+ recompute$2.dn = 16 /* FunctionNames.SubmitRecompute */;
1811
+ state$3.push({ time: time(evt), event: 39 /* Event.Submit */, data: { target: target(evt) } });
1812
+ schedule$1(encode$3.bind(this, 39 /* Event.Submit */));
1980
1813
  }
1981
- function stop$t() {
1982
- reset$i();
1814
+ function reset$e() {
1815
+ state$3 = [];
1983
1816
  }
1984
-
1985
- var state$7 = [];
1986
- function start$u() {
1987
- reset$h();
1817
+ function stop$o() {
1818
+ reset$e();
1988
1819
  }
1989
- function observe$a(root) {
1990
- bind(root, "cut", recompute$7.bind(this, 0 /* Clipboard.Cut */), true);
1991
- bind(root, "copy", recompute$7.bind(this, 1 /* Clipboard.Copy */), true);
1992
- bind(root, "paste", recompute$7.bind(this, 2 /* Clipboard.Paste */), true);
1820
+
1821
+ var data$a;
1822
+ function start$p() {
1823
+ bind(window, "pagehide", recompute$1);
1993
1824
  }
1994
- function recompute$7(action, evt) {
1995
- recompute$7.dn = 7 /* FunctionNames.ClipboardRecompute */;
1996
- state$7.push({ time: time(evt), event: 38 /* Event.Clipboard */, data: { target: target(evt), action: action } });
1997
- schedule$1(encode$3.bind(this, 38 /* Event.Clipboard */));
1825
+ function recompute$1(evt) {
1826
+ recompute$1.dn = 17 /* FunctionNames.UnloadRecompute */;
1827
+ data$a = { name: evt.type, persisted: evt.persisted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */ };
1828
+ encode$3(26 /* Event.Unload */, time(evt));
1829
+ stop();
1998
1830
  }
1999
- function reset$h() {
2000
- state$7 = [];
1831
+ function reset$d() {
1832
+ data$a = null;
2001
1833
  }
2002
- function stop$s() {
2003
- reset$h();
1834
+ function stop$n() {
1835
+ reset$d();
2004
1836
  }
2005
1837
 
2006
- var timeout$6 = null;
2007
- var state$6 = [];
2008
- function start$t() {
2009
- reset$g();
1838
+ var data$9;
1839
+ function start$o() {
1840
+ bind(document, "visibilitychange", recompute);
1841
+ recompute();
2010
1842
  }
2011
- function observe$9(root) {
2012
- bind(root, "input", recompute$6, true);
1843
+ function recompute(evt) {
1844
+ if (evt === void 0) { evt = null; }
1845
+ recompute.dn = 18 /* FunctionNames.VisibilityRecompute */;
1846
+ data$9 = { visible: "visibilityState" in document ? document.visibilityState : "default" };
1847
+ encode$3(28 /* Event.Visibility */, time(evt));
2013
1848
  }
2014
- function recompute$6(evt) {
2015
- recompute$6.dn = 9 /* FunctionNames.InputRecompute */;
2016
- var input = target(evt);
2017
- var value = get(input);
2018
- if (input && input.type && value) {
2019
- var v = input.value;
2020
- var t = input.type;
2021
- switch (input.type) {
2022
- case "radio":
2023
- case "checkbox":
2024
- v = input.checked ? "true" : "false";
2025
- break;
2026
- }
2027
- var data = { target: input, value: v, type: t };
2028
- // If last entry in the queue is for the same target node as the current one, remove it so we can later swap it with current data.
2029
- if (state$6.length > 0 && (state$6[state$6.length - 1].data.target === data.target)) {
2030
- state$6.pop();
2031
- }
2032
- state$6.push({ time: time(evt), event: 27 /* Event.Input */, data: data });
2033
- clearTimeout(timeout$6);
2034
- timeout$6 = setTimeout(process$7, 1000 /* Setting.InputLookAhead */, 27 /* Event.Input */);
2035
- }
2036
- }
2037
- function process$7(event) {
2038
- schedule$1(encode$3.bind(this, event));
2039
- }
2040
- function reset$g() {
2041
- state$6 = [];
1849
+ function reset$c() {
1850
+ data$9 = null;
2042
1851
  }
2043
- function stop$r() {
2044
- clearTimeout(timeout$6);
2045
- reset$g();
1852
+ function stop$m() {
1853
+ reset$c();
2046
1854
  }
2047
1855
 
2048
- var state$5 = [];
2049
- var timeout$5 = null;
2050
- var hasPrimaryTouch = false;
2051
- var primaryTouchId = 0;
2052
- var activeTouchPointIds = new Set();
2053
- function start$s() {
2054
- reset$f();
1856
+ function start$n() {
1857
+ start$n.dn = 8 /* FunctionNames.InteractionStart */;
1858
+ start$g();
1859
+ start$x();
1860
+ start$w();
1861
+ start$u();
1862
+ start$v();
1863
+ start$t();
1864
+ start$o();
1865
+ start$s();
1866
+ start$r();
1867
+ start$y();
1868
+ start$q();
1869
+ start$p();
2055
1870
  }
2056
- function observe$8(root) {
2057
- bind(root, "mousedown", mouse.bind(this, 13 /* Event.MouseDown */, root), true);
2058
- bind(root, "mouseup", mouse.bind(this, 14 /* Event.MouseUp */, root), true);
2059
- bind(root, "mousemove", mouse.bind(this, 12 /* Event.MouseMove */, root), true);
2060
- bind(root, "wheel", mouse.bind(this, 15 /* Event.MouseWheel */, root), true);
2061
- bind(root, "dblclick", mouse.bind(this, 16 /* Event.DoubleClick */, root), true);
2062
- bind(root, "touchstart", touch.bind(this, 17 /* Event.TouchStart */, root), true);
2063
- bind(root, "touchend", touch.bind(this, 18 /* Event.TouchEnd */, root), true);
2064
- bind(root, "touchmove", touch.bind(this, 19 /* Event.TouchMove */, root), true);
2065
- bind(root, "touchcancel", touch.bind(this, 20 /* Event.TouchCancel */, root), true);
1871
+ function stop$l() {
1872
+ stop$e();
1873
+ stop$v();
1874
+ stop$u();
1875
+ stop$s();
1876
+ stop$t();
1877
+ stop$r();
1878
+ stop$m();
1879
+ stop$q();
1880
+ stop$p();
1881
+ stop$w();
1882
+ stop$o();
1883
+ stop$n();
2066
1884
  }
2067
- function mouse(event, root, evt) {
2068
- mouse.dn = 10 /* FunctionNames.PointerMouse */;
2069
- var frame = iframe(root);
2070
- var d = frame ? frame.contentDocument.documentElement : document.documentElement;
2071
- var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
2072
- var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
2073
- // In case of iframe, we adjust (x,y) to be relative to top parent's origin
2074
- if (frame) {
2075
- var distance = offset(frame);
2076
- x = x ? x + Math.round(distance.x) : x;
2077
- y = y ? y + Math.round(distance.y) : y;
2078
- }
2079
- // Check for null values before processing this event
2080
- if (x !== null && y !== null) {
2081
- handler$2({ time: time(evt), event: event, data: { target: target(evt), x: x, y: y } });
1885
+ function observe$4(root) {
1886
+ observe$7(root);
1887
+ // Only monitor following interactions if the root node is a document
1888
+ // In case of shadow DOM, following events automatically bubble up to the parent document.
1889
+ if (root.nodeType === Node.DOCUMENT_NODE) {
1890
+ observe$b(root);
1891
+ observe$a(root);
1892
+ observe$8(root);
1893
+ observe$9(root);
1894
+ observe$6(root);
1895
+ observe$c(root);
1896
+ observe$5(root);
2082
1897
  }
2083
1898
  }
2084
- function touch(event, root, evt) {
2085
- touch.dn = 11 /* FunctionNames.PointerTouch */;
2086
- var frame = iframe(root);
2087
- var d = frame ? frame.contentDocument.documentElement : document.documentElement;
2088
- var touches = evt.changedTouches;
2089
- var t = time(evt);
2090
- if (touches) {
2091
- for (var i = 0; i < touches.length; i++) {
2092
- var entry = touches[i];
2093
- var x = "clientX" in entry ? Math.round(entry["clientX"] + d.scrollLeft) : null;
2094
- var y = "clientY" in entry ? Math.round(entry["clientY"] + d.scrollTop) : null;
2095
- x = x && frame ? x + Math.round(frame.offsetLeft) : x;
2096
- y = y && frame ? y + Math.round(frame.offsetTop) : y;
2097
- // We cannot rely on identifier to determine primary touch as its value doesn't always start with 0.
2098
- // Safari/Webkit uses the address of the UITouch object as the identifier value for each touch point.
2099
- var id = "identifier" in entry ? entry["identifier"] : undefined;
2100
- switch (event) {
2101
- case 17 /* Event.TouchStart */:
2102
- if (activeTouchPointIds.size === 0) {
2103
- // Track presence of primary touch separately to handle scenarios when same id is repeated
2104
- hasPrimaryTouch = true;
2105
- primaryTouchId = id;
2106
- }
2107
- activeTouchPointIds.add(id);
2108
- break;
2109
- case 18 /* Event.TouchEnd */:
2110
- case 20 /* Event.TouchCancel */:
2111
- activeTouchPointIds.delete(id);
2112
- break;
2113
- }
2114
- var isPrimary = hasPrimaryTouch && primaryTouchId === id;
2115
- // Check for null values before processing this event
2116
- if (x !== null && y !== null) {
2117
- handler$2({ time: t, event: event, data: { target: target(evt), x: x, y: y, id: id, isPrimary: isPrimary } });
2118
- }
2119
- // Reset primary touch point id once touch event ends
2120
- if (event === 20 /* Event.TouchCancel */ || event === 18 /* Event.TouchEnd */) {
2121
- if (primaryTouchId === id) {
2122
- hasPrimaryTouch = false;
1899
+
1900
+ var interaction = /*#__PURE__*/Object.freeze({
1901
+ __proto__: null,
1902
+ observe: observe$4,
1903
+ start: start$n,
1904
+ stop: stop$l
1905
+ });
1906
+
1907
+ // Following code takes an array of tokens and transforms it to optimize for repeating tokens and make it efficient to send over the wire
1908
+ // The way it works is that it iterate over all tokens and checks if the current token was already seen in the tokens array so far
1909
+ // If so, it replaces the token with its reference (index). This helps us save bytes by not repeating the same value twice.
1910
+ // E.g. If tokens array is: ["hello", "world", "coding", "language", "world", "language", "example"]
1911
+ // Then the resulting tokens array after following code execution would be: ["hello", "world", "coding", "language", [1, 3], "example"]
1912
+ // Where [1,3] points to tokens[1] => "world" and tokens[3] => "language"
1913
+ function tokenize (tokens) {
1914
+ var output = [];
1915
+ var lookup = {};
1916
+ var pointer = 0;
1917
+ var reference = null;
1918
+ for (var i = 0; i < tokens.length; i++) {
1919
+ // Only optimize for string values
1920
+ if (typeof tokens[i] === "string" /* Constant.String */) {
1921
+ var token = tokens[i];
1922
+ var index = lookup[token] || -1;
1923
+ if (index >= 0) {
1924
+ if (reference) {
1925
+ reference.push(index);
1926
+ }
1927
+ else {
1928
+ reference = [index];
1929
+ output.push(reference);
1930
+ pointer++;
2123
1931
  }
2124
1932
  }
1933
+ else {
1934
+ reference = null;
1935
+ output.push(token);
1936
+ lookup[token] = pointer++;
1937
+ }
1938
+ }
1939
+ else {
1940
+ // If the value is anything other than string, append it as it is to the output array
1941
+ // And, also increment the pointer to stay in sync with output array
1942
+ reference = null;
1943
+ output.push(tokens[i]);
1944
+ pointer++;
2125
1945
  }
2126
1946
  }
1947
+ return output;
2127
1948
  }
2128
- function handler$2(current) {
2129
- switch (current.event) {
2130
- case 12 /* Event.MouseMove */:
2131
- case 15 /* Event.MouseWheel */:
2132
- case 19 /* Event.TouchMove */:
2133
- var length_1 = state$5.length;
2134
- var last = length_1 > 1 ? state$5[length_1 - 2] : null;
2135
- if (last && similar$1(last, current)) {
2136
- state$5.pop();
2137
- }
2138
- state$5.push(current);
2139
- clearTimeout(timeout$5);
2140
- timeout$5 = setTimeout(process$6, 500 /* Setting.LookAhead */, current.event);
2141
- break;
2142
- default:
2143
- state$5.push(current);
2144
- process$6(current.event);
2145
- break;
1949
+
1950
+ var sheetUpdateState = [];
1951
+ var sheetAdoptionState = [];
1952
+ var replace = null;
1953
+ var replaceSync = null;
1954
+ var styleSheetId = 'claritySheetId';
1955
+ var styleSheetMap = {};
1956
+ var styleTimeMap = {};
1957
+ var documentNodes = [];
1958
+ var createdSheetIds = [];
1959
+ function start$m() {
1960
+ if (window['CSSStyleSheet'] && CSSStyleSheet.prototype) {
1961
+ if (replace === null) {
1962
+ replace = CSSStyleSheet.prototype.replace;
1963
+ CSSStyleSheet.prototype.replace = function () {
1964
+ if (active()) {
1965
+ max(36 /* Metric.ConstructedStyles */, 1);
1966
+ // if we haven't seen this stylesheet on this page yet, wait until the checkDocumentStyles has found it
1967
+ // and attached the sheet to a document. This way the timestamp of the style sheet creation will align
1968
+ // to when it is used in the document rather than potentially being misaligned during the traverse process.
1969
+ if (createdSheetIds.indexOf(this[styleSheetId]) > -1) {
1970
+ trackStyleChange(time(), this[styleSheetId], 1 /* StyleSheetOperation.Replace */, arguments[0]);
1971
+ }
1972
+ }
1973
+ return replace.apply(this, arguments);
1974
+ };
1975
+ }
1976
+ if (replaceSync === null) {
1977
+ replaceSync = CSSStyleSheet.prototype.replaceSync;
1978
+ CSSStyleSheet.prototype.replaceSync = function () {
1979
+ if (active()) {
1980
+ max(36 /* Metric.ConstructedStyles */, 1);
1981
+ // if we haven't seen this stylesheet on this page yet, wait until the checkDocumentStyles has found it
1982
+ // and attached the sheet to a document. This way the timestamp of the style sheet creation will align
1983
+ // to when it is used in the document rather than potentially being misaligned during the traverse process.
1984
+ if (createdSheetIds.indexOf(this[styleSheetId]) > -1) {
1985
+ trackStyleChange(time(), this[styleSheetId], 2 /* StyleSheetOperation.ReplaceSync */, arguments[0]);
1986
+ }
1987
+ }
1988
+ return replaceSync.apply(this, arguments);
1989
+ };
1990
+ }
2146
1991
  }
2147
1992
  }
2148
- function process$6(event) {
2149
- schedule$1(encode$3.bind(this, event));
2150
- }
2151
- function reset$f() {
2152
- state$5 = [];
2153
- }
2154
- function similar$1(last, current) {
2155
- var dx = last.data.x - current.data.x;
2156
- var dy = last.data.y - current.data.y;
2157
- var distance = Math.sqrt(dx * dx + dy * dy);
2158
- var gap = current.time - last.time;
2159
- var match = current.data.target === last.data.target;
2160
- return current.event === last.event && match && distance < 20 /* Setting.Distance */ && gap < 25 /* Setting.Interval */;
2161
- }
2162
- function stop$q() {
2163
- clearTimeout(timeout$5);
2164
- // Send out any pending pointer events in the pipeline
2165
- if (state$5.length > 0) {
2166
- process$6(state$5[state$5.length - 1].event);
1993
+ function checkDocumentStyles(documentNode, timestamp) {
1994
+ if (documentNodes.indexOf(documentNode) === -1) {
1995
+ documentNodes.push(documentNode);
2167
1996
  }
2168
- }
2169
-
2170
- var data$b;
2171
- var timeout$4 = null;
2172
- var initialStateLogged = false;
2173
- function start$r() {
2174
- initialStateLogged = false;
2175
- bind(window, "resize", recompute$5);
2176
- recompute$5();
2177
- }
2178
- function recompute$5() {
2179
- recompute$5.dn = 12 /* FunctionNames.ResizeRecompute */;
2180
- var de = document.documentElement;
2181
- // window.innerWidth includes width of the scrollbar and is not a true representation of the viewport width.
2182
- // Therefore, when possible, use documentElement's clientWidth property.
2183
- data$b = {
2184
- width: de && "clientWidth" in de ? Math.min(de.clientWidth, window.innerWidth) : window.innerWidth,
2185
- height: de && "clientHeight" in de ? Math.min(de.clientHeight, window.innerHeight) : window.innerHeight,
2186
- };
2187
- if (initialStateLogged) {
2188
- clearTimeout(timeout$4);
2189
- timeout$4 = setTimeout(process$5, 500 /* Setting.LookAhead */, 11 /* Event.Resize */);
2190
- }
2191
- else {
2192
- encode$3(11 /* Event.Resize */);
2193
- initialStateLogged = true;
2194
- }
2195
- }
2196
- function process$5(event) {
2197
- schedule$1(encode$3.bind(this, event));
2198
- }
2199
- function reset$e() {
2200
- data$b = null;
2201
- clearTimeout(timeout$4);
2202
- }
2203
- function stop$p() {
2204
- reset$e();
2205
- }
2206
-
2207
- var state$4 = [];
2208
- var initialTop = null;
2209
- var initialBottom = null;
2210
- var timeout$3 = null;
2211
- function start$q() {
2212
- state$4 = [];
2213
- recompute$4();
2214
- }
2215
- function observe$7(root) {
2216
- var frame = iframe(root);
2217
- var node = frame ? frame.contentWindow : (root === document ? window : root);
2218
- bind(node, "scroll", recompute$4, true);
2219
- }
2220
- function recompute$4(event) {
2221
- if (event === void 0) { event = null; }
2222
- recompute$4.dn = 13 /* FunctionNames.ScrollRecompute */;
2223
- var w = window;
2224
- var de = document.documentElement;
2225
- var element = event ? target(event) : de;
2226
- // If the target is a Document node, then identify corresponding documentElement and window for this document
2227
- if (element && element.nodeType === Node.DOCUMENT_NODE) {
2228
- var frame = iframe(element);
2229
- w = frame ? frame.contentWindow : w;
2230
- element = de = element.documentElement;
2231
- }
2232
- // Edge doesn't support scrollTop position on document.documentElement.
2233
- // For cross browser compatibility, looking up pageYOffset on window if the scroll is on document.
2234
- // And, if for some reason that is not available, fall back to looking up scrollTop on document.documentElement.
2235
- var x = element === de && "pageXOffset" in w ? Math.round(w.pageXOffset) : Math.round(element.scrollLeft);
2236
- var y = element === de && "pageYOffset" in w ? Math.round(w.pageYOffset) : Math.round(element.scrollTop);
2237
- var width = window.innerWidth;
2238
- var height = window.innerHeight;
2239
- var xPosition = width / 3;
2240
- var yOffset = width > height ? height * 0.15 : height * 0.2;
2241
- var startYPosition = yOffset;
2242
- var endYPosition = height - yOffset;
2243
- var top = getPositionNode(xPosition, startYPosition);
2244
- var bottom = getPositionNode(xPosition, endYPosition);
2245
- var current = { time: time(event), event: 10 /* Event.Scroll */, data: { target: element, x: x, y: y, top: top, bottom: bottom } };
2246
- // We don't send any scroll events if this is the first event and the current position is top (0,0)
2247
- if ((event === null && x === 0 && y === 0) || (x === null || y === null)) {
2248
- initialTop = top;
2249
- initialBottom = bottom;
1997
+ timestamp = timestamp || time();
1998
+ if (!(documentNode === null || documentNode === void 0 ? void 0 : documentNode.adoptedStyleSheets)) {
1999
+ // if we don't have adoptedStyledSheets on the Node passed to us, we can short circuit.
2250
2000
  return;
2251
2001
  }
2252
- var length = state$4.length;
2253
- var last = length > 1 ? state$4[length - 2] : null;
2254
- if (last && similar(last, current)) {
2255
- state$4.pop();
2256
- }
2257
- state$4.push(current);
2258
- clearTimeout(timeout$3);
2259
- timeout$3 = setTimeout(process$4, 500 /* Setting.LookAhead */, 10 /* Event.Scroll */);
2260
- }
2261
- function getPositionNode(x, y) {
2262
- var _a, _b;
2263
- var node;
2264
- if ("caretPositionFromPoint" in document) {
2265
- node = (_a = document.caretPositionFromPoint(x, y)) === null || _a === void 0 ? void 0 : _a.offsetNode;
2266
- }
2267
- else if ("caretRangeFromPoint" in document) {
2268
- node = (_b = document.caretRangeFromPoint(x, y)) === null || _b === void 0 ? void 0 : _b.startContainer;
2002
+ max(36 /* Metric.ConstructedStyles */, 1);
2003
+ var currentStyleSheets = [];
2004
+ for (var _i = 0, _a = documentNode.adoptedStyleSheets; _i < _a.length; _i++) {
2005
+ var styleSheet = _a[_i];
2006
+ // If we haven't seen this style sheet on this page yet, we create a reference to it for the visualizer.
2007
+ // For SPA or times in which Clarity restarts on a given page, our visualizer would lose context
2008
+ // on the previously created style sheet for page N-1.
2009
+ // Then we synthetically call replaceSync with its contents to bootstrap it
2010
+ if (!styleSheet[styleSheetId] || createdSheetIds.indexOf(styleSheet[styleSheetId]) === -1) {
2011
+ styleSheet[styleSheetId] = shortid();
2012
+ createdSheetIds.push(styleSheet[styleSheetId]);
2013
+ trackStyleChange(timestamp, styleSheet[styleSheetId], 0 /* StyleSheetOperation.Create */);
2014
+ trackStyleChange(timestamp, styleSheet[styleSheetId], 2 /* StyleSheetOperation.ReplaceSync */, getCssRules(styleSheet));
2015
+ }
2016
+ currentStyleSheets.push(styleSheet[styleSheetId]);
2269
2017
  }
2270
- if (!node) {
2271
- node = document.elementFromPoint(x, y);
2018
+ var documentId = getId(documentNode, true);
2019
+ if (!styleSheetMap[documentId]) {
2020
+ styleSheetMap[documentId] = [];
2272
2021
  }
2273
- if (node && node.nodeType === Node.TEXT_NODE) {
2274
- node = node.parentNode;
2022
+ if (!arraysEqual(currentStyleSheets, styleSheetMap[documentId])) {
2023
+ // Using -1 to signify the root document node as we don't track that as part of our nodeMap
2024
+ trackStyleAdoption(timestamp, documentNode == document ? -1 : getId(documentNode), 3 /* StyleSheetOperation.SetAdoptedStyles */, currentStyleSheets);
2025
+ styleSheetMap[documentId] = currentStyleSheets;
2026
+ styleTimeMap[documentId] = timestamp;
2275
2027
  }
2276
- return node;
2277
- }
2278
- function reset$d() {
2279
- state$4 = [];
2280
- initialTop = null;
2281
- initialBottom = null;
2282
- }
2283
- function process$4(event) {
2284
- schedule$1(encode$3.bind(this, event));
2285
- }
2286
- function similar(last, current) {
2287
- var dx = last.data.x - current.data.x;
2288
- var dy = last.data.y - current.data.y;
2289
- return (dx * dx + dy * dy < 20 /* Setting.Distance */ * 20 /* Setting.Distance */) && (current.time - last.time < 25 /* Setting.Interval */);
2290
2028
  }
2291
2029
  function compute$8() {
2292
- var _a, _b;
2293
- compute$8.dn = 14 /* FunctionNames.ScrollCompute */;
2294
- if (initialTop) {
2295
- var top_1 = metadata$2(initialTop, null);
2296
- log(31 /* Dimension.InitialScrollTop */, (_a = top_1 === null || top_1 === void 0 ? void 0 : top_1.hash) === null || _a === void 0 ? void 0 : _a.join("." /* Constant.Dot */));
2297
- }
2298
- if (initialBottom) {
2299
- var bottom = metadata$2(initialBottom, null);
2300
- log(32 /* Dimension.InitialScrollBottom */, (_b = bottom === null || bottom === void 0 ? void 0 : bottom.hash) === null || _b === void 0 ? void 0 : _b.join("." /* Constant.Dot */));
2030
+ for (var _i = 0, documentNodes_1 = documentNodes; _i < documentNodes_1.length; _i++) {
2031
+ var documentNode = documentNodes_1[_i];
2032
+ var docId = documentNode == document ? -1 : getId(documentNode);
2033
+ var ts = docId in styleTimeMap ? styleTimeMap[docId] : null;
2034
+ checkDocumentStyles(document, ts);
2301
2035
  }
2302
2036
  }
2303
- function stop$o() {
2304
- clearTimeout(timeout$3);
2305
- state$4 = [];
2306
- initialTop = null;
2307
- initialBottom = null;
2308
- }
2309
-
2310
- var data$a = null;
2311
- var previous = null;
2312
- var timeout$2 = null;
2313
- function start$p() {
2314
- reset$c();
2315
- }
2316
- function observe$6(root) {
2317
- bind(root, "selectstart", recompute$3.bind(this, root), true);
2318
- bind(root, "selectionchange", recompute$3.bind(this, root), true);
2037
+ function reset$b() {
2038
+ sheetAdoptionState = [];
2039
+ sheetUpdateState = [];
2319
2040
  }
2320
- function recompute$3(root) {
2321
- recompute$3.dn = 15 /* FunctionNames.SelectionRecompute */;
2322
- var doc = root.nodeType === Node.DOCUMENT_NODE ? root : document;
2323
- var current = doc.getSelection();
2324
- // Bail out if we don't have a valid selection
2325
- if (current === null) {
2326
- return;
2327
- }
2328
- // Bail out if we got a valid selection but not valid nodes
2329
- // In Edge, selectionchange gets fired even on interactions like right clicks and
2330
- // can result in null anchorNode and focusNode if there was no previous selection on page
2331
- // Also, ignore any selections that start and end at the exact same point
2332
- if ((current.anchorNode === null && current.focusNode === null) ||
2333
- (current.anchorNode === current.focusNode && current.anchorOffset === current.focusOffset)) {
2334
- return;
2335
- }
2336
- var startNode = data$a.start ? data$a.start : null;
2337
- if (previous !== null && data$a.start !== null && startNode !== current.anchorNode) {
2338
- clearTimeout(timeout$2);
2339
- process$3(21 /* Event.Selection */);
2340
- }
2341
- data$a = {
2342
- start: current.anchorNode,
2343
- startOffset: current.anchorOffset,
2344
- end: current.focusNode,
2345
- endOffset: current.focusOffset
2346
- };
2347
- previous = current;
2348
- clearTimeout(timeout$2);
2349
- timeout$2 = setTimeout(process$3, 500 /* Setting.LookAhead */, 21 /* Event.Selection */);
2041
+ function stop$k() {
2042
+ styleSheetMap = {};
2043
+ styleTimeMap = {};
2044
+ documentNodes = [];
2045
+ createdSheetIds = [];
2046
+ reset$b();
2350
2047
  }
2351
- function process$3(event) {
2352
- schedule$1(encode$3.bind(this, event));
2048
+ function trackStyleChange(time, id, operation, cssRules) {
2049
+ sheetUpdateState.push({
2050
+ time: time,
2051
+ event: 46 /* Event.StyleSheetUpdate */,
2052
+ data: {
2053
+ id: id,
2054
+ operation: operation,
2055
+ cssRules: cssRules
2056
+ }
2057
+ });
2058
+ encode$4(46 /* Event.StyleSheetUpdate */);
2353
2059
  }
2354
- function reset$c() {
2355
- previous = null;
2356
- data$a = { start: 0, startOffset: 0, end: 0, endOffset: 0 };
2060
+ function trackStyleAdoption(time, id, operation, newIds) {
2061
+ sheetAdoptionState.push({
2062
+ time: time,
2063
+ event: 45 /* Event.StyleSheetAdoption */,
2064
+ data: {
2065
+ id: id,
2066
+ operation: operation,
2067
+ newIds: newIds
2068
+ }
2069
+ });
2070
+ encode$4(45 /* Event.StyleSheetAdoption */);
2357
2071
  }
2358
- function stop$n() {
2359
- reset$c();
2360
- clearTimeout(timeout$2);
2072
+ function arraysEqual(a, b) {
2073
+ if (a.length !== b.length) {
2074
+ return false;
2075
+ }
2076
+ return a.every(function (value, index) { return value === b[index]; });
2361
2077
  }
2362
2078
 
2363
- var state$3 = [];
2364
- function start$o() {
2365
- reset$b();
2079
+ var state$2 = [];
2080
+ var elementAnimate = null;
2081
+ var animationPlay = null;
2082
+ var animationPause = null;
2083
+ var animationCommitStyles = null;
2084
+ var animationCancel = null;
2085
+ var animationFinish = null;
2086
+ var animationId = 'clarityAnimationId';
2087
+ var operationCount = 'clarityOperationCount';
2088
+ var maxOperations = 20;
2089
+ function start$l() {
2090
+ if (window["Animation"] &&
2091
+ window["Animation"].prototype &&
2092
+ window["KeyframeEffect"] &&
2093
+ window["KeyframeEffect"].prototype &&
2094
+ window["KeyframeEffect"].prototype.getKeyframes &&
2095
+ window["KeyframeEffect"].prototype.getTiming) {
2096
+ reset$a();
2097
+ overrideAnimationHelper(animationPlay, "play");
2098
+ overrideAnimationHelper(animationPause, "pause");
2099
+ overrideAnimationHelper(animationCommitStyles, "commitStyles");
2100
+ overrideAnimationHelper(animationCancel, "cancel");
2101
+ overrideAnimationHelper(animationFinish, "finish");
2102
+ if (elementAnimate === null) {
2103
+ elementAnimate = Element.prototype.animate;
2104
+ Element.prototype.animate = function () {
2105
+ var createdAnimation = elementAnimate.apply(this, arguments);
2106
+ trackAnimationOperation(createdAnimation, "play");
2107
+ return createdAnimation;
2108
+ };
2109
+ }
2110
+ if (document.getAnimations) {
2111
+ for (var _i = 0, _a = document.getAnimations(); _i < _a.length; _i++) {
2112
+ var animation = _a[_i];
2113
+ if (animation.playState === "finished") {
2114
+ trackAnimationOperation(animation, "finish");
2115
+ }
2116
+ else if (animation.playState === "paused" || animation.playState === "idle") {
2117
+ trackAnimationOperation(animation, "pause");
2118
+ }
2119
+ else if (animation.playState === "running") {
2120
+ trackAnimationOperation(animation, "play");
2121
+ }
2122
+ }
2123
+ }
2124
+ }
2366
2125
  }
2367
- function observe$5(root) {
2368
- bind(root, "submit", recompute$2, true);
2126
+ function reset$a() {
2127
+ state$2 = [];
2369
2128
  }
2370
- function recompute$2(evt) {
2371
- recompute$2.dn = 16 /* FunctionNames.SubmitRecompute */;
2372
- state$3.push({ time: time(evt), event: 39 /* Event.Submit */, data: { target: target(evt) } });
2373
- schedule$1(encode$3.bind(this, 39 /* Event.Submit */));
2129
+ function track$6(time, id, operation, keyFrames, timing, targetId, timeline) {
2130
+ state$2.push({
2131
+ time: time,
2132
+ event: 44 /* Event.Animation */,
2133
+ data: {
2134
+ id: id,
2135
+ operation: operation,
2136
+ keyFrames: keyFrames,
2137
+ timing: timing,
2138
+ targetId: targetId,
2139
+ timeline: timeline
2140
+ }
2141
+ });
2142
+ encode$4(44 /* Event.Animation */);
2374
2143
  }
2375
- function reset$b() {
2376
- state$3 = [];
2144
+ function stop$j() {
2145
+ reset$a();
2377
2146
  }
2378
- function stop$m() {
2379
- reset$b();
2147
+ function overrideAnimationHelper(functionToOverride, name) {
2148
+ if (functionToOverride === null) {
2149
+ functionToOverride = Animation.prototype[name];
2150
+ Animation.prototype[name] = function () {
2151
+ trackAnimationOperation(this, name);
2152
+ return functionToOverride.apply(this, arguments);
2153
+ };
2154
+ }
2155
+ }
2156
+ function trackAnimationOperation(animation, name) {
2157
+ if (active()) {
2158
+ var effect = animation.effect;
2159
+ var target = getId(effect.target);
2160
+ if (target !== null && effect.getKeyframes && effect.getTiming) {
2161
+ if (!animation[animationId]) {
2162
+ animation[animationId] = shortid();
2163
+ animation[operationCount] = 0;
2164
+ var keyframes = effect.getKeyframes();
2165
+ var timing = effect.getTiming();
2166
+ track$6(time(), animation[animationId], 0 /* AnimationOperation.Create */, JSON.stringify(keyframes), JSON.stringify(timing), target);
2167
+ }
2168
+ if (animation[operationCount]++ < maxOperations) {
2169
+ var operation = null;
2170
+ switch (name) {
2171
+ case "play":
2172
+ operation = 1 /* AnimationOperation.Play */;
2173
+ break;
2174
+ case "pause":
2175
+ operation = 2 /* AnimationOperation.Pause */;
2176
+ break;
2177
+ case "cancel":
2178
+ operation = 3 /* AnimationOperation.Cancel */;
2179
+ break;
2180
+ case "finish":
2181
+ operation = 4 /* AnimationOperation.Finish */;
2182
+ break;
2183
+ case "commitStyles":
2184
+ operation = 5 /* AnimationOperation.CommitStyles */;
2185
+ break;
2186
+ }
2187
+ if (operation) {
2188
+ track$6(time(), animation[animationId], operation);
2189
+ }
2190
+ }
2191
+ }
2192
+ }
2380
2193
  }
2381
2194
 
2382
- var data$9;
2383
- function start$n() {
2384
- bind(window, "pagehide", recompute$1);
2195
+ function encode$4 (type, timer, ts) {
2196
+ if (timer === void 0) { timer = null; }
2197
+ if (ts === void 0) { ts = null; }
2198
+ return __awaiter(this, void 0, void 0, function () {
2199
+ var eventTime, tokens, _a, d, _i, _b, r, _c, _d, entry, _e, _f, entry, _g, _h, entry, values, _j, values_1, value, state, data, active, suspend, privacy, mangle, keys, _k, keys_1, key, box, factor, attr;
2200
+ return __generator(this, function (_l) {
2201
+ switch (_l.label) {
2202
+ case 0:
2203
+ eventTime = ts || time();
2204
+ tokens = [eventTime, type];
2205
+ _a = type;
2206
+ switch (_a) {
2207
+ case 8 /* Event.Document */: return [3 /*break*/, 1];
2208
+ case 7 /* Event.Region */: return [3 /*break*/, 2];
2209
+ case 45 /* Event.StyleSheetAdoption */: return [3 /*break*/, 3];
2210
+ case 46 /* Event.StyleSheetUpdate */: return [3 /*break*/, 3];
2211
+ case 44 /* Event.Animation */: return [3 /*break*/, 4];
2212
+ case 5 /* Event.Discover */: return [3 /*break*/, 5];
2213
+ case 6 /* Event.Mutation */: return [3 /*break*/, 5];
2214
+ }
2215
+ return [3 /*break*/, 12];
2216
+ case 1:
2217
+ d = data$8;
2218
+ tokens.push(d.width);
2219
+ tokens.push(d.height);
2220
+ track$8(type, d.width, d.height);
2221
+ queue(tokens);
2222
+ return [3 /*break*/, 12];
2223
+ case 2:
2224
+ for (_i = 0, _b = state$1; _i < _b.length; _i++) {
2225
+ r = _b[_i];
2226
+ tokens = [r.time, 7 /* Event.Region */];
2227
+ tokens.push(r.data.id);
2228
+ tokens.push(r.data.interaction);
2229
+ tokens.push(r.data.visibility);
2230
+ tokens.push(r.data.name);
2231
+ queue(tokens);
2232
+ }
2233
+ reset$6();
2234
+ return [3 /*break*/, 12];
2235
+ case 3:
2236
+ for (_c = 0, _d = sheetAdoptionState; _c < _d.length; _c++) {
2237
+ entry = _d[_c];
2238
+ tokens = [entry.time, entry.event];
2239
+ tokens.push(entry.data.id);
2240
+ tokens.push(entry.data.operation);
2241
+ tokens.push(entry.data.newIds);
2242
+ queue(tokens);
2243
+ }
2244
+ for (_e = 0, _f = sheetUpdateState; _e < _f.length; _e++) {
2245
+ entry = _f[_e];
2246
+ tokens = [entry.time, entry.event];
2247
+ tokens.push(entry.data.id);
2248
+ tokens.push(entry.data.operation);
2249
+ tokens.push(entry.data.cssRules);
2250
+ queue(tokens);
2251
+ }
2252
+ reset$b();
2253
+ return [3 /*break*/, 12];
2254
+ case 4:
2255
+ for (_g = 0, _h = state$2; _g < _h.length; _g++) {
2256
+ entry = _h[_g];
2257
+ tokens = [entry.time, entry.event];
2258
+ tokens.push(entry.data.id);
2259
+ tokens.push(entry.data.operation);
2260
+ tokens.push(entry.data.keyFrames);
2261
+ tokens.push(entry.data.timing);
2262
+ tokens.push(entry.data.timeline);
2263
+ tokens.push(entry.data.targetId);
2264
+ queue(tokens);
2265
+ }
2266
+ reset$a();
2267
+ return [3 /*break*/, 12];
2268
+ case 5:
2269
+ // Check if we are operating within the context of the current page
2270
+ if (state$a(timer) === 2 /* Task.Stop */) {
2271
+ return [3 /*break*/, 12];
2272
+ }
2273
+ values = updates$2();
2274
+ if (!(values.length > 0)) return [3 /*break*/, 11];
2275
+ _j = 0, values_1 = values;
2276
+ _l.label = 6;
2277
+ case 6:
2278
+ if (!(_j < values_1.length)) return [3 /*break*/, 10];
2279
+ value = values_1[_j];
2280
+ state = state$a(timer);
2281
+ if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 8];
2282
+ return [4 /*yield*/, suspend$1(timer)];
2283
+ case 7:
2284
+ state = _l.sent();
2285
+ _l.label = 8;
2286
+ case 8:
2287
+ if (state === 2 /* Task.Stop */) {
2288
+ return [3 /*break*/, 10];
2289
+ }
2290
+ data = value.data;
2291
+ active = value.metadata.active;
2292
+ suspend = value.metadata.suspend;
2293
+ privacy = value.metadata.privacy;
2294
+ mangle = shouldMangle(value);
2295
+ keys = active ? ["tag", "attributes", "value"] : ["tag"];
2296
+ for (_k = 0, keys_1 = keys; _k < keys_1.length; _k++) {
2297
+ key = keys_1[_k];
2298
+ // we check for data[key] === '' because we want to encode empty strings as well, especially for value - which if skipped can cause our decoder to assume the final
2299
+ // attribute was the value for the node
2300
+ if (data[key] || data[key] === '') {
2301
+ switch (key) {
2302
+ case "tag":
2303
+ box = size(value);
2304
+ factor = mangle ? -1 : 1;
2305
+ tokens.push(value.id * factor);
2306
+ if (value.parent && active) {
2307
+ tokens.push(value.parent);
2308
+ if (value.previous) {
2309
+ tokens.push(value.previous);
2310
+ }
2311
+ }
2312
+ tokens.push(suspend ? "*M" /* Constant.SuspendMutationTag */ : data[key]);
2313
+ if (box && box.length === 2) {
2314
+ tokens.push("".concat("#" /* Constant.Hash */).concat(str$1(box[0]), ".").concat(str$1(box[1])));
2315
+ }
2316
+ break;
2317
+ case "attributes":
2318
+ for (attr in data[key]) {
2319
+ if (data[key][attr] !== undefined) {
2320
+ tokens.push(attribute(attr, data[key][attr], privacy));
2321
+ }
2322
+ }
2323
+ break;
2324
+ case "value":
2325
+ check$4(value.metadata.fraud, value.id, data[key]);
2326
+ tokens.push(text$1(data[key], data.tag, privacy, mangle));
2327
+ break;
2328
+ }
2329
+ }
2330
+ }
2331
+ _l.label = 9;
2332
+ case 9:
2333
+ _j++;
2334
+ return [3 /*break*/, 6];
2335
+ case 10:
2336
+ if (type === 6 /* Event.Mutation */) {
2337
+ activity(eventTime);
2338
+ }
2339
+ queue(tokenize(tokens), !config$2.lean);
2340
+ _l.label = 11;
2341
+ case 11: return [3 /*break*/, 12];
2342
+ case 12: return [2 /*return*/];
2343
+ }
2344
+ });
2345
+ });
2385
2346
  }
2386
- function recompute$1(evt) {
2387
- recompute$1.dn = 17 /* FunctionNames.UnloadRecompute */;
2388
- data$9 = { name: evt.type, persisted: evt.persisted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */ };
2389
- encode$3(26 /* Event.Unload */, time(evt));
2390
- stop();
2347
+ function shouldMangle(value) {
2348
+ var privacy = value.metadata.privacy;
2349
+ return value.data.tag === "*T" /* Constant.TextTag */ && !(privacy === 0 /* Privacy.None */ || privacy === 1 /* Privacy.Sensitive */);
2391
2350
  }
2392
- function reset$a() {
2393
- data$9 = null;
2351
+ function size(value) {
2352
+ if (value.metadata.size !== null && value.metadata.size.length === 0) {
2353
+ var img = getNode(value.id);
2354
+ if (img) {
2355
+ return [Math.floor(img.offsetWidth * 100 /* Setting.BoxPrecision */), Math.floor(img.offsetHeight * 100 /* Setting.BoxPrecision */)];
2356
+ }
2357
+ }
2358
+ return value.metadata.size;
2394
2359
  }
2395
- function stop$l() {
2396
- reset$a();
2360
+ function str$1(input) {
2361
+ return input.toString(36);
2362
+ }
2363
+ function attribute(key, value, privacy) {
2364
+ return "".concat(key, "=").concat(text$1(value, key.indexOf("data-" /* Constant.DataAttribute */) === 0 ? "data-" /* Constant.DataAttribute */ : key, privacy));
2397
2365
  }
2398
2366
 
2399
2367
  var data$8;
2400
- function start$m() {
2401
- bind(document, "visibilitychange", recompute);
2402
- recompute();
2403
- }
2404
- function recompute(evt) {
2405
- if (evt === void 0) { evt = null; }
2406
- recompute.dn = 18 /* FunctionNames.VisibilityRecompute */;
2407
- data$8 = { visible: "visibilityState" in document ? document.visibilityState : "default" };
2408
- encode$3(28 /* Event.Visibility */, time(evt));
2409
- }
2410
2368
  function reset$9() {
2411
2369
  data$8 = null;
2412
2370
  }
2413
- function stop$k() {
2371
+ function start$k() {
2414
2372
  reset$9();
2373
+ compute$7();
2415
2374
  }
2416
-
2417
- function start$l() {
2418
- start$l.dn = 8 /* FunctionNames.InteractionStart */;
2419
- start$g();
2420
- start$v();
2421
- start$u();
2422
- start$s();
2423
- start$t();
2424
- start$r();
2425
- start$m();
2426
- start$q();
2427
- start$p();
2428
- start$w();
2429
- start$o();
2430
- start$n();
2431
- }
2432
- function stop$j() {
2433
- stop$e();
2434
- stop$t();
2435
- stop$s();
2436
- stop$q();
2437
- stop$r();
2438
- stop$p();
2439
- stop$k();
2440
- stop$o();
2441
- stop$n();
2442
- stop$u();
2443
- stop$m();
2444
- stop$l();
2445
- }
2446
- function observe$4(root) {
2447
- observe$7(root);
2448
- // Only monitor following interactions if the root node is a document
2449
- // In case of shadow DOM, following events automatically bubble up to the parent document.
2450
- if (root.nodeType === Node.DOCUMENT_NODE) {
2451
- observe$b(root);
2452
- observe$a(root);
2453
- observe$8(root);
2454
- observe$9(root);
2455
- observe$6(root);
2456
- observe$c(root);
2457
- observe$5(root);
2375
+ function compute$7() {
2376
+ compute$7.dn = 19 /* FunctionNames.DocumentCompute */;
2377
+ var body = document.body;
2378
+ var d = document.documentElement;
2379
+ var bodyClientWidth = body ? body.clientWidth : null;
2380
+ var bodyScrollWidth = body ? body.scrollWidth : null;
2381
+ var bodyOffsetWidth = body ? body.offsetWidth : null;
2382
+ var documentClientWidth = d ? d.clientWidth : null;
2383
+ var documentScrollWidth = d ? d.scrollWidth : null;
2384
+ var documentOffsetWidth = d ? d.offsetWidth : null;
2385
+ var width = Math.max(bodyClientWidth, bodyScrollWidth, bodyOffsetWidth, documentClientWidth, documentScrollWidth, documentOffsetWidth);
2386
+ var bodyClientHeight = body ? body.clientHeight : null;
2387
+ var bodyScrollHeight = body ? body.scrollHeight : null;
2388
+ var bodyOffsetHeight = body ? body.offsetHeight : null;
2389
+ var documentClientHeight = d ? d.clientHeight : null;
2390
+ var documentScrollHeight = d ? d.scrollHeight : null;
2391
+ var documentOffsetHeight = d ? d.offsetHeight : null;
2392
+ var height = Math.max(bodyClientHeight, bodyScrollHeight, bodyOffsetHeight, documentClientHeight, documentScrollHeight, documentOffsetHeight);
2393
+ // Check that width or height has changed from before, and also that width & height are not null values
2394
+ if ((data$8 === null || width !== data$8.width || height !== data$8.height) && width !== null && height !== null) {
2395
+ data$8 = { width: width, height: height };
2396
+ encode$4(8 /* Event.Document */);
2458
2397
  }
2459
2398
  }
2460
-
2461
- var interaction = /*#__PURE__*/Object.freeze({
2462
- __proto__: null,
2463
- observe: observe$4,
2464
- start: start$l,
2465
- stop: stop$j
2466
- });
2399
+ function stop$i() {
2400
+ reset$9();
2401
+ }
2467
2402
 
2468
2403
  function traverse (root, timer, source, timestamp) {
2469
2404
  return __awaiter(this, void 0, void 0, function () {
@@ -2502,7 +2437,7 @@ function traverse (root, timer, source, timestamp) {
2502
2437
  });
2503
2438
  }
2504
2439
 
2505
- var observers = [];
2440
+ var observers = new Set();
2506
2441
  var mutations = [];
2507
2442
  var throttledMutations = {};
2508
2443
  var insertRule = null;
@@ -2515,18 +2450,16 @@ var timeout$1 = null;
2515
2450
  var throttleDelay = null;
2516
2451
  var activePeriod = null;
2517
2452
  var history$4 = {};
2518
- var criticalPeriod = null;
2519
2453
  var observedNodes = new WeakMap();
2520
2454
  // We ignore mutations if these attributes are updated
2521
2455
  var IGNORED_ATTRIBUTES = ["data-google-query-id", "data-load-complete", "data-google-container-id"];
2522
- function start$k() {
2523
- start$k.dn = 21 /* FunctionNames.MutationStart */;
2524
- observers = [];
2456
+ function start$j() {
2457
+ start$j.dn = 21 /* FunctionNames.MutationStart */;
2458
+ observers = new Set();
2525
2459
  queue$2 = [];
2526
2460
  timeout$1 = null;
2527
2461
  activePeriod = 0;
2528
2462
  history$4 = {};
2529
- criticalPeriod = 0;
2530
2463
  observedNodes = new WeakMap();
2531
2464
  // Some popular open source libraries, like styled-components, optimize performance
2532
2465
  // by injecting CSS using insertRule API vs. appending text node. A side effect of
@@ -2589,22 +2522,17 @@ function start$k() {
2589
2522
  }
2590
2523
  }
2591
2524
  function observe$3(node) {
2592
- var _a;
2593
2525
  // Create a new observer for every time a new DOM tree (e.g. root document or shadowdom root) is discovered on the page
2594
2526
  // In the case of shadow dom, any mutations that happen within the shadow dom are not bubbled up to the host document
2595
2527
  // For this reason, we need to wire up mutations every time we see a new shadow dom.
2596
2528
  // Also, wrap it inside a try / catch. In certain browsers (e.g. legacy Edge), observer on shadow dom can throw errors
2597
2529
  try {
2598
- // Cleanup old observer if present.
2599
- if (observedNodes.has(node)) {
2600
- (_a = observedNodes.get(node)) === null || _a === void 0 ? void 0 : _a.disconnect();
2601
- }
2602
2530
  var m = api("MutationObserver" /* Constant.MutationObserver */);
2603
2531
  var observer = m in window ? new window[m](measure(handle$1)) : null;
2604
2532
  if (observer) {
2605
2533
  observer.observe(node, { attributes: true, childList: true, characterData: true, subtree: true });
2606
2534
  observedNodes.set(node, observer);
2607
- observers.push(observer);
2535
+ observers.add(observer);
2608
2536
  }
2609
2537
  }
2610
2538
  catch (e) {
@@ -2615,29 +2543,36 @@ function monitor(frame) {
2615
2543
  // Bind to iframe's onload event so we get notified anytime there's an update to iframe content.
2616
2544
  // This includes cases where iframe location is updated without explicitly updating src attribute
2617
2545
  // E.g. iframe.contentWindow.location.href = "new-location";
2618
- if (has(frame) === false) {
2546
+ if (has$1(frame) === false) {
2619
2547
  bind(frame, "load" /* Constant.LoadEvent */, generate.bind(this, frame, "childList" /* Constant.ChildList */), true);
2620
2548
  }
2621
2549
  }
2622
- function stop$i() {
2623
- for (var _i = 0, observers_1 = observers; _i < observers_1.length; _i++) {
2624
- var observer = observers_1[_i];
2550
+ function stop$h() {
2551
+ for (var _i = 0, _a = Array.from(observers); _i < _a.length; _i++) {
2552
+ var observer = _a[_i];
2625
2553
  if (observer) {
2626
2554
  observer.disconnect();
2627
2555
  }
2628
2556
  }
2629
- observers = [];
2557
+ observers = new Set();
2630
2558
  history$4 = {};
2631
2559
  mutations = [];
2632
- throttledMutations = [];
2560
+ throttledMutations = {};
2633
2561
  queue$2 = [];
2634
2562
  activePeriod = 0;
2635
2563
  timeout$1 = null;
2636
- criticalPeriod = 0;
2564
+ observedNodes = new WeakMap();
2637
2565
  }
2638
2566
  function active$2() {
2639
2567
  activePeriod = time() + 3000 /* Setting.MutationActivePeriod */;
2640
- criticalPeriod = time() + config$2.criticalMs;
2568
+ }
2569
+ function disconnect(n) {
2570
+ var ob = observedNodes.get(n);
2571
+ if (ob) {
2572
+ ob.disconnect();
2573
+ observers.delete(ob);
2574
+ observedNodes.delete(n);
2575
+ }
2641
2576
  }
2642
2577
  function handle$1(m) {
2643
2578
  handle$1.dn = 22 /* FunctionNames.MutationHandle */;
@@ -2646,7 +2581,7 @@ function handle$1(m) {
2646
2581
  track$7(6 /* Event.Mutation */, now);
2647
2582
  mutations.push({ time: now, mutations: m });
2648
2583
  schedule$1(process$2, 1 /* Priority.High */).then(function () {
2649
- setTimeout(compute$9);
2584
+ setTimeout(compute$7);
2650
2585
  measure(compute$6)();
2651
2586
  });
2652
2587
  }
@@ -2700,7 +2635,7 @@ function process$2() {
2700
2635
  switch (_d.label) {
2701
2636
  case 0:
2702
2637
  timer = { id: id(), cost: 3 /* Metric.LayoutCost */ };
2703
- start$y(timer);
2638
+ start$z(timer);
2704
2639
  _d.label = 1;
2705
2640
  case 1:
2706
2641
  if (!(mutations.length > 0)) return [3 /*break*/, 7];
@@ -2750,7 +2685,7 @@ function process$2() {
2750
2685
  _d.label = 13;
2751
2686
  case 13:
2752
2687
  cleanHistory();
2753
- stop$w(timer);
2688
+ stop$x(timer);
2754
2689
  return [2 /*return*/];
2755
2690
  }
2756
2691
  });
@@ -2777,17 +2712,13 @@ function track$5(m, timer, instance, timestamp) {
2777
2712
  // calculate inactive period based on the timestamp of the mutation not when the mutation is processed
2778
2713
  var inactive = timestamp > activePeriod;
2779
2714
  // Calculate critical period based on when mutation is processed
2780
- var critical = instance < criticalPeriod;
2781
2715
  var target = get(m.target);
2782
- var element_1 = target && target.selector ? target.selector.join() : m.target.nodeName;
2716
+ var element = target && target.selector ? target.selector.join() : m.target.nodeName;
2783
2717
  var parent_1 = value.selector ? value.selector.join() : "" /* Constant.Empty */;
2784
- // Check if its a low priority (e.g., ads related) element mutation happening during critical period
2785
- // If the discard list is empty, we discard all mutations during critical period
2786
- var lowPriMutation = config$2.throttleMutations && critical && (config$2.discard.length === 0 || config$2.discard.some(function (key) { return element_1.includes(key); }));
2787
2718
  // We use selector, instead of id, to determine the key (signature for the mutation) because in some cases
2788
2719
  // repeated mutations can cause elements to be destroyed and then recreated as new DOM nodes
2789
2720
  // In those cases, IDs will change however the selector (which is relative to DOM xPath) remains the same
2790
- var key = [parent_1, element_1, m.attributeName, names(m.addedNodes), names(m.removedNodes)].join();
2721
+ var key = [parent_1, element, m.attributeName, names(m.addedNodes), names(m.removedNodes)].join();
2791
2722
  // Initialize an entry if it doesn't already exist
2792
2723
  history$4[key] = key in history$4 ? history$4[key] : [0, instance];
2793
2724
  var h = history$4[key];
@@ -2796,12 +2727,12 @@ function track$5(m, timer, instance, timestamp) {
2796
2727
  processNodeList(h[2], 2 /* Source.ChildListRemove */, timer, timestamp);
2797
2728
  }
2798
2729
  // Update the counter, do not reset counter if its critical period
2799
- h[0] = inactive || lowPriMutation ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
2730
+ h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
2800
2731
  h[1] = instance;
2801
2732
  // Return updated mutation type based on,
2802
2733
  // 1. if we have already hit the threshold or not
2803
2734
  // 2. if its a low priority mutation happening during critical time period
2804
- if (h[0] >= 10 /* Setting.MutationSuspendThreshold */ || lowPriMutation) {
2735
+ if (h[0] >= 10 /* Setting.MutationSuspendThreshold */) {
2805
2736
  // Store a reference to removedNodes so we can process them later
2806
2737
  // when we resume mutations again on user interactions
2807
2738
  h[2] = m.removedNodes;
@@ -2891,7 +2822,7 @@ function trigger$2() {
2891
2822
  if (node) {
2892
2823
  var shadowRoot = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
2893
2824
  // Skip re-processing shadowRoot if it was already discovered
2894
- if (shadowRoot && has(node)) {
2825
+ if (shadowRoot && has$1(node)) {
2895
2826
  continue;
2896
2827
  }
2897
2828
  generate(node, shadowRoot ? "childList" /* Constant.ChildList */ : "characterData" /* Constant.CharacterData */);
@@ -2985,7 +2916,7 @@ function processNode (node, source, timestamp) {
2985
2916
  var _a;
2986
2917
  var child = null;
2987
2918
  // Do not track this change if we are attempting to remove a node before discovering it
2988
- if (source === 2 /* Source.ChildListRemove */ && has(node) === false) {
2919
+ if (source === 2 /* Source.ChildListRemove */ && has$1(node) === false) {
2989
2920
  return child;
2990
2921
  }
2991
2922
  // Special handling for text nodes that belong to style nodes
@@ -2995,7 +2926,7 @@ function processNode (node, source, timestamp) {
2995
2926
  node.parentElement.tagName === "STYLE") {
2996
2927
  node = node.parentNode;
2997
2928
  }
2998
- var add = has(node) === false;
2929
+ var add = has$1(node) === false;
2999
2930
  var call = add ? "add" : "update";
3000
2931
  var parent = node.parentElement ? node.parentElement : null;
3001
2932
  var insideFrame = node.ownerDocument !== document;
@@ -3012,8 +2943,9 @@ function processNode (node, source, timestamp) {
3012
2943
  case Node.DOCUMENT_NODE:
3013
2944
  // We check for regions in the beginning when discovering document and
3014
2945
  // later whenever there are new additions or modifications to DOM (mutations)
3015
- if (node === document)
2946
+ if (node === document) {
3016
2947
  parse$1(document);
2948
+ }
3017
2949
  checkDocumentStyles(node, timestamp);
3018
2950
  observe$2(node);
3019
2951
  break;
@@ -3049,7 +2981,7 @@ function processNode (node, source, timestamp) {
3049
2981
  // Also, we do not track text nodes for STYLE tags
3050
2982
  // The only exception is when we receive a mutation to remove the text node, in that case
3051
2983
  // parent will be null, but we can still process the node by checking it's an update call.
3052
- if (call === "update" || (parent && has(parent) && parent.tagName !== "STYLE" && parent.tagName !== "NOSCRIPT")) {
2984
+ if (call === "update" || (parent && has$1(parent) && parent.tagName !== "STYLE" && parent.tagName !== "NOSCRIPT")) {
3053
2985
  var textData = { tag: "*T" /* Constant.TextTag */, value: node.nodeValue };
3054
2986
  dom[call](node, parent, textData, source);
3055
2987
  }
@@ -3135,6 +3067,9 @@ function processNode (node, source, timestamp) {
3135
3067
  child = iframe$1.contentDocument;
3136
3068
  }
3137
3069
  }
3070
+ if (source === 2 /* Source.ChildListRemove */) {
3071
+ removeObserver(iframe$1);
3072
+ }
3138
3073
  dom[call](node, parent, frameData, source);
3139
3074
  break;
3140
3075
  case "LINK":
@@ -3178,12 +3113,30 @@ function processNode (node, source, timestamp) {
3178
3113
  return child;
3179
3114
  }
3180
3115
  function observe$2(root) {
3181
- if (has(root)) {
3116
+ if (has$1(root) || has(root)) {
3182
3117
  return;
3183
3118
  }
3184
3119
  observe$3(root); // Observe mutations for this root node
3185
3120
  observe$4(root); // Observe interactions for this root node
3186
3121
  }
3122
+ function removeObserver(root) {
3123
+ // iframes will have load event listeners and they should be removed when iframe is removed
3124
+ // from the document
3125
+ unbind(root);
3126
+ var _a = iframeContent(root) || {}, _b = _a.doc, doc = _b === void 0 ? null : _b, _c = _a.win, win = _c === void 0 ? null : _c;
3127
+ if (win) {
3128
+ // For iframes, scroll event is observed on content window and this needs to be removed as well
3129
+ unbind(win);
3130
+ }
3131
+ if (doc) {
3132
+ // When an iframe is removed, we should also remove all listeners attached to its document
3133
+ // to avoid memory leaks.
3134
+ unbind(doc);
3135
+ disconnect(doc);
3136
+ // Remove iframe and content document from maps tracking them
3137
+ removeIFrame(root, doc);
3138
+ }
3139
+ }
3187
3140
  function getStyleValue(style) {
3188
3141
  // Call trim on the text content to ensure we do not process white spaces ( , \n, \r\n, \t, etc.)
3189
3142
  // Also, check if stylesheet has any data-* attribute, if so process rules instead of looking up text
@@ -3196,457 +3149,543 @@ function getStyleValue(style) {
3196
3149
  }
3197
3150
  return value;
3198
3151
  }
3199
- function getCssRules(sheet) {
3200
- var value = "" /* Constant.Empty */;
3201
- var cssRules = null;
3202
- // Firefox throws a SecurityError when trying to access cssRules of a stylesheet from a different domain
3152
+ function getCssRules(sheet) {
3153
+ var value = "" /* Constant.Empty */;
3154
+ var cssRules = null;
3155
+ // Firefox throws a SecurityError when trying to access cssRules of a stylesheet from a different domain
3156
+ try {
3157
+ cssRules = sheet ? sheet.cssRules : [];
3158
+ }
3159
+ catch (e) {
3160
+ log$1(1 /* Code.CssRules */, 1 /* Severity.Warning */, e ? e.name : null);
3161
+ if (e && e.name !== "SecurityError") {
3162
+ throw e;
3163
+ }
3164
+ }
3165
+ if (cssRules !== null) {
3166
+ for (var i = 0; i < cssRules.length; i++) {
3167
+ value += cssRules[i].cssText;
3168
+ }
3169
+ }
3170
+ return value;
3171
+ }
3172
+ function getAttributes(element) {
3173
+ var output = {};
3174
+ var attributes = element.attributes;
3175
+ if (attributes && attributes.length > 0) {
3176
+ for (var i = 0; i < attributes.length; i++) {
3177
+ var name_1 = attributes[i].name;
3178
+ if (IGNORE_ATTRIBUTES.indexOf(name_1) < 0) {
3179
+ output[name_1] = attributes[i].value;
3180
+ }
3181
+ }
3182
+ }
3183
+ // For INPUT tags read the dynamic "value" property if an explicit "value" attribute is not set
3184
+ if (element.tagName === "INPUT" /* Constant.InputTag */ && !("value" /* Constant.Value */ in output) && element.value) {
3185
+ output["value" /* Constant.Value */] = element.value;
3186
+ }
3187
+ return output;
3188
+ }
3189
+
3190
+ var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
3191
+ var selectorMap = {};
3192
+ function reset$8() {
3193
+ selectorMap = {};
3194
+ }
3195
+ function get$1(input, type) {
3196
+ var a = input.attributes;
3197
+ var prefix = input.prefix ? input.prefix[type] : null;
3198
+ var suffix = type === 0 /* Selector.Alpha */ ? "".concat("~" /* Constant.Tilde */).concat(input.position - 1) : ":nth-of-type(".concat(input.position, ")");
3199
+ switch (input.tag) {
3200
+ case "STYLE":
3201
+ case "TITLE":
3202
+ case "LINK":
3203
+ case "META":
3204
+ case "*T" /* Constant.TextTag */:
3205
+ case "*D" /* Constant.DocumentTag */:
3206
+ return "" /* Constant.Empty */;
3207
+ case "HTML":
3208
+ return "HTML" /* Constant.HTML */;
3209
+ default:
3210
+ if (prefix === null) {
3211
+ return "" /* Constant.Empty */;
3212
+ }
3213
+ prefix = "".concat(prefix).concat(">" /* Constant.Separator */);
3214
+ input.tag = input.tag.indexOf("svg:" /* Constant.SvgPrefix */) === 0 ? input.tag.substr("svg:" /* Constant.SvgPrefix */.length) : input.tag;
3215
+ var selector = "".concat(prefix).concat(input.tag).concat(suffix);
3216
+ var id = "id" /* Constant.Id */ in a && a["id" /* Constant.Id */].length > 0 ? a["id" /* Constant.Id */] : null;
3217
+ var classes = input.tag !== "BODY" /* Constant.BodyTag */ && "class" /* Constant.Class */ in a && a["class" /* Constant.Class */].length > 0 ? a["class" /* Constant.Class */].trim().split(/\s+/).filter(function (c) { return filter(c); }).join("." /* Constant.Period */) : null;
3218
+ if (classes && classes.length > 0) {
3219
+ if (type === 0 /* Selector.Alpha */) {
3220
+ // In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
3221
+ // If the node has valid class name(s) then drop relative positioning within the parent path to keep things simple.
3222
+ var key = "".concat(getDomPath(prefix)).concat(input.tag).concat("." /* Constant.Dot */).concat(classes);
3223
+ if (!(key in selectorMap)) {
3224
+ selectorMap[key] = [];
3225
+ }
3226
+ if (selectorMap[key].indexOf(input.id) < 0) {
3227
+ selectorMap[key].push(input.id);
3228
+ }
3229
+ selector = "".concat(key).concat("~" /* Constant.Tilde */).concat(selectorMap[key].indexOf(input.id));
3230
+ }
3231
+ else {
3232
+ // In Beta mode, we continue to look at query selectors in context of the full page
3233
+ selector = "".concat(prefix).concat(input.tag, ".").concat(classes).concat(suffix);
3234
+ }
3235
+ }
3236
+ // Update selector to use "id" field when available. There are two exceptions:
3237
+ // (1) if "id" appears to be an auto generated string token, e.g. guid or a random id containing digits
3238
+ // (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
3239
+ selector = id && filter(id) ? "".concat(getDomPrefix(prefix)).concat("#" /* Constant.Hash */).concat(id) : selector;
3240
+ return selector;
3241
+ }
3242
+ }
3243
+ function getDomPrefix(prefix) {
3244
+ var shadowDomStart = prefix.lastIndexOf("*S" /* Constant.ShadowDomTag */);
3245
+ var iframeDomStart = prefix.lastIndexOf("".concat("iframe:" /* Constant.IFramePrefix */).concat("HTML" /* Constant.HTML */));
3246
+ var domStart = Math.max(shadowDomStart, iframeDomStart);
3247
+ if (domStart < 0) {
3248
+ return "" /* Constant.Empty */;
3249
+ }
3250
+ return prefix.substring(0, prefix.indexOf(">" /* Constant.Separator */, domStart) + 1);
3251
+ }
3252
+ function getDomPath(input) {
3253
+ var parts = input.split(">" /* Constant.Separator */);
3254
+ for (var i = 0; i < parts.length; i++) {
3255
+ var tIndex = parts[i].indexOf("~" /* Constant.Tilde */);
3256
+ var dIndex = parts[i].indexOf("." /* Constant.Dot */);
3257
+ parts[i] = parts[i].substring(0, dIndex > 0 ? dIndex : (tIndex > 0 ? tIndex : parts[i].length));
3258
+ }
3259
+ return parts.join(">" /* Constant.Separator */);
3260
+ }
3261
+ // Check if the given input string has digits or excluded class names
3262
+ function filter(value) {
3263
+ if (!value) {
3264
+ return false;
3265
+ } // Do not process empty strings
3266
+ if (excludeClassNames.some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
3267
+ return false;
3268
+ }
3269
+ for (var i = 0; i < value.length; i++) {
3270
+ var c = value.charCodeAt(i);
3271
+ if (c >= 48 /* Character.Zero */ && c <= 57 /* Character.Nine */) {
3272
+ return false;
3273
+ }
3274
+ }
3275
+ return true;
3276
+ }
3277
+
3278
+ var selector = /*#__PURE__*/Object.freeze({
3279
+ __proto__: null,
3280
+ get: get$1,
3281
+ reset: reset$8
3282
+ });
3283
+
3284
+ var index = 1;
3285
+ var nodesMap = null; // Maps id => node to retrieve further node details using id.
3286
+ var values = [];
3287
+ var updateMap = [];
3288
+ var hashMap = {};
3289
+ var override = [];
3290
+ var unmask = [];
3291
+ var maskText = [];
3292
+ var maskExclude = [];
3293
+ var maskDisable = [];
3294
+ var maskTags = [];
3295
+ // The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
3296
+ var idMap = null; // Maps node => id.
3297
+ var iframeMap = null; // Maps iframe's contentDocument => parent iframe element
3298
+ var iframeContentMap = null; // Maps parent iframe element => iframe's contentDocument & contentWindow
3299
+ var privacyMap = null; // Maps node => Privacy (enum)
3300
+ var fraudMap = null; // Maps node => FraudId (number)
3301
+ function start$i() {
3302
+ reset$7();
3303
+ parse$1(document, true);
3304
+ }
3305
+ function stop$g() {
3306
+ reset$7();
3307
+ }
3308
+ function reset$7() {
3309
+ index = 1;
3310
+ values = [];
3311
+ updateMap = [];
3312
+ hashMap = {};
3313
+ override = [];
3314
+ unmask = [];
3315
+ maskText = "address,password,contact" /* Mask.Text */.split("," /* Constant.Comma */);
3316
+ maskExclude = "password,secret,pass,social,ssn,code,hidden" /* Mask.Exclude */.split("," /* Constant.Comma */);
3317
+ maskDisable = "radio,checkbox,range,button,reset,submit" /* Mask.Disable */.split("," /* Constant.Comma */);
3318
+ maskTags = "INPUT,SELECT,TEXTAREA" /* Mask.Tags */.split("," /* Constant.Comma */);
3319
+ nodesMap = new Map();
3320
+ idMap = new WeakMap();
3321
+ iframeMap = new WeakMap();
3322
+ iframeContentMap = new WeakMap();
3323
+ privacyMap = new WeakMap();
3324
+ fraudMap = new WeakMap();
3325
+ reset$8();
3326
+ }
3327
+ // We parse new root nodes for any regions or masked nodes in the beginning (document) and
3328
+ // later whenever there are new additions or modifications to DOM (mutations)
3329
+ function parse$1(root, init) {
3330
+ if (init === void 0) { init = false; }
3331
+ // Wrap selectors in a try / catch block.
3332
+ // It's possible for script to receive invalid selectors, e.g. "'#id'" with extra quotes, and cause the code below to fail
3203
3333
  try {
3204
- cssRules = sheet ? sheet.cssRules : [];
3334
+ // Parse unmask configuration into separate query selectors and override tokens as part of initialization
3335
+ if (init) {
3336
+ config$2.unmask.forEach(function (x) { return x.indexOf("!" /* Constant.Bang */) < 0 ? unmask.push(x) : override.push(x.substr(1)); });
3337
+ }
3338
+ // Since mutations may happen on leaf nodes too, e.g. text nodes, which may not support all selector APIs.
3339
+ // We ensure that the root note supports querySelectorAll API before executing the code below to identify new regions.
3340
+ if ("querySelectorAll" in root) {
3341
+ config$2.regions.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return observe$1(e, "".concat(x[0])); }); }); // Regions
3342
+ config$2.mask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 3 /* Privacy.TextImage */); }); }); // Masked Elements
3343
+ config$2.checksum.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return fraudMap.set(e, x[0]); }); }); // Fraud Checksum Check
3344
+ unmask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 0 /* Privacy.None */); }); }); // Unmasked Elements
3345
+ }
3205
3346
  }
3206
3347
  catch (e) {
3207
- log$1(1 /* Code.CssRules */, 1 /* Severity.Warning */, e ? e.name : null);
3208
- if (e && e.name !== "SecurityError") {
3209
- throw e;
3210
- }
3348
+ log$1(5 /* Code.Selector */, 1 /* Severity.Warning */, e ? e.name : null);
3211
3349
  }
3212
- if (cssRules !== null) {
3213
- for (var i = 0; i < cssRules.length; i++) {
3214
- value += cssRules[i].cssText;
3215
- }
3350
+ }
3351
+ function getId(node, autogen) {
3352
+ if (autogen === void 0) { autogen = false; }
3353
+ if (node === null) {
3354
+ return null;
3216
3355
  }
3217
- return value;
3356
+ var id = idMap.get(node);
3357
+ if (!id && autogen) {
3358
+ id = index++;
3359
+ idMap.set(node, id);
3360
+ }
3361
+ return id ? id : null;
3218
3362
  }
3219
- function getAttributes(element) {
3220
- var output = {};
3221
- var attributes = element.attributes;
3222
- if (attributes && attributes.length > 0) {
3223
- for (var i = 0; i < attributes.length; i++) {
3224
- var name_1 = attributes[i].name;
3225
- if (IGNORE_ATTRIBUTES.indexOf(name_1) < 0) {
3226
- output[name_1] = attributes[i].value;
3227
- }
3228
- }
3363
+ function add(node, parent, data, source) {
3364
+ var parentId = parent ? getId(parent) : null;
3365
+ // Do not add detached nodes
3366
+ if ((!parent || !parentId) && node.host == null && node.nodeType !== Node.DOCUMENT_TYPE_NODE) {
3367
+ return;
3229
3368
  }
3230
- // For INPUT tags read the dynamic "value" property if an explicit "value" attribute is not set
3231
- if (element.tagName === "INPUT" /* Constant.InputTag */ && !("value" /* Constant.Value */ in output) && element.value) {
3232
- output["value" /* Constant.Value */] = element.value;
3369
+ var id = getId(node, true);
3370
+ var previousId = getPreviousId(node);
3371
+ var parentValue = null;
3372
+ var regionId = exists(node) ? id : null;
3373
+ var fraudId = fraudMap.has(node) ? fraudMap.get(node) : null;
3374
+ var privacyId = config$2.content ? 1 /* Privacy.Sensitive */ : 3 /* Privacy.TextImage */;
3375
+ if (parentId >= 0 && values[parentId]) {
3376
+ parentValue = values[parentId];
3377
+ parentValue.children.push(id);
3378
+ regionId = regionId === null ? parentValue.region : regionId;
3379
+ fraudId = fraudId === null ? parentValue.metadata.fraud : fraudId;
3380
+ privacyId = parentValue.metadata.privacy;
3233
3381
  }
3234
- return output;
3382
+ // If there's an explicit region attribute set on the element, use it to mark a region on the page
3383
+ if (data.attributes && "data-clarity-region" /* Constant.RegionData */ in data.attributes) {
3384
+ observe$1(node, data.attributes["data-clarity-region" /* Constant.RegionData */]);
3385
+ regionId = id;
3386
+ }
3387
+ nodesMap.set(id, node);
3388
+ values[id] = {
3389
+ id: id,
3390
+ parent: parentId,
3391
+ previous: previousId,
3392
+ children: [],
3393
+ data: data,
3394
+ selector: null,
3395
+ hash: null,
3396
+ region: regionId,
3397
+ metadata: { active: true, suspend: false, privacy: privacyId, position: null, fraud: fraudId, size: null },
3398
+ };
3399
+ privacy(node, values[id], parentValue);
3400
+ updateSelector(values[id]);
3401
+ updateImageSize(values[id]);
3402
+ track$4(id, source);
3235
3403
  }
3236
-
3237
- var sheetUpdateState = [];
3238
- var sheetAdoptionState = [];
3239
- var replace = null;
3240
- var replaceSync = null;
3241
- var styleSheetId = 'claritySheetId';
3242
- var styleSheetMap = {};
3243
- var styleTimeMap = {};
3244
- var documentNodes = [];
3245
- var createdSheetIds = [];
3246
- function start$j() {
3247
- if (window['CSSStyleSheet'] && CSSStyleSheet.prototype) {
3248
- if (replace === null) {
3249
- replace = CSSStyleSheet.prototype.replace;
3250
- CSSStyleSheet.prototype.replace = function () {
3251
- if (active()) {
3252
- max(36 /* Metric.ConstructedStyles */, 1);
3253
- // if we haven't seen this stylesheet on this page yet, wait until the checkDocumentStyles has found it
3254
- // and attached the sheet to a document. This way the timestamp of the style sheet creation will align
3255
- // to when it is used in the document rather than potentially being misaligned during the traverse process.
3256
- if (createdSheetIds.indexOf(this[styleSheetId]) > -1) {
3257
- trackStyleChange(time(), this[styleSheetId], 1 /* StyleSheetOperation.Replace */, arguments[0]);
3258
- }
3259
- }
3260
- return replace.apply(this, arguments);
3261
- };
3404
+ function update$1(node, parent, data, source) {
3405
+ var id = getId(node);
3406
+ var parentId = parent ? getId(parent) : null;
3407
+ var previousId = getPreviousId(node);
3408
+ var changed = false;
3409
+ var parentChanged = false;
3410
+ if (id in values) {
3411
+ var value = values[id];
3412
+ value.metadata.active = true;
3413
+ // Handle case where internal ordering may have changed
3414
+ if (value.previous !== previousId) {
3415
+ changed = true;
3416
+ value.previous = previousId;
3262
3417
  }
3263
- if (replaceSync === null) {
3264
- replaceSync = CSSStyleSheet.prototype.replaceSync;
3265
- CSSStyleSheet.prototype.replaceSync = function () {
3266
- if (active()) {
3267
- max(36 /* Metric.ConstructedStyles */, 1);
3268
- // if we haven't seen this stylesheet on this page yet, wait until the checkDocumentStyles has found it
3269
- // and attached the sheet to a document. This way the timestamp of the style sheet creation will align
3270
- // to when it is used in the document rather than potentially being misaligned during the traverse process.
3271
- if (createdSheetIds.indexOf(this[styleSheetId]) > -1) {
3272
- trackStyleChange(time(), this[styleSheetId], 2 /* StyleSheetOperation.ReplaceSync */, arguments[0]);
3273
- }
3418
+ // Handle case where parent might have been updated
3419
+ if (value.parent !== parentId) {
3420
+ changed = true;
3421
+ var oldParentId = value.parent;
3422
+ value.parent = parentId;
3423
+ // Move this node to the right location under new parent
3424
+ if (parentId !== null && parentId >= 0) {
3425
+ var childIndex = previousId === null ? 0 : values[parentId].children.indexOf(previousId) + 1;
3426
+ values[parentId].children.splice(childIndex, 0, id);
3427
+ // Update region after the move
3428
+ value.region = exists(node) ? id : values[parentId].region;
3429
+ }
3430
+ else {
3431
+ // Mark this element as deleted if the parent has been updated to null
3432
+ remove(id, source);
3433
+ }
3434
+ // Remove reference to this node from the old parent
3435
+ if (oldParentId !== null && oldParentId >= 0) {
3436
+ var nodeIndex = values[oldParentId].children.indexOf(id);
3437
+ if (nodeIndex >= 0) {
3438
+ values[oldParentId].children.splice(nodeIndex, 1);
3274
3439
  }
3275
- return replaceSync.apply(this, arguments);
3276
- };
3440
+ }
3441
+ parentChanged = true;
3277
3442
  }
3278
- }
3279
- }
3280
- function checkDocumentStyles(documentNode, timestamp) {
3281
- if (documentNodes.indexOf(documentNode) === -1) {
3282
- documentNodes.push(documentNode);
3283
- }
3284
- timestamp = timestamp || time();
3285
- if (!(documentNode === null || documentNode === void 0 ? void 0 : documentNode.adoptedStyleSheets)) {
3286
- // if we don't have adoptedStyledSheets on the Node passed to us, we can short circuit.
3287
- return;
3288
- }
3289
- max(36 /* Metric.ConstructedStyles */, 1);
3290
- var currentStyleSheets = [];
3291
- for (var _i = 0, _a = documentNode.adoptedStyleSheets; _i < _a.length; _i++) {
3292
- var styleSheet = _a[_i];
3293
- // If we haven't seen this style sheet on this page yet, we create a reference to it for the visualizer.
3294
- // For SPA or times in which Clarity restarts on a given page, our visualizer would lose context
3295
- // on the previously created style sheet for page N-1.
3296
- // Then we synthetically call replaceSync with its contents to bootstrap it
3297
- if (!styleSheet[styleSheetId] || createdSheetIds.indexOf(styleSheet[styleSheetId]) === -1) {
3298
- styleSheet[styleSheetId] = shortid();
3299
- createdSheetIds.push(styleSheet[styleSheetId]);
3300
- trackStyleChange(timestamp, styleSheet[styleSheetId], 0 /* StyleSheetOperation.Create */);
3301
- trackStyleChange(timestamp, styleSheet[styleSheetId], 2 /* StyleSheetOperation.ReplaceSync */, getCssRules(styleSheet));
3443
+ // Update data
3444
+ for (var key in data) {
3445
+ if (diff(value["data"], data, key)) {
3446
+ changed = true;
3447
+ value["data"][key] = data[key];
3448
+ }
3302
3449
  }
3303
- currentStyleSheets.push(styleSheet[styleSheetId]);
3304
- }
3305
- var documentId = getId(documentNode, true);
3306
- if (!styleSheetMap[documentId]) {
3307
- styleSheetMap[documentId] = [];
3308
- }
3309
- if (!arraysEqual(currentStyleSheets, styleSheetMap[documentId])) {
3310
- // Using -1 to signify the root document node as we don't track that as part of our nodeMap
3311
- trackStyleAdoption(timestamp, documentNode == document ? -1 : getId(documentNode), 3 /* StyleSheetOperation.SetAdoptedStyles */, currentStyleSheets);
3312
- styleSheetMap[documentId] = currentStyleSheets;
3313
- styleTimeMap[documentId] = timestamp;
3450
+ // Update selector
3451
+ updateSelector(value);
3452
+ track$4(id, source, changed, parentChanged);
3314
3453
  }
3315
3454
  }
3316
- function compute$7() {
3317
- for (var _i = 0, documentNodes_1 = documentNodes; _i < documentNodes_1.length; _i++) {
3318
- var documentNode = documentNodes_1[_i];
3319
- var docId = documentNode == document ? -1 : getId(documentNode);
3320
- var ts = docId in styleTimeMap ? styleTimeMap[docId] : null;
3321
- checkDocumentStyles(document, ts);
3455
+ function sameorigin(node) {
3456
+ var output = false;
3457
+ if (node.nodeType === Node.ELEMENT_NODE && node.tagName === "IFRAME" /* Constant.IFrameTag */) {
3458
+ var frame = node;
3459
+ // To determine if the iframe is same-origin or not, we try accessing it's contentDocument.
3460
+ // If the browser throws an exception, we assume it's cross-origin and move on.
3461
+ // However, if we do a get a valid document object back, we assume the contents are accessible and iframe is same-origin.
3462
+ try {
3463
+ var doc = frame.contentDocument;
3464
+ if (doc) {
3465
+ iframeMap.set(frame.contentDocument, frame);
3466
+ iframeContentMap.set(frame, { doc: frame.contentDocument, win: frame.contentWindow });
3467
+ output = true;
3468
+ }
3469
+ }
3470
+ catch ( /* do nothing */_a) { /* do nothing */ }
3322
3471
  }
3472
+ return output;
3323
3473
  }
3324
- function reset$8() {
3325
- sheetAdoptionState = [];
3326
- sheetUpdateState = [];
3474
+ function iframe(node) {
3475
+ var doc = node.nodeType === Node.DOCUMENT_NODE ? node : null;
3476
+ return doc && iframeMap.has(doc) ? iframeMap.get(doc) : null;
3327
3477
  }
3328
- function stop$h() {
3329
- styleSheetMap = {};
3330
- styleTimeMap = {};
3331
- documentNodes = [];
3332
- createdSheetIds = [];
3333
- reset$8();
3478
+ function iframeContent(frame) {
3479
+ if (iframeContentMap.has(frame)) {
3480
+ return iframeContentMap.get(frame);
3481
+ }
3482
+ return null;
3334
3483
  }
3335
- function trackStyleChange(time, id, operation, cssRules) {
3336
- sheetUpdateState.push({
3337
- time: time,
3338
- event: 46 /* Event.StyleSheetUpdate */,
3339
- data: {
3340
- id: id,
3341
- operation: operation,
3342
- cssRules: cssRules
3343
- }
3344
- });
3345
- encode$4(46 /* Event.StyleSheetUpdate */);
3484
+ function removeIFrame(frame, doc) {
3485
+ iframeContentMap.delete(frame);
3486
+ iframeMap.delete(doc);
3346
3487
  }
3347
- function trackStyleAdoption(time, id, operation, newIds) {
3348
- sheetAdoptionState.push({
3349
- time: time,
3350
- event: 45 /* Event.StyleSheetAdoption */,
3351
- data: {
3352
- id: id,
3353
- operation: operation,
3354
- newIds: newIds
3355
- }
3356
- });
3357
- encode$4(45 /* Event.StyleSheetAdoption */);
3488
+ function privacy(node, value, parent) {
3489
+ var _a;
3490
+ var data = value.data;
3491
+ var metadata = value.metadata;
3492
+ var current = metadata.privacy;
3493
+ var attributes = data.attributes || {};
3494
+ var tag = data.tag.toUpperCase();
3495
+ switch (true) {
3496
+ case maskTags.indexOf(tag) >= 0:
3497
+ var type = attributes["type" /* Constant.Type */];
3498
+ var meta_1 = "" /* Constant.Empty */;
3499
+ var excludedPrivacyAttributes_1 = ["class" /* Constant.Class */, "style" /* Constant.Style */];
3500
+ Object.keys(attributes)
3501
+ .filter(function (x) { return !excludedPrivacyAttributes_1.includes(x); })
3502
+ .forEach(function (x) { return (meta_1 += attributes[x].toLowerCase()); });
3503
+ var exclude = maskExclude.some(function (x) { return meta_1.indexOf(x) >= 0; });
3504
+ // Regardless of privacy mode, always mask off user input from input boxes or drop downs with two exceptions:
3505
+ // (1) The node is detected to be one of the excluded fields, in which case we drop everything
3506
+ // (2) The node's type is one of the allowed types (like checkboxes)
3507
+ metadata.privacy = tag === "INPUT" /* Constant.InputTag */ && maskDisable.indexOf(type) >= 0 ? current : (exclude ? 4 /* Privacy.Exclude */ : 2 /* Privacy.Text */);
3508
+ break;
3509
+ case "data-clarity-mask" /* Constant.MaskData */ in attributes:
3510
+ metadata.privacy = 3 /* Privacy.TextImage */;
3511
+ break;
3512
+ case "data-clarity-unmask" /* Constant.UnmaskData */ in attributes:
3513
+ metadata.privacy = 0 /* Privacy.None */;
3514
+ break;
3515
+ case privacyMap.has(node):
3516
+ // If this node was explicitly configured to contain sensitive content, honor that privacy setting
3517
+ metadata.privacy = privacyMap.get(node);
3518
+ break;
3519
+ case fraudMap.has(node):
3520
+ // If this node was explicitly configured to be evaluated for fraud, then also mask content
3521
+ metadata.privacy = 2 /* Privacy.Text */;
3522
+ break;
3523
+ case tag === "*T" /* Constant.TextTag */:
3524
+ // If it's a text node belonging to a STYLE or TITLE tag or one of scrub exceptions, then capture content
3525
+ var pTag = parent && parent.data ? parent.data.tag : "" /* Constant.Empty */;
3526
+ var pSelector_1 = parent && parent.selector ? parent.selector[1 /* Selector.Default */] : "" /* Constant.Empty */;
3527
+ var tags = ["STYLE" /* Constant.StyleTag */, "TITLE" /* Constant.TitleTag */, "svg:style" /* Constant.SvgStyle */];
3528
+ metadata.privacy = tags.includes(pTag) || override.some(function (x) { return pSelector_1.indexOf(x) >= 0; }) ? 0 /* Privacy.None */ : current;
3529
+ break;
3530
+ case current === 1 /* Privacy.Sensitive */:
3531
+ // In a mode where we mask sensitive information by default, look through class names to aggressively mask content
3532
+ metadata.privacy = inspect(attributes["class" /* Constant.Class */], maskText, metadata);
3533
+ break;
3534
+ case tag === "IMG" /* Constant.ImageTag */:
3535
+ // Mask images with blob src as it is not publicly available anyway.
3536
+ if ((_a = attributes.src) === null || _a === void 0 ? void 0 : _a.startsWith('blob:')) {
3537
+ metadata.privacy = 3 /* Privacy.TextImage */;
3538
+ }
3539
+ break;
3540
+ }
3358
3541
  }
3359
- function arraysEqual(a, b) {
3360
- if (a.length !== b.length) {
3361
- return false;
3542
+ function inspect(input, lookup, metadata) {
3543
+ if (input && lookup.some(function (x) { return input.indexOf(x) >= 0; })) {
3544
+ return 2 /* Privacy.Text */;
3362
3545
  }
3363
- return a.every(function (value, index) { return value === b[index]; });
3546
+ return metadata.privacy;
3364
3547
  }
3365
-
3366
- var state$2 = [];
3367
- var elementAnimate = null;
3368
- var animationPlay = null;
3369
- var animationPause = null;
3370
- var animationCommitStyles = null;
3371
- var animationCancel = null;
3372
- var animationFinish = null;
3373
- var animationId = 'clarityAnimationId';
3374
- var operationCount = 'clarityOperationCount';
3375
- var maxOperations = 20;
3376
- function start$i() {
3377
- if (window["Animation"] &&
3378
- window["Animation"].prototype &&
3379
- window["KeyframeEffect"] &&
3380
- window["KeyframeEffect"].prototype &&
3381
- window["KeyframeEffect"].prototype.getKeyframes &&
3382
- window["KeyframeEffect"].prototype.getTiming) {
3383
- reset$7();
3384
- overrideAnimationHelper(animationPlay, "play");
3385
- overrideAnimationHelper(animationPause, "pause");
3386
- overrideAnimationHelper(animationCommitStyles, "commitStyles");
3387
- overrideAnimationHelper(animationCancel, "cancel");
3388
- overrideAnimationHelper(animationFinish, "finish");
3389
- if (elementAnimate === null) {
3390
- elementAnimate = Element.prototype.animate;
3391
- Element.prototype.animate = function () {
3392
- var createdAnimation = elementAnimate.apply(this, arguments);
3393
- trackAnimationOperation(createdAnimation, "play");
3394
- return createdAnimation;
3395
- };
3548
+ function diff(a, b, field) {
3549
+ if (typeof a[field] === "object" && typeof b[field] === "object") {
3550
+ for (var key in a[field]) {
3551
+ if (a[field][key] !== b[field][key]) {
3552
+ return true;
3553
+ }
3396
3554
  }
3397
- if (document.getAnimations) {
3398
- for (var _i = 0, _a = document.getAnimations(); _i < _a.length; _i++) {
3399
- var animation = _a[_i];
3400
- if (animation.playState === "finished") {
3401
- trackAnimationOperation(animation, "finish");
3402
- }
3403
- else if (animation.playState === "paused" || animation.playState === "idle") {
3404
- trackAnimationOperation(animation, "pause");
3405
- }
3406
- else if (animation.playState === "running") {
3407
- trackAnimationOperation(animation, "play");
3408
- }
3555
+ for (var key in b[field]) {
3556
+ if (b[field][key] !== a[field][key]) {
3557
+ return true;
3409
3558
  }
3410
3559
  }
3560
+ return false;
3411
3561
  }
3562
+ return a[field] !== b[field];
3412
3563
  }
3413
- function reset$7() {
3414
- state$2 = [];
3415
- }
3416
- function track$4(time, id, operation, keyFrames, timing, targetId, timeline) {
3417
- state$2.push({
3418
- time: time,
3419
- event: 44 /* Event.Animation */,
3420
- data: {
3421
- id: id,
3422
- operation: operation,
3423
- keyFrames: keyFrames,
3424
- timing: timing,
3425
- targetId: targetId,
3426
- timeline: timeline
3564
+ function position(parent, child) {
3565
+ child.metadata.position = 1;
3566
+ var idx = parent ? parent.children.indexOf(child.id) : -1;
3567
+ while (idx-- > 0) {
3568
+ var sibling = values[parent.children[idx]];
3569
+ if (child.data.tag === sibling.data.tag) {
3570
+ child.metadata.position = sibling.metadata.position + 1;
3571
+ break;
3427
3572
  }
3428
- });
3429
- encode$4(44 /* Event.Animation */);
3573
+ }
3574
+ return child.metadata.position;
3430
3575
  }
3431
- function stop$g() {
3432
- reset$7();
3576
+ function updateSelector(value) {
3577
+ var parent = value.parent && value.parent in values ? values[value.parent] : null;
3578
+ var prefix = parent ? parent.selector : null;
3579
+ var d = value.data;
3580
+ var p = position(parent, value);
3581
+ var s = { id: value.id, tag: d.tag, prefix: prefix, position: p, attributes: d.attributes };
3582
+ value.selector = [get$1(s, 0 /* Selector.Alpha */), get$1(s, 1 /* Selector.Beta */)];
3583
+ value.hash = value.selector.map(function (x) { return x ? hash(x) : null; });
3584
+ value.hash.forEach(function (h) { return hashMap[h] = value.id; });
3433
3585
  }
3434
- function overrideAnimationHelper(functionToOverride, name) {
3435
- if (functionToOverride === null) {
3436
- functionToOverride = Animation.prototype[name];
3437
- Animation.prototype[name] = function () {
3438
- trackAnimationOperation(this, name);
3439
- return functionToOverride.apply(this, arguments);
3440
- };
3586
+ function hashText(hash) {
3587
+ var id = lookup(hash);
3588
+ var node = getNode(id);
3589
+ return node !== null && node.textContent !== null ? node.textContent.substr(0, 25 /* Setting.ClickText */) : '';
3590
+ }
3591
+ function getNode(id) {
3592
+ return nodesMap.has(id) ? nodesMap.get(id) : null;
3593
+ }
3594
+ function getValue(id) {
3595
+ if (id in values) {
3596
+ return values[id];
3441
3597
  }
3598
+ return null;
3442
3599
  }
3443
- function trackAnimationOperation(animation, name) {
3444
- if (active()) {
3445
- var effect = animation.effect;
3446
- var target = getId(effect.target);
3447
- if (target !== null && effect.getKeyframes && effect.getTiming) {
3448
- if (!animation[animationId]) {
3449
- animation[animationId] = shortid();
3450
- animation[operationCount] = 0;
3451
- var keyframes = effect.getKeyframes();
3452
- var timing = effect.getTiming();
3453
- track$4(time(), animation[animationId], 0 /* AnimationOperation.Create */, JSON.stringify(keyframes), JSON.stringify(timing), target);
3454
- }
3455
- if (animation[operationCount]++ < maxOperations) {
3456
- var operation = null;
3457
- switch (name) {
3458
- case "play":
3459
- operation = 1 /* AnimationOperation.Play */;
3460
- break;
3461
- case "pause":
3462
- operation = 2 /* AnimationOperation.Pause */;
3463
- break;
3464
- case "cancel":
3465
- operation = 3 /* AnimationOperation.Cancel */;
3466
- break;
3467
- case "finish":
3468
- operation = 4 /* AnimationOperation.Finish */;
3469
- break;
3470
- case "commitStyles":
3471
- operation = 5 /* AnimationOperation.CommitStyles */;
3472
- break;
3473
- }
3474
- if (operation) {
3475
- track$4(time(), animation[animationId], operation);
3476
- }
3477
- }
3600
+ function get(node) {
3601
+ var id = getId(node);
3602
+ return id in values ? values[id] : null;
3603
+ }
3604
+ function lookup(hash) {
3605
+ return hash in hashMap ? hashMap[hash] : null;
3606
+ }
3607
+ function has$1(node) {
3608
+ return nodesMap.has(getId(node));
3609
+ }
3610
+ function updates$2() {
3611
+ var output = [];
3612
+ for (var _i = 0, updateMap_1 = updateMap; _i < updateMap_1.length; _i++) {
3613
+ var id = updateMap_1[_i];
3614
+ if (id in values) {
3615
+ output.push(values[id]);
3478
3616
  }
3479
3617
  }
3618
+ updateMap = [];
3619
+ return output;
3480
3620
  }
3481
-
3482
- function encode$4 (type, timer, ts) {
3483
- if (timer === void 0) { timer = null; }
3484
- if (ts === void 0) { ts = null; }
3485
- return __awaiter(this, void 0, void 0, function () {
3486
- var eventTime, tokens, _a, d, _i, _b, r, _c, _d, entry, _e, _f, entry, _g, _h, entry, values, _j, values_1, value, state, data, active, suspend, privacy, mangle, keys, _k, keys_1, key, box, factor, attr;
3487
- return __generator(this, function (_l) {
3488
- switch (_l.label) {
3489
- case 0:
3490
- eventTime = ts || time();
3491
- tokens = [eventTime, type];
3492
- _a = type;
3493
- switch (_a) {
3494
- case 8 /* Event.Document */: return [3 /*break*/, 1];
3495
- case 7 /* Event.Region */: return [3 /*break*/, 2];
3496
- case 45 /* Event.StyleSheetAdoption */: return [3 /*break*/, 3];
3497
- case 46 /* Event.StyleSheetUpdate */: return [3 /*break*/, 3];
3498
- case 44 /* Event.Animation */: return [3 /*break*/, 4];
3499
- case 5 /* Event.Discover */: return [3 /*break*/, 5];
3500
- case 6 /* Event.Mutation */: return [3 /*break*/, 5];
3501
- }
3502
- return [3 /*break*/, 12];
3503
- case 1:
3504
- d = data$c;
3505
- tokens.push(d.width);
3506
- tokens.push(d.height);
3507
- track$8(type, d.width, d.height);
3508
- queue(tokens);
3509
- return [3 /*break*/, 12];
3510
- case 2:
3511
- for (_i = 0, _b = state$1; _i < _b.length; _i++) {
3512
- r = _b[_i];
3513
- tokens = [r.time, 7 /* Event.Region */];
3514
- tokens.push(r.data.id);
3515
- tokens.push(r.data.interaction);
3516
- tokens.push(r.data.visibility);
3517
- tokens.push(r.data.name);
3518
- queue(tokens);
3519
- }
3520
- reset$6();
3521
- return [3 /*break*/, 12];
3522
- case 3:
3523
- for (_c = 0, _d = sheetAdoptionState; _c < _d.length; _c++) {
3524
- entry = _d[_c];
3525
- tokens = [entry.time, entry.event];
3526
- tokens.push(entry.data.id);
3527
- tokens.push(entry.data.operation);
3528
- tokens.push(entry.data.newIds);
3529
- queue(tokens);
3530
- }
3531
- for (_e = 0, _f = sheetUpdateState; _e < _f.length; _e++) {
3532
- entry = _f[_e];
3533
- tokens = [entry.time, entry.event];
3534
- tokens.push(entry.data.id);
3535
- tokens.push(entry.data.operation);
3536
- tokens.push(entry.data.cssRules);
3537
- queue(tokens);
3538
- }
3539
- reset$8();
3540
- return [3 /*break*/, 12];
3541
- case 4:
3542
- for (_g = 0, _h = state$2; _g < _h.length; _g++) {
3543
- entry = _h[_g];
3544
- tokens = [entry.time, entry.event];
3545
- tokens.push(entry.data.id);
3546
- tokens.push(entry.data.operation);
3547
- tokens.push(entry.data.keyFrames);
3548
- tokens.push(entry.data.timing);
3549
- tokens.push(entry.data.timeline);
3550
- tokens.push(entry.data.targetId);
3551
- queue(tokens);
3552
- }
3553
- reset$7();
3554
- return [3 /*break*/, 12];
3555
- case 5:
3556
- // Check if we are operating within the context of the current page
3557
- if (state$a(timer) === 2 /* Task.Stop */) {
3558
- return [3 /*break*/, 12];
3559
- }
3560
- values = updates$2();
3561
- if (!(values.length > 0)) return [3 /*break*/, 11];
3562
- _j = 0, values_1 = values;
3563
- _l.label = 6;
3564
- case 6:
3565
- if (!(_j < values_1.length)) return [3 /*break*/, 10];
3566
- value = values_1[_j];
3567
- state = state$a(timer);
3568
- if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 8];
3569
- return [4 /*yield*/, suspend$1(timer)];
3570
- case 7:
3571
- state = _l.sent();
3572
- _l.label = 8;
3573
- case 8:
3574
- if (state === 2 /* Task.Stop */) {
3575
- return [3 /*break*/, 10];
3576
- }
3577
- data = value.data;
3578
- active = value.metadata.active;
3579
- suspend = value.metadata.suspend;
3580
- privacy = value.metadata.privacy;
3581
- mangle = shouldMangle(value);
3582
- keys = active ? ["tag", "attributes", "value"] : ["tag"];
3583
- for (_k = 0, keys_1 = keys; _k < keys_1.length; _k++) {
3584
- key = keys_1[_k];
3585
- if (data[key]) {
3586
- switch (key) {
3587
- case "tag":
3588
- box = size(value);
3589
- factor = mangle ? -1 : 1;
3590
- tokens.push(value.id * factor);
3591
- if (value.parent && active) {
3592
- tokens.push(value.parent);
3593
- if (value.previous) {
3594
- tokens.push(value.previous);
3595
- }
3596
- }
3597
- tokens.push(suspend ? "*M" /* Constant.SuspendMutationTag */ : data[key]);
3598
- if (box && box.length === 2) {
3599
- tokens.push("".concat("#" /* Constant.Hash */).concat(str$1(box[0]), ".").concat(str$1(box[1])));
3600
- }
3601
- break;
3602
- case "attributes":
3603
- for (attr in data[key]) {
3604
- if (data[key][attr] !== undefined) {
3605
- tokens.push(attribute(attr, data[key][attr], privacy));
3606
- }
3607
- }
3608
- break;
3609
- case "value":
3610
- check$4(value.metadata.fraud, value.id, data[key]);
3611
- tokens.push(text$1(data[key], data.tag, privacy, mangle));
3612
- break;
3613
- }
3614
- }
3615
- }
3616
- _l.label = 9;
3617
- case 9:
3618
- _j++;
3619
- return [3 /*break*/, 6];
3620
- case 10:
3621
- if (type === 6 /* Event.Mutation */) {
3622
- activity(eventTime);
3623
- }
3624
- queue(tokenize(tokens), !config$2.lean);
3625
- _l.label = 11;
3626
- case 11: return [3 /*break*/, 12];
3627
- case 12: return [2 /*return*/];
3628
- }
3629
- });
3630
- });
3621
+ function remove(id, source) {
3622
+ if (id in values) {
3623
+ var value = values[id];
3624
+ value.metadata.active = false;
3625
+ value.parent = null;
3626
+ track$4(id, source);
3627
+ // Clean up node references for removed nodes
3628
+ removeNodeFromNodesMap(id);
3629
+ }
3631
3630
  }
3632
- function shouldMangle(value) {
3633
- var privacy = value.metadata.privacy;
3634
- return value.data.tag === "*T" /* Constant.TextTag */ && !(privacy === 0 /* Privacy.None */ || privacy === 1 /* Privacy.Sensitive */);
3631
+ function removeNodeFromNodesMap(id) {
3632
+ var nodeToBeRemoved = nodesMap.get(id);
3633
+ // Shadow dom roots shouldn't be deleted,
3634
+ // we should keep listening to the mutations there even they're not rendered in the DOM.
3635
+ if ((nodeToBeRemoved === null || nodeToBeRemoved === void 0 ? void 0 : nodeToBeRemoved.nodeType) === Node.DOCUMENT_FRAGMENT_NODE) {
3636
+ return;
3637
+ }
3638
+ if (nodeToBeRemoved && (nodeToBeRemoved === null || nodeToBeRemoved === void 0 ? void 0 : nodeToBeRemoved.nodeType) === Node.ELEMENT_NODE && nodeToBeRemoved["tagName"] === "IFRAME") {
3639
+ var iframe_1 = nodeToBeRemoved;
3640
+ removeObserver(iframe_1);
3641
+ }
3642
+ nodesMap.delete(id);
3643
+ var value = id in values ? values[id] : null;
3644
+ if (value && value.children) {
3645
+ for (var _i = 0, _a = value.children; _i < _a.length; _i++) {
3646
+ var childId = _a[_i];
3647
+ removeNodeFromNodesMap(childId);
3648
+ }
3649
+ }
3635
3650
  }
3636
- function size(value) {
3637
- if (value.metadata.size !== null && value.metadata.size.length === 0) {
3638
- var img = getNode(value.id);
3639
- if (img) {
3640
- return [Math.floor(img.offsetWidth * 100 /* Setting.BoxPrecision */), Math.floor(img.offsetHeight * 100 /* Setting.BoxPrecision */)];
3651
+ function updateImageSize(value) {
3652
+ // If this element is a image node, and is masked, then track box model for the current element
3653
+ if (value.data.tag === "IMG" /* Constant.ImageTag */ && value.metadata.privacy === 3 /* Privacy.TextImage */) {
3654
+ var img_1 = getNode(value.id);
3655
+ // We will not capture the natural image dimensions until it loads.
3656
+ if (img_1 && (!img_1.complete || img_1.naturalWidth === 0)) {
3657
+ // This will trigger mutation to update the original width and height after image loads.
3658
+ bind(img_1, 'load', function () {
3659
+ img_1.setAttribute('data-clarity-loaded', "".concat(shortid()));
3660
+ });
3641
3661
  }
3662
+ value.metadata.size = [];
3642
3663
  }
3643
- return value.metadata.size;
3644
3664
  }
3645
- function str$1(input) {
3646
- return input.toString(36);
3665
+ function getPreviousId(node) {
3666
+ var id = null;
3667
+ // Some nodes may not have an ID by design since Clarity skips over tags like SCRIPT, NOSCRIPT, META, COMMENTS, etc..
3668
+ // In that case, we keep going back and check for their sibling until we find a sibling with ID or no more sibling nodes are left.
3669
+ while (id === null && node.previousSibling) {
3670
+ id = getId(node.previousSibling);
3671
+ node = node.previousSibling;
3672
+ }
3673
+ return id;
3647
3674
  }
3648
- function attribute(key, value, privacy) {
3649
- return "".concat(key, "=").concat(text$1(value, key.indexOf("data-" /* Constant.DataAttribute */) === 0 ? "data-" /* Constant.DataAttribute */ : key, privacy));
3675
+ function track$4(id, source, changed, parentChanged) {
3676
+ if (changed === void 0) { changed = true; }
3677
+ if (parentChanged === void 0) { parentChanged = false; }
3678
+ // Keep track of the order in which mutations happened, they may not be sequential
3679
+ // Edge case: If an element is added later on, and pre-discovered element is moved as a child.
3680
+ // In that case, we need to reorder the pre-discovered element in the update list to keep visualization consistent.
3681
+ var uIndex = updateMap.indexOf(id);
3682
+ if (uIndex >= 0 && source === 1 /* Source.ChildListAdd */ && parentChanged) {
3683
+ updateMap.splice(uIndex, 1);
3684
+ updateMap.push(id);
3685
+ }
3686
+ else if (uIndex === -1 && changed) {
3687
+ updateMap.push(id);
3688
+ }
3650
3689
  }
3651
3690
 
3652
3691
  var state$1 = [];
@@ -3805,7 +3844,7 @@ function target(evt) {
3805
3844
  function metadata$2(node, event, text) {
3806
3845
  if (text === void 0) { text = null; }
3807
3846
  // If the node is null, we return a reserved value for id: 0. Valid assignment of id begins from 1+.
3808
- var output = { id: 0, hash: null, privacy: 2 /* Privacy.Text */, node: node };
3847
+ var output = { id: 0, hash: null, privacy: 2 /* Privacy.Text */ };
3809
3848
  if (node) {
3810
3849
  var value = get(node);
3811
3850
  if (value !== null) {
@@ -3859,7 +3898,7 @@ function encode$3 (type, ts) {
3859
3898
  track$8(entry.event, entry.data.x, entry.data.y, entry.time);
3860
3899
  }
3861
3900
  }
3862
- reset$f();
3901
+ reset$i();
3863
3902
  break;
3864
3903
  case 9 /* Event.Click */:
3865
3904
  for (_b = 0, _c = state$8; _b < _c.length; _b++) {
@@ -3883,7 +3922,7 @@ function encode$3 (type, ts) {
3883
3922
  queue(tokens);
3884
3923
  track$2(entry.time, entry.event, cHash, entry.data.x, entry.data.y, entry.data.reaction, entry.data.context);
3885
3924
  }
3886
- reset$i();
3925
+ reset$l();
3887
3926
  break;
3888
3927
  case 38 /* Event.Clipboard */:
3889
3928
  for (_d = 0, _e = state$7; _d < _e.length; _d++) {
@@ -3896,21 +3935,21 @@ function encode$3 (type, ts) {
3896
3935
  queue(tokens);
3897
3936
  }
3898
3937
  }
3899
- reset$h();
3938
+ reset$k();
3900
3939
  break;
3901
3940
  case 11 /* Event.Resize */:
3902
- r = data$b;
3941
+ r = data$c;
3903
3942
  tokens.push(r.width);
3904
3943
  tokens.push(r.height);
3905
3944
  track$8(type, r.width, r.height);
3906
- reset$e();
3945
+ reset$h();
3907
3946
  queue(tokens);
3908
3947
  break;
3909
3948
  case 26 /* Event.Unload */:
3910
- u = data$9;
3949
+ u = data$a;
3911
3950
  tokens.push(u.name);
3912
3951
  tokens.push(u.persisted);
3913
- reset$a();
3952
+ reset$d();
3914
3953
  queue(tokens);
3915
3954
  break;
3916
3955
  case 27 /* Event.Input */:
@@ -3922,10 +3961,10 @@ function encode$3 (type, ts) {
3922
3961
  tokens.push(text$1(entry.data.value, "input", iTarget.privacy, false, entry.data.type));
3923
3962
  queue(tokens);
3924
3963
  }
3925
- reset$g();
3964
+ reset$j();
3926
3965
  break;
3927
3966
  case 21 /* Event.Selection */:
3928
- s = data$a;
3967
+ s = data$b;
3929
3968
  if (s) {
3930
3969
  startTarget = metadata$2(s.start, type);
3931
3970
  endTarget = metadata$2(s.end, type);
@@ -3933,7 +3972,7 @@ function encode$3 (type, ts) {
3933
3972
  tokens.push(s.startOffset);
3934
3973
  tokens.push(endTarget.id);
3935
3974
  tokens.push(s.endOffset);
3936
- reset$c();
3975
+ reset$f();
3937
3976
  queue(tokens);
3938
3977
  }
3939
3978
  break;
@@ -3956,7 +3995,7 @@ function encode$3 (type, ts) {
3956
3995
  track$8(entry.event, entry.data.x, entry.data.y, entry.time);
3957
3996
  }
3958
3997
  }
3959
- reset$d();
3998
+ reset$g();
3960
3999
  break;
3961
4000
  case 42 /* Event.Change */:
3962
4001
  for (_k = 0, _l = state$9; _k < _l.length; _k++) {
@@ -3972,7 +4011,7 @@ function encode$3 (type, ts) {
3972
4011
  queue(tokens);
3973
4012
  }
3974
4013
  }
3975
- reset$j();
4014
+ reset$m();
3976
4015
  break;
3977
4016
  case 39 /* Event.Submit */:
3978
4017
  for (_m = 0, _o = state$3; _m < _o.length; _m++) {
@@ -3984,7 +4023,7 @@ function encode$3 (type, ts) {
3984
4023
  queue(tokens);
3985
4024
  }
3986
4025
  }
3987
- reset$b();
4026
+ reset$e();
3988
4027
  break;
3989
4028
  case 22 /* Event.Timeline */:
3990
4029
  for (_p = 0, _q = updates$1; _p < _q.length; _p++) {
@@ -4001,11 +4040,11 @@ function encode$3 (type, ts) {
4001
4040
  reset$5();
4002
4041
  break;
4003
4042
  case 28 /* Event.Visibility */:
4004
- v = data$8;
4043
+ v = data$9;
4005
4044
  tokens.push(v.visible);
4006
4045
  queue(tokens);
4007
4046
  visibility(t, v.visible);
4008
- reset$9();
4047
+ reset$c();
4009
4048
  break;
4010
4049
  }
4011
4050
  return [2 /*return*/];
@@ -4156,7 +4195,7 @@ function upload(final) {
4156
4195
  compute$6();
4157
4196
  compute$5();
4158
4197
  compute$a();
4159
- compute$7();
4198
+ compute$8();
4160
4199
  last = final === true;
4161
4200
  e = JSON.stringify(envelope(last));
4162
4201
  a = "[".concat(analysis.join(), "]");
@@ -4483,15 +4522,15 @@ function trigger$1(input) {
4483
4522
  }
4484
4523
  switch (source) {
4485
4524
  case 0 /* ExtractSource.Javascript */:
4486
- var variable = value.substring(1, value.length);
4525
+ var variable = value.slice(1);
4487
4526
  variables[key][id] = parse(variable);
4488
4527
  break;
4489
4528
  case 2 /* ExtractSource.Text */:
4490
4529
  selectors[key][id] = value;
4491
4530
  break;
4492
4531
  case 4 /* ExtractSource.Hash */:
4493
- var hash = value.substring(1, value.length);
4494
- hashes[key][id] = hash;
4532
+ var hash_1 = value.slice(1);
4533
+ hashes[key][id] = hash_1;
4495
4534
  break;
4496
4535
  }
4497
4536
  }
@@ -4518,17 +4557,23 @@ function compute$4() {
4518
4557
  }
4519
4558
  var selectorData = selectors[key];
4520
4559
  for (var s in selectorData) {
4560
+ var shouldMask = false;
4521
4561
  var selectorKey = parseInt(s);
4522
- var nodes = document.querySelectorAll(selectorData[selectorKey]);
4562
+ var selector = selectorData[selectorKey];
4563
+ if (selector.startsWith("@" /* Constant.At */)) {
4564
+ shouldMask = true;
4565
+ selector = selector.slice(1);
4566
+ }
4567
+ var nodes = document.querySelectorAll(selector);
4523
4568
  if (nodes) {
4524
- var text = Array.from(nodes).map(function (e) { return e.textContent; });
4525
- update(key, selectorKey, text.join("<SEP>" /* Constant.Seperator */).substring(0, 10000 /* Setting.ExtractLimit */));
4569
+ var text = Array.from(nodes).map(function (e) { return e.textContent; }).join("<SEP>" /* Constant.Seperator */);
4570
+ update(key, selectorKey, (shouldMask ? hash(text).trim() : text).slice(0, 10000 /* Setting.ExtractLimit */));
4526
4571
  }
4527
4572
  }
4528
4573
  var hashData = hashes[key];
4529
4574
  for (var h in hashData) {
4530
4575
  var hashKey = parseInt(h);
4531
- var content = hashText(hashData[hashKey]).trim().substring(0, 10000 /* Setting.ExtractLimit */);
4576
+ var content = hashText(hashData[hashKey]).trim().slice(0, 10000 /* Setting.ExtractLimit */);
4532
4577
  update(key, hashKey, content);
4533
4578
  }
4534
4579
  }
@@ -4572,9 +4617,9 @@ function parse(variable) {
4572
4617
  var conditionStart = part.indexOf("{" /* Constant.ConditionStart */);
4573
4618
  var conditionEnd = part.indexOf("}" /* Constant.ConditionEnd */);
4574
4619
  syntax.push({
4575
- name: arrayStart > 0 ? part.substring(0, arrayStart) : (conditionStart > 0 ? part.substring(0, conditionStart) : part),
4620
+ name: arrayStart > 0 ? part.slice(0, arrayStart) : (conditionStart > 0 ? part.slice(0, conditionStart) : part),
4576
4621
  type: arrayStart > 0 ? 1 /* Type.Array */ : (conditionStart > 0 ? 2 /* Type.Object */ : 3 /* Type.Simple */),
4577
- condition: conditionStart > 0 ? part.substring(conditionStart + 1, conditionEnd) : null
4622
+ condition: conditionStart > 0 ? part.slice(conditionStart + 1, conditionEnd) : null
4578
4623
  });
4579
4624
  }
4580
4625
  return syntax;
@@ -4613,7 +4658,7 @@ function evaluate(variable, base) {
4613
4658
  }
4614
4659
  function str(input) {
4615
4660
  // Automatically trim string to max of Setting.ExtractLimit to avoid fetching long strings
4616
- return input ? JSON.stringify(input).substring(0, 10000 /* Setting.ExtractLimit */) : input;
4661
+ return input ? JSON.stringify(input).slice(0, 10000 /* Setting.ExtractLimit */) : input;
4617
4662
  }
4618
4663
  function match(base, condition) {
4619
4664
  if (condition) {
@@ -5285,7 +5330,7 @@ function measure (method) {
5285
5330
  };
5286
5331
  }
5287
5332
 
5288
- var bindings = [];
5333
+ var bindings = new Map();
5289
5334
  function bind(target, event, listener, capture, passive) {
5290
5335
  if (capture === void 0) { capture = false; }
5291
5336
  if (passive === void 0) { passive = true; }
@@ -5294,7 +5339,10 @@ function bind(target, event, listener, capture, passive) {
5294
5339
  // E.g. Iframe may start off as same-origin but later turn into cross-origin, and the following lines will throw an exception.
5295
5340
  try {
5296
5341
  target[api("addEventListener" /* Constant.AddEventListener */)](event, listener, { capture: capture, passive: passive });
5297
- bindings.push({ event: event, target: target, listener: listener, options: { capture: capture, passive: passive } });
5342
+ if (!has(target)) {
5343
+ bindings.set(target, []);
5344
+ }
5345
+ bindings.get(target).push({ event: event, listener: listener, options: { capture: capture, passive: passive } });
5298
5346
  }
5299
5347
  catch (_a) {
5300
5348
  /* do nothing */
@@ -5302,17 +5350,31 @@ function bind(target, event, listener, capture, passive) {
5302
5350
  }
5303
5351
  function reset$1() {
5304
5352
  // Walk through existing list of bindings and remove them all
5305
- for (var _i = 0, bindings_1 = bindings; _i < bindings_1.length; _i++) {
5306
- var binding = bindings_1[_i];
5353
+ bindings.forEach(function (bindingsPerTarget, target) {
5354
+ resetByTarget(bindingsPerTarget, target);
5355
+ });
5356
+ bindings = new Map();
5357
+ }
5358
+ function unbind(target) {
5359
+ if (!has(target)) {
5360
+ return;
5361
+ }
5362
+ resetByTarget(bindings.get(target), target);
5363
+ }
5364
+ function has(target) {
5365
+ return bindings.has(target);
5366
+ }
5367
+ function resetByTarget(bindingsPerTarget, target) {
5368
+ bindingsPerTarget.forEach(function (binding) {
5307
5369
  // Wrapping inside try / catch to avoid situations where the element may be destroyed before we get a chance to unbind
5308
5370
  try {
5309
- binding.target[api("removeEventListener" /* Constant.RemoveEventListener */)](binding.event, binding.listener, { capture: binding.options.capture, passive: binding.options.passive });
5371
+ target[api("removeEventListener" /* Constant.RemoveEventListener */)](binding.event, binding.listener, { capture: binding.options.capture, passive: binding.options.passive });
5310
5372
  }
5311
5373
  catch (_a) {
5312
5374
  /* do nothing */
5313
5375
  }
5314
- }
5315
- bindings = [];
5376
+ });
5377
+ bindings.delete(target);
5316
5378
  }
5317
5379
 
5318
5380
  var pushState = null;
@@ -5376,7 +5438,7 @@ var status = false;
5376
5438
  function start$6() {
5377
5439
  status = true;
5378
5440
  start$I();
5379
- reset$l();
5441
+ reset$n();
5380
5442
  reset$1();
5381
5443
  reset$2();
5382
5444
  start$7();
@@ -5385,7 +5447,7 @@ function stop$5() {
5385
5447
  stop$6();
5386
5448
  reset$2();
5387
5449
  reset$1();
5388
- reset$l();
5450
+ reset$n();
5389
5451
  stop$F();
5390
5452
  status = false;
5391
5453
  }
@@ -5460,9 +5522,9 @@ var diagnostic = /*#__PURE__*/Object.freeze({
5460
5522
 
5461
5523
  function start$4() {
5462
5524
  schedule$1(discover, 1 /* Priority.High */).then(function () {
5463
- measure(compute$9)();
5525
+ measure(compute$7)();
5464
5526
  measure(compute$6)();
5465
- measure(compute$8)();
5527
+ measure(compute$9)();
5466
5528
  });
5467
5529
  }
5468
5530
  function discover() {
@@ -5473,7 +5535,7 @@ function discover() {
5473
5535
  case 0:
5474
5536
  ts = time();
5475
5537
  timer = { id: id(), cost: 3 /* Metric.LayoutCost */ };
5476
- start$y(timer);
5538
+ start$z(timer);
5477
5539
  return [4 /*yield*/, traverse(document, timer, 0 /* Source.Discover */, ts)];
5478
5540
  case 1:
5479
5541
  _a.sent();
@@ -5481,7 +5543,7 @@ function discover() {
5481
5543
  return [4 /*yield*/, encode$4(5 /* Event.Discover */, timer, ts)];
5482
5544
  case 2:
5483
5545
  _a.sent();
5484
- stop$w(timer);
5546
+ stop$x(timer);
5485
5547
  return [2 /*return*/];
5486
5548
  }
5487
5549
  });
@@ -5492,29 +5554,29 @@ function start$3() {
5492
5554
  start$3.dn = 20 /* FunctionNames.LayoutStart */;
5493
5555
  // The order below is important
5494
5556
  // and is determined by interdependencies of modules
5495
- start$x();
5557
+ start$k();
5496
5558
  start$h();
5497
- start$z();
5559
+ start$i();
5498
5560
  if (config$2.delayDom) {
5499
5561
  // Lazy load layout module as part of page load time performance improvements experiment
5500
5562
  bind(window, 'load', function () {
5501
- start$k();
5563
+ start$j();
5502
5564
  });
5503
5565
  }
5504
5566
  else {
5505
- start$k();
5567
+ start$j();
5506
5568
  }
5507
5569
  start$4();
5508
- start$j();
5509
- start$i();
5570
+ start$m();
5571
+ start$l();
5510
5572
  }
5511
5573
  function stop$3() {
5512
5574
  stop$f();
5513
- stop$x();
5514
- stop$i();
5515
- stop$v();
5516
- stop$h();
5517
5575
  stop$g();
5576
+ stop$h();
5577
+ stop$i();
5578
+ stop$k();
5579
+ stop$j();
5518
5580
  }
5519
5581
 
5520
5582
  var layout = /*#__PURE__*/Object.freeze({