clarity-js 0.7.2 → 0.7.4

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.
@@ -1,92 +1,74 @@
1
- var dom = /*#__PURE__*/Object.freeze({
2
- __proto__: null,
3
- get start () { return start$i; },
4
- get stop () { return stop$g; },
5
- get parse () { return parse$1; },
6
- get getId () { return getId; },
7
- get add () { return add; },
8
- get update () { return update$1; },
9
- get sameorigin () { return sameorigin; },
10
- get iframe () { return iframe; },
11
- get hashText () { return hashText; },
12
- get getNode () { return getNode; },
13
- get getValue () { return getValue; },
14
- get get () { return get; },
15
- get lookup () { return lookup; },
16
- get has () { return has; },
17
- get updates () { return updates$2; }
18
- });
19
1
  var upload$1 = /*#__PURE__*/Object.freeze({
20
2
  __proto__: null,
21
- get track () { return track$1; },
22
- get start () { return start$f; },
23
3
  get queue () { return queue; },
24
- get stop () { return stop$d; }
4
+ get start () { return start$f; },
5
+ get stop () { return stop$d; },
6
+ get track () { return track$1; }
25
7
  });
26
8
  var extract = /*#__PURE__*/Object.freeze({
27
9
  __proto__: null,
28
- get data () { return data$5; },
29
- get keys () { return keys; },
30
- get fragments () { return fragments; },
31
- get start () { return start$c; },
32
10
  get clone () { return clone; },
33
11
  get compute () { return compute$4; },
12
+ get data () { return data$5; },
13
+ get keys () { return keys; },
34
14
  get reset () { return reset$4; },
35
- get update () { return update; },
36
- get stop () { return stop$b; }
15
+ get start () { return start$c; },
16
+ get stop () { return stop$b; },
17
+ get trigger () { return trigger$1; },
18
+ get update () { return update; }
37
19
  });
38
20
  var limit = /*#__PURE__*/Object.freeze({
39
21
  __proto__: null,
40
- get data () { return data$4; },
41
- get start () { return start$b; },
42
22
  get check () { return check$2; },
43
- get trigger () { return trigger; },
44
23
  get compute () { return compute$3; },
45
- get stop () { return stop$a; }
24
+ get data () { return data$4; },
25
+ get start () { return start$b; },
26
+ get stop () { return stop$a; },
27
+ get trigger () { return trigger; }
46
28
  });
47
29
  var dimension = /*#__PURE__*/Object.freeze({
48
30
  __proto__: null,
31
+ get compute () { return compute$2; },
49
32
  get data () { return data$3; },
50
- get updates () { return updates; },
33
+ get log () { return log; },
34
+ get reset () { return reset$3; },
51
35
  get start () { return start$a; },
52
36
  get stop () { return stop$9; },
53
- get log () { return log; },
54
- get compute () { return compute$2; },
55
- get reset () { return reset$3; }
37
+ get updates () { return updates; }
56
38
  });
57
39
  var metadata$1 = /*#__PURE__*/Object.freeze({
58
40
  __proto__: null,
59
- get data () { return data$2; },
60
41
  get callbacks () { return callbacks; },
61
- get start () { return start$9; },
62
- get stop () { return stop$8; },
63
- get metadata () { return metadata; },
64
- get id () { return id; },
65
- get consent () { return consent; },
66
42
  get clear () { return clear; },
67
- get save () { return save; }
43
+ get consent () { return consent; },
44
+ get data () { return data$2; },
45
+ get id () { return id; },
46
+ get metadata () { return metadata; },
47
+ get save () { return save; },
48
+ get start () { return start$9; },
49
+ get stop () { return stop$8; }
68
50
  });
69
51
  var envelope$1 = /*#__PURE__*/Object.freeze({
70
52
  __proto__: null,
71
53
  get data () { return data$1; },
54
+ get envelope () { return envelope; },
72
55
  get start () { return start$8; },
73
- get stop () { return stop$7; },
74
- get envelope () { return envelope; }
56
+ get stop () { return stop$7; }
75
57
  });
76
58
  var clarity = /*#__PURE__*/Object.freeze({
77
59
  __proto__: null,
78
- get version () { return version$1; },
79
- get start () { return start; },
80
- get pause () { return pause; },
81
- get resume () { return resume; },
82
- get stop () { return stop; },
83
60
  get consent () { return consent; },
84
61
  get event () { return event; },
62
+ get hashText () { return hashText; },
85
63
  get identify () { return identify; },
64
+ get metadata () { return metadata; },
65
+ get pause () { return pause; },
66
+ get resume () { return resume; },
86
67
  get set () { return set; },
68
+ get start () { return start; },
69
+ get stop () { return stop; },
87
70
  get upgrade () { return upgrade; },
88
- get metadata () { return metadata; },
89
- get hashText () { return hashText; }
71
+ get version () { return version$1; }
90
72
  });
91
73
 
92
74
  var w = window;
@@ -135,7 +117,6 @@ var config$1 = {
135
117
  mask: [],
136
118
  unmask: [],
137
119
  regions: [],
138
- extract: [],
139
120
  cookies: [],
140
121
  fraud: true,
141
122
  checksum: [],
@@ -166,7 +147,7 @@ function stop$C() {
166
147
  startTime = 0;
167
148
  }
168
149
 
169
- var version$1 = "0.7.2";
150
+ var version$1 = "0.7.4";
170
151
 
171
152
  // tslint:disable: no-bitwise
172
153
  function hash (input, precision) {
@@ -414,14 +395,14 @@ function stop$B() {
414
395
 
415
396
  var baseline = /*#__PURE__*/Object.freeze({
416
397
  __proto__: null,
417
- get state () { return state$a; },
418
- start: start$F,
419
- reset: reset$q,
420
- track: track$7,
421
398
  activity: activity,
422
- visibility: visibility,
423
399
  compute: compute$c,
424
- stop: stop$B
400
+ reset: reset$q,
401
+ start: start$F,
402
+ get state () { return state$a; },
403
+ stop: stop$B,
404
+ track: track$7,
405
+ visibility: visibility
425
406
  });
426
407
 
427
408
  var data$j = null;
@@ -532,8 +513,8 @@ function stop$z() {
532
513
  var ping$1 = /*#__PURE__*/Object.freeze({
533
514
  __proto__: null,
534
515
  get data () { return data$h; },
535
- start: start$D,
536
516
  reset: reset$o,
517
+ start: start$D,
537
518
  stop: stop$z
538
519
  });
539
520
 
@@ -570,12 +551,12 @@ function reset$n() {
570
551
 
571
552
  var summary = /*#__PURE__*/Object.freeze({
572
553
  __proto__: null,
554
+ compute: compute$a,
573
555
  get data () { return data$g; },
556
+ reset: reset$n,
574
557
  start: start$C,
575
558
  stop: stop$y,
576
- track: track$6,
577
- compute: compute$a,
578
- reset: reset$n
559
+ track: track$6
579
560
  });
580
561
 
581
562
  var data$f = null;
@@ -611,8 +592,8 @@ var upgrade$1 = /*#__PURE__*/Object.freeze({
611
592
  __proto__: null,
612
593
  get data () { return data$f; },
613
594
  start: start$B,
614
- upgrade: upgrade,
615
- stop: stop$x
595
+ stop: stop$x,
596
+ upgrade: upgrade
616
597
  });
617
598
 
618
599
  var data$e = null;
@@ -657,12 +638,12 @@ function stop$w() {
657
638
 
658
639
  var variable = /*#__PURE__*/Object.freeze({
659
640
  __proto__: null,
641
+ compute: compute$9,
660
642
  get data () { return data$e; },
661
- start: start$A,
662
- set: set,
663
643
  identify: identify,
664
- compute: compute$9,
665
644
  reset: reset$m,
645
+ set: set,
646
+ start: start$A,
666
647
  stop: stop$w
667
648
  });
668
649
 
@@ -697,7 +678,7 @@ function __generator(thisArg, body) {
697
678
  function verb(n) { return function (v) { return step([n, v]); }; }
698
679
  function step(op) {
699
680
  if (f) throw new TypeError("Generator is already executing.");
700
- while (_) try {
681
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
701
682
  if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
702
683
  if (y = 0, t) op = [op[0] & 2, t.value];
703
684
  switch (op[0]) {
@@ -915,2112 +896,2100 @@ function filter(value) {
915
896
 
916
897
  var selector = /*#__PURE__*/Object.freeze({
917
898
  __proto__: null,
918
- reset: reset$l,
919
- get: get$1
899
+ get: get$1,
900
+ reset: reset$l
920
901
  });
921
902
 
922
- // Track the start time to be able to compute duration at the end of the task
923
- var idleTimeout = 5000;
924
- var tracker = {};
925
- var queuedTasks = [];
926
- var activeTask = null;
927
- var pauseTask = null;
928
- var resumeResolve = null;
929
- function pause$1() {
930
- if (pauseTask === null) {
931
- pauseTask = new Promise(function (resolve) {
932
- resumeResolve = resolve;
933
- });
934
- }
903
+ var index = 1;
904
+ var nodes = [];
905
+ var values = [];
906
+ var updateMap = [];
907
+ var hashMap = {};
908
+ var override = [];
909
+ var unmask = [];
910
+ var maskText = [];
911
+ var maskExclude = [];
912
+ var maskDisable = [];
913
+ var maskTags = [];
914
+ // The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
915
+ var idMap = null; // Maps node => id.
916
+ var iframeMap = null; // Maps iframe's contentDocument => parent iframe element
917
+ var privacyMap = null; // Maps node => Privacy (enum)
918
+ var fraudMap = null; // Maps node => FraudId (number)
919
+ function start$x() {
920
+ reset$k();
921
+ parse$1(document, true);
935
922
  }
936
- function resume$1() {
937
- if (pauseTask) {
938
- resumeResolve();
939
- pauseTask = null;
940
- if (activeTask === null) {
941
- run();
942
- }
943
- }
923
+ function stop$u() {
924
+ reset$k();
944
925
  }
945
926
  function reset$k() {
946
- tracker = {};
947
- queuedTasks = [];
948
- activeTask = null;
949
- pauseTask = null;
950
- }
951
- function schedule$1(task, priority) {
952
- if (priority === void 0) { priority = 0 /* Priority.Normal */; }
953
- return __awaiter(this, void 0, void 0, function () {
954
- var _i, queuedTasks_1, q, promise;
955
- return __generator(this, function (_a) {
956
- // If this task is already scheduled, skip it
957
- for (_i = 0, queuedTasks_1 = queuedTasks; _i < queuedTasks_1.length; _i++) {
958
- q = queuedTasks_1[_i];
959
- if (q.task === task) {
960
- return [2 /*return*/];
961
- }
962
- }
963
- promise = new Promise(function (resolve) {
964
- var insert = priority === 1 /* Priority.High */ ? "unshift" : "push";
965
- // Queue this task for asynchronous execution later
966
- // We also store a unique page identifier (id) along with the task to ensure
967
- // ensure that we do not accidentally execute this task in context of a different page
968
- queuedTasks[insert]({ task: task, resolve: resolve, id: id() });
969
- });
970
- // If there is no active task running, and Clarity is not in pause state,
971
- // invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
972
- if (activeTask === null && pauseTask === null) {
973
- run();
974
- }
975
- return [2 /*return*/, promise];
976
- });
977
- });
927
+ index = 1;
928
+ nodes = [];
929
+ values = [];
930
+ updateMap = [];
931
+ hashMap = {};
932
+ override = [];
933
+ unmask = [];
934
+ maskText = "address,password,contact" /* Mask.Text */.split("," /* Constant.Comma */);
935
+ maskExclude = "password,secret,pass,social,ssn,code,hidden" /* Mask.Exclude */.split("," /* Constant.Comma */);
936
+ maskDisable = "radio,checkbox,range,button,reset,submit" /* Mask.Disable */.split("," /* Constant.Comma */);
937
+ maskTags = "INPUT,SELECT,TEXTAREA" /* Mask.Tags */.split("," /* Constant.Comma */);
938
+ idMap = new WeakMap();
939
+ iframeMap = new WeakMap();
940
+ privacyMap = new WeakMap();
941
+ fraudMap = new WeakMap();
942
+ reset$l();
978
943
  }
979
- function run() {
980
- var entry = queuedTasks.shift();
981
- if (entry) {
982
- activeTask = entry;
983
- entry.task().then(function () {
984
- // Bail out if the context in which this task was operating is different from the current page
985
- // An example scenario where task could span across pages is Single Page Applications (SPA)
986
- // A task that started on page #1, but completes on page #2
987
- if (entry.id !== id()) {
988
- return;
989
- }
990
- entry.resolve();
991
- activeTask = null; // Reset active task back to null now that the promise is resolved
992
- run();
993
- }).catch(function (error) {
994
- // If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
995
- if (entry.id !== id()) {
996
- return;
997
- }
998
- if (error) {
999
- log$1(0 /* Code.RunTask */, 1 /* Severity.Warning */, error.name, error.message, error.stack);
1000
- }
1001
- activeTask = null;
1002
- run();
1003
- });
944
+ // We parse new root nodes for any regions or masked nodes in the beginning (document) and
945
+ // later whenever there are new additions or modifications to DOM (mutations)
946
+ function parse$1(root, init) {
947
+ if (init === void 0) { init = false; }
948
+ // Wrap selectors in a try / catch block.
949
+ // It's possible for script to receive invalid selectors, e.g. "'#id'" with extra quotes, and cause the code below to fail
950
+ try {
951
+ // Parse unmask configuration into separate query selectors and override tokens as part of initialization
952
+ if (init) {
953
+ config$1.unmask.forEach(function (x) { return x.indexOf("!" /* Constant.Bang */) < 0 ? unmask.push(x) : override.push(x.substr(1)); });
954
+ }
955
+ // Since mutations may happen on leaf nodes too, e.g. text nodes, which may not support all selector APIs.
956
+ // We ensure that the root note supports querySelectorAll API before executing the code below to identify new regions.
957
+ if ("querySelectorAll" in root) {
958
+ config$1.regions.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return observe$c(e, "".concat(x[0])); }); }); // Regions
959
+ config$1.mask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 3 /* Privacy.TextImage */); }); }); // Masked Elements
960
+ config$1.checksum.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return fraudMap.set(e, x[0]); }); }); // Fraud Checksum Check
961
+ unmask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 0 /* Privacy.None */); }); }); // Unmasked Elements
962
+ }
1004
963
  }
1005
- }
1006
- function state$9(timer) {
1007
- var id = key(timer);
1008
- if (id in tracker) {
1009
- var elapsed = performance.now() - tracker[id].start;
1010
- return (elapsed > tracker[id].yield) ? 0 /* Task.Wait */ : 1 /* Task.Run */;
964
+ catch (e) {
965
+ log$1(5 /* Code.Selector */, 1 /* Severity.Warning */, e ? e.name : null);
1011
966
  }
1012
- // If this task is no longer being tracked, send stop message to the caller
1013
- return 2 /* Task.Stop */;
1014
967
  }
1015
- function start$x(timer) {
1016
- tracker[key(timer)] = { start: performance.now(), calls: 0, yield: 30 /* Setting.LongTask */ };
1017
- }
1018
- function restart$2(timer) {
1019
- var id = key(timer);
1020
- if (tracker && tracker[id]) {
1021
- var c = tracker[id].calls;
1022
- var y = tracker[id].yield;
1023
- start$x(timer);
1024
- tracker[id].calls = c + 1;
1025
- tracker[id].yield = y;
968
+ function getId(node, autogen) {
969
+ if (autogen === void 0) { autogen = false; }
970
+ if (node === null) {
971
+ return null;
1026
972
  }
1027
- }
1028
- function stop$u(timer) {
1029
- var end = performance.now();
1030
- var id = key(timer);
1031
- var duration = end - tracker[id].start;
1032
- sum(timer.cost, duration);
1033
- count$1(5 /* Metric.InvokeCount */);
1034
- // For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
1035
- // However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
1036
- if (tracker[id].calls > 0) {
1037
- sum(4 /* Metric.TotalCost */, duration);
973
+ var id = idMap.get(node);
974
+ if (!id && autogen) {
975
+ id = index++;
976
+ idMap.set(node, id);
1038
977
  }
978
+ return id ? id : null;
1039
979
  }
1040
- function suspend$1(timer) {
1041
- return __awaiter(this, void 0, void 0, function () {
1042
- var id, _a;
1043
- return __generator(this, function (_b) {
1044
- switch (_b.label) {
1045
- case 0:
1046
- id = key(timer);
1047
- if (!(id in tracker)) return [3 /*break*/, 2];
1048
- stop$u(timer);
1049
- _a = tracker[id];
1050
- return [4 /*yield*/, wait()];
1051
- case 1:
1052
- _a.yield = (_b.sent()).timeRemaining();
1053
- restart$2(timer);
1054
- _b.label = 2;
1055
- case 2:
1056
- // After we are done with suspending task, ensure that we are still operating in the right context
1057
- // If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
1058
- return [2 /*return*/, id in tracker ? 1 /* Task.Run */ : 2 /* Task.Stop */];
1059
- }
1060
- });
1061
- });
1062
- }
1063
- function key(timer) {
1064
- return "".concat(timer.id, ".").concat(timer.cost);
1065
- }
1066
- function wait() {
1067
- return __awaiter(this, void 0, void 0, function () {
1068
- return __generator(this, function (_a) {
1069
- switch (_a.label) {
1070
- case 0:
1071
- if (!pauseTask) return [3 /*break*/, 2];
1072
- return [4 /*yield*/, pauseTask];
1073
- case 1:
1074
- _a.sent();
1075
- _a.label = 2;
1076
- case 2: return [2 /*return*/, new Promise(function (resolve) {
1077
- requestIdleCallback(resolve, { timeout: idleTimeout });
1078
- })];
1079
- }
1080
- });
1081
- });
1082
- }
1083
- // Use native implementation of requestIdleCallback if it exists.
1084
- // Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
1085
- // While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
1086
- // Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
1087
- // This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
1088
- // For non-blocking behavior, We need to know when browser has finished painiting. This can be accomplished in two different ways (hacks):
1089
- // (1) Use MessageChannel to pass the message, and browser will receive the message right after pain event has occured.
1090
- // (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
1091
- // Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
1092
- // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
1093
- function requestIdleCallbackPolyfill(callback, options) {
1094
- var startTime = performance.now();
1095
- var channel = new MessageChannel();
1096
- var incoming = channel.port1;
1097
- var outgoing = channel.port2;
1098
- incoming.onmessage = function (event) {
1099
- var currentTime = performance.now();
1100
- var elapsed = currentTime - startTime;
1101
- var duration = currentTime - event.data;
1102
- if (duration > 30 /* Setting.LongTask */ && elapsed < options.timeout) {
1103
- requestAnimationFrame(function () { outgoing.postMessage(currentTime); });
1104
- }
1105
- else {
1106
- var didTimeout_1 = elapsed > options.timeout;
1107
- callback({
1108
- didTimeout: didTimeout_1,
1109
- timeRemaining: function () { return didTimeout_1 ? 30 /* Setting.LongTask */ : Math.max(0, 30 /* Setting.LongTask */ - duration); }
1110
- });
1111
- }
980
+ function add(node, parent, data, source) {
981
+ var id = getId(node, true);
982
+ var parentId = parent ? getId(parent) : null;
983
+ var previousId = getPreviousId(node);
984
+ var parentValue = null;
985
+ var regionId = exists(node) ? id : null;
986
+ var fraudId = fraudMap.has(node) ? fraudMap.get(node) : null;
987
+ var privacyId = config$1.content ? 1 /* Privacy.Sensitive */ : 3 /* Privacy.TextImage */;
988
+ if (parentId >= 0 && values[parentId]) {
989
+ parentValue = values[parentId];
990
+ parentValue.children.push(id);
991
+ regionId = regionId === null ? parentValue.region : regionId;
992
+ fraudId = fraudId === null ? parentValue.metadata.fraud : fraudId;
993
+ privacyId = parentValue.metadata.privacy;
994
+ }
995
+ // If there's an explicit region attribute set on the element, use it to mark a region on the page
996
+ if (data.attributes && "data-clarity-region" /* Constant.RegionData */ in data.attributes) {
997
+ observe$c(node, data.attributes["data-clarity-region" /* Constant.RegionData */]);
998
+ regionId = id;
999
+ }
1000
+ nodes[id] = node;
1001
+ values[id] = {
1002
+ id: id,
1003
+ parent: parentId,
1004
+ previous: previousId,
1005
+ children: [],
1006
+ data: data,
1007
+ selector: null,
1008
+ hash: null,
1009
+ region: regionId,
1010
+ metadata: { active: true, suspend: false, privacy: privacyId, position: null, fraud: fraudId, size: null },
1112
1011
  };
1113
- requestAnimationFrame(function () { outgoing.postMessage(performance.now()); });
1012
+ privacy(node, values[id], parentValue);
1013
+ updateSelector(values[id]);
1014
+ size$1(values[id]);
1015
+ track$5(id, source);
1114
1016
  }
1115
- var requestIdleCallback = window["requestIdleCallback"] || requestIdleCallbackPolyfill;
1116
-
1117
- // Following code takes an array of tokens and transforms it to optimize for repeating tokens and make it efficient to send over the wire
1118
- // 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
1119
- // If so, it replaces the token with its reference (index). This helps us save bytes by not repeating the same value twice.
1120
- // E.g. If tokens array is: ["hello", "world", "coding", "language", "world", "language", "example"]
1121
- // Then the resulting tokens array after following code execution would be: ["hello", "world", "coding", "language", [1, 3], "example"]
1122
- // Where [1,3] points to tokens[1] => "world" and tokens[3] => "language"
1123
- function tokenize (tokens) {
1124
- var output = [];
1125
- var lookup = {};
1126
- var pointer = 0;
1127
- var reference = null;
1128
- for (var i = 0; i < tokens.length; i++) {
1129
- // Only optimize for string values
1130
- if (typeof tokens[i] === "string" /* Constant.String */) {
1131
- var token = tokens[i];
1132
- var index = lookup[token] || -1;
1133
- if (index >= 0) {
1134
- if (reference) {
1135
- reference.push(index);
1136
- }
1137
- else {
1138
- reference = [index];
1139
- output.push(reference);
1140
- pointer++;
1141
- }
1017
+ function update$1(node, parent, data, source) {
1018
+ var id = getId(node);
1019
+ var parentId = parent ? getId(parent) : null;
1020
+ var previousId = getPreviousId(node);
1021
+ var changed = false;
1022
+ var parentChanged = false;
1023
+ if (id in values) {
1024
+ var value = values[id];
1025
+ value.metadata.active = true;
1026
+ // Handle case where internal ordering may have changed
1027
+ if (value.previous !== previousId) {
1028
+ changed = true;
1029
+ value.previous = previousId;
1030
+ }
1031
+ // Handle case where parent might have been updated
1032
+ if (value.parent !== parentId) {
1033
+ changed = true;
1034
+ var oldParentId = value.parent;
1035
+ value.parent = parentId;
1036
+ // Move this node to the right location under new parent
1037
+ if (parentId !== null && parentId >= 0) {
1038
+ var childIndex = previousId === null ? 0 : values[parentId].children.indexOf(previousId) + 1;
1039
+ values[parentId].children.splice(childIndex, 0, id);
1040
+ // Update region after the move
1041
+ value.region = exists(node) ? id : values[parentId].region;
1142
1042
  }
1143
1043
  else {
1144
- reference = null;
1145
- output.push(token);
1146
- lookup[token] = pointer++;
1044
+ // Mark this element as deleted if the parent has been updated to null
1045
+ remove(id, source);
1046
+ }
1047
+ // Remove reference to this node from the old parent
1048
+ if (oldParentId !== null && oldParentId >= 0) {
1049
+ var nodeIndex = values[oldParentId].children.indexOf(id);
1050
+ if (nodeIndex >= 0) {
1051
+ values[oldParentId].children.splice(nodeIndex, 1);
1052
+ }
1147
1053
  }
1054
+ parentChanged = true;
1148
1055
  }
1149
- else {
1150
- // If the value is anything other than string, append it as it is to the output array
1151
- // And, also increment the pointer to stay in sync with output array
1152
- reference = null;
1153
- output.push(tokens[i]);
1154
- pointer++;
1056
+ // Update data
1057
+ for (var key in data) {
1058
+ if (diff(value["data"], data, key)) {
1059
+ changed = true;
1060
+ value["data"][key] = data[key];
1061
+ }
1062
+ }
1063
+ // Update selector
1064
+ updateSelector(value);
1065
+ track$5(id, source, changed, parentChanged);
1066
+ }
1067
+ }
1068
+ function sameorigin(node) {
1069
+ var output = false;
1070
+ if (node.nodeType === Node.ELEMENT_NODE && node.tagName === "IFRAME" /* Constant.IFrameTag */) {
1071
+ var frame = node;
1072
+ // To determine if the iframe is same-origin or not, we try accessing it's contentDocument.
1073
+ // If the browser throws an exception, we assume it's cross-origin and move on.
1074
+ // However, if we do a get a valid document object back, we assume the contents are accessible and iframe is same-origin.
1075
+ try {
1076
+ var doc = frame.contentDocument;
1077
+ if (doc) {
1078
+ iframeMap.set(frame.contentDocument, frame);
1079
+ output = true;
1080
+ }
1155
1081
  }
1082
+ catch ( /* do nothing */_a) { /* do nothing */ }
1156
1083
  }
1157
1084
  return output;
1158
- }
1159
-
1160
- function encode$4 (type, timer, ts) {
1161
- if (timer === void 0) { timer = null; }
1162
- if (ts === void 0) { ts = null; }
1163
- return __awaiter(this, void 0, void 0, function () {
1164
- var eventTime, tokens, _a, d, _i, _b, r, values, _c, values_1, value, state, data, active, suspend, privacy, mangle, keys, _d, keys_1, key, box, factor, attr;
1165
- return __generator(this, function (_e) {
1166
- switch (_e.label) {
1167
- case 0:
1168
- eventTime = ts || time();
1169
- tokens = [eventTime, type];
1170
- _a = type;
1171
- switch (_a) {
1172
- case 8 /* Event.Document */: return [3 /*break*/, 1];
1173
- case 7 /* Event.Region */: return [3 /*break*/, 2];
1174
- case 5 /* Event.Discover */: return [3 /*break*/, 3];
1175
- case 6 /* Event.Mutation */: return [3 /*break*/, 3];
1176
- }
1177
- return [3 /*break*/, 10];
1178
- case 1:
1179
- d = data$c;
1180
- tokens.push(d.width);
1181
- tokens.push(d.height);
1182
- track$7(type, d.width, d.height);
1183
- queue(tokens);
1184
- return [3 /*break*/, 10];
1185
- case 2:
1186
- for (_i = 0, _b = state$1; _i < _b.length; _i++) {
1187
- r = _b[_i];
1188
- tokens = [r.time, 7 /* Event.Region */];
1189
- tokens.push(r.data.id);
1190
- tokens.push(r.data.interaction);
1191
- tokens.push(r.data.visibility);
1192
- tokens.push(r.data.name);
1193
- queue(tokens);
1194
- }
1195
- reset$6();
1196
- return [3 /*break*/, 10];
1197
- case 3:
1198
- // Check if we are operating within the context of the current page
1199
- if (state$9(timer) === 2 /* Task.Stop */) {
1200
- return [3 /*break*/, 10];
1201
- }
1202
- values = updates$2();
1203
- if (!(values.length > 0)) return [3 /*break*/, 9];
1204
- _c = 0, values_1 = values;
1205
- _e.label = 4;
1206
- case 4:
1207
- if (!(_c < values_1.length)) return [3 /*break*/, 8];
1208
- value = values_1[_c];
1209
- state = state$9(timer);
1210
- if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 6];
1211
- return [4 /*yield*/, suspend$1(timer)];
1212
- case 5:
1213
- state = _e.sent();
1214
- _e.label = 6;
1215
- case 6:
1216
- if (state === 2 /* Task.Stop */) {
1217
- return [3 /*break*/, 8];
1218
- }
1219
- data = value.data;
1220
- active = value.metadata.active;
1221
- suspend = value.metadata.suspend;
1222
- privacy = value.metadata.privacy;
1223
- mangle = shouldMangle(value);
1224
- keys = active ? ["tag", "attributes", "value"] : ["tag"];
1225
- for (_d = 0, keys_1 = keys; _d < keys_1.length; _d++) {
1226
- key = keys_1[_d];
1227
- if (data[key]) {
1228
- switch (key) {
1229
- case "tag":
1230
- box = size$1(value);
1231
- factor = mangle ? -1 : 1;
1232
- tokens.push(value.id * factor);
1233
- if (value.parent && active) {
1234
- tokens.push(value.parent);
1235
- }
1236
- if (value.previous && active) {
1237
- tokens.push(value.previous);
1238
- }
1239
- tokens.push(suspend ? "*M" /* Constant.SuspendMutationTag */ : data[key]);
1240
- if (box && box.length === 2) {
1241
- tokens.push("".concat("#" /* Constant.Hash */).concat(str$1(box[0]), ".").concat(str$1(box[1])));
1242
- }
1243
- break;
1244
- case "attributes":
1245
- for (attr in data[key]) {
1246
- if (data[key][attr] !== undefined) {
1247
- tokens.push(attribute(attr, data[key][attr], privacy));
1248
- }
1249
- }
1250
- break;
1251
- case "value":
1252
- check$4(value.metadata.fraud, value.id, data[key]);
1253
- tokens.push(text$1(data[key], data.tag, privacy, mangle));
1254
- break;
1255
- }
1256
- }
1257
- }
1258
- _e.label = 7;
1259
- case 7:
1260
- _c++;
1261
- return [3 /*break*/, 4];
1262
- case 8:
1263
- if (type === 6 /* Event.Mutation */) {
1264
- activity(eventTime);
1265
- }
1266
- queue(tokenize(tokens), !config$1.lean);
1267
- _e.label = 9;
1268
- case 9: return [3 /*break*/, 10];
1269
- case 10: return [2 /*return*/];
1270
- }
1271
- });
1272
- });
1273
1085
  }
1274
- function shouldMangle(value) {
1275
- var privacy = value.metadata.privacy;
1276
- return value.data.tag === "*T" /* Constant.TextTag */ && !(privacy === 0 /* Privacy.None */ || privacy === 1 /* Privacy.Sensitive */);
1086
+ function iframe(node) {
1087
+ var doc = node.nodeType === Node.DOCUMENT_NODE ? node : null;
1088
+ return doc && iframeMap.has(doc) ? iframeMap.get(doc) : null;
1277
1089
  }
1278
- function size$1(value) {
1279
- if (value.metadata.size !== null && value.metadata.size.length === 0) {
1280
- var img = getNode(value.id);
1281
- if (img) {
1282
- return [Math.floor(img.offsetWidth * 100 /* Setting.BoxPrecision */), Math.floor(img.offsetHeight * 100 /* Setting.BoxPrecision */)];
1090
+ function privacy(node, value, parent) {
1091
+ var data = value.data;
1092
+ var metadata = value.metadata;
1093
+ var current = metadata.privacy;
1094
+ var attributes = data.attributes || {};
1095
+ var tag = data.tag.toUpperCase();
1096
+ switch (true) {
1097
+ case maskTags.indexOf(tag) >= 0:
1098
+ var type = attributes["type" /* Constant.Type */];
1099
+ var meta_1 = "" /* Constant.Empty */;
1100
+ Object.keys(attributes).forEach(function (x) { return meta_1 += attributes[x].toLowerCase(); });
1101
+ var exclude = maskExclude.some(function (x) { return meta_1.indexOf(x) >= 0; });
1102
+ // Regardless of privacy mode, always mask off user input from input boxes or drop downs with two exceptions:
1103
+ // (1) The node is detected to be one of the excluded fields, in which case we drop everything
1104
+ // (2) The node's type is one of the allowed types (like checkboxes)
1105
+ metadata.privacy = tag === "INPUT" /* Constant.InputTag */ && maskDisable.indexOf(type) >= 0 ? current : (exclude ? 4 /* Privacy.Exclude */ : 2 /* Privacy.Text */);
1106
+ break;
1107
+ case "data-clarity-mask" /* Constant.MaskData */ in attributes:
1108
+ metadata.privacy = 3 /* Privacy.TextImage */;
1109
+ break;
1110
+ case "data-clarity-unmask" /* Constant.UnmaskData */ in attributes:
1111
+ metadata.privacy = 0 /* Privacy.None */;
1112
+ break;
1113
+ case privacyMap.has(node):
1114
+ // If this node was explicitly configured to contain sensitive content, honor that privacy setting
1115
+ metadata.privacy = privacyMap.get(node);
1116
+ break;
1117
+ case fraudMap.has(node):
1118
+ // If this node was explicitly configured to be evaluated for fraud, then also mask content
1119
+ metadata.privacy = 2 /* Privacy.Text */;
1120
+ break;
1121
+ case tag === "*T" /* Constant.TextTag */:
1122
+ // If it's a text node belonging to a STYLE or TITLE tag or one of scrub exceptions, then capture content
1123
+ var pTag = parent && parent.data ? parent.data.tag : "" /* Constant.Empty */;
1124
+ var pSelector_1 = parent && parent.selector ? parent.selector[1 /* Selector.Default */] : "" /* Constant.Empty */;
1125
+ var tags = ["STYLE" /* Constant.StyleTag */, "TITLE" /* Constant.TitleTag */, "svg:style" /* Constant.SvgStyle */];
1126
+ metadata.privacy = tags.includes(pTag) || override.some(function (x) { return pSelector_1.indexOf(x) >= 0; }) ? 0 /* Privacy.None */ : current;
1127
+ break;
1128
+ case current === 1 /* Privacy.Sensitive */:
1129
+ // In a mode where we mask sensitive information by default, look through class names to aggressively mask content
1130
+ metadata.privacy = inspect(attributes["class" /* Constant.Class */], maskText, metadata);
1131
+ break;
1132
+ }
1133
+ }
1134
+ function inspect(input, lookup, metadata) {
1135
+ if (input && lookup.some(function (x) { return input.indexOf(x) >= 0; })) {
1136
+ return 2 /* Privacy.Text */;
1137
+ }
1138
+ return metadata.privacy;
1139
+ }
1140
+ function diff(a, b, field) {
1141
+ if (typeof a[field] === "object" && typeof b[field] === "object") {
1142
+ for (var key in a[field]) {
1143
+ if (a[field][key] !== b[field][key]) {
1144
+ return true;
1145
+ }
1283
1146
  }
1147
+ for (var key in b[field]) {
1148
+ if (b[field][key] !== a[field][key]) {
1149
+ return true;
1150
+ }
1151
+ }
1152
+ return false;
1284
1153
  }
1285
- return value.metadata.size;
1154
+ return a[field] !== b[field];
1286
1155
  }
1287
- function str$1(input) {
1288
- return input.toString(36);
1156
+ function position(parent, child) {
1157
+ child.metadata.position = 1;
1158
+ var idx = parent ? parent.children.indexOf(child.id) : -1;
1159
+ while (idx-- > 0) {
1160
+ var sibling = values[parent.children[idx]];
1161
+ if (child.data.tag === sibling.data.tag) {
1162
+ child.metadata.position = sibling.metadata.position + 1;
1163
+ break;
1164
+ }
1165
+ }
1166
+ return child.metadata.position;
1289
1167
  }
1290
- function attribute(key, value, privacy) {
1291
- return "".concat(key, "=").concat(text$1(value, key, privacy));
1292
- }
1293
-
1294
- var data$c;
1295
- function reset$j() {
1296
- data$c = null;
1168
+ function updateSelector(value) {
1169
+ var parent = value.parent && value.parent in values ? values[value.parent] : null;
1170
+ var prefix = parent ? parent.selector : null;
1171
+ var d = value.data;
1172
+ var p = position(parent, value);
1173
+ var s = { id: value.id, tag: d.tag, prefix: prefix, position: p, attributes: d.attributes };
1174
+ value.selector = [get$1(s, 0 /* Selector.Alpha */), get$1(s, 1 /* Selector.Beta */)];
1175
+ value.hash = value.selector.map(function (x) { return x ? hash(x) : null; });
1176
+ value.hash.forEach(function (h) { return hashMap[h] = value.id; });
1297
1177
  }
1298
- function start$w() {
1299
- reset$j();
1300
- compute$7();
1178
+ function hashText(hash) {
1179
+ var id = lookup(hash);
1180
+ var node = getNode(id);
1181
+ return node !== null && node.textContent !== null ? node.textContent.substr(0, 25 /* Setting.ClickText */) : '';
1301
1182
  }
1302
- function compute$7() {
1303
- var body = document.body;
1304
- var d = document.documentElement;
1305
- var bodyClientWidth = body ? body.clientWidth : null;
1306
- var bodyScrollWidth = body ? body.scrollWidth : null;
1307
- var bodyOffsetWidth = body ? body.offsetWidth : null;
1308
- var documentClientWidth = d ? d.clientWidth : null;
1309
- var documentScrollWidth = d ? d.scrollWidth : null;
1310
- var documentOffsetWidth = d ? d.offsetWidth : null;
1311
- var width = Math.max(bodyClientWidth, bodyScrollWidth, bodyOffsetWidth, documentClientWidth, documentScrollWidth, documentOffsetWidth);
1312
- var bodyClientHeight = body ? body.clientHeight : null;
1313
- var bodyScrollHeight = body ? body.scrollHeight : null;
1314
- var bodyOffsetHeight = body ? body.offsetHeight : null;
1315
- var documentClientHeight = d ? d.clientHeight : null;
1316
- var documentScrollHeight = d ? d.scrollHeight : null;
1317
- var documentOffsetHeight = d ? d.offsetHeight : null;
1318
- var height = Math.max(bodyClientHeight, bodyScrollHeight, bodyOffsetHeight, documentClientHeight, documentScrollHeight, documentOffsetHeight);
1319
- // Check that width or height has changed from before, and also that width & height are not null values
1320
- if ((data$c === null || width !== data$c.width || height !== data$c.height) && width !== null && height !== null) {
1321
- data$c = { width: width, height: height };
1322
- encode$4(8 /* Event.Document */);
1183
+ function getNode(id) {
1184
+ if (id in nodes) {
1185
+ return nodes[id];
1323
1186
  }
1187
+ return null;
1324
1188
  }
1325
- function end() {
1326
- reset$j();
1327
- }
1328
-
1329
- var state$8 = [];
1330
- function start$v() {
1331
- reset$i();
1189
+ function getValue(id) {
1190
+ if (id in values) {
1191
+ return values[id];
1192
+ }
1193
+ return null;
1332
1194
  }
1333
- function observe$c(root) {
1334
- bind(root, "change", recompute$8, true);
1195
+ function get(node) {
1196
+ var id = getId(node);
1197
+ return id in values ? values[id] : null;
1335
1198
  }
1336
- function recompute$8(evt) {
1337
- var element = target(evt);
1338
- if (element) {
1339
- var value = element.value;
1340
- var checksum = value && value.length >= 5 /* Setting.WordLength */ && config$1.fraud ? hash(value, 24 /* Setting.ChecksumPrecision */) : "" /* Constant.Empty */;
1341
- state$8.push({ time: time(evt), event: 42 /* Event.Change */, data: { target: target(evt), type: element.type, value: value, checksum: checksum } });
1342
- schedule$1(encode$3.bind(this, 42 /* Event.Change */));
1343
- }
1199
+ function lookup(hash) {
1200
+ return hash in hashMap ? hashMap[hash] : null;
1344
1201
  }
1345
- function reset$i() {
1346
- state$8 = [];
1202
+ function has(node) {
1203
+ return getId(node) in nodes;
1347
1204
  }
1348
- function stop$t() {
1349
- reset$i();
1350
- }
1351
-
1352
- function offset (element) {
1353
- var output = { x: 0, y: 0 };
1354
- // Walk up the chain to ensure we compute offset distance correctly
1355
- // In case where we may have nested IFRAMEs, we keep walking up until we get to the top most parent page
1356
- if (element && element.offsetParent) {
1357
- do {
1358
- var parent_1 = element.offsetParent;
1359
- var frame = parent_1 === null ? iframe(element.ownerDocument) : null;
1360
- output.x += element.offsetLeft;
1361
- output.y += element.offsetTop;
1362
- element = frame ? frame : parent_1;
1363
- } while (element);
1205
+ function updates$2() {
1206
+ var output = [];
1207
+ for (var _i = 0, updateMap_1 = updateMap; _i < updateMap_1.length; _i++) {
1208
+ var id = updateMap_1[_i];
1209
+ if (id in values) {
1210
+ output.push(values[id]);
1211
+ }
1364
1212
  }
1213
+ updateMap = [];
1365
1214
  return output;
1366
- }
1367
-
1368
- var UserInputTags = ["input", "textarea", "radio", "button", "canvas"];
1369
- var state$7 = [];
1370
- function start$u() {
1371
- reset$h();
1372
- }
1373
- function observe$b(root) {
1374
- bind(root, "click", handler$3.bind(this, 9 /* Event.Click */, root), true);
1375
1215
  }
1376
- function handler$3(event, root, evt) {
1377
- var frame = iframe(root);
1378
- var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1379
- var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
1380
- var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
1381
- // In case of iframe, we adjust (x,y) to be relative to top parent's origin
1382
- if (frame) {
1383
- var distance = offset(frame);
1384
- x = x ? x + Math.round(distance.x) : x;
1385
- y = y ? y + Math.round(distance.y) : y;
1216
+ function remove(id, source) {
1217
+ if (id in values) {
1218
+ var value = values[id];
1219
+ value.metadata.active = false;
1220
+ value.parent = null;
1221
+ track$5(id, source);
1386
1222
  }
1387
- var t = target(evt);
1388
- // Find nearest anchor tag (<a/>) parent if current target node is part of one
1389
- // If present, we use the returned link element to populate text and link properties below
1390
- var a = link(t);
1391
- // Get layout rectangle for the target element
1392
- var l = layout$1(t);
1393
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
1394
- // This property helps differentiate between a keyboard navigation vs. pointer click
1395
- // In case of a keyboard navigation, we use center of target element as (x,y)
1396
- if (evt.detail === 0 && l) {
1397
- x = Math.round(l.x + (l.w / 2));
1398
- y = Math.round(l.y + (l.h / 2));
1223
+ }
1224
+ function size$1(value) {
1225
+ // If this element is a image node, and is masked, then track box model for the current element
1226
+ if (value.data.tag === "IMG" /* Constant.ImageTag */ && value.metadata.privacy === 3 /* Privacy.TextImage */) {
1227
+ value.metadata.size = [];
1399
1228
  }
1400
- var eX = l ? Math.max(Math.floor(((x - l.x) / l.w) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1401
- var eY = l ? Math.max(Math.floor(((y - l.y) / l.h) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1402
- // Check for null values before processing this event
1403
- if (x !== null && y !== null) {
1404
- state$7.push({
1405
- time: time(evt),
1406
- event: event,
1407
- data: {
1408
- target: t,
1409
- x: x,
1410
- y: y,
1411
- eX: eX,
1412
- eY: eY,
1413
- button: evt.button,
1414
- reaction: reaction(t),
1415
- context: context(a),
1416
- text: text(t),
1417
- link: a ? a.href : null,
1418
- hash: null,
1419
- trust: evt.isTrusted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */
1420
- }
1421
- });
1422
- schedule$1(encode$3.bind(this, event));
1423
- }
1424
- }
1425
- function text(element) {
1426
- var output = null;
1427
- if (element) {
1428
- // Grab text using "textContent" for most HTMLElements, however, use "value" for HTMLInputElements and "alt" for HTMLImageElement.
1429
- var t = element.textContent || element.value || element.alt;
1430
- if (t) {
1431
- // Trim any spaces at the beginning or at the end of string
1432
- // Also, replace multiple occurrence of space characters with a single white space
1433
- // Finally, send only first few characters as specified by the Setting
1434
- output = t.trim().replace(/\s+/g, " " /* Constant.Space */).substr(0, 25 /* Setting.ClickText */);
1435
- }
1436
- }
1437
- return output;
1438
1229
  }
1439
- function reaction(element) {
1440
- if (element.nodeType === Node.ELEMENT_NODE) {
1441
- var tag = element.tagName.toLowerCase();
1442
- if (UserInputTags.indexOf(tag) >= 0) {
1443
- return 0 /* BooleanFlag.False */;
1444
- }
1230
+ function getPreviousId(node) {
1231
+ var id = null;
1232
+ // Some nodes may not have an ID by design since Clarity skips over tags like SCRIPT, NOSCRIPT, META, COMMENTS, etc..
1233
+ // 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.
1234
+ while (id === null && node.previousSibling) {
1235
+ id = getId(node.previousSibling);
1236
+ node = node.previousSibling;
1445
1237
  }
1446
- return 1 /* BooleanFlag.True */;
1238
+ return id;
1447
1239
  }
1448
- function layout$1(element) {
1449
- var box = null;
1450
- var de = document.documentElement;
1451
- if (typeof element.getBoundingClientRect === "function") {
1452
- // getBoundingClientRect returns rectangle relative positioning to viewport
1453
- var rect = element.getBoundingClientRect();
1454
- if (rect && rect.width > 0 && rect.height > 0) {
1455
- // Add viewport's scroll position to rectangle to get position relative to document origin
1456
- // Also: using Math.floor() instead of Math.round() because in Edge,
1457
- // getBoundingClientRect returns partial pixel values (e.g. 162.5px) and Chrome already
1458
- // floors the value (e.g. 162px). This keeps consistent behavior across browsers.
1459
- box = {
1460
- x: Math.floor(rect.left + ("pageXOffset" in window ? window.pageXOffset : de.scrollLeft)),
1461
- y: Math.floor(rect.top + ("pageYOffset" in window ? window.pageYOffset : de.scrollTop)),
1462
- w: Math.floor(rect.width),
1463
- h: Math.floor(rect.height)
1464
- };
1465
- }
1240
+ function track$5(id, source, changed, parentChanged) {
1241
+ if (changed === void 0) { changed = true; }
1242
+ if (parentChanged === void 0) { parentChanged = false; }
1243
+ // Keep track of the order in which mutations happened, they may not be sequential
1244
+ // Edge case: If an element is added later on, and pre-discovered element is moved as a child.
1245
+ // In that case, we need to reorder the pre-discovered element in the update list to keep visualization consistent.
1246
+ var uIndex = updateMap.indexOf(id);
1247
+ if (uIndex >= 0 && source === 1 /* Source.ChildListAdd */ && parentChanged) {
1248
+ updateMap.splice(uIndex, 1);
1249
+ updateMap.push(id);
1466
1250
  }
1467
- return box;
1468
- }
1469
- function context(a) {
1470
- if (a && a.hasAttribute("target" /* Constant.Target */)) {
1471
- switch (a.getAttribute("target" /* Constant.Target */)) {
1472
- case "_blank" /* Constant.Blank */: return 1 /* BrowsingContext.Blank */;
1473
- case "_parent" /* Constant.Parent */: return 2 /* BrowsingContext.Parent */;
1474
- case "_top" /* Constant.Top */: return 3 /* BrowsingContext.Top */;
1475
- }
1251
+ else if (uIndex === -1 && changed) {
1252
+ updateMap.push(id);
1476
1253
  }
1477
- return 0 /* BrowsingContext.Self */;
1478
- }
1479
- function reset$h() {
1480
- state$7 = [];
1481
- }
1482
- function stop$s() {
1483
- reset$h();
1484
1254
  }
1485
1255
 
1486
- var state$6 = [];
1487
- function start$t() {
1488
- reset$g();
1489
- }
1490
- function observe$a(root) {
1491
- bind(root, "cut", recompute$7.bind(this, 0 /* Clipboard.Cut */), true);
1492
- bind(root, "copy", recompute$7.bind(this, 1 /* Clipboard.Copy */), true);
1493
- bind(root, "paste", recompute$7.bind(this, 2 /* Clipboard.Paste */), true);
1256
+ var dom = /*#__PURE__*/Object.freeze({
1257
+ __proto__: null,
1258
+ add: add,
1259
+ get: get,
1260
+ getId: getId,
1261
+ getNode: getNode,
1262
+ getValue: getValue,
1263
+ has: has,
1264
+ hashText: hashText,
1265
+ iframe: iframe,
1266
+ lookup: lookup,
1267
+ parse: parse$1,
1268
+ sameorigin: sameorigin,
1269
+ start: start$x,
1270
+ stop: stop$u,
1271
+ update: update$1,
1272
+ updates: updates$2
1273
+ });
1274
+
1275
+ // Track the start time to be able to compute duration at the end of the task
1276
+ var idleTimeout = 5000;
1277
+ var tracker = {};
1278
+ var queuedTasks = [];
1279
+ var activeTask = null;
1280
+ var pauseTask = null;
1281
+ var resumeResolve = null;
1282
+ function pause$1() {
1283
+ if (pauseTask === null) {
1284
+ pauseTask = new Promise(function (resolve) {
1285
+ resumeResolve = resolve;
1286
+ });
1287
+ }
1494
1288
  }
1495
- function recompute$7(action, evt) {
1496
- state$6.push({ time: time(evt), event: 38 /* Event.Clipboard */, data: { target: target(evt), action: action } });
1497
- schedule$1(encode$3.bind(this, 38 /* Event.Clipboard */));
1289
+ function resume$1() {
1290
+ if (pauseTask) {
1291
+ resumeResolve();
1292
+ pauseTask = null;
1293
+ if (activeTask === null) {
1294
+ run();
1295
+ }
1296
+ }
1498
1297
  }
1499
- function reset$g() {
1500
- state$6 = [];
1298
+ function reset$j() {
1299
+ tracker = {};
1300
+ queuedTasks = [];
1301
+ activeTask = null;
1302
+ pauseTask = null;
1501
1303
  }
1502
- function stop$r() {
1503
- reset$g();
1504
- }
1505
-
1506
- var timeout$5 = null;
1507
- var state$5 = [];
1508
- function start$s() {
1509
- reset$f();
1304
+ function schedule$1(task, priority) {
1305
+ if (priority === void 0) { priority = 0 /* Priority.Normal */; }
1306
+ return __awaiter(this, void 0, void 0, function () {
1307
+ var _i, queuedTasks_1, q, promise;
1308
+ return __generator(this, function (_a) {
1309
+ // If this task is already scheduled, skip it
1310
+ for (_i = 0, queuedTasks_1 = queuedTasks; _i < queuedTasks_1.length; _i++) {
1311
+ q = queuedTasks_1[_i];
1312
+ if (q.task === task) {
1313
+ return [2 /*return*/];
1314
+ }
1315
+ }
1316
+ promise = new Promise(function (resolve) {
1317
+ var insert = priority === 1 /* Priority.High */ ? "unshift" : "push";
1318
+ // Queue this task for asynchronous execution later
1319
+ // We also store a unique page identifier (id) along with the task to ensure
1320
+ // ensure that we do not accidentally execute this task in context of a different page
1321
+ queuedTasks[insert]({ task: task, resolve: resolve, id: id() });
1322
+ });
1323
+ // If there is no active task running, and Clarity is not in pause state,
1324
+ // invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
1325
+ if (activeTask === null && pauseTask === null) {
1326
+ run();
1327
+ }
1328
+ return [2 /*return*/, promise];
1329
+ });
1330
+ });
1510
1331
  }
1511
- function observe$9(root) {
1512
- bind(root, "input", recompute$6, true);
1332
+ function run() {
1333
+ var entry = queuedTasks.shift();
1334
+ if (entry) {
1335
+ activeTask = entry;
1336
+ entry.task().then(function () {
1337
+ // Bail out if the context in which this task was operating is different from the current page
1338
+ // An example scenario where task could span across pages is Single Page Applications (SPA)
1339
+ // A task that started on page #1, but completes on page #2
1340
+ if (entry.id !== id()) {
1341
+ return;
1342
+ }
1343
+ entry.resolve();
1344
+ activeTask = null; // Reset active task back to null now that the promise is resolved
1345
+ run();
1346
+ }).catch(function (error) {
1347
+ // If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
1348
+ if (entry.id !== id()) {
1349
+ return;
1350
+ }
1351
+ if (error) {
1352
+ log$1(0 /* Code.RunTask */, 1 /* Severity.Warning */, error.name, error.message, error.stack);
1353
+ }
1354
+ activeTask = null;
1355
+ run();
1356
+ });
1357
+ }
1513
1358
  }
1514
- function recompute$6(evt) {
1515
- var input = target(evt);
1516
- var value = get(input);
1517
- if (input && input.type && value) {
1518
- var v = input.value;
1519
- switch (input.type) {
1520
- case "radio":
1521
- case "checkbox":
1522
- v = input.checked ? "true" : "false";
1523
- break;
1524
- }
1525
- var data = { target: input, value: v };
1526
- // 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.
1527
- if (state$5.length > 0 && (state$5[state$5.length - 1].data.target === data.target)) {
1528
- state$5.pop();
1529
- }
1530
- state$5.push({ time: time(evt), event: 27 /* Event.Input */, data: data });
1531
- clearTimeout(timeout$5);
1532
- timeout$5 = setTimeout(process$6, 1000 /* Setting.InputLookAhead */, 27 /* Event.Input */);
1359
+ function state$9(timer) {
1360
+ var id = key(timer);
1361
+ if (id in tracker) {
1362
+ var elapsed = performance.now() - tracker[id].start;
1363
+ return (elapsed > tracker[id].yield) ? 0 /* Task.Wait */ : 1 /* Task.Run */;
1533
1364
  }
1365
+ // If this task is no longer being tracked, send stop message to the caller
1366
+ return 2 /* Task.Stop */;
1534
1367
  }
1535
- function process$6(event) {
1536
- schedule$1(encode$3.bind(this, event));
1368
+ function start$w(timer) {
1369
+ tracker[key(timer)] = { start: performance.now(), calls: 0, yield: 30 /* Setting.LongTask */ };
1537
1370
  }
1538
- function reset$f() {
1539
- state$5 = [];
1371
+ function restart$2(timer) {
1372
+ var id = key(timer);
1373
+ if (tracker && tracker[id]) {
1374
+ var c = tracker[id].calls;
1375
+ var y = tracker[id].yield;
1376
+ start$w(timer);
1377
+ tracker[id].calls = c + 1;
1378
+ tracker[id].yield = y;
1379
+ }
1540
1380
  }
1541
- function stop$q() {
1542
- clearTimeout(timeout$5);
1543
- reset$f();
1544
- }
1545
-
1546
- var state$4 = [];
1547
- var timeout$4 = null;
1548
- function start$r() {
1549
- reset$e();
1381
+ function stop$t(timer) {
1382
+ var end = performance.now();
1383
+ var id = key(timer);
1384
+ var duration = end - tracker[id].start;
1385
+ sum(timer.cost, duration);
1386
+ count$1(5 /* Metric.InvokeCount */);
1387
+ // For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
1388
+ // However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
1389
+ if (tracker[id].calls > 0) {
1390
+ sum(4 /* Metric.TotalCost */, duration);
1391
+ }
1550
1392
  }
1551
- function observe$8(root) {
1552
- bind(root, "mousedown", mouse.bind(this, 13 /* Event.MouseDown */, root), true);
1553
- bind(root, "mouseup", mouse.bind(this, 14 /* Event.MouseUp */, root), true);
1554
- bind(root, "mousemove", mouse.bind(this, 12 /* Event.MouseMove */, root), true);
1555
- bind(root, "wheel", mouse.bind(this, 15 /* Event.MouseWheel */, root), true);
1556
- bind(root, "dblclick", mouse.bind(this, 16 /* Event.DoubleClick */, root), true);
1557
- bind(root, "touchstart", touch.bind(this, 17 /* Event.TouchStart */, root), true);
1558
- bind(root, "touchend", touch.bind(this, 18 /* Event.TouchEnd */, root), true);
1559
- bind(root, "touchmove", touch.bind(this, 19 /* Event.TouchMove */, root), true);
1560
- bind(root, "touchcancel", touch.bind(this, 20 /* Event.TouchCancel */, root), true);
1393
+ function suspend$1(timer) {
1394
+ return __awaiter(this, void 0, void 0, function () {
1395
+ var id, _a;
1396
+ return __generator(this, function (_b) {
1397
+ switch (_b.label) {
1398
+ case 0:
1399
+ id = key(timer);
1400
+ if (!(id in tracker)) return [3 /*break*/, 2];
1401
+ stop$t(timer);
1402
+ _a = tracker[id];
1403
+ return [4 /*yield*/, wait()];
1404
+ case 1:
1405
+ _a.yield = (_b.sent()).timeRemaining();
1406
+ restart$2(timer);
1407
+ _b.label = 2;
1408
+ case 2:
1409
+ // After we are done with suspending task, ensure that we are still operating in the right context
1410
+ // If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
1411
+ return [2 /*return*/, id in tracker ? 1 /* Task.Run */ : 2 /* Task.Stop */];
1412
+ }
1413
+ });
1414
+ });
1561
1415
  }
1562
- function mouse(event, root, evt) {
1563
- var frame = iframe(root);
1564
- var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1565
- var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
1566
- var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
1567
- // In case of iframe, we adjust (x,y) to be relative to top parent's origin
1568
- if (frame) {
1569
- var distance = offset(frame);
1570
- x = x ? x + Math.round(distance.x) : x;
1571
- y = y ? y + Math.round(distance.y) : y;
1572
- }
1573
- // Check for null values before processing this event
1574
- if (x !== null && y !== null) {
1575
- handler$2({ time: time(evt), event: event, data: { target: target(evt), x: x, y: y } });
1576
- }
1416
+ function key(timer) {
1417
+ return "".concat(timer.id, ".").concat(timer.cost);
1577
1418
  }
1578
- function touch(event, root, evt) {
1579
- var frame = iframe(root);
1580
- var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1581
- var touches = evt.changedTouches;
1582
- var t = time(evt);
1583
- if (touches) {
1584
- for (var i = 0; i < touches.length; i++) {
1585
- var entry = touches[i];
1586
- var x = "clientX" in entry ? Math.round(entry["clientX"] + d.scrollLeft) : null;
1587
- var y = "clientY" in entry ? Math.round(entry["clientY"] + d.scrollTop) : null;
1588
- x = x && frame ? x + Math.round(frame.offsetLeft) : x;
1589
- y = y && frame ? y + Math.round(frame.offsetTop) : y;
1590
- // Check for null values before processing this event
1591
- if (x !== null && y !== null) {
1592
- handler$2({ time: t, event: event, data: { target: target(evt), x: x, y: y } });
1419
+ function wait() {
1420
+ return __awaiter(this, void 0, void 0, function () {
1421
+ return __generator(this, function (_a) {
1422
+ switch (_a.label) {
1423
+ case 0:
1424
+ if (!pauseTask) return [3 /*break*/, 2];
1425
+ return [4 /*yield*/, pauseTask];
1426
+ case 1:
1427
+ _a.sent();
1428
+ _a.label = 2;
1429
+ case 2: return [2 /*return*/, new Promise(function (resolve) {
1430
+ requestIdleCallback(resolve, { timeout: idleTimeout });
1431
+ })];
1593
1432
  }
1433
+ });
1434
+ });
1435
+ }
1436
+ // Use native implementation of requestIdleCallback if it exists.
1437
+ // Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
1438
+ // While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
1439
+ // Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
1440
+ // This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
1441
+ // For non-blocking behavior, We need to know when browser has finished painiting. This can be accomplished in two different ways (hacks):
1442
+ // (1) Use MessageChannel to pass the message, and browser will receive the message right after pain event has occured.
1443
+ // (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
1444
+ // Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
1445
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
1446
+ function requestIdleCallbackPolyfill(callback, options) {
1447
+ var startTime = performance.now();
1448
+ var channel = new MessageChannel();
1449
+ var incoming = channel.port1;
1450
+ var outgoing = channel.port2;
1451
+ incoming.onmessage = function (event) {
1452
+ var currentTime = performance.now();
1453
+ var elapsed = currentTime - startTime;
1454
+ var duration = currentTime - event.data;
1455
+ if (duration > 30 /* Setting.LongTask */ && elapsed < options.timeout) {
1456
+ requestAnimationFrame(function () { outgoing.postMessage(currentTime); });
1594
1457
  }
1595
- }
1458
+ else {
1459
+ var didTimeout_1 = elapsed > options.timeout;
1460
+ callback({
1461
+ didTimeout: didTimeout_1,
1462
+ timeRemaining: function () { return didTimeout_1 ? 30 /* Setting.LongTask */ : Math.max(0, 30 /* Setting.LongTask */ - duration); }
1463
+ });
1464
+ }
1465
+ };
1466
+ requestAnimationFrame(function () { outgoing.postMessage(performance.now()); });
1596
1467
  }
1597
- function handler$2(current) {
1598
- switch (current.event) {
1599
- case 12 /* Event.MouseMove */:
1600
- case 15 /* Event.MouseWheel */:
1601
- case 19 /* Event.TouchMove */:
1602
- var length_1 = state$4.length;
1603
- var last = length_1 > 1 ? state$4[length_1 - 2] : null;
1604
- if (last && similar$1(last, current)) {
1605
- state$4.pop();
1468
+ var requestIdleCallback = window["requestIdleCallback"] || requestIdleCallbackPolyfill;
1469
+
1470
+ // Following code takes an array of tokens and transforms it to optimize for repeating tokens and make it efficient to send over the wire
1471
+ // 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
1472
+ // If so, it replaces the token with its reference (index). This helps us save bytes by not repeating the same value twice.
1473
+ // E.g. If tokens array is: ["hello", "world", "coding", "language", "world", "language", "example"]
1474
+ // Then the resulting tokens array after following code execution would be: ["hello", "world", "coding", "language", [1, 3], "example"]
1475
+ // Where [1,3] points to tokens[1] => "world" and tokens[3] => "language"
1476
+ function tokenize (tokens) {
1477
+ var output = [];
1478
+ var lookup = {};
1479
+ var pointer = 0;
1480
+ var reference = null;
1481
+ for (var i = 0; i < tokens.length; i++) {
1482
+ // Only optimize for string values
1483
+ if (typeof tokens[i] === "string" /* Constant.String */) {
1484
+ var token = tokens[i];
1485
+ var index = lookup[token] || -1;
1486
+ if (index >= 0) {
1487
+ if (reference) {
1488
+ reference.push(index);
1489
+ }
1490
+ else {
1491
+ reference = [index];
1492
+ output.push(reference);
1493
+ pointer++;
1494
+ }
1606
1495
  }
1607
- state$4.push(current);
1608
- clearTimeout(timeout$4);
1609
- timeout$4 = setTimeout(process$5, 500 /* Setting.LookAhead */, current.event);
1610
- break;
1611
- default:
1612
- state$4.push(current);
1613
- process$5(current.event);
1614
- break;
1615
- }
1616
- }
1617
- function process$5(event) {
1618
- schedule$1(encode$3.bind(this, event));
1619
- }
1620
- function reset$e() {
1621
- state$4 = [];
1622
- }
1623
- function similar$1(last, current) {
1624
- var dx = last.data.x - current.data.x;
1625
- var dy = last.data.y - current.data.y;
1626
- var distance = Math.sqrt(dx * dx + dy * dy);
1627
- var gap = current.time - last.time;
1628
- var match = current.data.target === last.data.target;
1629
- return current.event === last.event && match && distance < 20 /* Setting.Distance */ && gap < 25 /* Setting.Interval */;
1630
- }
1631
- function stop$p() {
1632
- clearTimeout(timeout$4);
1633
- // Send out any pending pointer events in the pipeline
1634
- if (state$4.length > 0) {
1635
- process$5(state$4[state$4.length - 1].event);
1496
+ else {
1497
+ reference = null;
1498
+ output.push(token);
1499
+ lookup[token] = pointer++;
1500
+ }
1501
+ }
1502
+ else {
1503
+ // If the value is anything other than string, append it as it is to the output array
1504
+ // And, also increment the pointer to stay in sync with output array
1505
+ reference = null;
1506
+ output.push(tokens[i]);
1507
+ pointer++;
1508
+ }
1636
1509
  }
1510
+ return output;
1637
1511
  }
1638
1512
 
1639
- var data$b;
1640
- function start$q() {
1641
- bind(window, "resize", recompute$5);
1642
- recompute$5();
1513
+ var data$c;
1514
+ function reset$i() {
1515
+ data$c = null;
1643
1516
  }
1644
- function recompute$5() {
1645
- var de = document.documentElement;
1646
- // window.innerWidth includes width of the scrollbar and is not a true representation of the viewport width.
1647
- // Therefore, when possible, use documentElement's clientWidth property.
1648
- data$b = {
1649
- width: de && "clientWidth" in de ? Math.min(de.clientWidth, window.innerWidth) : window.innerWidth,
1650
- height: de && "clientHeight" in de ? Math.min(de.clientHeight, window.innerHeight) : window.innerHeight,
1651
- };
1652
- encode$3(11 /* Event.Resize */);
1517
+ function start$v() {
1518
+ reset$i();
1519
+ compute$7();
1653
1520
  }
1654
- function reset$d() {
1655
- data$b = null;
1521
+ function compute$7() {
1522
+ var body = document.body;
1523
+ var d = document.documentElement;
1524
+ var bodyClientWidth = body ? body.clientWidth : null;
1525
+ var bodyScrollWidth = body ? body.scrollWidth : null;
1526
+ var bodyOffsetWidth = body ? body.offsetWidth : null;
1527
+ var documentClientWidth = d ? d.clientWidth : null;
1528
+ var documentScrollWidth = d ? d.scrollWidth : null;
1529
+ var documentOffsetWidth = d ? d.offsetWidth : null;
1530
+ var width = Math.max(bodyClientWidth, bodyScrollWidth, bodyOffsetWidth, documentClientWidth, documentScrollWidth, documentOffsetWidth);
1531
+ var bodyClientHeight = body ? body.clientHeight : null;
1532
+ var bodyScrollHeight = body ? body.scrollHeight : null;
1533
+ var bodyOffsetHeight = body ? body.offsetHeight : null;
1534
+ var documentClientHeight = d ? d.clientHeight : null;
1535
+ var documentScrollHeight = d ? d.scrollHeight : null;
1536
+ var documentOffsetHeight = d ? d.offsetHeight : null;
1537
+ var height = Math.max(bodyClientHeight, bodyScrollHeight, bodyOffsetHeight, documentClientHeight, documentScrollHeight, documentOffsetHeight);
1538
+ // Check that width or height has changed from before, and also that width & height are not null values
1539
+ if ((data$c === null || width !== data$c.width || height !== data$c.height) && width !== null && height !== null) {
1540
+ data$c = { width: width, height: height };
1541
+ encode$4(8 /* Event.Document */);
1542
+ }
1656
1543
  }
1657
- function stop$o() {
1658
- reset$d();
1544
+ function end() {
1545
+ reset$i();
1659
1546
  }
1660
1547
 
1661
- var state$3 = [];
1662
- var timeout$3 = null;
1663
- function start$p() {
1664
- state$3 = [];
1665
- recompute$4();
1666
- }
1667
- function observe$7(root) {
1668
- var frame = iframe(root);
1669
- var node = frame ? frame.contentWindow : (root === document ? window : root);
1670
- bind(node, "scroll", recompute$4, true);
1671
- }
1672
- function recompute$4(event) {
1673
- if (event === void 0) { event = null; }
1674
- var w = window;
1675
- var de = document.documentElement;
1676
- var element = event ? target(event) : de;
1677
- // If the target is a Document node, then identify corresponding documentElement and window for this document
1678
- if (element && element.nodeType === Node.DOCUMENT_NODE) {
1679
- var frame = iframe(element);
1680
- w = frame ? frame.contentWindow : w;
1681
- element = de = element.documentElement;
1682
- }
1683
- // Edge doesn't support scrollTop position on document.documentElement.
1684
- // For cross browser compatibility, looking up pageYOffset on window if the scroll is on document.
1685
- // And, if for some reason that is not available, fall back to looking up scrollTop on document.documentElement.
1686
- var x = element === de && "pageXOffset" in w ? Math.round(w.pageXOffset) : Math.round(element.scrollLeft);
1687
- var y = element === de && "pageYOffset" in w ? Math.round(w.pageYOffset) : Math.round(element.scrollTop);
1688
- var current = { time: time(event), event: 10 /* Event.Scroll */, data: { target: element, x: x, y: y } };
1689
- // We don't send any scroll events if this is the first event and the current position is top (0,0)
1690
- if ((event === null && x === 0 && y === 0) || (x === null || y === null)) {
1691
- return;
1692
- }
1693
- var length = state$3.length;
1694
- var last = length > 1 ? state$3[length - 2] : null;
1695
- if (last && similar(last, current)) {
1696
- state$3.pop();
1697
- }
1698
- state$3.push(current);
1699
- clearTimeout(timeout$3);
1700
- timeout$3 = setTimeout(process$4, 500 /* Setting.LookAhead */, 10 /* Event.Scroll */);
1548
+ function encode$4 (type, timer, ts) {
1549
+ if (timer === void 0) { timer = null; }
1550
+ if (ts === void 0) { ts = null; }
1551
+ return __awaiter(this, void 0, void 0, function () {
1552
+ var eventTime, tokens, _a, d, _i, _b, r, values, _c, values_1, value, state, data, active, suspend, privacy, mangle, keys, _d, keys_1, key, box, factor, attr;
1553
+ return __generator(this, function (_e) {
1554
+ switch (_e.label) {
1555
+ case 0:
1556
+ eventTime = ts || time();
1557
+ tokens = [eventTime, type];
1558
+ _a = type;
1559
+ switch (_a) {
1560
+ case 8 /* Event.Document */: return [3 /*break*/, 1];
1561
+ case 7 /* Event.Region */: return [3 /*break*/, 2];
1562
+ case 5 /* Event.Discover */: return [3 /*break*/, 3];
1563
+ case 6 /* Event.Mutation */: return [3 /*break*/, 3];
1564
+ }
1565
+ return [3 /*break*/, 10];
1566
+ case 1:
1567
+ d = data$c;
1568
+ tokens.push(d.width);
1569
+ tokens.push(d.height);
1570
+ track$7(type, d.width, d.height);
1571
+ queue(tokens);
1572
+ return [3 /*break*/, 10];
1573
+ case 2:
1574
+ for (_i = 0, _b = state$8; _i < _b.length; _i++) {
1575
+ r = _b[_i];
1576
+ tokens = [r.time, 7 /* Event.Region */];
1577
+ tokens.push(r.data.id);
1578
+ tokens.push(r.data.interaction);
1579
+ tokens.push(r.data.visibility);
1580
+ tokens.push(r.data.name);
1581
+ queue(tokens);
1582
+ }
1583
+ reset$h();
1584
+ return [3 /*break*/, 10];
1585
+ case 3:
1586
+ // Check if we are operating within the context of the current page
1587
+ if (state$9(timer) === 2 /* Task.Stop */) {
1588
+ return [3 /*break*/, 10];
1589
+ }
1590
+ values = updates$2();
1591
+ if (!(values.length > 0)) return [3 /*break*/, 9];
1592
+ _c = 0, values_1 = values;
1593
+ _e.label = 4;
1594
+ case 4:
1595
+ if (!(_c < values_1.length)) return [3 /*break*/, 8];
1596
+ value = values_1[_c];
1597
+ state = state$9(timer);
1598
+ if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 6];
1599
+ return [4 /*yield*/, suspend$1(timer)];
1600
+ case 5:
1601
+ state = _e.sent();
1602
+ _e.label = 6;
1603
+ case 6:
1604
+ if (state === 2 /* Task.Stop */) {
1605
+ return [3 /*break*/, 8];
1606
+ }
1607
+ data = value.data;
1608
+ active = value.metadata.active;
1609
+ suspend = value.metadata.suspend;
1610
+ privacy = value.metadata.privacy;
1611
+ mangle = shouldMangle(value);
1612
+ keys = active ? ["tag", "attributes", "value"] : ["tag"];
1613
+ for (_d = 0, keys_1 = keys; _d < keys_1.length; _d++) {
1614
+ key = keys_1[_d];
1615
+ if (data[key]) {
1616
+ switch (key) {
1617
+ case "tag":
1618
+ box = size(value);
1619
+ factor = mangle ? -1 : 1;
1620
+ tokens.push(value.id * factor);
1621
+ if (value.parent && active) {
1622
+ tokens.push(value.parent);
1623
+ }
1624
+ if (value.previous && active) {
1625
+ tokens.push(value.previous);
1626
+ }
1627
+ tokens.push(suspend ? "*M" /* Constant.SuspendMutationTag */ : data[key]);
1628
+ if (box && box.length === 2) {
1629
+ tokens.push("".concat("#" /* Constant.Hash */).concat(str$1(box[0]), ".").concat(str$1(box[1])));
1630
+ }
1631
+ break;
1632
+ case "attributes":
1633
+ for (attr in data[key]) {
1634
+ if (data[key][attr] !== undefined) {
1635
+ tokens.push(attribute(attr, data[key][attr], privacy));
1636
+ }
1637
+ }
1638
+ break;
1639
+ case "value":
1640
+ check$4(value.metadata.fraud, value.id, data[key]);
1641
+ tokens.push(text$1(data[key], data.tag, privacy, mangle));
1642
+ break;
1643
+ }
1644
+ }
1645
+ }
1646
+ _e.label = 7;
1647
+ case 7:
1648
+ _c++;
1649
+ return [3 /*break*/, 4];
1650
+ case 8:
1651
+ if (type === 6 /* Event.Mutation */) {
1652
+ activity(eventTime);
1653
+ }
1654
+ queue(tokenize(tokens), !config$1.lean);
1655
+ _e.label = 9;
1656
+ case 9: return [3 /*break*/, 10];
1657
+ case 10: return [2 /*return*/];
1658
+ }
1659
+ });
1660
+ });
1701
1661
  }
1702
- function reset$c() {
1703
- state$3 = [];
1662
+ function shouldMangle(value) {
1663
+ var privacy = value.metadata.privacy;
1664
+ return value.data.tag === "*T" /* Constant.TextTag */ && !(privacy === 0 /* Privacy.None */ || privacy === 1 /* Privacy.Sensitive */);
1704
1665
  }
1705
- function process$4(event) {
1706
- schedule$1(encode$3.bind(this, event));
1666
+ function size(value) {
1667
+ if (value.metadata.size !== null && value.metadata.size.length === 0) {
1668
+ var img = getNode(value.id);
1669
+ if (img) {
1670
+ return [Math.floor(img.offsetWidth * 100 /* Setting.BoxPrecision */), Math.floor(img.offsetHeight * 100 /* Setting.BoxPrecision */)];
1671
+ }
1672
+ }
1673
+ return value.metadata.size;
1707
1674
  }
1708
- function similar(last, current) {
1709
- var dx = last.data.x - current.data.x;
1710
- var dy = last.data.y - current.data.y;
1711
- return (dx * dx + dy * dy < 20 /* Setting.Distance */ * 20 /* Setting.Distance */) && (current.time - last.time < 25 /* Setting.Interval */);
1675
+ function str$1(input) {
1676
+ return input.toString(36);
1712
1677
  }
1713
- function stop$n() {
1714
- clearTimeout(timeout$3);
1715
- state$3 = [];
1678
+ function attribute(key, value, privacy) {
1679
+ return "".concat(key, "=").concat(text$1(value, key, privacy));
1716
1680
  }
1717
1681
 
1718
- var data$a = null;
1719
- var previous = null;
1720
- var timeout$2 = null;
1721
- function start$o() {
1722
- reset$b();
1723
- }
1724
- function observe$6(root) {
1725
- bind(root, "selectstart", recompute$3.bind(this, root), true);
1726
- bind(root, "selectionchange", recompute$3.bind(this, root), true);
1682
+ var state$8 = [];
1683
+ var regionMap = null; // Maps region nodes => region name
1684
+ var regions = {};
1685
+ var queue$2 = [];
1686
+ var watch = false;
1687
+ var observer$1 = null;
1688
+ function start$u() {
1689
+ reset$h();
1690
+ observer$1 = null;
1691
+ regionMap = new WeakMap();
1692
+ regions = {};
1693
+ queue$2 = [];
1694
+ watch = window["IntersectionObserver"] ? true : false;
1727
1695
  }
1728
- function recompute$3(root) {
1729
- var doc = root.nodeType === Node.DOCUMENT_NODE ? root : document;
1730
- var current = doc.getSelection();
1731
- // Bail out if we don't have a valid selection
1732
- if (current === null) {
1733
- return;
1734
- }
1735
- // Bail out if we got a valid selection but not valid nodes
1736
- // In Edge, selectionchange gets fired even on interactions like right clicks and
1737
- // can result in null anchorNode and focusNode if there was no previous selection on page
1738
- // Also, ignore any selections that start and end at the exact same point
1739
- if ((current.anchorNode === null && current.focusNode === null) ||
1740
- (current.anchorNode === current.focusNode && current.anchorOffset === current.focusOffset)) {
1741
- return;
1742
- }
1743
- var startNode = data$a.start ? data$a.start : null;
1744
- if (previous !== null && data$a.start !== null && startNode !== current.anchorNode) {
1745
- clearTimeout(timeout$2);
1746
- process$3(21 /* Event.Selection */);
1696
+ function observe$c(node, name) {
1697
+ if (regionMap.has(node) === false) {
1698
+ regionMap.set(node, name);
1699
+ observer$1 = observer$1 === null && watch ? new IntersectionObserver(handler$3, {
1700
+ // Get notified as intersection continues to change
1701
+ // This allows us to process regions that get partially hidden during the lifetime of the page
1702
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#creating_an_intersection_observer
1703
+ // By default, intersection observers only fire an event when even a single pixel is visible and not thereafter.
1704
+ threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
1705
+ }) : observer$1;
1706
+ if (observer$1 && node && node.nodeType === Node.ELEMENT_NODE) {
1707
+ observer$1.observe(node);
1708
+ }
1747
1709
  }
1748
- data$a = {
1749
- start: current.anchorNode,
1750
- startOffset: current.anchorOffset,
1751
- end: current.focusNode,
1752
- endOffset: current.focusOffset
1753
- };
1754
- previous = current;
1755
- clearTimeout(timeout$2);
1756
- timeout$2 = setTimeout(process$3, 500 /* Setting.LookAhead */, 21 /* Event.Selection */);
1757
- }
1758
- function process$3(event) {
1759
- schedule$1(encode$3.bind(this, event));
1760
1710
  }
1761
- function reset$b() {
1762
- previous = null;
1763
- data$a = { start: 0, startOffset: 0, end: 0, endOffset: 0 };
1764
- }
1765
- function stop$m() {
1766
- reset$b();
1767
- clearTimeout(timeout$2);
1768
- }
1769
-
1770
- var state$2 = [];
1771
- function start$n() {
1772
- reset$a();
1711
+ function exists(node) {
1712
+ // Check if regionMap is not null before looking up a node
1713
+ // Since, dom module stops after region module, it's possible that we may set regionMap to be null
1714
+ // and still attempt to call exists on a late coming DOM mutation (or addition), effectively causing a script error
1715
+ return regionMap && regionMap.has(node);
1773
1716
  }
1774
- function observe$5(root) {
1775
- bind(root, "submit", recompute$2, true);
1717
+ function track$4(id, event) {
1718
+ var node = getNode(id);
1719
+ var data = id in regions ? regions[id] : { id: id, visibility: 0 /* RegionVisibility.Rendered */, interaction: 16 /* InteractionState.None */, name: regionMap.get(node) };
1720
+ // Determine the interaction state based on incoming event
1721
+ var interaction = 16 /* InteractionState.None */;
1722
+ switch (event) {
1723
+ case 9 /* Event.Click */:
1724
+ interaction = 20 /* InteractionState.Clicked */;
1725
+ break;
1726
+ case 27 /* Event.Input */:
1727
+ interaction = 30 /* InteractionState.Input */;
1728
+ break;
1729
+ }
1730
+ // Process updates to this region, if applicable
1731
+ process$6(node, data, interaction, data.visibility);
1776
1732
  }
1777
- function recompute$2(evt) {
1778
- state$2.push({ time: time(evt), event: 39 /* Event.Submit */, data: { target: target(evt) } });
1779
- schedule$1(encode$3.bind(this, 39 /* Event.Submit */));
1733
+ function compute$6() {
1734
+ // Process any regions where we couldn't resolve an "id" for at the time of last intersection observer event
1735
+ // This could happen in cases where elements are not yet processed by Clarity's virtual DOM but browser reports a change, regardless.
1736
+ // For those cases we add them to the queue and re-process them below
1737
+ var q = [];
1738
+ for (var _i = 0, queue_1 = queue$2; _i < queue_1.length; _i++) {
1739
+ var r = queue_1[_i];
1740
+ var id = getId(r.node);
1741
+ if (!(id in regions)) {
1742
+ if (id) {
1743
+ r.data.id = id;
1744
+ regions[id] = r.data;
1745
+ state$8.push(clone$1(r.data));
1746
+ }
1747
+ else {
1748
+ q.push(r);
1749
+ }
1750
+ }
1751
+ }
1752
+ queue$2 = q;
1753
+ // Schedule encode only when we have at least one valid data entry
1754
+ if (state$8.length > 0) {
1755
+ encode$4(7 /* Event.Region */);
1756
+ }
1780
1757
  }
1781
- function reset$a() {
1782
- state$2 = [];
1758
+ function handler$3(entries) {
1759
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
1760
+ var entry = entries_1[_i];
1761
+ var target = entry.target;
1762
+ var rect = entry.boundingClientRect;
1763
+ var overlap = entry.intersectionRect;
1764
+ var viewport = entry.rootBounds;
1765
+ // Only capture regions that have non-zero width or height to avoid tracking and sending regions
1766
+ // that cannot ever be seen by the user. In some cases, websites will have a multiple copy of the same region
1767
+ // like search box - one for desktop, and another for mobile. In those cases, CSS media queries determine which one should be visible.
1768
+ // Also, if these regions ever become non-zero width or height (through AJAX, user action or orientation change) - we will automatically start monitoring them from that point onwards
1769
+ if (regionMap.has(target) && rect.width + rect.height > 0 && viewport.width > 0 && viewport.height > 0) {
1770
+ var id = target ? getId(target) : null;
1771
+ var data = id in regions ? regions[id] : { id: id, name: regionMap.get(target), interaction: 16 /* InteractionState.None */, visibility: 0 /* RegionVisibility.Rendered */ };
1772
+ // For regions that have relatively smaller area, we look at intersection ratio and see the overlap relative to element's area
1773
+ // However, for larger regions, area of regions could be bigger than viewport and therefore comparison is relative to visible area
1774
+ var viewportRatio = overlap ? (overlap.width * overlap.height * 1.0) / (viewport.width * viewport.height) : 0;
1775
+ var visible = viewportRatio > 0.05 /* Setting.ViewportIntersectionRatio */ || entry.intersectionRatio > 0.8 /* Setting.IntersectionRatio */;
1776
+ // If an element is either visible or was visible and has been scrolled to the end
1777
+ // i.e. Scrolled to end is determined by if the starting position of the element + the window height is more than the total element height.
1778
+ // starting position is relative to the viewport - so Intersection observer returns a negative value for rect.top to indicate that the element top is above the viewport
1779
+ var scrolledToEnd = (visible || data.visibility == 10 /* RegionVisibility.Visible */) && Math.abs(rect.top) + viewport.height > rect.height;
1780
+ // Process updates to this region, if applicable
1781
+ process$6(target, data, data.interaction, (scrolledToEnd ?
1782
+ 13 /* RegionVisibility.ScrolledToEnd */ :
1783
+ (visible ? 10 /* RegionVisibility.Visible */ : 0 /* RegionVisibility.Rendered */)));
1784
+ // Stop observing this element now that we have already received scrolled signal
1785
+ if (data.visibility >= 13 /* RegionVisibility.ScrolledToEnd */ && observer$1) {
1786
+ observer$1.unobserve(target);
1787
+ }
1788
+ }
1789
+ }
1790
+ if (state$8.length > 0) {
1791
+ encode$4(7 /* Event.Region */);
1792
+ }
1783
1793
  }
1784
- function stop$l() {
1785
- reset$a();
1786
- }
1787
-
1788
- var data$9;
1789
- function start$m() {
1790
- bind(window, "pagehide", recompute$1);
1794
+ function process$6(n, d, s, v) {
1795
+ // Check if received a state that supersedes existing state
1796
+ var updated = s > d.interaction || v > d.visibility;
1797
+ d.interaction = s > d.interaction ? s : d.interaction;
1798
+ d.visibility = v > d.visibility ? v : d.visibility;
1799
+ // If the corresponding node is already discovered, update the internal state
1800
+ // Otherwise, track it in a queue to reprocess later.
1801
+ if (d.id) {
1802
+ if ((d.id in regions && updated) || !(d.id in regions)) {
1803
+ regions[d.id] = d;
1804
+ state$8.push(clone$1(d));
1805
+ }
1806
+ }
1807
+ else {
1808
+ queue$2.push({ node: n, data: d });
1809
+ }
1791
1810
  }
1792
- function recompute$1(evt) {
1793
- data$9 = { name: evt.type };
1794
- encode$3(26 /* Event.Unload */, time(evt));
1795
- stop();
1811
+ function clone$1(r) {
1812
+ return { time: time(), data: { id: r.id, interaction: r.interaction, visibility: r.visibility, name: r.name } };
1796
1813
  }
1797
- function reset$9() {
1798
- data$9 = null;
1814
+ function reset$h() {
1815
+ state$8 = [];
1799
1816
  }
1800
- function stop$k() {
1801
- reset$9();
1817
+ function stop$s() {
1818
+ reset$h();
1819
+ regionMap = null;
1820
+ regions = {};
1821
+ queue$2 = [];
1822
+ if (observer$1) {
1823
+ observer$1.disconnect();
1824
+ observer$1 = null;
1825
+ }
1826
+ watch = false;
1802
1827
  }
1803
1828
 
1804
- var data$8;
1805
- function start$l() {
1806
- bind(document, "visibilitychange", recompute);
1807
- recompute();
1829
+ var state$7 = [];
1830
+ function start$t() {
1831
+ reset$g();
1808
1832
  }
1809
- function recompute(evt) {
1810
- if (evt === void 0) { evt = null; }
1811
- data$8 = { visible: "visibilityState" in document ? document.visibilityState : "default" };
1812
- encode$3(28 /* Event.Visibility */, time(evt));
1833
+ function observe$b(root) {
1834
+ bind(root, "change", recompute$8, true);
1813
1835
  }
1814
- function reset$8() {
1815
- data$8 = null;
1836
+ function recompute$8(evt) {
1837
+ var element = target(evt);
1838
+ if (element) {
1839
+ var value = element.value;
1840
+ var checksum = value && value.length >= 5 /* Setting.WordLength */ && config$1.fraud ? hash(value, 24 /* Setting.ChecksumPrecision */) : "" /* Constant.Empty */;
1841
+ state$7.push({ time: time(evt), event: 42 /* Event.Change */, data: { target: target(evt), type: element.type, value: value, checksum: checksum } });
1842
+ schedule$1(encode$3.bind(this, 42 /* Event.Change */));
1843
+ }
1816
1844
  }
1817
- function stop$j() {
1818
- reset$8();
1845
+ function reset$g() {
1846
+ state$7 = [];
1847
+ }
1848
+ function stop$r() {
1849
+ reset$g();
1819
1850
  }
1820
1851
 
1821
- function start$k() {
1822
- start$g();
1823
- start$u();
1824
- start$t();
1825
- start$r();
1826
- start$s();
1827
- start$q();
1828
- start$l();
1829
- start$p();
1830
- start$o();
1831
- start$v();
1832
- start$n();
1833
- start$m();
1834
- }
1835
- function stop$i() {
1836
- stop$e();
1837
- stop$s();
1838
- stop$r();
1839
- stop$p();
1840
- stop$q();
1841
- stop$o();
1842
- stop$j();
1843
- stop$n();
1844
- stop$m();
1845
- stop$t();
1846
- stop$l();
1847
- stop$k();
1848
- }
1849
- function observe$4(root) {
1850
- observe$7(root);
1851
- // Only monitor following interactions if the root node is a document
1852
- // In case of shadow DOM, following events automatically bubble up to the parent document.
1853
- if (root.nodeType === Node.DOCUMENT_NODE) {
1854
- observe$b(root);
1855
- observe$a(root);
1856
- observe$8(root);
1857
- observe$9(root);
1858
- observe$6(root);
1859
- observe$c(root);
1860
- observe$5(root);
1852
+ function offset (element) {
1853
+ var output = { x: 0, y: 0 };
1854
+ // Walk up the chain to ensure we compute offset distance correctly
1855
+ // In case where we may have nested IFRAMEs, we keep walking up until we get to the top most parent page
1856
+ if (element && element.offsetParent) {
1857
+ do {
1858
+ var parent_1 = element.offsetParent;
1859
+ var frame = parent_1 === null ? iframe(element.ownerDocument) : null;
1860
+ output.x += element.offsetLeft;
1861
+ output.y += element.offsetTop;
1862
+ element = frame ? frame : parent_1;
1863
+ } while (element);
1861
1864
  }
1865
+ return output;
1862
1866
  }
1863
1867
 
1864
- var interaction = /*#__PURE__*/Object.freeze({
1865
- __proto__: null,
1866
- start: start$k,
1867
- stop: stop$i,
1868
- observe: observe$4
1869
- });
1870
-
1871
- var digitsRegex = /[^0-9\.]/g;
1872
- /* JSON+LD (Linked Data) Recursive Parser */
1873
- function ld(json) {
1874
- for (var _i = 0, _a = Object.keys(json); _i < _a.length; _i++) {
1875
- var key = _a[_i];
1876
- var value = json[key];
1877
- if (key === "@type" /* JsonLD.Type */ && typeof value === "string") {
1878
- value = value.toLowerCase();
1879
- /* Normalizations */
1880
- value = value.indexOf("article" /* JsonLD.Article */) >= 0 || value.indexOf("posting" /* JsonLD.Posting */) >= 0 ? "article" /* JsonLD.Article */ : value;
1881
- switch (value) {
1882
- case "article" /* JsonLD.Article */:
1883
- case "recipe" /* JsonLD.Recipe */:
1884
- log(5 /* Dimension.SchemaType */, json[key]);
1885
- log(8 /* Dimension.AuthorName */, json["creator" /* JsonLD.Creator */]);
1886
- log(18 /* Dimension.Headline */, json["headline" /* JsonLD.Headline */]);
1887
- break;
1888
- case "product" /* JsonLD.Product */:
1889
- log(5 /* Dimension.SchemaType */, json[key]);
1890
- log(10 /* Dimension.ProductName */, json["name" /* JsonLD.Name */]);
1891
- log(12 /* Dimension.ProductSku */, json["sku" /* JsonLD.Sku */]);
1892
- if (json["brand" /* JsonLD.Brand */]) {
1893
- log(6 /* Dimension.ProductBrand */, json["brand" /* JsonLD.Brand */]["name" /* JsonLD.Name */]);
1894
- }
1895
- break;
1896
- case "aggregaterating" /* JsonLD.AggregateRating */:
1897
- if (json["ratingValue" /* JsonLD.RatingValue */]) {
1898
- max(11 /* Metric.RatingValue */, num$1(json["ratingValue" /* JsonLD.RatingValue */], 100 /* Setting.RatingScale */));
1899
- max(18 /* Metric.BestRating */, num$1(json["bestRating" /* JsonLD.BestRating */]));
1900
- max(19 /* Metric.WorstRating */, num$1(json["worstRating" /* JsonLD.WorstRating */]));
1901
- }
1902
- max(12 /* Metric.RatingCount */, num$1(json["ratingCount" /* JsonLD.RatingCount */]));
1903
- max(17 /* Metric.ReviewCount */, num$1(json["reviewCount" /* JsonLD.ReviewCount */]));
1904
- break;
1905
- case "person" /* JsonLD.Author */:
1906
- log(8 /* Dimension.AuthorName */, json["name" /* JsonLD.Name */]);
1907
- break;
1908
- case "offer" /* JsonLD.Offer */:
1909
- log(7 /* Dimension.ProductAvailability */, json["availability" /* JsonLD.Availability */]);
1910
- log(14 /* Dimension.ProductCondition */, json["itemCondition" /* JsonLD.ItemCondition */]);
1911
- log(13 /* Dimension.ProductCurrency */, json["priceCurrency" /* JsonLD.PriceCurrency */]);
1912
- log(12 /* Dimension.ProductSku */, json["sku" /* JsonLD.Sku */]);
1913
- max(13 /* Metric.ProductPrice */, num$1(json["price" /* JsonLD.Price */]));
1914
- break;
1915
- case "brand" /* JsonLD.Brand */:
1916
- log(6 /* Dimension.ProductBrand */, json["name" /* JsonLD.Name */]);
1917
- break;
1918
- }
1919
- }
1920
- // Continue parsing nested objects
1921
- if (value !== null && typeof (value) === "object" /* Constant.Object */) {
1922
- ld(value);
1923
- }
1924
- }
1868
+ var UserInputTags = ["input", "textarea", "radio", "button", "canvas"];
1869
+ var state$6 = [];
1870
+ function start$s() {
1871
+ reset$f();
1925
1872
  }
1926
- function num$1(input, scale) {
1927
- if (scale === void 0) { scale = 1; }
1928
- if (input !== null) {
1929
- switch (typeof input) {
1930
- case "number" /* Constant.Number */: return Math.round(input * scale);
1931
- case "string" /* Constant.String */: return Math.round(parseFloat(input.replace(digitsRegex, "" /* Constant.Empty */)) * scale);
1932
- }
1873
+ function observe$a(root) {
1874
+ bind(root, "click", handler$2.bind(this, 9 /* Event.Click */, root), true);
1875
+ }
1876
+ function handler$2(event, root, evt) {
1877
+ var frame = iframe(root);
1878
+ var d = frame ? frame.contentDocument.documentElement : document.documentElement;
1879
+ var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
1880
+ var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
1881
+ // In case of iframe, we adjust (x,y) to be relative to top parent's origin
1882
+ if (frame) {
1883
+ var distance = offset(frame);
1884
+ x = x ? x + Math.round(distance.x) : x;
1885
+ y = y ? y + Math.round(distance.y) : y;
1933
1886
  }
1934
- return null;
1935
- }
1936
-
1937
- var IGNORE_ATTRIBUTES = ["title", "alt", "onload", "onfocus", "onerror", "data-drupal-form-submit-last"];
1938
- var newlineRegex = /[\r\n]+/g;
1939
- function processNode (node, source) {
1940
- var _a;
1941
- var child = null;
1942
- // Do not track this change if we are attempting to remove a node before discovering it
1943
- if (source === 2 /* Source.ChildListRemove */ && has(node) === false) {
1944
- return child;
1887
+ var t = target(evt);
1888
+ // Find nearest anchor tag (<a/>) parent if current target node is part of one
1889
+ // If present, we use the returned link element to populate text and link properties below
1890
+ var a = link(t);
1891
+ // Get layout rectangle for the target element
1892
+ var l = layout$1(t);
1893
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
1894
+ // This property helps differentiate between a keyboard navigation vs. pointer click
1895
+ // In case of a keyboard navigation, we use center of target element as (x,y)
1896
+ if (evt.detail === 0 && l) {
1897
+ x = Math.round(l.x + (l.w / 2));
1898
+ y = Math.round(l.y + (l.h / 2));
1945
1899
  }
1946
- // Special handling for text nodes that belong to style nodes
1947
- if (source !== 0 /* Source.Discover */ &&
1948
- node.nodeType === Node.TEXT_NODE &&
1949
- node.parentElement &&
1950
- node.parentElement.tagName === "STYLE") {
1951
- node = node.parentNode;
1952
- }
1953
- var add = has(node) === false;
1954
- var call = add ? "add" : "update";
1955
- var parent = node.parentElement ? node.parentElement : null;
1956
- var insideFrame = node.ownerDocument !== document;
1957
- switch (node.nodeType) {
1958
- case Node.DOCUMENT_TYPE_NODE:
1959
- parent = insideFrame && node.parentNode ? iframe(node.parentNode) : parent;
1960
- var docTypePrefix = insideFrame ? "iframe:" /* Constant.IFramePrefix */ : "" /* Constant.Empty */;
1961
- var doctype = node;
1962
- var docAttributes = { name: doctype.name, publicId: doctype.publicId, systemId: doctype.systemId };
1963
- var docData = { tag: docTypePrefix + "*D" /* Constant.DocumentTag */, attributes: docAttributes };
1964
- dom[call](node, parent, docData, source);
1965
- break;
1966
- case Node.DOCUMENT_NODE:
1967
- // We check for regions in the beginning when discovering document and
1968
- // later whenever there are new additions or modifications to DOM (mutations)
1969
- if (node === document)
1970
- parse$1(document);
1971
- observe$3(node);
1972
- break;
1973
- case Node.DOCUMENT_FRAGMENT_NODE:
1974
- var shadowRoot = node;
1975
- if (shadowRoot.host) {
1976
- parse$1(shadowRoot);
1977
- var type = typeof (shadowRoot.constructor);
1978
- if (type === "function" /* Constant.Function */ && shadowRoot.constructor.toString().indexOf("[native code]" /* Constant.NativeCode */) >= 0) {
1979
- observe$3(shadowRoot);
1980
- // See: https://wicg.github.io/construct-stylesheets/ for more details on adoptedStyleSheets.
1981
- // At the moment, we are only able to capture "open" shadow DOM nodes. If they are closed, they are not accessible.
1982
- // In future we may decide to proxy "attachShadow" call to gain access, but at the moment, we don't want to
1983
- // cause any unintended side effect to the page. We will re-evaluate after we gather more real world data on this.
1984
- var style = "" /* Constant.Empty */;
1985
- var adoptedStyleSheets = "adoptedStyleSheets" in shadowRoot ? shadowRoot["adoptedStyleSheets"] : [];
1986
- for (var _i = 0, adoptedStyleSheets_1 = adoptedStyleSheets; _i < adoptedStyleSheets_1.length; _i++) {
1987
- var styleSheet = adoptedStyleSheets_1[_i];
1988
- style += getCssRules(styleSheet);
1989
- }
1990
- var fragementData = { tag: "*S" /* Constant.ShadowDomTag */, attributes: { style: style } };
1991
- dom[call](node, shadowRoot.host, fragementData, source);
1992
- }
1993
- else {
1994
- // If the browser doesn't support shadow DOM natively, we detect that, and send appropriate tag back.
1995
- // The differentiation is important because we don't have to observe pollyfill shadow DOM nodes,
1996
- // the same way we observe real shadow DOM nodes (encapsulation provided by the browser).
1997
- dom[call](node, shadowRoot.host, { tag: "*P" /* Constant.PolyfillShadowDomTag */, attributes: {} }, source);
1998
- }
1999
- }
2000
- break;
2001
- case Node.TEXT_NODE:
2002
- // In IE11 TEXT_NODE doesn't expose a valid parentElement property. Instead we need to lookup parentNode property.
2003
- parent = parent ? parent : node.parentNode;
2004
- // Account for this text node only if we are tracking the parent node
2005
- // We do not wish to track text nodes for ignored parent nodes, like script tags
2006
- // Also, we do not track text nodes for STYLE tags
2007
- // The only exception is when we receive a mutation to remove the text node, in that case
2008
- // parent will be null, but we can still process the node by checking it's an update call.
2009
- if (call === "update" || (parent && has(parent) && parent.tagName !== "STYLE")) {
2010
- var textData = { tag: "*T" /* Constant.TextTag */, value: node.nodeValue };
2011
- dom[call](node, parent, textData, source);
2012
- }
2013
- break;
2014
- case Node.ELEMENT_NODE:
2015
- var element = node;
2016
- var tag = element.tagName;
2017
- var attributes = getAttributes(element);
2018
- // In some cases, external libraries like vue-fragment, can modify parentNode property to not be in sync with the DOM
2019
- // For correctness, we first look at parentElement and if it not present then fall back to using parentNode
2020
- parent = node.parentElement ? node.parentElement : (node.parentNode ? node.parentNode : null);
2021
- // If we encounter a node that is part of SVG namespace, prefix the tag with SVG_PREFIX
2022
- if (element.namespaceURI === "http://www.w3.org/2000/svg" /* Constant.SvgNamespace */) {
2023
- tag = "svg:" /* Constant.SvgPrefix */ + tag;
2024
- }
2025
- switch (tag) {
2026
- case "HTML":
2027
- parent = insideFrame && parent ? iframe(parent) : null;
2028
- var htmlPrefix = insideFrame ? "iframe:" /* Constant.IFramePrefix */ : "" /* Constant.Empty */;
2029
- var htmlData = { tag: htmlPrefix + tag, attributes: attributes };
2030
- dom[call](node, parent, htmlData, source);
2031
- break;
2032
- case "SCRIPT":
2033
- if ("type" /* Constant.Type */ in attributes && attributes["type" /* Constant.Type */] === "application/ld+json" /* Constant.JsonLD */) {
2034
- try {
2035
- ld(JSON.parse(element.text.replace(newlineRegex, "" /* Constant.Empty */)));
2036
- }
2037
- catch ( /* do nothing */_b) { /* do nothing */ }
2038
- }
2039
- break;
2040
- case "NOSCRIPT":
2041
- break;
2042
- case "META":
2043
- var key = ("property" /* Constant.Property */ in attributes ?
2044
- "property" /* Constant.Property */ :
2045
- ("name" /* Constant.Name */ in attributes ? "name" /* Constant.Name */ : null));
2046
- if (key && "content" /* Constant.Content */ in attributes) {
2047
- var content = attributes["content" /* Constant.Content */];
2048
- switch (attributes[key]) {
2049
- case "og:title" /* Constant.ogTitle */:
2050
- log(20 /* Dimension.MetaTitle */, content);
2051
- break;
2052
- case "og:type" /* Constant.ogType */:
2053
- log(19 /* Dimension.MetaType */, content);
2054
- break;
2055
- case "generator" /* Constant.Generator */:
2056
- log(21 /* Dimension.Generator */, content);
2057
- break;
2058
- }
2059
- }
2060
- break;
2061
- case "HEAD":
2062
- var head = { tag: tag, attributes: attributes };
2063
- var l = insideFrame && ((_a = node.ownerDocument) === null || _a === void 0 ? void 0 : _a.location) ? node.ownerDocument.location : location;
2064
- head.attributes["*B" /* Constant.Base */] = l.protocol + "//" + l.hostname + l.pathname;
2065
- dom[call](node, parent, head, source);
2066
- break;
2067
- case "BASE":
2068
- // Override the auto detected base path to explicit value specified in this tag
2069
- var baseHead = get(node.parentElement);
2070
- if (baseHead) {
2071
- // We create "a" element so we can generate protocol and hostname for relative paths like "/path/"
2072
- var a = document.createElement("a");
2073
- a.href = attributes["href"];
2074
- baseHead.data.attributes["*B" /* Constant.Base */] = a.protocol + "//" + a.hostname + a.pathname;
2075
- }
2076
- break;
2077
- case "STYLE":
2078
- var styleData = { tag: tag, attributes: attributes, value: getStyleValue(element) };
2079
- dom[call](node, parent, styleData, source);
2080
- break;
2081
- case "IFRAME":
2082
- var iframe$1 = node;
2083
- var frameData = { tag: tag, attributes: attributes };
2084
- if (sameorigin(iframe$1)) {
2085
- monitor(iframe$1);
2086
- frameData.attributes["*O" /* Constant.SameOrigin */] = "true";
2087
- if (iframe$1.contentDocument && iframe$1.contentWindow && iframe$1.contentDocument.readyState !== "loading") {
2088
- child = iframe$1.contentDocument;
2089
- }
2090
- }
2091
- dom[call](node, parent, frameData, source);
2092
- break;
2093
- default:
2094
- var data = { tag: tag, attributes: attributes };
2095
- if (element.shadowRoot) {
2096
- child = element.shadowRoot;
2097
- }
2098
- dom[call](node, parent, data, source);
2099
- break;
1900
+ var eX = l ? Math.max(Math.floor(((x - l.x) / l.w) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1901
+ var eY = l ? Math.max(Math.floor(((y - l.y) / l.h) * 32767 /* Setting.ClickPrecision */), 0) : 0;
1902
+ // Check for null values before processing this event
1903
+ if (x !== null && y !== null) {
1904
+ state$6.push({
1905
+ time: time(evt),
1906
+ event: event,
1907
+ data: {
1908
+ target: t,
1909
+ x: x,
1910
+ y: y,
1911
+ eX: eX,
1912
+ eY: eY,
1913
+ button: evt.button,
1914
+ reaction: reaction(t),
1915
+ context: context(a),
1916
+ text: text(t),
1917
+ link: a ? a.href : null,
1918
+ hash: null,
1919
+ trust: evt.isTrusted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */
2100
1920
  }
2101
- break;
2102
- }
2103
- return child;
2104
- }
2105
- function observe$3(root) {
2106
- if (has(root)) {
2107
- return;
1921
+ });
1922
+ schedule$1(encode$3.bind(this, event));
2108
1923
  }
2109
- observe$2(root); // Observe mutations for this root node
2110
- observe$4(root); // Observe interactions for this root node
2111
1924
  }
2112
- function getStyleValue(style) {
2113
- // Call trim on the text content to ensure we do not process white spaces ( , \n, \r\n, \t, etc.)
2114
- // Also, check if stylesheet has any data-* attribute, if so process rules instead of looking up text
2115
- var value = style.textContent ? style.textContent.trim() : "" /* Constant.Empty */;
2116
- var dataset = style.dataset ? Object.keys(style.dataset).length : 0;
2117
- if (value.length === 0 || dataset > 0) {
2118
- value = getCssRules(style.sheet);
1925
+ function text(element) {
1926
+ var output = null;
1927
+ if (element) {
1928
+ // Grab text using "textContent" for most HTMLElements, however, use "value" for HTMLInputElements and "alt" for HTMLImageElement.
1929
+ var t = element.textContent || element.value || element.alt;
1930
+ if (t) {
1931
+ // Trim any spaces at the beginning or at the end of string
1932
+ // Also, replace multiple occurrence of space characters with a single white space
1933
+ // Finally, send only first few characters as specified by the Setting
1934
+ output = t.trim().replace(/\s+/g, " " /* Constant.Space */).substr(0, 25 /* Setting.ClickText */);
1935
+ }
2119
1936
  }
2120
- return value;
1937
+ return output;
2121
1938
  }
2122
- function getCssRules(sheet) {
2123
- var value = "" /* Constant.Empty */;
2124
- var cssRules = null;
2125
- // Firefox throws a SecurityError when trying to access cssRules of a stylesheet from a different domain
2126
- try {
2127
- cssRules = sheet ? sheet.cssRules : [];
2128
- }
2129
- catch (e) {
2130
- log$1(1 /* Code.CssRules */, 1 /* Severity.Warning */, e ? e.name : null);
2131
- if (e && e.name !== "SecurityError") {
2132
- throw e;
1939
+ function reaction(element) {
1940
+ if (element.nodeType === Node.ELEMENT_NODE) {
1941
+ var tag = element.tagName.toLowerCase();
1942
+ if (UserInputTags.indexOf(tag) >= 0) {
1943
+ return 0 /* BooleanFlag.False */;
2133
1944
  }
2134
1945
  }
2135
- if (cssRules !== null) {
2136
- for (var i = 0; i < cssRules.length; i++) {
2137
- value += cssRules[i].cssText;
1946
+ return 1 /* BooleanFlag.True */;
1947
+ }
1948
+ function layout$1(element) {
1949
+ var box = null;
1950
+ var de = document.documentElement;
1951
+ if (typeof element.getBoundingClientRect === "function") {
1952
+ // getBoundingClientRect returns rectangle relative positioning to viewport
1953
+ var rect = element.getBoundingClientRect();
1954
+ if (rect && rect.width > 0 && rect.height > 0) {
1955
+ // Add viewport's scroll position to rectangle to get position relative to document origin
1956
+ // Also: using Math.floor() instead of Math.round() because in Edge,
1957
+ // getBoundingClientRect returns partial pixel values (e.g. 162.5px) and Chrome already
1958
+ // floors the value (e.g. 162px). This keeps consistent behavior across browsers.
1959
+ box = {
1960
+ x: Math.floor(rect.left + ("pageXOffset" in window ? window.pageXOffset : de.scrollLeft)),
1961
+ y: Math.floor(rect.top + ("pageYOffset" in window ? window.pageYOffset : de.scrollTop)),
1962
+ w: Math.floor(rect.width),
1963
+ h: Math.floor(rect.height)
1964
+ };
2138
1965
  }
2139
1966
  }
2140
- return value;
1967
+ return box;
2141
1968
  }
2142
- function getAttributes(element) {
2143
- var output = {};
2144
- var attributes = element.attributes;
2145
- if (attributes && attributes.length > 0) {
2146
- for (var i = 0; i < attributes.length; i++) {
2147
- var name_1 = attributes[i].name;
2148
- if (IGNORE_ATTRIBUTES.indexOf(name_1) < 0) {
2149
- output[name_1] = attributes[i].value;
2150
- }
1969
+ function context(a) {
1970
+ if (a && a.hasAttribute("target" /* Constant.Target */)) {
1971
+ switch (a.getAttribute("target" /* Constant.Target */)) {
1972
+ case "_blank" /* Constant.Blank */: return 1 /* BrowsingContext.Blank */;
1973
+ case "_parent" /* Constant.Parent */: return 2 /* BrowsingContext.Parent */;
1974
+ case "_top" /* Constant.Top */: return 3 /* BrowsingContext.Top */;
2151
1975
  }
2152
1976
  }
2153
- // For INPUT tags read the dynamic "value" property if an explicit "value" attribute is not set
2154
- if (element.tagName === "INPUT" /* Constant.InputTag */ && !("value" /* Constant.Value */ in output) && element.value) {
2155
- output["value" /* Constant.Value */] = element.value;
2156
- }
2157
- return output;
1977
+ return 0 /* BrowsingContext.Self */;
1978
+ }
1979
+ function reset$f() {
1980
+ state$6 = [];
1981
+ }
1982
+ function stop$q() {
1983
+ reset$f();
2158
1984
  }
2159
1985
 
2160
- function traverse (root, timer, source) {
2161
- return __awaiter(this, void 0, void 0, function () {
2162
- var queue, node, next, state, subnode;
2163
- return __generator(this, function (_a) {
2164
- switch (_a.label) {
2165
- case 0:
2166
- queue = [root];
2167
- _a.label = 1;
2168
- case 1:
2169
- if (!(queue.length > 0)) return [3 /*break*/, 4];
2170
- node = queue.shift();
2171
- next = node.firstChild;
2172
- while (next) {
2173
- queue.push(next);
2174
- next = next.nextSibling;
2175
- }
2176
- state = state$9(timer);
2177
- if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 3];
2178
- return [4 /*yield*/, suspend$1(timer)];
2179
- case 2:
2180
- state = _a.sent();
2181
- _a.label = 3;
2182
- case 3:
2183
- if (state === 2 /* Task.Stop */) {
2184
- return [3 /*break*/, 4];
2185
- }
2186
- subnode = processNode(node, source);
2187
- if (subnode) {
2188
- queue.push(subnode);
2189
- }
2190
- return [3 /*break*/, 1];
2191
- case 4: return [2 /*return*/];
2192
- }
2193
- });
2194
- });
1986
+ var state$5 = [];
1987
+ function start$r() {
1988
+ reset$e();
1989
+ }
1990
+ function observe$9(root) {
1991
+ bind(root, "cut", recompute$7.bind(this, 0 /* Clipboard.Cut */), true);
1992
+ bind(root, "copy", recompute$7.bind(this, 1 /* Clipboard.Copy */), true);
1993
+ bind(root, "paste", recompute$7.bind(this, 2 /* Clipboard.Paste */), true);
1994
+ }
1995
+ function recompute$7(action, evt) {
1996
+ state$5.push({ time: time(evt), event: 38 /* Event.Clipboard */, data: { target: target(evt), action: action } });
1997
+ schedule$1(encode$3.bind(this, 38 /* Event.Clipboard */));
1998
+ }
1999
+ function reset$e() {
2000
+ state$5 = [];
2001
+ }
2002
+ function stop$p() {
2003
+ reset$e();
2195
2004
  }
2196
2005
 
2197
- var observers = [];
2198
- var mutations = [];
2199
- var insertRule = null;
2200
- var deleteRule = null;
2201
- var attachShadow = null;
2202
- var queue$2 = [];
2203
- var timeout$1 = null;
2204
- var activePeriod = null;
2205
- var history$4 = {};
2206
- function start$j() {
2207
- observers = [];
2208
- queue$2 = [];
2209
- timeout$1 = null;
2210
- activePeriod = 0;
2211
- history$4 = {};
2212
- // Some popular open source libraries, like styled-components, optimize performance
2213
- // by injecting CSS using insertRule API vs. appending text node. A side effect of
2214
- // using javascript API is that it doesn't trigger DOM mutation and therefore we
2215
- // need to override the insertRule API and listen for changes manually.
2216
- if (insertRule === null) {
2217
- insertRule = CSSStyleSheet.prototype.insertRule;
2218
- CSSStyleSheet.prototype.insertRule = function () {
2219
- if (active()) {
2220
- schedule(this.ownerNode);
2221
- }
2222
- return insertRule.apply(this, arguments);
2223
- };
2224
- }
2225
- if (deleteRule === null) {
2226
- deleteRule = CSSStyleSheet.prototype.deleteRule;
2227
- CSSStyleSheet.prototype.deleteRule = function () {
2228
- if (active()) {
2229
- schedule(this.ownerNode);
2230
- }
2231
- return deleteRule.apply(this, arguments);
2232
- };
2233
- }
2234
- // Add a hook to attachShadow API calls
2235
- // In case we are unable to add a hook and browser throws an exception,
2236
- // reset attachShadow variable and resume processing like before
2237
- if (attachShadow === null) {
2238
- attachShadow = Element.prototype.attachShadow;
2239
- try {
2240
- Element.prototype.attachShadow = function () {
2241
- if (active()) {
2242
- return schedule(attachShadow.apply(this, arguments));
2243
- }
2244
- else {
2245
- return attachShadow.apply(this, arguments);
2246
- }
2247
- };
2006
+ var timeout$5 = null;
2007
+ var state$4 = [];
2008
+ function start$q() {
2009
+ reset$d();
2010
+ }
2011
+ function observe$8(root) {
2012
+ bind(root, "input", recompute$6, true);
2013
+ }
2014
+ function recompute$6(evt) {
2015
+ var input = target(evt);
2016
+ var value = get(input);
2017
+ if (input && input.type && value) {
2018
+ var v = input.value;
2019
+ switch (input.type) {
2020
+ case "radio":
2021
+ case "checkbox":
2022
+ v = input.checked ? "true" : "false";
2023
+ break;
2248
2024
  }
2249
- catch (_a) {
2250
- attachShadow = null;
2025
+ var data = { target: input, value: v };
2026
+ // 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.
2027
+ if (state$4.length > 0 && (state$4[state$4.length - 1].data.target === data.target)) {
2028
+ state$4.pop();
2251
2029
  }
2030
+ state$4.push({ time: time(evt), event: 27 /* Event.Input */, data: data });
2031
+ clearTimeout(timeout$5);
2032
+ timeout$5 = setTimeout(process$5, 1000 /* Setting.InputLookAhead */, 27 /* Event.Input */);
2252
2033
  }
2253
2034
  }
2254
- function observe$2(node) {
2255
- // Create a new observer for every time a new DOM tree (e.g. root document or shadowdom root) is discovered on the page
2256
- // In the case of shadow dom, any mutations that happen within the shadow dom are not bubbled up to the host document
2257
- // For this reason, we need to wire up mutations every time we see a new shadow dom.
2258
- // Also, wrap it inside a try / catch. In certain browsers (e.g. legacy Edge), observer on shadow dom can throw errors
2259
- try {
2260
- var m = api("MutationObserver" /* Constant.MutationObserver */);
2261
- var observer = m in window ? new window[m](measure(handle$1)) : null;
2262
- if (observer) {
2263
- observer.observe(node, { attributes: true, childList: true, characterData: true, subtree: true });
2264
- observers.push(observer);
2265
- }
2035
+ function process$5(event) {
2036
+ schedule$1(encode$3.bind(this, event));
2037
+ }
2038
+ function reset$d() {
2039
+ state$4 = [];
2040
+ }
2041
+ function stop$o() {
2042
+ clearTimeout(timeout$5);
2043
+ reset$d();
2044
+ }
2045
+
2046
+ var state$3 = [];
2047
+ var timeout$4 = null;
2048
+ function start$p() {
2049
+ reset$c();
2050
+ }
2051
+ function observe$7(root) {
2052
+ bind(root, "mousedown", mouse.bind(this, 13 /* Event.MouseDown */, root), true);
2053
+ bind(root, "mouseup", mouse.bind(this, 14 /* Event.MouseUp */, root), true);
2054
+ bind(root, "mousemove", mouse.bind(this, 12 /* Event.MouseMove */, root), true);
2055
+ bind(root, "wheel", mouse.bind(this, 15 /* Event.MouseWheel */, root), true);
2056
+ bind(root, "dblclick", mouse.bind(this, 16 /* Event.DoubleClick */, root), true);
2057
+ bind(root, "touchstart", touch.bind(this, 17 /* Event.TouchStart */, root), true);
2058
+ bind(root, "touchend", touch.bind(this, 18 /* Event.TouchEnd */, root), true);
2059
+ bind(root, "touchmove", touch.bind(this, 19 /* Event.TouchMove */, root), true);
2060
+ bind(root, "touchcancel", touch.bind(this, 20 /* Event.TouchCancel */, root), true);
2061
+ }
2062
+ function mouse(event, root, evt) {
2063
+ var frame = iframe(root);
2064
+ var d = frame ? frame.contentDocument.documentElement : document.documentElement;
2065
+ var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
2066
+ var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
2067
+ // In case of iframe, we adjust (x,y) to be relative to top parent's origin
2068
+ if (frame) {
2069
+ var distance = offset(frame);
2070
+ x = x ? x + Math.round(distance.x) : x;
2071
+ y = y ? y + Math.round(distance.y) : y;
2266
2072
  }
2267
- catch (e) {
2268
- log$1(2 /* Code.MutationObserver */, 0 /* Severity.Info */, e ? e.name : null);
2073
+ // Check for null values before processing this event
2074
+ if (x !== null && y !== null) {
2075
+ handler$1({ time: time(evt), event: event, data: { target: target(evt), x: x, y: y } });
2269
2076
  }
2270
2077
  }
2271
- function monitor(frame) {
2272
- // Bind to iframe's onload event so we get notified anytime there's an update to iframe content.
2273
- // This includes cases where iframe location is updated without explicitly updating src attribute
2274
- // E.g. iframe.contentWindow.location.href = "new-location";
2275
- if (has(frame) === false) {
2276
- bind(frame, "load" /* Constant.LoadEvent */, generate.bind(this, frame, "childList" /* Constant.ChildList */), true);
2078
+ function touch(event, root, evt) {
2079
+ var frame = iframe(root);
2080
+ var d = frame ? frame.contentDocument.documentElement : document.documentElement;
2081
+ var touches = evt.changedTouches;
2082
+ var t = time(evt);
2083
+ if (touches) {
2084
+ for (var i = 0; i < touches.length; i++) {
2085
+ var entry = touches[i];
2086
+ var x = "clientX" in entry ? Math.round(entry["clientX"] + d.scrollLeft) : null;
2087
+ var y = "clientY" in entry ? Math.round(entry["clientY"] + d.scrollTop) : null;
2088
+ x = x && frame ? x + Math.round(frame.offsetLeft) : x;
2089
+ y = y && frame ? y + Math.round(frame.offsetTop) : y;
2090
+ // Check for null values before processing this event
2091
+ if (x !== null && y !== null) {
2092
+ handler$1({ time: t, event: event, data: { target: target(evt), x: x, y: y } });
2093
+ }
2094
+ }
2277
2095
  }
2278
2096
  }
2279
- function stop$h() {
2280
- for (var _i = 0, observers_1 = observers; _i < observers_1.length; _i++) {
2281
- var observer = observers_1[_i];
2282
- if (observer) {
2283
- observer.disconnect();
2284
- }
2097
+ function handler$1(current) {
2098
+ switch (current.event) {
2099
+ case 12 /* Event.MouseMove */:
2100
+ case 15 /* Event.MouseWheel */:
2101
+ case 19 /* Event.TouchMove */:
2102
+ var length_1 = state$3.length;
2103
+ var last = length_1 > 1 ? state$3[length_1 - 2] : null;
2104
+ if (last && similar$1(last, current)) {
2105
+ state$3.pop();
2106
+ }
2107
+ state$3.push(current);
2108
+ clearTimeout(timeout$4);
2109
+ timeout$4 = setTimeout(process$4, 500 /* Setting.LookAhead */, current.event);
2110
+ break;
2111
+ default:
2112
+ state$3.push(current);
2113
+ process$4(current.event);
2114
+ break;
2285
2115
  }
2286
- observers = [];
2287
- history$4 = {};
2288
- mutations = [];
2289
- queue$2 = [];
2290
- activePeriod = 0;
2291
- timeout$1 = null;
2292
2116
  }
2293
- function active$2() {
2294
- activePeriod = time() + 3000 /* Setting.MutationActivePeriod */;
2117
+ function process$4(event) {
2118
+ schedule$1(encode$3.bind(this, event));
2295
2119
  }
2296
- function handle$1(m) {
2297
- // Queue up mutation records for asynchronous processing
2298
- var now = time();
2299
- track$6(6 /* Event.Mutation */, now);
2300
- mutations.push({ time: now, mutations: m });
2301
- schedule$1(process$2, 1 /* Priority.High */).then(function () {
2302
- setTimeout(compute$7);
2303
- measure(compute$6)();
2304
- });
2120
+ function reset$c() {
2121
+ state$3 = [];
2305
2122
  }
2306
- function process$2() {
2307
- return __awaiter(this, void 0, void 0, function () {
2308
- var timer, record, instance, _i, _a, mutation, state, target, type, value;
2309
- return __generator(this, function (_b) {
2310
- switch (_b.label) {
2311
- case 0:
2312
- timer = { id: id(), cost: 3 /* Metric.LayoutCost */ };
2313
- start$x(timer);
2314
- _b.label = 1;
2315
- case 1:
2316
- if (!(mutations.length > 0)) return [3 /*break*/, 8];
2317
- record = mutations.shift();
2318
- instance = time();
2319
- _i = 0, _a = record.mutations;
2320
- _b.label = 2;
2321
- case 2:
2322
- if (!(_i < _a.length)) return [3 /*break*/, 6];
2323
- mutation = _a[_i];
2324
- state = state$9(timer);
2325
- if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 4];
2326
- return [4 /*yield*/, suspend$1(timer)];
2327
- case 3:
2328
- state = _b.sent();
2329
- _b.label = 4;
2330
- case 4:
2331
- if (state === 2 /* Task.Stop */) {
2332
- return [3 /*break*/, 6];
2333
- }
2334
- target = mutation.target;
2335
- type = track$5(mutation, timer, instance);
2336
- if (type && target && target.ownerDocument) {
2337
- parse$1(target.ownerDocument);
2338
- }
2339
- if (type && target && target.nodeType == Node.DOCUMENT_FRAGMENT_NODE && target.host) {
2340
- parse$1(target);
2341
- }
2342
- switch (type) {
2343
- case "attributes" /* Constant.Attributes */:
2344
- processNode(target, 3 /* Source.Attributes */);
2345
- break;
2346
- case "characterData" /* Constant.CharacterData */:
2347
- processNode(target, 4 /* Source.CharacterData */);
2348
- break;
2349
- case "childList" /* Constant.ChildList */:
2350
- processNodeList(mutation.addedNodes, 1 /* Source.ChildListAdd */, timer);
2351
- processNodeList(mutation.removedNodes, 2 /* Source.ChildListRemove */, timer);
2352
- break;
2353
- case "suspend" /* Constant.Suspend */:
2354
- value = get(target);
2355
- if (value) {
2356
- value.metadata.suspend = true;
2357
- }
2358
- break;
2359
- }
2360
- _b.label = 5;
2361
- case 5:
2362
- _i++;
2363
- return [3 /*break*/, 2];
2364
- case 6: return [4 /*yield*/, encode$4(6 /* Event.Mutation */, timer, record.time)];
2365
- case 7:
2366
- _b.sent();
2367
- return [3 /*break*/, 1];
2368
- case 8:
2369
- stop$u(timer);
2370
- return [2 /*return*/];
2371
- }
2372
- });
2373
- });
2123
+ function similar$1(last, current) {
2124
+ var dx = last.data.x - current.data.x;
2125
+ var dy = last.data.y - current.data.y;
2126
+ var distance = Math.sqrt(dx * dx + dy * dy);
2127
+ var gap = current.time - last.time;
2128
+ var match = current.data.target === last.data.target;
2129
+ return current.event === last.event && match && distance < 20 /* Setting.Distance */ && gap < 25 /* Setting.Interval */;
2374
2130
  }
2375
- function track$5(m, timer, instance) {
2376
- var value = m.target ? get(m.target.parentNode) : null;
2377
- // Check if the parent is already discovered and that the parent is not the document root
2378
- if (value && value.data.tag !== "HTML" /* Constant.HTML */) {
2379
- var inactive = time() > activePeriod;
2380
- var target = get(m.target);
2381
- var element = target && target.selector ? target.selector.join() : m.target.nodeName;
2382
- var parent_1 = value.selector ? value.selector.join() : "" /* Constant.Empty */;
2383
- // We use selector, instead of id, to determine the key (signature for the mutation) because in some cases
2384
- // repeated mutations can cause elements to be destroyed and then recreated as new DOM nodes
2385
- // In those cases, IDs will change however the selector (which is relative to DOM xPath) remains the same
2386
- var key = [parent_1, element, m.attributeName, names(m.addedNodes), names(m.removedNodes)].join();
2387
- // Initialize an entry if it doesn't already exist
2388
- history$4[key] = key in history$4 ? history$4[key] : [0, instance];
2389
- var h = history$4[key];
2390
- // Lookup any pending nodes queued up for removal, and process them now if we suspended a mutation before
2391
- if (inactive === false && h[0] >= 10 /* Setting.MutationSuspendThreshold */) {
2392
- processNodeList(h[2], 2 /* Source.ChildListRemove */, timer);
2393
- }
2394
- // Update the counter
2395
- h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
2396
- h[1] = instance;
2397
- // Return updated mutation type based on if we have already hit the threshold or not
2398
- if (h[0] === 10 /* Setting.MutationSuspendThreshold */) {
2399
- // Store a reference to removedNodes so we can process them later
2400
- // when we resume mutations again on user interactions
2401
- h[2] = m.removedNodes;
2402
- return "suspend" /* Constant.Suspend */;
2403
- }
2404
- else if (h[0] > 10 /* Setting.MutationSuspendThreshold */) {
2405
- return "" /* Constant.Empty */;
2406
- }
2131
+ function stop$n() {
2132
+ clearTimeout(timeout$4);
2133
+ // Send out any pending pointer events in the pipeline
2134
+ if (state$3.length > 0) {
2135
+ process$4(state$3[state$3.length - 1].event);
2407
2136
  }
2408
- return m.type;
2137
+ }
2138
+
2139
+ var data$b;
2140
+ function start$o() {
2141
+ bind(window, "resize", recompute$5);
2142
+ recompute$5();
2409
2143
  }
2410
- function names(nodes) {
2411
- var output = [];
2412
- for (var i = 0; nodes && i < nodes.length; i++) {
2413
- output.push(nodes[i].nodeName);
2414
- }
2415
- return output.join();
2144
+ function recompute$5() {
2145
+ var de = document.documentElement;
2146
+ // window.innerWidth includes width of the scrollbar and is not a true representation of the viewport width.
2147
+ // Therefore, when possible, use documentElement's clientWidth property.
2148
+ data$b = {
2149
+ width: de && "clientWidth" in de ? Math.min(de.clientWidth, window.innerWidth) : window.innerWidth,
2150
+ height: de && "clientHeight" in de ? Math.min(de.clientHeight, window.innerHeight) : window.innerHeight,
2151
+ };
2152
+ encode$3(11 /* Event.Resize */);
2416
2153
  }
2417
- function processNodeList(list, source, timer) {
2418
- return __awaiter(this, void 0, void 0, function () {
2419
- var length, i, state;
2420
- return __generator(this, function (_a) {
2421
- switch (_a.label) {
2422
- case 0:
2423
- length = list ? list.length : 0;
2424
- i = 0;
2425
- _a.label = 1;
2426
- case 1:
2427
- if (!(i < length)) return [3 /*break*/, 6];
2428
- if (!(source === 1 /* Source.ChildListAdd */)) return [3 /*break*/, 2];
2429
- traverse(list[i], timer, source);
2430
- return [3 /*break*/, 5];
2431
- case 2:
2432
- state = state$9(timer);
2433
- if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 4];
2434
- return [4 /*yield*/, suspend$1(timer)];
2435
- case 3:
2436
- state = _a.sent();
2437
- _a.label = 4;
2438
- case 4:
2439
- if (state === 2 /* Task.Stop */) {
2440
- return [3 /*break*/, 6];
2441
- }
2442
- processNode(list[i], source);
2443
- _a.label = 5;
2444
- case 5:
2445
- i++;
2446
- return [3 /*break*/, 1];
2447
- case 6: return [2 /*return*/];
2448
- }
2449
- });
2450
- });
2154
+ function reset$b() {
2155
+ data$b = null;
2451
2156
  }
2452
- function schedule(node, fragment) {
2453
- if (fragment === void 0) { fragment = false; }
2454
- // Only schedule manual trigger for this node if it's not already in the queue
2455
- if (queue$2.indexOf(node) < 0) {
2456
- queue$2.push(node);
2157
+ function stop$m() {
2158
+ reset$b();
2159
+ }
2160
+
2161
+ var state$2 = [];
2162
+ var timeout$3 = null;
2163
+ function start$n() {
2164
+ state$2 = [];
2165
+ recompute$4();
2166
+ }
2167
+ function observe$6(root) {
2168
+ var frame = iframe(root);
2169
+ var node = frame ? frame.contentWindow : (root === document ? window : root);
2170
+ bind(node, "scroll", recompute$4, true);
2171
+ }
2172
+ function recompute$4(event) {
2173
+ if (event === void 0) { event = null; }
2174
+ var w = window;
2175
+ var de = document.documentElement;
2176
+ var element = event ? target(event) : de;
2177
+ // If the target is a Document node, then identify corresponding documentElement and window for this document
2178
+ if (element && element.nodeType === Node.DOCUMENT_NODE) {
2179
+ var frame = iframe(element);
2180
+ w = frame ? frame.contentWindow : w;
2181
+ element = de = element.documentElement;
2457
2182
  }
2458
- // Cancel any previous trigger before scheduling a new one.
2459
- // It's common for a webpage to call multiple synchronous "insertRule" / "deleteRule" calls.
2460
- // And in those cases we do not wish to monitor changes multiple times for the same node.
2461
- if (timeout$1) {
2462
- clearTimeout(timeout$1);
2183
+ // Edge doesn't support scrollTop position on document.documentElement.
2184
+ // For cross browser compatibility, looking up pageYOffset on window if the scroll is on document.
2185
+ // And, if for some reason that is not available, fall back to looking up scrollTop on document.documentElement.
2186
+ var x = element === de && "pageXOffset" in w ? Math.round(w.pageXOffset) : Math.round(element.scrollLeft);
2187
+ var y = element === de && "pageYOffset" in w ? Math.round(w.pageYOffset) : Math.round(element.scrollTop);
2188
+ var current = { time: time(event), event: 10 /* Event.Scroll */, data: { target: element, x: x, y: y } };
2189
+ // We don't send any scroll events if this is the first event and the current position is top (0,0)
2190
+ if ((event === null && x === 0 && y === 0) || (x === null || y === null)) {
2191
+ return;
2463
2192
  }
2464
- timeout$1 = setTimeout(function () { trigger$1(fragment); }, 33 /* Setting.LookAhead */);
2465
- return node;
2466
- }
2467
- function trigger$1(fragment) {
2468
- for (var _i = 0, queue_1 = queue$2; _i < queue_1.length; _i++) {
2469
- var node = queue_1[_i];
2470
- // Generate a mutation for this node only if it still exists
2471
- if (node) {
2472
- var shadowRoot = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
2473
- // Skip re-processing shadowRoot if it was already discovered
2474
- if (shadowRoot && has(node)) {
2475
- continue;
2476
- }
2477
- generate(node, shadowRoot || fragment ? "childList" /* Constant.ChildList */ : "characterData" /* Constant.CharacterData */);
2478
- }
2193
+ var length = state$2.length;
2194
+ var last = length > 1 ? state$2[length - 2] : null;
2195
+ if (last && similar(last, current)) {
2196
+ state$2.pop();
2479
2197
  }
2480
- queue$2 = [];
2198
+ state$2.push(current);
2199
+ clearTimeout(timeout$3);
2200
+ timeout$3 = setTimeout(process$3, 500 /* Setting.LookAhead */, 10 /* Event.Scroll */);
2481
2201
  }
2482
- function generate(target, type) {
2483
- measure(handle$1)([{
2484
- addedNodes: [target],
2485
- attributeName: null,
2486
- attributeNamespace: null,
2487
- nextSibling: null,
2488
- oldValue: null,
2489
- previousSibling: null,
2490
- removedNodes: [],
2491
- target: target,
2492
- type: type
2493
- }]);
2202
+ function reset$a() {
2203
+ state$2 = [];
2204
+ }
2205
+ function process$3(event) {
2206
+ schedule$1(encode$3.bind(this, event));
2207
+ }
2208
+ function similar(last, current) {
2209
+ var dx = last.data.x - current.data.x;
2210
+ var dy = last.data.y - current.data.y;
2211
+ return (dx * dx + dy * dy < 20 /* Setting.Distance */ * 20 /* Setting.Distance */) && (current.time - last.time < 25 /* Setting.Interval */);
2212
+ }
2213
+ function stop$l() {
2214
+ clearTimeout(timeout$3);
2215
+ state$2 = [];
2494
2216
  }
2495
2217
 
2496
- var index = 1;
2497
- var nodes = [];
2498
- var values = [];
2499
- var updateMap = [];
2500
- var hashMap = {};
2501
- var override = [];
2502
- var unmask = [];
2503
- var updatedFragments = {};
2504
- var maskText = [];
2505
- var maskExclude = [];
2506
- var maskDisable = [];
2507
- var maskTags = [];
2508
- // The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
2509
- var idMap = null; // Maps node => id.
2510
- var iframeMap = null; // Maps iframe's contentDocument => parent iframe element
2511
- var privacyMap = null; // Maps node => Privacy (enum)
2512
- var fraudMap = null; // Maps node => FraudId (number)
2513
- function start$i() {
2514
- reset$7();
2515
- parse$1(document, true);
2218
+ var data$a = null;
2219
+ var previous = null;
2220
+ var timeout$2 = null;
2221
+ function start$m() {
2222
+ reset$9();
2516
2223
  }
2517
- function stop$g() {
2518
- reset$7();
2224
+ function observe$5(root) {
2225
+ bind(root, "selectstart", recompute$3.bind(this, root), true);
2226
+ bind(root, "selectionchange", recompute$3.bind(this, root), true);
2227
+ }
2228
+ function recompute$3(root) {
2229
+ var doc = root.nodeType === Node.DOCUMENT_NODE ? root : document;
2230
+ var current = doc.getSelection();
2231
+ // Bail out if we don't have a valid selection
2232
+ if (current === null) {
2233
+ return;
2234
+ }
2235
+ // Bail out if we got a valid selection but not valid nodes
2236
+ // In Edge, selectionchange gets fired even on interactions like right clicks and
2237
+ // can result in null anchorNode and focusNode if there was no previous selection on page
2238
+ // Also, ignore any selections that start and end at the exact same point
2239
+ if ((current.anchorNode === null && current.focusNode === null) ||
2240
+ (current.anchorNode === current.focusNode && current.anchorOffset === current.focusOffset)) {
2241
+ return;
2242
+ }
2243
+ var startNode = data$a.start ? data$a.start : null;
2244
+ if (previous !== null && data$a.start !== null && startNode !== current.anchorNode) {
2245
+ clearTimeout(timeout$2);
2246
+ process$2(21 /* Event.Selection */);
2247
+ }
2248
+ data$a = {
2249
+ start: current.anchorNode,
2250
+ startOffset: current.anchorOffset,
2251
+ end: current.focusNode,
2252
+ endOffset: current.focusOffset
2253
+ };
2254
+ previous = current;
2255
+ clearTimeout(timeout$2);
2256
+ timeout$2 = setTimeout(process$2, 500 /* Setting.LookAhead */, 21 /* Event.Selection */);
2257
+ }
2258
+ function process$2(event) {
2259
+ schedule$1(encode$3.bind(this, event));
2260
+ }
2261
+ function reset$9() {
2262
+ previous = null;
2263
+ data$a = { start: 0, startOffset: 0, end: 0, endOffset: 0 };
2264
+ }
2265
+ function stop$k() {
2266
+ reset$9();
2267
+ clearTimeout(timeout$2);
2268
+ }
2269
+
2270
+ var state$1 = [];
2271
+ function start$l() {
2272
+ reset$8();
2273
+ }
2274
+ function observe$4(root) {
2275
+ bind(root, "submit", recompute$2, true);
2276
+ }
2277
+ function recompute$2(evt) {
2278
+ state$1.push({ time: time(evt), event: 39 /* Event.Submit */, data: { target: target(evt) } });
2279
+ schedule$1(encode$3.bind(this, 39 /* Event.Submit */));
2280
+ }
2281
+ function reset$8() {
2282
+ state$1 = [];
2283
+ }
2284
+ function stop$j() {
2285
+ reset$8();
2286
+ }
2287
+
2288
+ var data$9;
2289
+ function start$k() {
2290
+ bind(window, "pagehide", recompute$1);
2291
+ }
2292
+ function recompute$1(evt) {
2293
+ data$9 = { name: evt.type };
2294
+ encode$3(26 /* Event.Unload */, time(evt));
2295
+ stop();
2519
2296
  }
2520
2297
  function reset$7() {
2521
- index = 1;
2522
- nodes = [];
2523
- values = [];
2524
- updateMap = [];
2525
- hashMap = {};
2526
- override = [];
2527
- unmask = [];
2528
- maskText = "address,password,contact" /* Mask.Text */.split("," /* Constant.Comma */);
2529
- maskExclude = "password,secret,pass,social,ssn,code,hidden" /* Mask.Exclude */.split("," /* Constant.Comma */);
2530
- maskDisable = "radio,checkbox,range,button,reset,submit" /* Mask.Disable */.split("," /* Constant.Comma */);
2531
- maskTags = "INPUT,SELECT,TEXTAREA" /* Mask.Tags */.split("," /* Constant.Comma */);
2532
- idMap = new WeakMap();
2533
- iframeMap = new WeakMap();
2534
- privacyMap = new WeakMap();
2535
- fraudMap = new WeakMap();
2536
- reset$l();
2298
+ data$9 = null;
2537
2299
  }
2538
- // We parse new root nodes for any regions or masked nodes in the beginning (document) and
2539
- // later whenever there are new additions or modifications to DOM (mutations)
2540
- function parse$1(root, init) {
2541
- if (init === void 0) { init = false; }
2542
- // Wrap selectors in a try / catch block.
2543
- // It's possible for script to receive invalid selectors, e.g. "'#id'" with extra quotes, and cause the code below to fail
2544
- try {
2545
- // Parse unmask configuration into separate query selectors and override tokens as part of initialization
2546
- if (init) {
2547
- config$1.unmask.forEach(function (x) { return x.indexOf("!" /* Constant.Bang */) < 0 ? unmask.push(x) : override.push(x.substr(1)); });
2300
+ function stop$i() {
2301
+ reset$7();
2302
+ }
2303
+
2304
+ var data$8;
2305
+ function start$j() {
2306
+ bind(document, "visibilitychange", recompute);
2307
+ recompute();
2308
+ }
2309
+ function recompute(evt) {
2310
+ if (evt === void 0) { evt = null; }
2311
+ data$8 = { visible: "visibilityState" in document ? document.visibilityState : "default" };
2312
+ encode$3(28 /* Event.Visibility */, time(evt));
2313
+ }
2314
+ function reset$6() {
2315
+ data$8 = null;
2316
+ }
2317
+ function stop$h() {
2318
+ reset$6();
2319
+ }
2320
+
2321
+ function start$i() {
2322
+ start$g();
2323
+ start$s();
2324
+ start$r();
2325
+ start$p();
2326
+ start$q();
2327
+ start$o();
2328
+ start$j();
2329
+ start$n();
2330
+ start$m();
2331
+ start$t();
2332
+ start$l();
2333
+ start$k();
2334
+ }
2335
+ function stop$g() {
2336
+ stop$e();
2337
+ stop$q();
2338
+ stop$p();
2339
+ stop$n();
2340
+ stop$o();
2341
+ stop$m();
2342
+ stop$h();
2343
+ stop$l();
2344
+ stop$k();
2345
+ stop$r();
2346
+ stop$j();
2347
+ stop$i();
2348
+ }
2349
+ function observe$3(root) {
2350
+ observe$6(root);
2351
+ // Only monitor following interactions if the root node is a document
2352
+ // In case of shadow DOM, following events automatically bubble up to the parent document.
2353
+ if (root.nodeType === Node.DOCUMENT_NODE) {
2354
+ observe$a(root);
2355
+ observe$9(root);
2356
+ observe$7(root);
2357
+ observe$8(root);
2358
+ observe$5(root);
2359
+ observe$b(root);
2360
+ observe$4(root);
2361
+ }
2362
+ }
2363
+
2364
+ var interaction = /*#__PURE__*/Object.freeze({
2365
+ __proto__: null,
2366
+ observe: observe$3,
2367
+ start: start$i,
2368
+ stop: stop$g
2369
+ });
2370
+
2371
+ var digitsRegex = /[^0-9\.]/g;
2372
+ /* JSON+LD (Linked Data) Recursive Parser */
2373
+ function ld(json) {
2374
+ for (var _i = 0, _a = Object.keys(json); _i < _a.length; _i++) {
2375
+ var key = _a[_i];
2376
+ var value = json[key];
2377
+ if (key === "@type" /* JsonLD.Type */ && typeof value === "string") {
2378
+ value = value.toLowerCase();
2379
+ /* Normalizations */
2380
+ value = value.indexOf("article" /* JsonLD.Article */) >= 0 || value.indexOf("posting" /* JsonLD.Posting */) >= 0 ? "article" /* JsonLD.Article */ : value;
2381
+ switch (value) {
2382
+ case "article" /* JsonLD.Article */:
2383
+ case "recipe" /* JsonLD.Recipe */:
2384
+ log(5 /* Dimension.SchemaType */, json[key]);
2385
+ log(8 /* Dimension.AuthorName */, json["creator" /* JsonLD.Creator */]);
2386
+ log(18 /* Dimension.Headline */, json["headline" /* JsonLD.Headline */]);
2387
+ break;
2388
+ case "product" /* JsonLD.Product */:
2389
+ log(5 /* Dimension.SchemaType */, json[key]);
2390
+ log(10 /* Dimension.ProductName */, json["name" /* JsonLD.Name */]);
2391
+ log(12 /* Dimension.ProductSku */, json["sku" /* JsonLD.Sku */]);
2392
+ if (json["brand" /* JsonLD.Brand */]) {
2393
+ log(6 /* Dimension.ProductBrand */, json["brand" /* JsonLD.Brand */]["name" /* JsonLD.Name */]);
2394
+ }
2395
+ break;
2396
+ case "aggregaterating" /* JsonLD.AggregateRating */:
2397
+ if (json["ratingValue" /* JsonLD.RatingValue */]) {
2398
+ max(11 /* Metric.RatingValue */, num$1(json["ratingValue" /* JsonLD.RatingValue */], 100 /* Setting.RatingScale */));
2399
+ max(18 /* Metric.BestRating */, num$1(json["bestRating" /* JsonLD.BestRating */]));
2400
+ max(19 /* Metric.WorstRating */, num$1(json["worstRating" /* JsonLD.WorstRating */]));
2401
+ }
2402
+ max(12 /* Metric.RatingCount */, num$1(json["ratingCount" /* JsonLD.RatingCount */]));
2403
+ max(17 /* Metric.ReviewCount */, num$1(json["reviewCount" /* JsonLD.ReviewCount */]));
2404
+ break;
2405
+ case "person" /* JsonLD.Author */:
2406
+ log(8 /* Dimension.AuthorName */, json["name" /* JsonLD.Name */]);
2407
+ break;
2408
+ case "offer" /* JsonLD.Offer */:
2409
+ log(7 /* Dimension.ProductAvailability */, json["availability" /* JsonLD.Availability */]);
2410
+ log(14 /* Dimension.ProductCondition */, json["itemCondition" /* JsonLD.ItemCondition */]);
2411
+ log(13 /* Dimension.ProductCurrency */, json["priceCurrency" /* JsonLD.PriceCurrency */]);
2412
+ log(12 /* Dimension.ProductSku */, json["sku" /* JsonLD.Sku */]);
2413
+ max(13 /* Metric.ProductPrice */, num$1(json["price" /* JsonLD.Price */]));
2414
+ break;
2415
+ case "brand" /* JsonLD.Brand */:
2416
+ log(6 /* Dimension.ProductBrand */, json["name" /* JsonLD.Name */]);
2417
+ break;
2418
+ }
2548
2419
  }
2549
- // Since mutations may happen on leaf nodes too, e.g. text nodes, which may not support all selector APIs.
2550
- // We ensure that the root note supports querySelectorAll API before executing the code below to identify new regions.
2551
- if ("querySelectorAll" in root) {
2552
- config$1.regions.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return observe$1(e, "".concat(x[0])); }); }); // Regions
2553
- config$1.mask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 3 /* Privacy.TextImage */); }); }); // Masked Elements
2554
- config$1.checksum.forEach(function (x) { return root.querySelectorAll(x[1]).forEach(function (e) { return fraudMap.set(e, x[0]); }); }); // Fraud Checksum Check
2555
- unmask.forEach(function (x) { return root.querySelectorAll(x).forEach(function (e) { return privacyMap.set(e, 0 /* Privacy.None */); }); }); // Unmasked Elements
2420
+ // Continue parsing nested objects
2421
+ if (value !== null && typeof (value) === "object" /* Constant.Object */) {
2422
+ ld(value);
2556
2423
  }
2557
2424
  }
2558
- catch (e) {
2559
- log$1(5 /* Code.Selector */, 1 /* Severity.Warning */, e ? e.name : null);
2425
+ }
2426
+ function num$1(input, scale) {
2427
+ if (scale === void 0) { scale = 1; }
2428
+ if (input !== null) {
2429
+ switch (typeof input) {
2430
+ case "number" /* Constant.Number */: return Math.round(input * scale);
2431
+ case "string" /* Constant.String */: return Math.round(parseFloat(input.replace(digitsRegex, "" /* Constant.Empty */)) * scale);
2432
+ }
2433
+ }
2434
+ return null;
2435
+ }
2436
+
2437
+ var IGNORE_ATTRIBUTES = ["title", "alt", "onload", "onfocus", "onerror", "data-drupal-form-submit-last"];
2438
+ var newlineRegex = /[\r\n]+/g;
2439
+ function processNode (node, source) {
2440
+ var _a;
2441
+ var child = null;
2442
+ // Do not track this change if we are attempting to remove a node before discovering it
2443
+ if (source === 2 /* Source.ChildListRemove */ && has(node) === false) {
2444
+ return child;
2445
+ }
2446
+ // Special handling for text nodes that belong to style nodes
2447
+ if (source !== 0 /* Source.Discover */ &&
2448
+ node.nodeType === Node.TEXT_NODE &&
2449
+ node.parentElement &&
2450
+ node.parentElement.tagName === "STYLE") {
2451
+ node = node.parentNode;
2452
+ }
2453
+ var add = has(node) === false;
2454
+ var call = add ? "add" : "update";
2455
+ var parent = node.parentElement ? node.parentElement : null;
2456
+ var insideFrame = node.ownerDocument !== document;
2457
+ switch (node.nodeType) {
2458
+ case Node.DOCUMENT_TYPE_NODE:
2459
+ parent = insideFrame && node.parentNode ? iframe(node.parentNode) : parent;
2460
+ var docTypePrefix = insideFrame ? "iframe:" /* Constant.IFramePrefix */ : "" /* Constant.Empty */;
2461
+ var doctype = node;
2462
+ var docAttributes = { name: doctype.name, publicId: doctype.publicId, systemId: doctype.systemId };
2463
+ var docData = { tag: docTypePrefix + "*D" /* Constant.DocumentTag */, attributes: docAttributes };
2464
+ dom[call](node, parent, docData, source);
2465
+ break;
2466
+ case Node.DOCUMENT_NODE:
2467
+ // We check for regions in the beginning when discovering document and
2468
+ // later whenever there are new additions or modifications to DOM (mutations)
2469
+ if (node === document)
2470
+ parse$1(document);
2471
+ observe$2(node);
2472
+ break;
2473
+ case Node.DOCUMENT_FRAGMENT_NODE:
2474
+ var shadowRoot = node;
2475
+ if (shadowRoot.host) {
2476
+ parse$1(shadowRoot);
2477
+ var type = typeof (shadowRoot.constructor);
2478
+ if (type === "function" /* Constant.Function */ && shadowRoot.constructor.toString().indexOf("[native code]" /* Constant.NativeCode */) >= 0) {
2479
+ observe$2(shadowRoot);
2480
+ // See: https://wicg.github.io/construct-stylesheets/ for more details on adoptedStyleSheets.
2481
+ // At the moment, we are only able to capture "open" shadow DOM nodes. If they are closed, they are not accessible.
2482
+ // In future we may decide to proxy "attachShadow" call to gain access, but at the moment, we don't want to
2483
+ // cause any unintended side effect to the page. We will re-evaluate after we gather more real world data on this.
2484
+ var style = "" /* Constant.Empty */;
2485
+ var adoptedStyleSheets = "adoptedStyleSheets" in shadowRoot ? shadowRoot["adoptedStyleSheets"] : [];
2486
+ for (var _i = 0, adoptedStyleSheets_1 = adoptedStyleSheets; _i < adoptedStyleSheets_1.length; _i++) {
2487
+ var styleSheet = adoptedStyleSheets_1[_i];
2488
+ style += getCssRules(styleSheet);
2489
+ }
2490
+ var fragementData = { tag: "*S" /* Constant.ShadowDomTag */, attributes: { style: style } };
2491
+ dom[call](node, shadowRoot.host, fragementData, source);
2492
+ }
2493
+ else {
2494
+ // If the browser doesn't support shadow DOM natively, we detect that, and send appropriate tag back.
2495
+ // The differentiation is important because we don't have to observe pollyfill shadow DOM nodes,
2496
+ // the same way we observe real shadow DOM nodes (encapsulation provided by the browser).
2497
+ dom[call](node, shadowRoot.host, { tag: "*P" /* Constant.PolyfillShadowDomTag */, attributes: {} }, source);
2498
+ }
2499
+ }
2500
+ break;
2501
+ case Node.TEXT_NODE:
2502
+ // In IE11 TEXT_NODE doesn't expose a valid parentElement property. Instead we need to lookup parentNode property.
2503
+ parent = parent ? parent : node.parentNode;
2504
+ // Account for this text node only if we are tracking the parent node
2505
+ // We do not wish to track text nodes for ignored parent nodes, like script tags
2506
+ // Also, we do not track text nodes for STYLE tags
2507
+ // The only exception is when we receive a mutation to remove the text node, in that case
2508
+ // parent will be null, but we can still process the node by checking it's an update call.
2509
+ if (call === "update" || (parent && has(parent) && parent.tagName !== "STYLE")) {
2510
+ var textData = { tag: "*T" /* Constant.TextTag */, value: node.nodeValue };
2511
+ dom[call](node, parent, textData, source);
2512
+ }
2513
+ break;
2514
+ case Node.ELEMENT_NODE:
2515
+ var element = node;
2516
+ var tag = element.tagName;
2517
+ var attributes = getAttributes(element);
2518
+ // In some cases, external libraries like vue-fragment, can modify parentNode property to not be in sync with the DOM
2519
+ // For correctness, we first look at parentElement and if it not present then fall back to using parentNode
2520
+ parent = node.parentElement ? node.parentElement : (node.parentNode ? node.parentNode : null);
2521
+ // If we encounter a node that is part of SVG namespace, prefix the tag with SVG_PREFIX
2522
+ if (element.namespaceURI === "http://www.w3.org/2000/svg" /* Constant.SvgNamespace */) {
2523
+ tag = "svg:" /* Constant.SvgPrefix */ + tag;
2524
+ }
2525
+ switch (tag) {
2526
+ case "HTML":
2527
+ parent = insideFrame && parent ? iframe(parent) : null;
2528
+ var htmlPrefix = insideFrame ? "iframe:" /* Constant.IFramePrefix */ : "" /* Constant.Empty */;
2529
+ var htmlData = { tag: htmlPrefix + tag, attributes: attributes };
2530
+ dom[call](node, parent, htmlData, source);
2531
+ break;
2532
+ case "SCRIPT":
2533
+ if ("type" /* Constant.Type */ in attributes && attributes["type" /* Constant.Type */] === "application/ld+json" /* Constant.JsonLD */) {
2534
+ try {
2535
+ ld(JSON.parse(element.text.replace(newlineRegex, "" /* Constant.Empty */)));
2536
+ }
2537
+ catch ( /* do nothing */_b) { /* do nothing */ }
2538
+ }
2539
+ break;
2540
+ case "NOSCRIPT":
2541
+ break;
2542
+ case "META":
2543
+ var key = ("property" /* Constant.Property */ in attributes ?
2544
+ "property" /* Constant.Property */ :
2545
+ ("name" /* Constant.Name */ in attributes ? "name" /* Constant.Name */ : null));
2546
+ if (key && "content" /* Constant.Content */ in attributes) {
2547
+ var content = attributes["content" /* Constant.Content */];
2548
+ switch (attributes[key]) {
2549
+ case "og:title" /* Constant.ogTitle */:
2550
+ log(20 /* Dimension.MetaTitle */, content);
2551
+ break;
2552
+ case "og:type" /* Constant.ogType */:
2553
+ log(19 /* Dimension.MetaType */, content);
2554
+ break;
2555
+ case "generator" /* Constant.Generator */:
2556
+ log(21 /* Dimension.Generator */, content);
2557
+ break;
2558
+ }
2559
+ }
2560
+ break;
2561
+ case "HEAD":
2562
+ var head = { tag: tag, attributes: attributes };
2563
+ var l = insideFrame && ((_a = node.ownerDocument) === null || _a === void 0 ? void 0 : _a.location) ? node.ownerDocument.location : location;
2564
+ head.attributes["*B" /* Constant.Base */] = l.protocol + "//" + l.hostname + l.pathname;
2565
+ dom[call](node, parent, head, source);
2566
+ break;
2567
+ case "BASE":
2568
+ // Override the auto detected base path to explicit value specified in this tag
2569
+ var baseHead = get(node.parentElement);
2570
+ if (baseHead) {
2571
+ // We create "a" element so we can generate protocol and hostname for relative paths like "/path/"
2572
+ var a = document.createElement("a");
2573
+ a.href = attributes["href"];
2574
+ baseHead.data.attributes["*B" /* Constant.Base */] = a.protocol + "//" + a.hostname + a.pathname;
2575
+ }
2576
+ break;
2577
+ case "STYLE":
2578
+ var styleData = { tag: tag, attributes: attributes, value: getStyleValue(element) };
2579
+ dom[call](node, parent, styleData, source);
2580
+ break;
2581
+ case "IFRAME":
2582
+ var iframe$1 = node;
2583
+ var frameData = { tag: tag, attributes: attributes };
2584
+ if (sameorigin(iframe$1)) {
2585
+ monitor(iframe$1);
2586
+ frameData.attributes["*O" /* Constant.SameOrigin */] = "true";
2587
+ if (iframe$1.contentDocument && iframe$1.contentWindow && iframe$1.contentDocument.readyState !== "loading") {
2588
+ child = iframe$1.contentDocument;
2589
+ }
2590
+ }
2591
+ dom[call](node, parent, frameData, source);
2592
+ break;
2593
+ default:
2594
+ var data = { tag: tag, attributes: attributes };
2595
+ if (element.shadowRoot) {
2596
+ child = element.shadowRoot;
2597
+ }
2598
+ dom[call](node, parent, data, source);
2599
+ break;
2600
+ }
2601
+ break;
2560
2602
  }
2603
+ return child;
2561
2604
  }
2562
- function getId(node, autogen) {
2563
- if (autogen === void 0) { autogen = false; }
2564
- if (node === null) {
2565
- return null;
2566
- }
2567
- var id = idMap.get(node);
2568
- if (!id && autogen) {
2569
- id = index++;
2570
- idMap.set(node, id);
2605
+ function observe$2(root) {
2606
+ if (has(root)) {
2607
+ return;
2571
2608
  }
2572
- return id ? id : null;
2609
+ observe$1(root); // Observe mutations for this root node
2610
+ observe$3(root); // Observe interactions for this root node
2573
2611
  }
2574
- function add(node, parent, data, source) {
2575
- var id = getId(node, true);
2576
- var parentId = parent ? getId(parent) : null;
2577
- var previousId = getPreviousId(node);
2578
- var parentValue = null;
2579
- var regionId = exists(node) ? id : null;
2580
- var fragmentId = null;
2581
- var fraudId = fraudMap.has(node) ? fraudMap.get(node) : null;
2582
- var privacyId = config$1.content ? 1 /* Privacy.Sensitive */ : 3 /* Privacy.TextImage */;
2583
- if (parentId >= 0 && values[parentId]) {
2584
- parentValue = values[parentId];
2585
- parentValue.children.push(id);
2586
- regionId = regionId === null ? parentValue.region : regionId;
2587
- fragmentId = parentValue.fragment;
2588
- fraudId = fraudId === null ? parentValue.metadata.fraud : fraudId;
2589
- privacyId = parentValue.metadata.privacy;
2590
- }
2591
- // If there's an explicit region attribute set on the element, use it to mark a region on the page
2592
- if (data.attributes && "data-clarity-region" /* Constant.RegionData */ in data.attributes) {
2593
- observe$1(node, data.attributes["data-clarity-region" /* Constant.RegionData */]);
2594
- regionId = id;
2612
+ function getStyleValue(style) {
2613
+ // Call trim on the text content to ensure we do not process white spaces ( , \n, \r\n, \t, etc.)
2614
+ // Also, check if stylesheet has any data-* attribute, if so process rules instead of looking up text
2615
+ var value = style.textContent ? style.textContent.trim() : "" /* Constant.Empty */;
2616
+ var dataset = style.dataset ? Object.keys(style.dataset).length : 0;
2617
+ if (value.length === 0 || dataset > 0) {
2618
+ value = getCssRules(style.sheet);
2595
2619
  }
2596
- nodes[id] = node;
2597
- values[id] = {
2598
- id: id,
2599
- parent: parentId,
2600
- previous: previousId,
2601
- children: [],
2602
- data: data,
2603
- selector: null,
2604
- hash: null,
2605
- region: regionId,
2606
- metadata: { active: true, suspend: false, privacy: privacyId, position: null, fraud: fraudId, size: null },
2607
- fragment: fragmentId,
2608
- };
2609
- privacy(node, values[id], parentValue);
2610
- updateSelector(values[id]);
2611
- size(values[id]);
2612
- track$4(id, source, values[id].fragment);
2620
+ return value;
2613
2621
  }
2614
- function update$1(node, parent, data, source) {
2615
- var id = getId(node);
2616
- var parentId = parent ? getId(parent) : null;
2617
- var previousId = getPreviousId(node);
2618
- var changed = false;
2619
- var parentChanged = false;
2620
- if (id in values) {
2621
- var value = values[id];
2622
- value.metadata.active = true;
2623
- // Handle case where internal ordering may have changed
2624
- if (value.previous !== previousId) {
2625
- changed = true;
2626
- value.previous = previousId;
2627
- }
2628
- // Handle case where parent might have been updated
2629
- if (value.parent !== parentId) {
2630
- changed = true;
2631
- var oldParentId = value.parent;
2632
- value.parent = parentId;
2633
- // Move this node to the right location under new parent
2634
- if (parentId !== null && parentId >= 0) {
2635
- var childIndex = previousId === null ? 0 : values[parentId].children.indexOf(previousId) + 1;
2636
- values[parentId].children.splice(childIndex, 0, id);
2637
- // Update region after the move
2638
- value.region = exists(node) ? id : values[parentId].region;
2639
- }
2640
- else {
2641
- // Mark this element as deleted if the parent has been updated to null
2642
- remove(id, source);
2643
- }
2644
- // Remove reference to this node from the old parent
2645
- if (oldParentId !== null && oldParentId >= 0) {
2646
- var nodeIndex = values[oldParentId].children.indexOf(id);
2647
- if (nodeIndex >= 0) {
2648
- values[oldParentId].children.splice(nodeIndex, 1);
2649
- }
2650
- }
2651
- parentChanged = true;
2652
- }
2653
- // Update data
2654
- for (var key in data) {
2655
- if (diff(value["data"], data, key)) {
2656
- changed = true;
2657
- value["data"][key] = data[key];
2658
- }
2622
+ function getCssRules(sheet) {
2623
+ var value = "" /* Constant.Empty */;
2624
+ var cssRules = null;
2625
+ // Firefox throws a SecurityError when trying to access cssRules of a stylesheet from a different domain
2626
+ try {
2627
+ cssRules = sheet ? sheet.cssRules : [];
2628
+ }
2629
+ catch (e) {
2630
+ log$1(1 /* Code.CssRules */, 1 /* Severity.Warning */, e ? e.name : null);
2631
+ if (e && e.name !== "SecurityError") {
2632
+ throw e;
2659
2633
  }
2660
- // track node if it is a part of scheduled fragment mutation
2661
- if (value.fragment && updatedFragments[value.fragment]) {
2662
- changed = true;
2634
+ }
2635
+ if (cssRules !== null) {
2636
+ for (var i = 0; i < cssRules.length; i++) {
2637
+ value += cssRules[i].cssText;
2663
2638
  }
2664
- // Update selector
2665
- updateSelector(value);
2666
- track$4(id, source, values[id].fragment, changed, parentChanged);
2667
2639
  }
2640
+ return value;
2668
2641
  }
2669
- function sameorigin(node) {
2670
- var output = false;
2671
- if (node.nodeType === Node.ELEMENT_NODE && node.tagName === "IFRAME" /* Constant.IFrameTag */) {
2672
- var frame = node;
2673
- // To determine if the iframe is same-origin or not, we try accessing it's contentDocument.
2674
- // If the browser throws an exception, we assume it's cross-origin and move on.
2675
- // However, if we do a get a valid document object back, we assume the contents are accessible and iframe is same-origin.
2676
- try {
2677
- var doc = frame.contentDocument;
2678
- if (doc) {
2679
- iframeMap.set(frame.contentDocument, frame);
2680
- output = true;
2642
+ function getAttributes(element) {
2643
+ var output = {};
2644
+ var attributes = element.attributes;
2645
+ if (attributes && attributes.length > 0) {
2646
+ for (var i = 0; i < attributes.length; i++) {
2647
+ var name_1 = attributes[i].name;
2648
+ if (IGNORE_ATTRIBUTES.indexOf(name_1) < 0) {
2649
+ output[name_1] = attributes[i].value;
2681
2650
  }
2682
2651
  }
2683
- catch ( /* do nothing */_a) { /* do nothing */ }
2684
2652
  }
2685
- return output;
2686
- }
2687
- function iframe(node) {
2688
- var doc = node.nodeType === Node.DOCUMENT_NODE ? node : null;
2689
- return doc && iframeMap.has(doc) ? iframeMap.get(doc) : null;
2690
- }
2691
- function privacy(node, value, parent) {
2692
- var data = value.data;
2693
- var metadata = value.metadata;
2694
- var current = metadata.privacy;
2695
- var attributes = data.attributes || {};
2696
- var tag = data.tag.toUpperCase();
2697
- switch (true) {
2698
- case maskTags.indexOf(tag) >= 0:
2699
- var type = attributes["type" /* Constant.Type */];
2700
- var meta_1 = "" /* Constant.Empty */;
2701
- Object.keys(attributes).forEach(function (x) { return meta_1 += attributes[x].toLowerCase(); });
2702
- var exclude = maskExclude.some(function (x) { return meta_1.indexOf(x) >= 0; });
2703
- // Regardless of privacy mode, always mask off user input from input boxes or drop downs with two exceptions:
2704
- // (1) The node is detected to be one of the excluded fields, in which case we drop everything
2705
- // (2) The node's type is one of the allowed types (like checkboxes)
2706
- metadata.privacy = tag === "INPUT" /* Constant.InputTag */ && maskDisable.indexOf(type) >= 0 ? current : (exclude ? 4 /* Privacy.Exclude */ : 2 /* Privacy.Text */);
2707
- break;
2708
- case "data-clarity-mask" /* Constant.MaskData */ in attributes:
2709
- metadata.privacy = 3 /* Privacy.TextImage */;
2710
- break;
2711
- case "data-clarity-unmask" /* Constant.UnmaskData */ in attributes:
2712
- metadata.privacy = 0 /* Privacy.None */;
2713
- break;
2714
- case privacyMap.has(node):
2715
- // If this node was explicitly configured to contain sensitive content, honor that privacy setting
2716
- metadata.privacy = privacyMap.get(node);
2717
- break;
2718
- case fraudMap.has(node):
2719
- // If this node was explicitly configured to be evaluated for fraud, then also mask content
2720
- metadata.privacy = 2 /* Privacy.Text */;
2721
- break;
2722
- case tag === "*T" /* Constant.TextTag */:
2723
- // If it's a text node belonging to a STYLE or TITLE tag or one of scrub exceptions, then capture content
2724
- var pTag = parent && parent.data ? parent.data.tag : "" /* Constant.Empty */;
2725
- var pSelector_1 = parent && parent.selector ? parent.selector[1 /* Selector.Default */] : "" /* Constant.Empty */;
2726
- var tags = ["STYLE" /* Constant.StyleTag */, "TITLE" /* Constant.TitleTag */, "svg:style" /* Constant.SvgStyle */];
2727
- metadata.privacy = tags.includes(pTag) || override.some(function (x) { return pSelector_1.indexOf(x) >= 0; }) ? 0 /* Privacy.None */ : current;
2728
- break;
2729
- case current === 1 /* Privacy.Sensitive */:
2730
- // In a mode where we mask sensitive information by default, look through class names to aggressively mask content
2731
- metadata.privacy = inspect(attributes["class" /* Constant.Class */], maskText, metadata);
2732
- break;
2653
+ // For INPUT tags read the dynamic "value" property if an explicit "value" attribute is not set
2654
+ if (element.tagName === "INPUT" /* Constant.InputTag */ && !("value" /* Constant.Value */ in output) && element.value) {
2655
+ output["value" /* Constant.Value */] = element.value;
2733
2656
  }
2734
- }
2735
- function inspect(input, lookup, metadata) {
2736
- if (input && lookup.some(function (x) { return input.indexOf(x) >= 0; })) {
2737
- return 2 /* Privacy.Text */;
2657
+ return output;
2658
+ }
2659
+
2660
+ function traverse (root, timer, source) {
2661
+ return __awaiter(this, void 0, void 0, function () {
2662
+ var queue, node, next, state, subnode;
2663
+ return __generator(this, function (_a) {
2664
+ switch (_a.label) {
2665
+ case 0:
2666
+ queue = [root];
2667
+ _a.label = 1;
2668
+ case 1:
2669
+ if (!(queue.length > 0)) return [3 /*break*/, 4];
2670
+ node = queue.shift();
2671
+ next = node.firstChild;
2672
+ while (next) {
2673
+ queue.push(next);
2674
+ next = next.nextSibling;
2675
+ }
2676
+ state = state$9(timer);
2677
+ if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 3];
2678
+ return [4 /*yield*/, suspend$1(timer)];
2679
+ case 2:
2680
+ state = _a.sent();
2681
+ _a.label = 3;
2682
+ case 3:
2683
+ if (state === 2 /* Task.Stop */) {
2684
+ return [3 /*break*/, 4];
2685
+ }
2686
+ subnode = processNode(node, source);
2687
+ if (subnode) {
2688
+ queue.push(subnode);
2689
+ }
2690
+ return [3 /*break*/, 1];
2691
+ case 4: return [2 /*return*/];
2692
+ }
2693
+ });
2694
+ });
2695
+ }
2696
+
2697
+ var observers = [];
2698
+ var mutations = [];
2699
+ var insertRule = null;
2700
+ var deleteRule = null;
2701
+ var attachShadow = null;
2702
+ var queue$1 = [];
2703
+ var timeout$1 = null;
2704
+ var activePeriod = null;
2705
+ var history$4 = {};
2706
+ function start$h() {
2707
+ observers = [];
2708
+ queue$1 = [];
2709
+ timeout$1 = null;
2710
+ activePeriod = 0;
2711
+ history$4 = {};
2712
+ // Some popular open source libraries, like styled-components, optimize performance
2713
+ // by injecting CSS using insertRule API vs. appending text node. A side effect of
2714
+ // using javascript API is that it doesn't trigger DOM mutation and therefore we
2715
+ // need to override the insertRule API and listen for changes manually.
2716
+ if (insertRule === null) {
2717
+ insertRule = CSSStyleSheet.prototype.insertRule;
2718
+ CSSStyleSheet.prototype.insertRule = function () {
2719
+ if (active()) {
2720
+ schedule(this.ownerNode);
2721
+ }
2722
+ return insertRule.apply(this, arguments);
2723
+ };
2738
2724
  }
2739
- return metadata.privacy;
2740
- }
2741
- function diff(a, b, field) {
2742
- if (typeof a[field] === "object" && typeof b[field] === "object") {
2743
- for (var key in a[field]) {
2744
- if (a[field][key] !== b[field][key]) {
2745
- return true;
2725
+ if (deleteRule === null) {
2726
+ deleteRule = CSSStyleSheet.prototype.deleteRule;
2727
+ CSSStyleSheet.prototype.deleteRule = function () {
2728
+ if (active()) {
2729
+ schedule(this.ownerNode);
2746
2730
  }
2731
+ return deleteRule.apply(this, arguments);
2732
+ };
2733
+ }
2734
+ // Add a hook to attachShadow API calls
2735
+ // In case we are unable to add a hook and browser throws an exception,
2736
+ // reset attachShadow variable and resume processing like before
2737
+ if (attachShadow === null) {
2738
+ attachShadow = Element.prototype.attachShadow;
2739
+ try {
2740
+ Element.prototype.attachShadow = function () {
2741
+ if (active()) {
2742
+ return schedule(attachShadow.apply(this, arguments));
2743
+ }
2744
+ else {
2745
+ return attachShadow.apply(this, arguments);
2746
+ }
2747
+ };
2747
2748
  }
2748
- for (var key in b[field]) {
2749
- if (b[field][key] !== a[field][key]) {
2750
- return true;
2751
- }
2749
+ catch (_a) {
2750
+ attachShadow = null;
2752
2751
  }
2753
- return false;
2754
2752
  }
2755
- return a[field] !== b[field];
2756
2753
  }
2757
- function position(parent, child) {
2758
- child.metadata.position = 1;
2759
- var idx = parent ? parent.children.indexOf(child.id) : -1;
2760
- while (idx-- > 0) {
2761
- var sibling = values[parent.children[idx]];
2762
- if (child.data.tag === sibling.data.tag) {
2763
- child.metadata.position = sibling.metadata.position + 1;
2764
- break;
2754
+ function observe$1(node) {
2755
+ // Create a new observer for every time a new DOM tree (e.g. root document or shadowdom root) is discovered on the page
2756
+ // In the case of shadow dom, any mutations that happen within the shadow dom are not bubbled up to the host document
2757
+ // For this reason, we need to wire up mutations every time we see a new shadow dom.
2758
+ // Also, wrap it inside a try / catch. In certain browsers (e.g. legacy Edge), observer on shadow dom can throw errors
2759
+ try {
2760
+ var m = api("MutationObserver" /* Constant.MutationObserver */);
2761
+ var observer = m in window ? new window[m](measure(handle$1)) : null;
2762
+ if (observer) {
2763
+ observer.observe(node, { attributes: true, childList: true, characterData: true, subtree: true });
2764
+ observers.push(observer);
2765
2765
  }
2766
2766
  }
2767
- return child.metadata.position;
2768
- }
2769
- function updateSelector(value) {
2770
- var parent = value.parent && value.parent in values ? values[value.parent] : null;
2771
- var prefix = parent ? parent.selector : null;
2772
- var d = value.data;
2773
- var p = position(parent, value);
2774
- var s = { id: value.id, tag: d.tag, prefix: prefix, position: p, attributes: d.attributes };
2775
- value.selector = [get$1(s, 0 /* Selector.Alpha */), get$1(s, 1 /* Selector.Beta */)];
2776
- value.hash = value.selector.map(function (x) { return x ? hash(x) : null; });
2777
- value.hash.forEach(function (h) { return hashMap[h] = value.id; });
2778
- // Match fragment configuration against both alpha and beta hash
2779
- if (value.hash.some(function (h) { return fragments.indexOf(h) !== -1; })) {
2780
- value.fragment = value.id;
2781
- }
2782
- }
2783
- function hashText(hash) {
2784
- var id = lookup(hash);
2785
- var node = getNode(id);
2786
- return node !== null && node.textContent !== null ? node.textContent.substr(0, 25 /* Setting.ClickText */) : '';
2787
- }
2788
- function getNode(id) {
2789
- if (id in nodes) {
2790
- return nodes[id];
2767
+ catch (e) {
2768
+ log$1(2 /* Code.MutationObserver */, 0 /* Severity.Info */, e ? e.name : null);
2791
2769
  }
2792
- return null;
2793
2770
  }
2794
- function getValue(id) {
2795
- if (id in values) {
2796
- return values[id];
2771
+ function monitor(frame) {
2772
+ // Bind to iframe's onload event so we get notified anytime there's an update to iframe content.
2773
+ // This includes cases where iframe location is updated without explicitly updating src attribute
2774
+ // E.g. iframe.contentWindow.location.href = "new-location";
2775
+ if (has(frame) === false) {
2776
+ bind(frame, "load" /* Constant.LoadEvent */, generate.bind(this, frame, "childList" /* Constant.ChildList */), true);
2797
2777
  }
2798
- return null;
2799
- }
2800
- function get(node) {
2801
- var id = getId(node);
2802
- return id in values ? values[id] : null;
2803
- }
2804
- function lookup(hash) {
2805
- return hash in hashMap ? hashMap[hash] : null;
2806
- }
2807
- function has(node) {
2808
- return getId(node) in nodes;
2809
2778
  }
2810
- function updates$2() {
2811
- var output = [];
2812
- for (var _i = 0, updateMap_1 = updateMap; _i < updateMap_1.length; _i++) {
2813
- var id = updateMap_1[_i];
2814
- if (id in values) {
2815
- output.push(values[id]);
2779
+ function stop$f() {
2780
+ for (var _i = 0, observers_1 = observers; _i < observers_1.length; _i++) {
2781
+ var observer = observers_1[_i];
2782
+ if (observer) {
2783
+ observer.disconnect();
2816
2784
  }
2817
2785
  }
2818
- updateMap = [];
2819
- for (var id in updatedFragments) {
2820
- update(updatedFragments[id], id, true);
2821
- }
2822
- updatedFragments = {};
2823
- return output;
2824
- }
2825
- function remove(id, source) {
2826
- if (id in values) {
2827
- var value = values[id];
2828
- value.metadata.active = false;
2829
- value.parent = null;
2830
- track$4(id, source);
2831
- }
2832
- }
2833
- function size(value) {
2834
- // If this element is a image node, and is masked, then track box model for the current element
2835
- if (value.data.tag === "IMG" /* Constant.ImageTag */ && value.metadata.privacy === 3 /* Privacy.TextImage */) {
2836
- value.metadata.size = [];
2837
- }
2786
+ observers = [];
2787
+ history$4 = {};
2788
+ mutations = [];
2789
+ queue$1 = [];
2790
+ activePeriod = 0;
2791
+ timeout$1 = null;
2838
2792
  }
2839
- function getPreviousId(node) {
2840
- var id = null;
2841
- // Some nodes may not have an ID by design since Clarity skips over tags like SCRIPT, NOSCRIPT, META, COMMENTS, etc..
2842
- // 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.
2843
- while (id === null && node.previousSibling) {
2844
- id = getId(node.previousSibling);
2845
- node = node.previousSibling;
2846
- }
2847
- return id;
2793
+ function active$2() {
2794
+ activePeriod = time() + 3000 /* Setting.MutationActivePeriod */;
2848
2795
  }
2849
- function track$4(id, source, fragment, changed, parentChanged) {
2850
- if (fragment === void 0) { fragment = null; }
2851
- if (changed === void 0) { changed = true; }
2852
- if (parentChanged === void 0) { parentChanged = false; }
2853
- // if updated node is a part of fragment and the fragment is not being tracked currently, schedule a mutation on the fragment node
2854
- if (fragment && !updatedFragments[fragment]) {
2855
- var node = getNode(fragment);
2856
- var value = getValue(fragment);
2857
- if (node && value) {
2858
- schedule(node, true);
2859
- value.hash.forEach(function (h) {
2860
- if (fragments.indexOf(h) !== -1) {
2861
- updatedFragments[fragment] = h;
2862
- }
2863
- });
2864
- }
2865
- }
2866
- // Keep track of the order in which mutations happened, they may not be sequential
2867
- // Edge case: If an element is added later on, and pre-discovered element is moved as a child.
2868
- // In that case, we need to reorder the pre-discovered element in the update list to keep visualization consistent.
2869
- var uIndex = updateMap.indexOf(id);
2870
- if (uIndex >= 0 && source === 1 /* Source.ChildListAdd */ && parentChanged) {
2871
- updateMap.splice(uIndex, 1);
2872
- updateMap.push(id);
2873
- }
2874
- else if (uIndex === -1 && changed) {
2875
- updateMap.push(id);
2876
- }
2877
- }
2878
-
2879
- var state$1 = [];
2880
- var regionMap = null; // Maps region nodes => region name
2881
- var regions = {};
2882
- var queue$1 = [];
2883
- var watch = false;
2884
- var observer$1 = null;
2885
- function start$h() {
2886
- reset$6();
2887
- observer$1 = null;
2888
- regionMap = new WeakMap();
2889
- regions = {};
2890
- queue$1 = [];
2891
- watch = window["IntersectionObserver"] ? true : false;
2796
+ function handle$1(m) {
2797
+ // Queue up mutation records for asynchronous processing
2798
+ var now = time();
2799
+ track$6(6 /* Event.Mutation */, now);
2800
+ mutations.push({ time: now, mutations: m });
2801
+ schedule$1(process$1, 1 /* Priority.High */).then(function () {
2802
+ setTimeout(compute$7);
2803
+ measure(compute$6)();
2804
+ });
2892
2805
  }
2893
- function observe$1(node, name) {
2894
- if (regionMap.has(node) === false) {
2895
- regionMap.set(node, name);
2896
- observer$1 = observer$1 === null && watch ? new IntersectionObserver(handler$1, {
2897
- // Get notified as intersection continues to change
2898
- // This allows us to process regions that get partially hidden during the lifetime of the page
2899
- // See: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#creating_an_intersection_observer
2900
- // By default, intersection observers only fire an event when even a single pixel is visible and not thereafter.
2901
- threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
2902
- }) : observer$1;
2903
- if (observer$1 && node && node.nodeType === Node.ELEMENT_NODE) {
2904
- observer$1.observe(node);
2806
+ function process$1() {
2807
+ return __awaiter(this, void 0, void 0, function () {
2808
+ var timer, record, instance, _i, _a, mutation, state, target, type, value;
2809
+ return __generator(this, function (_b) {
2810
+ switch (_b.label) {
2811
+ case 0:
2812
+ timer = { id: id(), cost: 3 /* Metric.LayoutCost */ };
2813
+ start$w(timer);
2814
+ _b.label = 1;
2815
+ case 1:
2816
+ if (!(mutations.length > 0)) return [3 /*break*/, 8];
2817
+ record = mutations.shift();
2818
+ instance = time();
2819
+ _i = 0, _a = record.mutations;
2820
+ _b.label = 2;
2821
+ case 2:
2822
+ if (!(_i < _a.length)) return [3 /*break*/, 6];
2823
+ mutation = _a[_i];
2824
+ state = state$9(timer);
2825
+ if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 4];
2826
+ return [4 /*yield*/, suspend$1(timer)];
2827
+ case 3:
2828
+ state = _b.sent();
2829
+ _b.label = 4;
2830
+ case 4:
2831
+ if (state === 2 /* Task.Stop */) {
2832
+ return [3 /*break*/, 6];
2833
+ }
2834
+ target = mutation.target;
2835
+ type = track$3(mutation, timer, instance);
2836
+ if (type && target && target.ownerDocument) {
2837
+ parse$1(target.ownerDocument);
2838
+ }
2839
+ if (type && target && target.nodeType == Node.DOCUMENT_FRAGMENT_NODE && target.host) {
2840
+ parse$1(target);
2841
+ }
2842
+ switch (type) {
2843
+ case "attributes" /* Constant.Attributes */:
2844
+ processNode(target, 3 /* Source.Attributes */);
2845
+ break;
2846
+ case "characterData" /* Constant.CharacterData */:
2847
+ processNode(target, 4 /* Source.CharacterData */);
2848
+ break;
2849
+ case "childList" /* Constant.ChildList */:
2850
+ processNodeList(mutation.addedNodes, 1 /* Source.ChildListAdd */, timer);
2851
+ processNodeList(mutation.removedNodes, 2 /* Source.ChildListRemove */, timer);
2852
+ break;
2853
+ case "suspend" /* Constant.Suspend */:
2854
+ value = get(target);
2855
+ if (value) {
2856
+ value.metadata.suspend = true;
2857
+ }
2858
+ break;
2859
+ }
2860
+ _b.label = 5;
2861
+ case 5:
2862
+ _i++;
2863
+ return [3 /*break*/, 2];
2864
+ case 6: return [4 /*yield*/, encode$4(6 /* Event.Mutation */, timer, record.time)];
2865
+ case 7:
2866
+ _b.sent();
2867
+ return [3 /*break*/, 1];
2868
+ case 8:
2869
+ stop$t(timer);
2870
+ return [2 /*return*/];
2871
+ }
2872
+ });
2873
+ });
2874
+ }
2875
+ function track$3(m, timer, instance) {
2876
+ var value = m.target ? get(m.target.parentNode) : null;
2877
+ // Check if the parent is already discovered and that the parent is not the document root
2878
+ if (value && value.data.tag !== "HTML" /* Constant.HTML */) {
2879
+ var inactive = time() > activePeriod;
2880
+ var target = get(m.target);
2881
+ var element = target && target.selector ? target.selector.join() : m.target.nodeName;
2882
+ var parent_1 = value.selector ? value.selector.join() : "" /* Constant.Empty */;
2883
+ // We use selector, instead of id, to determine the key (signature for the mutation) because in some cases
2884
+ // repeated mutations can cause elements to be destroyed and then recreated as new DOM nodes
2885
+ // In those cases, IDs will change however the selector (which is relative to DOM xPath) remains the same
2886
+ var key = [parent_1, element, m.attributeName, names(m.addedNodes), names(m.removedNodes)].join();
2887
+ // Initialize an entry if it doesn't already exist
2888
+ history$4[key] = key in history$4 ? history$4[key] : [0, instance];
2889
+ var h = history$4[key];
2890
+ // Lookup any pending nodes queued up for removal, and process them now if we suspended a mutation before
2891
+ if (inactive === false && h[0] >= 10 /* Setting.MutationSuspendThreshold */) {
2892
+ processNodeList(h[2], 2 /* Source.ChildListRemove */, timer);
2893
+ }
2894
+ // Update the counter
2895
+ h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
2896
+ h[1] = instance;
2897
+ // Return updated mutation type based on if we have already hit the threshold or not
2898
+ if (h[0] === 10 /* Setting.MutationSuspendThreshold */) {
2899
+ // Store a reference to removedNodes so we can process them later
2900
+ // when we resume mutations again on user interactions
2901
+ h[2] = m.removedNodes;
2902
+ return "suspend" /* Constant.Suspend */;
2903
+ }
2904
+ else if (h[0] > 10 /* Setting.MutationSuspendThreshold */) {
2905
+ return "" /* Constant.Empty */;
2905
2906
  }
2906
2907
  }
2908
+ return m.type;
2907
2909
  }
2908
- function exists(node) {
2909
- // Check if regionMap is not null before looking up a node
2910
- // Since, dom module stops after region module, it's possible that we may set regionMap to be null
2911
- // and still attempt to call exists on a late coming DOM mutation (or addition), effectively causing a script error
2912
- return regionMap && regionMap.has(node);
2913
- }
2914
- function track$3(id, event) {
2915
- var node = getNode(id);
2916
- var data = id in regions ? regions[id] : { id: id, visibility: 0 /* RegionVisibility.Rendered */, interaction: 16 /* InteractionState.None */, name: regionMap.get(node) };
2917
- // Determine the interaction state based on incoming event
2918
- var interaction = 16 /* InteractionState.None */;
2919
- switch (event) {
2920
- case 9 /* Event.Click */:
2921
- interaction = 20 /* InteractionState.Clicked */;
2922
- break;
2923
- case 27 /* Event.Input */:
2924
- interaction = 30 /* InteractionState.Input */;
2925
- break;
2910
+ function names(nodes) {
2911
+ var output = [];
2912
+ for (var i = 0; nodes && i < nodes.length; i++) {
2913
+ output.push(nodes[i].nodeName);
2926
2914
  }
2927
- // Process updates to this region, if applicable
2928
- process$1(node, data, interaction, data.visibility);
2915
+ return output.join();
2929
2916
  }
2930
- function compute$6() {
2931
- // Process any regions where we couldn't resolve an "id" for at the time of last intersection observer event
2932
- // This could happen in cases where elements are not yet processed by Clarity's virtual DOM but browser reports a change, regardless.
2933
- // For those cases we add them to the queue and re-process them below
2934
- var q = [];
2935
- for (var _i = 0, queue_1 = queue$1; _i < queue_1.length; _i++) {
2936
- var r = queue_1[_i];
2937
- var id = getId(r.node);
2938
- if (!(id in regions)) {
2939
- if (id) {
2940
- r.data.id = id;
2941
- regions[id] = r.data;
2942
- state$1.push(clone$1(r.data));
2943
- }
2944
- else {
2945
- q.push(r);
2917
+ function processNodeList(list, source, timer) {
2918
+ return __awaiter(this, void 0, void 0, function () {
2919
+ var length, i, state;
2920
+ return __generator(this, function (_a) {
2921
+ switch (_a.label) {
2922
+ case 0:
2923
+ length = list ? list.length : 0;
2924
+ i = 0;
2925
+ _a.label = 1;
2926
+ case 1:
2927
+ if (!(i < length)) return [3 /*break*/, 6];
2928
+ if (!(source === 1 /* Source.ChildListAdd */)) return [3 /*break*/, 2];
2929
+ traverse(list[i], timer, source);
2930
+ return [3 /*break*/, 5];
2931
+ case 2:
2932
+ state = state$9(timer);
2933
+ if (!(state === 0 /* Task.Wait */)) return [3 /*break*/, 4];
2934
+ return [4 /*yield*/, suspend$1(timer)];
2935
+ case 3:
2936
+ state = _a.sent();
2937
+ _a.label = 4;
2938
+ case 4:
2939
+ if (state === 2 /* Task.Stop */) {
2940
+ return [3 /*break*/, 6];
2941
+ }
2942
+ processNode(list[i], source);
2943
+ _a.label = 5;
2944
+ case 5:
2945
+ i++;
2946
+ return [3 /*break*/, 1];
2947
+ case 6: return [2 /*return*/];
2946
2948
  }
2947
- }
2948
- }
2949
- queue$1 = q;
2950
- // Schedule encode only when we have at least one valid data entry
2951
- if (state$1.length > 0) {
2952
- encode$4(7 /* Event.Region */);
2953
- }
2949
+ });
2950
+ });
2954
2951
  }
2955
- function handler$1(entries) {
2956
- for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
2957
- var entry = entries_1[_i];
2958
- var target = entry.target;
2959
- var rect = entry.boundingClientRect;
2960
- var overlap = entry.intersectionRect;
2961
- var viewport = entry.rootBounds;
2962
- // Only capture regions that have non-zero width or height to avoid tracking and sending regions
2963
- // that cannot ever be seen by the user. In some cases, websites will have a multiple copy of the same region
2964
- // like search box - one for desktop, and another for mobile. In those cases, CSS media queries determine which one should be visible.
2965
- // Also, if these regions ever become non-zero width or height (through AJAX, user action or orientation change) - we will automatically start monitoring them from that point onwards
2966
- if (regionMap.has(target) && rect.width + rect.height > 0 && viewport.width > 0 && viewport.height > 0) {
2967
- var id = target ? getId(target) : null;
2968
- var data = id in regions ? regions[id] : { id: id, name: regionMap.get(target), interaction: 16 /* InteractionState.None */, visibility: 0 /* RegionVisibility.Rendered */ };
2969
- // For regions that have relatively smaller area, we look at intersection ratio and see the overlap relative to element's area
2970
- // However, for larger regions, area of regions could be bigger than viewport and therefore comparison is relative to visible area
2971
- var viewportRatio = overlap ? (overlap.width * overlap.height * 1.0) / (viewport.width * viewport.height) : 0;
2972
- var visible = viewportRatio > 0.05 /* Setting.ViewportIntersectionRatio */ || entry.intersectionRatio > 0.8 /* Setting.IntersectionRatio */;
2973
- // If an element is either visible or was visible and has been scrolled to the end
2974
- // i.e. Scrolled to end is determined by if the starting position of the element + the window height is more than the total element height.
2975
- // starting position is relative to the viewport - so Intersection observer returns a negative value for rect.top to indicate that the element top is above the viewport
2976
- var scrolledToEnd = (visible || data.visibility == 10 /* RegionVisibility.Visible */) && Math.abs(rect.top) + viewport.height > rect.height;
2977
- // Process updates to this region, if applicable
2978
- process$1(target, data, data.interaction, (scrolledToEnd ?
2979
- 13 /* RegionVisibility.ScrolledToEnd */ :
2980
- (visible ? 10 /* RegionVisibility.Visible */ : 0 /* RegionVisibility.Rendered */)));
2981
- // Stop observing this element now that we have already received scrolled signal
2982
- if (data.visibility >= 13 /* RegionVisibility.ScrolledToEnd */ && observer$1) {
2983
- observer$1.unobserve(target);
2984
- }
2985
- }
2952
+ function schedule(node) {
2953
+ // Only schedule manual trigger for this node if it's not already in the queue
2954
+ if (queue$1.indexOf(node) < 0) {
2955
+ queue$1.push(node);
2986
2956
  }
2987
- if (state$1.length > 0) {
2988
- encode$4(7 /* Event.Region */);
2957
+ // Cancel any previous trigger before scheduling a new one.
2958
+ // It's common for a webpage to call multiple synchronous "insertRule" / "deleteRule" calls.
2959
+ // And in those cases we do not wish to monitor changes multiple times for the same node.
2960
+ if (timeout$1) {
2961
+ clearTimeout(timeout$1);
2989
2962
  }
2963
+ timeout$1 = setTimeout(function () { trigger$2(); }, 33 /* Setting.LookAhead */);
2964
+ return node;
2990
2965
  }
2991
- function process$1(n, d, s, v) {
2992
- // Check if received a state that supersedes existing state
2993
- var updated = s > d.interaction || v > d.visibility;
2994
- d.interaction = s > d.interaction ? s : d.interaction;
2995
- d.visibility = v > d.visibility ? v : d.visibility;
2996
- // If the corresponding node is already discovered, update the internal state
2997
- // Otherwise, track it in a queue to reprocess later.
2998
- if (d.id) {
2999
- if ((d.id in regions && updated) || !(d.id in regions)) {
3000
- regions[d.id] = d;
3001
- state$1.push(clone$1(d));
2966
+ function trigger$2() {
2967
+ for (var _i = 0, queue_1 = queue$1; _i < queue_1.length; _i++) {
2968
+ var node = queue_1[_i];
2969
+ // Generate a mutation for this node only if it still exists
2970
+ if (node) {
2971
+ var shadowRoot = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
2972
+ // Skip re-processing shadowRoot if it was already discovered
2973
+ if (shadowRoot && has(node)) {
2974
+ continue;
2975
+ }
2976
+ generate(node, shadowRoot ? "childList" /* Constant.ChildList */ : "characterData" /* Constant.CharacterData */);
3002
2977
  }
3003
2978
  }
3004
- else {
3005
- queue$1.push({ node: n, data: d });
3006
- }
3007
- }
3008
- function clone$1(r) {
3009
- return { time: time(), data: { id: r.id, interaction: r.interaction, visibility: r.visibility, name: r.name } };
3010
- }
3011
- function reset$6() {
3012
- state$1 = [];
3013
- }
3014
- function stop$f() {
3015
- reset$6();
3016
- regionMap = null;
3017
- regions = {};
3018
2979
  queue$1 = [];
3019
- if (observer$1) {
3020
- observer$1.disconnect();
3021
- observer$1 = null;
3022
- }
3023
- watch = false;
2980
+ }
2981
+ function generate(target, type) {
2982
+ measure(handle$1)([{
2983
+ addedNodes: [target],
2984
+ attributeName: null,
2985
+ attributeNamespace: null,
2986
+ nextSibling: null,
2987
+ oldValue: null,
2988
+ previousSibling: null,
2989
+ removedNodes: [],
2990
+ target: target,
2991
+ type: type
2992
+ }]);
3024
2993
  }
3025
2994
 
3026
2995
  function target(evt) {
@@ -3053,7 +3022,7 @@ function metadata$2(node, event, text) {
3053
3022
  output.hash = value.hash;
3054
3023
  output.privacy = metadata_1.privacy;
3055
3024
  if (value.region) {
3056
- track$3(value.region, event);
3025
+ track$4(value.region, event);
3057
3026
  }
3058
3027
  if (metadata_1.fraud) {
3059
3028
  check$4(metadata_1.fraud, value.id, text || value.data.value);
@@ -3080,7 +3049,7 @@ function encode$3 (type, ts) {
3080
3049
  case 18 /* Event.TouchEnd */:
3081
3050
  case 19 /* Event.TouchMove */:
3082
3051
  case 20 /* Event.TouchCancel */:
3083
- for (_i = 0, _a = state$4; _i < _a.length; _i++) {
3052
+ for (_i = 0, _a = state$3; _i < _a.length; _i++) {
3084
3053
  entry = _a[_i];
3085
3054
  pTarget = metadata$2(entry.data.target, entry.event);
3086
3055
  if (pTarget.id > 0) {
@@ -3092,10 +3061,10 @@ function encode$3 (type, ts) {
3092
3061
  track$7(entry.event, entry.data.x, entry.data.y);
3093
3062
  }
3094
3063
  }
3095
- reset$e();
3064
+ reset$c();
3096
3065
  break;
3097
3066
  case 9 /* Event.Click */:
3098
- for (_b = 0, _c = state$7; _b < _c.length; _b++) {
3067
+ for (_b = 0, _c = state$6; _b < _c.length; _b++) {
3099
3068
  entry = _c[_b];
3100
3069
  cTarget = metadata$2(entry.data.target, entry.event, entry.data.text);
3101
3070
  tokens = [entry.time, entry.event];
@@ -3115,10 +3084,10 @@ function encode$3 (type, ts) {
3115
3084
  queue(tokens);
3116
3085
  track$2(entry.time, entry.event, cHash, entry.data.x, entry.data.y, entry.data.reaction, entry.data.context);
3117
3086
  }
3118
- reset$h();
3087
+ reset$f();
3119
3088
  break;
3120
3089
  case 38 /* Event.Clipboard */:
3121
- for (_d = 0, _e = state$6; _d < _e.length; _d++) {
3090
+ for (_d = 0, _e = state$5; _d < _e.length; _d++) {
3122
3091
  entry = _e[_d];
3123
3092
  tokens = [entry.time, entry.event];
3124
3093
  target = metadata$2(entry.data.target, entry.event);
@@ -3128,24 +3097,24 @@ function encode$3 (type, ts) {
3128
3097
  queue(tokens);
3129
3098
  }
3130
3099
  }
3131
- reset$g();
3100
+ reset$e();
3132
3101
  break;
3133
3102
  case 11 /* Event.Resize */:
3134
3103
  r = data$b;
3135
3104
  tokens.push(r.width);
3136
3105
  tokens.push(r.height);
3137
3106
  track$7(type, r.width, r.height);
3138
- reset$d();
3107
+ reset$b();
3139
3108
  queue(tokens);
3140
3109
  break;
3141
3110
  case 26 /* Event.Unload */:
3142
3111
  u = data$9;
3143
3112
  tokens.push(u.name);
3144
- reset$9();
3113
+ reset$7();
3145
3114
  queue(tokens);
3146
3115
  break;
3147
3116
  case 27 /* Event.Input */:
3148
- for (_f = 0, _g = state$5; _f < _g.length; _f++) {
3117
+ for (_f = 0, _g = state$4; _f < _g.length; _f++) {
3149
3118
  entry = _g[_f];
3150
3119
  iTarget = metadata$2(entry.data.target, entry.event, entry.data.value);
3151
3120
  tokens = [entry.time, entry.event];
@@ -3153,7 +3122,7 @@ function encode$3 (type, ts) {
3153
3122
  tokens.push(text$1(entry.data.value, "input", iTarget.privacy));
3154
3123
  queue(tokens);
3155
3124
  }
3156
- reset$f();
3125
+ reset$d();
3157
3126
  break;
3158
3127
  case 21 /* Event.Selection */:
3159
3128
  s = data$a;
@@ -3164,12 +3133,12 @@ function encode$3 (type, ts) {
3164
3133
  tokens.push(s.startOffset);
3165
3134
  tokens.push(endTarget.id);
3166
3135
  tokens.push(s.endOffset);
3167
- reset$b();
3136
+ reset$9();
3168
3137
  queue(tokens);
3169
3138
  }
3170
3139
  break;
3171
3140
  case 10 /* Event.Scroll */:
3172
- for (_h = 0, _j = state$3; _h < _j.length; _h++) {
3141
+ for (_h = 0, _j = state$2; _h < _j.length; _h++) {
3173
3142
  entry = _j[_h];
3174
3143
  sTarget = metadata$2(entry.data.target, entry.event);
3175
3144
  if (sTarget.id > 0) {
@@ -3181,10 +3150,10 @@ function encode$3 (type, ts) {
3181
3150
  track$7(entry.event, entry.data.x, entry.data.y);
3182
3151
  }
3183
3152
  }
3184
- reset$c();
3153
+ reset$a();
3185
3154
  break;
3186
3155
  case 42 /* Event.Change */:
3187
- for (_k = 0, _l = state$8; _k < _l.length; _k++) {
3156
+ for (_k = 0, _l = state$7; _k < _l.length; _k++) {
3188
3157
  entry = _l[_k];
3189
3158
  tokens = [entry.time, entry.event];
3190
3159
  target = metadata$2(entry.data.target, entry.event);
@@ -3197,10 +3166,10 @@ function encode$3 (type, ts) {
3197
3166
  queue(tokens);
3198
3167
  }
3199
3168
  }
3200
- reset$i();
3169
+ reset$g();
3201
3170
  break;
3202
3171
  case 39 /* Event.Submit */:
3203
- for (_m = 0, _o = state$2; _m < _o.length; _m++) {
3172
+ for (_m = 0, _o = state$1; _m < _o.length; _m++) {
3204
3173
  entry = _o[_m];
3205
3174
  tokens = [entry.time, entry.event];
3206
3175
  target = metadata$2(entry.data.target, entry.event);
@@ -3209,7 +3178,7 @@ function encode$3 (type, ts) {
3209
3178
  queue(tokens);
3210
3179
  }
3211
3180
  }
3212
- reset$a();
3181
+ reset$8();
3213
3182
  break;
3214
3183
  case 22 /* Event.Timeline */:
3215
3184
  for (_p = 0, _q = updates$1; _p < _q.length; _p++) {
@@ -3230,7 +3199,7 @@ function encode$3 (type, ts) {
3230
3199
  tokens.push(v.visible);
3231
3200
  queue(tokens);
3232
3201
  visibility(t, v.visible);
3233
- reset$8();
3202
+ reset$6();
3234
3203
  break;
3235
3204
  }
3236
3205
  return [2 /*return*/];
@@ -3531,22 +3500,31 @@ function delay() {
3531
3500
  return typeof config$1.upload === "string" /* Constant.String */ ? Math.max(Math.min(gap, 30000 /* Setting.MaxUploadDelay */), 100 /* Setting.MinUploadDelay */) : config$1.delay;
3532
3501
  }
3533
3502
  function response(payload) {
3534
- var parts = payload && payload.length > 0 ? payload.split(" ") : ["" /* Constant.Empty */];
3535
- switch (parts[0]) {
3536
- case "END" /* Constant.End */:
3537
- // Clear out session storage and end the session so we can start fresh the next time
3538
- trigger(6 /* Check.Server */);
3539
- break;
3540
- case "UPGRADE" /* Constant.Upgrade */:
3541
- // Upgrade current session to send back playback information
3542
- upgrade("Auto" /* Constant.Auto */);
3543
- break;
3544
- case "ACTION" /* Constant.Action */:
3545
- // Invoke action callback, if configured and has a valid value
3546
- if (config$1.action && parts.length > 1) {
3547
- config$1.action(parts[1]);
3548
- }
3549
- break;
3503
+ var lines = payload && payload.length > 0 ? payload.split("\n") : [];
3504
+ for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
3505
+ var line = lines_1[_i];
3506
+ var parts = line && line.length > 0 ? line.split(/ (.*)/) : ["" /* Constant.Empty */];
3507
+ switch (parts[0]) {
3508
+ case "END" /* Constant.End */:
3509
+ // Clear out session storage and end the session so we can start fresh the next time
3510
+ trigger(6 /* Check.Server */);
3511
+ break;
3512
+ case "UPGRADE" /* Constant.Upgrade */:
3513
+ // Upgrade current session to send back playback information
3514
+ upgrade("Auto" /* Constant.Auto */);
3515
+ break;
3516
+ case "ACTION" /* Constant.Action */:
3517
+ // Invoke action callback, if configured and has a valid value
3518
+ if (config$1.action && parts.length > 1) {
3519
+ config$1.action(parts[1]);
3520
+ }
3521
+ break;
3522
+ case "EXTRACT" /* Constant.Extract */:
3523
+ if (parts.length > 1) {
3524
+ trigger$1(parts[1]);
3525
+ }
3526
+ break;
3527
+ }
3550
3528
  }
3551
3529
  }
3552
3530
 
@@ -3651,30 +3629,27 @@ var data$5 = {};
3651
3629
  var keys = [];
3652
3630
  var variables = {};
3653
3631
  var selectors = {};
3654
- var fragments = [];
3655
3632
  function start$c() {
3633
+ reset$4();
3634
+ }
3635
+ function trigger$1(input) {
3656
3636
  try {
3657
- var e = config$1.extract;
3658
- if (!e) {
3659
- return;
3660
- }
3661
- for (var i = 0; i < e.length; i += 3) {
3662
- var source = e[i];
3663
- var key = e[i + 1];
3637
+ var parts = input && input.length > 0 ? input.split(/ (.*)/) : ["" /* Constant.Empty */];
3638
+ var key = parseInt(parts[0]);
3639
+ var values = parts.length > 1 ? JSON.parse(parts[1]) : {};
3640
+ variables[key] = {};
3641
+ selectors[key] = {};
3642
+ for (var v in values) {
3643
+ var id = parseInt(v);
3644
+ var value = values[v];
3645
+ var source = value.startsWith("~" /* Constant.Tilde */) ? 0 /* ExtractSource.Javascript */ : 2 /* ExtractSource.Text */;
3664
3646
  switch (source) {
3665
3647
  case 0 /* ExtractSource.Javascript */:
3666
- var variable = e[i + 2];
3667
- variables[key] = parse(variable);
3668
- break;
3669
- case 1 /* ExtractSource.Cookie */:
3670
- /*Todo: Add cookie extract logic*/
3648
+ var variable = value.substring(1, value.length);
3649
+ variables[key][id] = parse(variable);
3671
3650
  break;
3672
3651
  case 2 /* ExtractSource.Text */:
3673
- var match_1 = e[i + 2];
3674
- selectors[key] = match_1;
3675
- break;
3676
- case 3 /* ExtractSource.Fragment */:
3677
- fragments = e[i + 2];
3652
+ selectors[key][id] = value;
3678
3653
  break;
3679
3654
  }
3680
3655
  }
@@ -3689,15 +3664,25 @@ function clone(v) {
3689
3664
  function compute$4() {
3690
3665
  try {
3691
3666
  for (var v in variables) {
3692
- var value = str(evaluate(clone(variables[v])));
3693
- if (value) {
3694
- update(v, value);
3695
- }
3696
- }
3697
- for (var s in selectors) {
3698
- var node = document.querySelector(selectors[s]);
3699
- if (node) {
3700
- update(s, node.innerText);
3667
+ var key = parseInt(v);
3668
+ if (!(key in keys)) {
3669
+ var variableData = variables[key];
3670
+ for (var v_1 in variableData) {
3671
+ var variableKey = parseInt(v_1);
3672
+ var value = str(evaluate(clone(variableData[variableKey])));
3673
+ if (value) {
3674
+ update(key, variableKey, value);
3675
+ }
3676
+ }
3677
+ var selectorData = selectors[key];
3678
+ for (var s in selectorData) {
3679
+ var selectorKey = parseInt(s);
3680
+ var nodes = document.querySelectorAll(selectorData[selectorKey]);
3681
+ if (nodes) {
3682
+ var text = Array.from(nodes).map(function (e) { return e.innerText; });
3683
+ update(key, selectorKey, text.join("<SEP>" /* Constant.Seperator */).substring(0, 10000 /* Setting.ExtractLimit */));
3684
+ }
3685
+ }
3701
3686
  }
3702
3687
  }
3703
3688
  }
@@ -3707,20 +3692,20 @@ function compute$4() {
3707
3692
  encode$1(40 /* Event.Extract */);
3708
3693
  }
3709
3694
  function reset$4() {
3695
+ data$5 = {};
3710
3696
  keys = [];
3697
+ variables = {};
3698
+ selectors = {};
3711
3699
  }
3712
- function update(key, value, force) {
3713
- if (force === void 0) { force = false; }
3714
- if (!(key in data$5) || (key in data$5 && data$5[key] !== value) || force) {
3715
- data$5[key] = value;
3700
+ function update(key, subkey, value) {
3701
+ if (!(key in data$5)) {
3702
+ data$5[key] = [];
3716
3703
  keys.push(key);
3717
3704
  }
3705
+ data$5[key].push([subkey, value]);
3718
3706
  }
3719
3707
  function stop$b() {
3720
- data$5 = {};
3721
- keys = [];
3722
- variables = {};
3723
- selectors = {};
3708
+ reset$4();
3724
3709
  }
3725
3710
  function parse(variable) {
3726
3711
  var syntax = [];
@@ -3885,7 +3870,7 @@ function encode$1 (event) {
3885
3870
  for (var _d = 0, extractKeys_1 = extractKeys; _d < extractKeys_1.length; _d++) {
3886
3871
  var e = extractKeys_1[_d];
3887
3872
  tokens.push(e);
3888
- tokens.push(data$5[e]);
3873
+ tokens.push([].concat.apply([], data$5[e]));
3889
3874
  }
3890
3875
  reset$4();
3891
3876
  queue(tokens, false);
@@ -4386,7 +4371,7 @@ var status = false;
4386
4371
  function start$6() {
4387
4372
  status = true;
4388
4373
  start$G();
4389
- reset$k();
4374
+ reset$j();
4390
4375
  reset$1();
4391
4376
  reset$2();
4392
4377
  start$7();
@@ -4395,7 +4380,7 @@ function stop$5() {
4395
4380
  stop$6();
4396
4381
  reset$2();
4397
4382
  reset$1();
4398
- reset$k();
4383
+ reset$j();
4399
4384
  stop$C();
4400
4385
  status = false;
4401
4386
  }
@@ -4478,14 +4463,14 @@ function discover() {
4478
4463
  case 0:
4479
4464
  ts = time();
4480
4465
  timer = { id: id(), cost: 3 /* Metric.LayoutCost */ };
4481
- start$x(timer);
4466
+ start$w(timer);
4482
4467
  return [4 /*yield*/, traverse(document, timer, 0 /* Source.Discover */)];
4483
4468
  case 1:
4484
4469
  _a.sent();
4485
4470
  return [4 /*yield*/, encode$4(5 /* Event.Discover */, timer, ts)];
4486
4471
  case 2:
4487
4472
  _a.sent();
4488
- stop$u(timer);
4473
+ stop$t(timer);
4489
4474
  return [2 /*return*/];
4490
4475
  }
4491
4476
  });
@@ -4495,24 +4480,24 @@ function discover() {
4495
4480
  function start$3() {
4496
4481
  // The order below is important
4497
4482
  // and is determined by interdependencies of modules
4498
- start$w();
4483
+ start$v();
4484
+ start$u();
4485
+ start$x();
4499
4486
  start$h();
4500
- start$i();
4501
- start$j();
4502
4487
  start$4();
4503
4488
  }
4504
4489
  function stop$3() {
4490
+ stop$s();
4491
+ stop$u();
4505
4492
  stop$f();
4506
- stop$g();
4507
- stop$h();
4508
4493
  end();
4509
4494
  }
4510
4495
 
4511
4496
  var layout = /*#__PURE__*/Object.freeze({
4512
4497
  __proto__: null,
4498
+ hashText: hashText,
4513
4499
  start: start$3,
4514
- stop: stop$3,
4515
- hashText: hashText
4500
+ stop: stop$3
4516
4501
  });
4517
4502
 
4518
4503
  function encode (type) {
@@ -4664,11 +4649,6 @@ function process(entries) {
4664
4649
  break;
4665
4650
  }
4666
4651
  }
4667
- if (performance && "memory" /* Constant.Memory */ in performance && performance["memory" /* Constant.Memory */].usedJSHeapSize) {
4668
- // Track consumed memory (MBs) where "memory" API is available
4669
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory
4670
- max(30 /* Metric.UsedMemory */, Math.abs(performance["memory" /* Constant.Memory */].usedJSHeapSize / 1048576 /* Setting.MegaByte */));
4671
- }
4672
4652
  }
4673
4653
  function stop$2() {
4674
4654
  if (observer) {