clarity-js
Version:
An analytics library that uses web page interactions to generate aggregated insights
1,315 lines (1,288 loc) • 258 kB
JavaScript
var dom = /*#__PURE__*/Object.freeze({
__proto__: null,
get add () { return add; },
get get () { return get; },
get getId () { return getId; },
get getNode () { return getNode; },
get getValue () { return getValue; },
get has () { return has$1; },
get hashText () { return hashText; },
get iframe () { return iframe; },
get iframeContent () { return iframeContent; },
get lookup () { return lookup; },
get parse () { return parse; },
get removeIFrame () { return removeIFrame; },
get sameorigin () { return sameorigin; },
get start () { return start$j; },
get stop () { return stop$h; },
get update () { return update; },
get updates () { return updates$1; }
});
var upload$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
get queue () { return queue; },
get start () { return start$9; },
get stop () { return stop$8; },
get track () { return track; }
});
var clarity = /*#__PURE__*/Object.freeze({
__proto__: null,
get consent () { return consent; },
get event () { return event; },
get hashText () { return hashText; },
get identify () { return identify; },
get metadata () { return metadata; },
get pause () { return pause; },
get resume () { return resume; },
get set () { return set; },
get signal () { return signal; },
get start () { return start; },
get stop () { return stop; },
get upgrade () { return upgrade; },
get version () { return version$1; }
});
var config$2 = {
projectId: null,
delay: 1 * 1000 /* Time.Second */,
lean: false,
lite: false,
track: true,
content: true,
drop: [],
mask: [],
unmask: [],
regions: [],
cookies: [],
fraud: true,
checksum: [],
report: null,
upload: null,
fallback: null,
upgrade: null,
action: null,
dob: null,
delayDom: false,
throttleDom: true,
conversions: false,
includeSubdomains: true,
};
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
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;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
var catchallRegex = /\S/gi;
var maxUrlLength = 255;
var unicodeRegex = true;
var digitRegex = null;
var letterRegex = null;
var currencyRegex = null;
function text$1(value, hint, privacy, mangle, type) {
if (mangle === void 0) { mangle = false; }
if (value) {
if (hint === "input" && (type === "checkbox" || type === "radio")) {
return value;
}
switch (privacy) {
case 0 /* Privacy.None */:
return value;
case 1 /* Privacy.Sensitive */:
switch (hint) {
case "*T" /* Layout.Constant.TextTag */:
case "value":
case "placeholder":
case "click":
return redact$1(value);
case "input":
case "change":
return mangleToken(value);
}
return value;
case 2 /* Privacy.Text */:
case 3 /* Privacy.TextImage */:
switch (hint) {
case "*T" /* Layout.Constant.TextTag */:
case "data-" /* Layout.Constant.DataAttribute */:
return mangle ? mangleText(value) : mask(value);
case "src":
case "srcset":
case "title":
case "alt":
return privacy === 3 /* Privacy.TextImage */ ? "" /* Data.Constant.Empty */ : value;
case "value":
case "click":
case "input":
case "change":
return mangleToken(value);
case "placeholder":
return mask(value);
}
break;
case 4 /* Privacy.Exclude */:
switch (hint) {
case "*T" /* Layout.Constant.TextTag */:
case "data-" /* Layout.Constant.DataAttribute */:
return mangle ? mangleText(value) : mask(value);
case "value":
case "input":
case "click":
case "change":
return Array(5 /* Data.Setting.WordLength */).join("\u2022" /* Data.Constant.Mask */);
case "checksum":
return "" /* Data.Constant.Empty */;
}
break;
case 5 /* Privacy.Snapshot */:
switch (hint) {
case "*T" /* Layout.Constant.TextTag */:
case "data-" /* Layout.Constant.DataAttribute */:
return scrub(value, "\u25AA" /* Data.Constant.Letter */, "\u25AB" /* Data.Constant.Digit */);
case "value":
case "input":
case "click":
case "change":
return Array(5 /* Data.Setting.WordLength */).join("\u2022" /* Data.Constant.Mask */);
case "checksum":
case "src":
case "srcset":
case "alt":
case "title":
return "" /* Data.Constant.Empty */;
}
break;
}
}
return value;
}
function url$1(input, electron, truncate) {
if (electron === void 0) { electron = false; }
if (truncate === void 0) { truncate = false; }
var result = input;
// Replace the URL for Electron apps so we don't send back file:/// URL
if (electron) {
result = "".concat("https://" /* Data.Constant.HTTPS */).concat("Electron" /* Data.Constant.Electron */);
}
else {
var drop_1 = config$2.drop;
if (drop_1 && drop_1.length > 0 && input && input.indexOf("?") > 0) {
var _a = input.split("?"), path = _a[0], query = _a[1];
var swap_1 = "*na*" /* Data.Constant.Dropped */;
result =
"".concat(path, "?").concat(query
.split("&")
.map(function (p) { return (drop_1.some(function (x) { return p.indexOf("".concat(x, "=")) === 0; }) ? "".concat(p.split("=")[0], "=").concat(swap_1) : p); })
.join("&"));
}
}
if (truncate) {
result = result.substring(0, maxUrlLength);
}
return result;
}
function mangleText(value) {
var trimmed = value.trim();
if (trimmed.length > 0) {
var first = trimmed[0];
var index = value.indexOf(first);
var prefix = value.substr(0, index);
var suffix = value.substr(index + trimmed.length);
return "".concat(prefix).concat(trimmed.length.toString(36)).concat(suffix);
}
return value;
}
function mask(value) {
return value.replace(catchallRegex, "\u2022" /* Data.Constant.Mask */);
}
function scrub(value, letter, digit) {
regex(); // Initialize regular expressions
return value ? value.replace(letterRegex, letter).replace(digitRegex, digit) : value;
}
function mangleToken(value) {
var length = (Math.floor(value.length / 5 /* Data.Setting.WordLength */) + 1) * 5 /* Data.Setting.WordLength */;
var output = "" /* Layout.Constant.Empty */;
for (var i = 0; i < length; i++) {
output += i > 0 && i % 5 /* Data.Setting.WordLength */ === 0 ? " " /* Data.Constant.Space */ : "\u2022" /* Data.Constant.Mask */;
}
return output;
}
function regex() {
// Initialize unicode regex, if supported by the browser
// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
if (unicodeRegex && digitRegex === null) {
try {
digitRegex = /\p{N}/gu;
letterRegex = /\p{L}/gu;
currencyRegex = /\p{Sc}/gu;
}
catch (_a) {
unicodeRegex = false;
}
}
}
function redact$1(value) {
var spaceIndex = -1;
var gap = 0;
var hasDigit = false;
var hasEmail = false;
var hasWhitespace = false;
var array = null;
regex(); // Initialize regular expressions
for (var i = 0; i < value.length; i++) {
var c = value.charCodeAt(i);
hasDigit = hasDigit || (c >= 48 /* Data.Character.Zero */ && c <= 57 /* Data.Character.Nine */); // Check for digits in the current word
hasEmail = hasEmail || c === 64 /* Data.Character.At */; // Check for @ sign anywhere within the current word
hasWhitespace =
c === 9 /* Data.Character.Tab */ || c === 10 /* Data.Character.NewLine */ || c === 13 /* Data.Character.Return */ || c === 32 /* Data.Character.Blank */;
// Process each word as an individual token to redact any sensitive information
if (i === 0 || i === value.length - 1 || hasWhitespace) {
// Performance optimization: Lazy load string -> array conversion only when required
if (hasDigit || hasEmail) {
if (array === null) {
array = value.split("" /* Data.Constant.Empty */);
}
// Work on a token at a time so we don't have to apply regex to a larger string
var token = value.substring(spaceIndex + 1, hasWhitespace ? i : i + 1);
// Check if unicode regex is supported, otherwise fallback to calling mask function on this token
if (unicodeRegex && currencyRegex !== null) {
// Do not redact information if the token contains a currency symbol
token = token.match(currencyRegex) ? token : scrub(token, "\u25AA" /* Data.Constant.Letter */, "\u25AB" /* Data.Constant.Digit */);
}
else {
token = mask(token);
}
// Merge token back into array at the right place
array.splice(spaceIndex + 1 - gap, token.length, token);
gap += token.length - 1;
}
// Reset digit and email flags after every word boundary, except the beginning of string
if (hasWhitespace) {
hasDigit = false;
hasEmail = false;
spaceIndex = i;
}
}
}
return array ? array.join("" /* Data.Constant.Empty */) : value;
}
var startTime = 0;
function start$I() {
startTime = performance.now() + performance.timeOrigin;
}
// event.timestamp is number of milliseconds elapsed since the document was loaded
// since iframes can be loaded later the event timestamp is not the same as performance.now()
// converting everything to absolute time by adding timeorigin of the event view
// to synchronize times before calculating the difference with start time
function time(event) {
if (event === void 0) { event = null; }
var ts = event && event.timeStamp > 0 ? event.timeStamp : performance.now();
var origin = event && event.view ? event.view.performance.timeOrigin : performance.timeOrigin;
return Math.max(Math.round(ts + origin - startTime), 0);
}
function stop$F() {
startTime = 0;
}
var version$1 = "0.8.12";
function hash (input, precision) {
if (precision === void 0) { precision = null; }
// Code inspired from C# GetHashCode: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs
var hash = 0;
var hashOne = 5381;
var hashTwo = hashOne;
for (var i = 0; i < input.length; i += 2) {
var charOne = input.charCodeAt(i);
hashOne = ((hashOne << 5) + hashOne) ^ charOne;
if (i + 1 < input.length) {
var charTwo = input.charCodeAt(i + 1);
hashTwo = ((hashTwo << 5) + hashTwo) ^ charTwo;
}
}
// Replace the magic number from C# implementation (1566083941) with a smaller prime number (11579)
// This ensures we don't hit integer overflow and prevent collisions
hash = Math.abs(hashOne + hashTwo * 11579);
return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
}
var state$b = null;
var buffer = null;
var update$2 = false;
function start$H() {
update$2 = false;
reset$s();
}
function reset$s() {
// Baseline state holds the previous values - if it is updated in the current payload,
// reset the state to current value after sending the previous state
if (update$2) {
state$b = {
time: time(),
event: 4 /* Event.Baseline */,
data: {
visible: buffer.visible,
docWidth: buffer.docWidth,
docHeight: buffer.docHeight,
screenWidth: buffer.screenWidth,
screenHeight: buffer.screenHeight,
scrollX: buffer.scrollX,
scrollY: buffer.scrollY,
pointerX: buffer.pointerX,
pointerY: buffer.pointerY,
activityTime: buffer.activityTime,
scrollTime: buffer.scrollTime,
pointerTime: buffer.pointerTime,
moveX: buffer.moveX,
moveY: buffer.moveY,
moveTime: buffer.moveTime,
downX: buffer.downX,
downY: buffer.downY,
downTime: buffer.downTime,
upX: buffer.upX,
upY: buffer.upY,
upTime: buffer.upTime,
pointerPrevX: buffer.pointerPrevX,
pointerPrevY: buffer.pointerPrevY,
pointerPrevTime: buffer.pointerPrevTime,
},
};
}
buffer = buffer
? buffer
: {
visible: 1 /* BooleanFlag.True */,
docWidth: 0,
docHeight: 0,
screenWidth: 0,
screenHeight: 0,
scrollX: 0,
scrollY: 0,
pointerX: 0,
pointerY: 0,
activityTime: 0,
scrollTime: 0,
pointerTime: undefined,
moveX: undefined,
moveY: undefined,
moveTime: undefined,
downX: undefined,
downY: undefined,
downTime: undefined,
upX: undefined,
upY: undefined,
upTime: undefined,
pointerPrevX: undefined,
pointerPrevY: undefined,
pointerPrevTime: undefined,
};
}
function track$8(event, x, y, time) {
switch (event) {
case 8 /* Event.Document */:
buffer.docWidth = x;
buffer.docHeight = y;
break;
case 11 /* Event.Resize */:
buffer.screenWidth = x;
buffer.screenHeight = y;
break;
case 10 /* Event.Scroll */:
buffer.scrollX = x;
buffer.scrollY = y;
buffer.scrollTime = time;
break;
case 12 /* Event.MouseMove */:
buffer.moveX = x;
buffer.moveY = y;
buffer.moveTime = time;
buffer.pointerPrevX = buffer.pointerX;
buffer.pointerPrevY = buffer.pointerY;
buffer.pointerPrevTime = buffer.pointerTime;
buffer.pointerX = x;
buffer.pointerY = y;
buffer.pointerTime = time;
break;
case 13 /* Event.MouseDown */:
buffer.downX = x;
buffer.downY = y;
buffer.downTime = time;
buffer.pointerPrevX = buffer.pointerX;
buffer.pointerPrevY = buffer.pointerY;
buffer.pointerPrevTime = buffer.pointerTime;
buffer.pointerX = x;
buffer.pointerY = y;
buffer.pointerTime = time;
break;
case 14 /* Event.MouseUp */:
buffer.upX = x;
buffer.upY = y;
buffer.upTime = time;
buffer.pointerPrevX = buffer.pointerX;
buffer.pointerPrevY = buffer.pointerY;
buffer.pointerPrevTime = buffer.pointerTime;
buffer.pointerX = x;
buffer.pointerY = y;
buffer.pointerTime = time;
break;
default:
buffer.pointerPrevX = buffer.pointerX;
buffer.pointerPrevY = buffer.pointerY;
buffer.pointerPrevTime = buffer.pointerTime;
buffer.pointerX = x;
buffer.pointerY = y;
buffer.pointerTime = time;
break;
}
update$2 = true;
}
function activity(t) {
buffer.activityTime = t;
}
function visibility(t, visible) {
buffer.visible = visible === "visible" ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */;
if (!buffer.visible) {
activity(t);
}
update$2 = true;
}
function compute$e() {
if (update$2) {
encode$2(4 /* Event.Baseline */);
}
}
function stop$E() {
reset$s();
}
var baseline = /*#__PURE__*/Object.freeze({
__proto__: null,
activity: activity,
compute: compute$e,
reset: reset$s,
start: start$H,
get state () { return state$b; },
stop: stop$E,
track: track$8,
visibility: visibility
});
var data$j = null;
// custom events allow 2 parameters or 1 parameter to be passed. If 2 are passed we
// consider it a key value pair. If only 1 is passed we only consider the event to have a value.
function event(a, b) {
if (active() && a && typeof a === "string" && a.length < 255) {
if (b && typeof b === "string" && b.length < 255) {
data$j = { key: a, value: b };
}
else {
data$j = { value: a };
}
encode$2(24 /* Event.Custom */);
}
}
var data$i = {};
var keys = new Set();
var variables = {};
var selectors = {};
var hashes = {};
var validation = {};
function start$G() {
reset$r();
}
// Input string is of the following form:
// EXTRACT 101|element { "1": ".class1", "2": "~window.a.b", "3": "!abc"}
// if element is present on the page it will set up event 101 to grab the contents of the class1 selector into component 1,
// the javascript evaluated contents of window.a.b into component 2,
// and the contents of Clarity's hash abc into component 3
function trigger$2(input) {
try {
var parts = input && input.length > 0 ? input.split(/ (.*)/) : ["" /* Constant.Empty */];
var keyparts = parts[0].split(/\|(.*)/);
var key = Number.parseInt(keyparts[0]);
var element = keyparts.length > 1 ? keyparts[1] : "" /* Constant.Empty */;
var values = parts.length > 1 ? JSON.parse(parts[1]) : {};
variables[key] = {};
selectors[key] = {};
hashes[key] = {};
validation[key] = element;
for (var v in values) {
// values is a set of strings for proper JSON parsing, but it's more efficient
// to interact with them as numbers
var id = Number.parseInt(v);
var value = values[v];
var source = 2 /* ExtractSource.Text */;
if (value.startsWith("~" /* Constant.Tilde */)) {
source = 0 /* ExtractSource.Javascript */;
}
else if (value.startsWith("!" /* Constant.Bang */)) {
source = 4 /* ExtractSource.Hash */;
}
switch (source) {
case 0 /* ExtractSource.Javascript */: {
var variable = value.slice(1);
variables[key][id] = parse$1(variable);
break;
}
case 2 /* ExtractSource.Text */:
selectors[key][id] = value;
break;
case 4 /* ExtractSource.Hash */: {
var hash_1 = value.slice(1);
hashes[key][id] = hash_1;
break;
}
}
}
}
catch (e) {
log(8 /* Code.Config */, 1 /* Severity.Warning */, e ? e.name : null);
}
}
function clone$1(v) {
return JSON.parse(JSON.stringify(v));
}
function compute$d() {
try {
for (var v in variables) {
var key = Number.parseInt(v);
if (validation[key] === "" /* Constant.Empty */ || document.querySelector(validation[key])) {
var variableData = variables[key];
for (var v_1 in variableData) {
var variableKey = Number.parseInt(v_1);
var value = str$1(evaluate(clone$1(variableData[variableKey])));
if (value) {
update$1(key, variableKey, value);
}
}
var selectorData = selectors[key];
for (var s in selectorData) {
var shouldMask = false;
var selectorKey = Number.parseInt(s);
var selector = selectorData[selectorKey];
if (selector.startsWith("@" /* Constant.At */)) {
shouldMask = true;
selector = selector.slice(1);
}
var nodes = document.querySelectorAll(selector);
if (nodes) {
var text = Array.from(nodes)
.map(function (e) { return e.textContent; })
.join("<SEP>" /* Constant.Seperator */);
update$1(key, selectorKey, (shouldMask ? hash(text).trim() : text).slice(0, 10000 /* Setting.ExtractLimit */));
}
}
var hashData = hashes[key];
for (var h in hashData) {
var hashKey = Number.parseInt(h);
var content = hashText(hashData[hashKey]).trim().slice(0, 10000 /* Setting.ExtractLimit */);
update$1(key, hashKey, content);
}
}
}
if (keys.size > 0) {
encode$2(40 /* Event.Extract */);
}
}
catch (e) {
log(5 /* Code.Selector */, 1 /* Severity.Warning */, e ? e.name : null);
}
}
function reset$r() {
keys.clear();
}
function update$1(key, subkey, value) {
var update = false;
if (!(key in data$i)) {
data$i[key] = {};
update = true;
}
if (!isEmpty(hashes[key]) && (!(subkey in data$i[key]) || data$i[key][subkey] !== value)) {
update = true;
}
data$i[key][subkey] = value;
if (update) {
keys.add(key);
}
return;
}
function stop$D() {
reset$r();
}
function parse$1(variable) {
var syntax = [];
var parts = variable.split("." /* Constant.Dot */);
while (parts.length > 0) {
var part = parts.shift();
var arrayStart = part.indexOf("[" /* Constant.ArrayStart */);
var conditionStart = part.indexOf("{" /* Constant.ConditionStart */);
var conditionEnd = part.indexOf("}" /* Constant.ConditionEnd */);
syntax.push({
name: arrayStart > 0 ? part.slice(0, arrayStart) : conditionStart > 0 ? part.slice(0, conditionStart) : part,
type: arrayStart > 0 ? 1 /* Type.Array */ : conditionStart > 0 ? 2 /* Type.Object */ : 3 /* Type.Simple */,
condition: conditionStart > 0 ? part.slice(conditionStart + 1, conditionEnd) : null,
});
}
return syntax;
}
// The function below takes in a variable name in following format: "a.b.c" and safely evaluates its value in javascript context
// For instance, for a.b.c, it will first check window["a"]. If it exists, it will recursively look at: window["a"]["b"] and finally,
// return the value for window["a"]["b"]["c"].
// biome-ignore lint/complexity/noBannedTypes: type of base is intentionally generic
// biome-ignore lint/suspicious/noExplicitAny: type of return value isn't known
function evaluate(variable, base) {
if (base === void 0) { base = window; }
if (variable.length === 0) {
return base;
}
var part = variable.shift();
// biome-ignore lint/suspicious/noImplicitAnyLet: type of return value isn't known
var output;
if (base === null || base === void 0 ? void 0 : base[part.name]) {
var obj = base[part.name];
if (part.type !== 1 /* Type.Array */ && match(obj, part.condition)) {
output = evaluate(variable, obj);
}
else if (Array.isArray(obj)) {
var filtered = [];
for (var _i = 0, obj_1 = obj; _i < obj_1.length; _i++) {
var value = obj_1[_i];
if (match(value, part.condition)) {
var op = evaluate(variable, value);
if (op) {
filtered.push(op);
}
}
}
output = filtered;
}
return output;
}
return null;
}
function str$1(input) {
// Automatically trim string to max of Setting.ExtractLimit to avoid fetching long strings
return input ? JSON.stringify(input).slice(0, 10000 /* Setting.ExtractLimit */) : input;
}
// biome-ignore lint/complexity/noBannedTypes: type of base is intentionally generic
function match(base, condition) {
if (condition) {
var prop = condition.split(":");
return prop.length > 1 ? base[prop[0]] === prop[1] : base[prop[0]];
}
return true;
}
// biome-ignore lint/complexity/noBannedTypes: type of obj is intentionally generic
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
var extract = /*#__PURE__*/Object.freeze({
__proto__: null,
clone: clone$1,
compute: compute$d,
data: data$i,
keys: keys,
reset: reset$r,
start: start$G,
stop: stop$D,
trigger: trigger$2,
update: update$1
});
var data$h = null;
var updates$3 = null;
function start$F() {
data$h = {};
updates$3 = {};
count$1(5 /* Metric.InvokeCount */);
}
function stop$C() {
data$h = {};
updates$3 = {};
}
function count$1(metric) {
if (!(metric in data$h)) {
data$h[metric] = 0;
}
if (!(metric in updates$3)) {
updates$3[metric] = 0;
}
data$h[metric]++;
updates$3[metric]++;
}
function sum(metric, value) {
if (value !== null) {
if (!(metric in data$h)) {
data$h[metric] = 0;
}
if (!(metric in updates$3)) {
updates$3[metric] = 0;
}
data$h[metric] += value;
updates$3[metric] += value;
}
}
function max(metric, value) {
// Ensure that we do not process null or NaN values
if (value !== null && Number.isNaN(value) === false) {
if (!(metric in data$h)) {
data$h[metric] = 0;
}
if (value > data$h[metric] || data$h[metric] === 0) {
updates$3[metric] = value;
data$h[metric] = value;
}
}
}
function compute$c() {
encode$2(0 /* Event.Metric */);
}
function reset$q() {
updates$3 = {};
}
function setTimeout$1(handler, timeout, event) {
return window.setTimeout(measure(handler), timeout, event);
}
function clearTimeout(handle) {
window.clearTimeout(handle);
}
var data$g;
var last = 0;
var interval = 0;
var timeout$7 = null;
function start$E() {
interval = 60000 /* Setting.PingInterval */;
last = 0;
}
function reset$p() {
if (timeout$7) {
clearTimeout(timeout$7);
}
timeout$7 = setTimeout$1(ping, interval);
last = time();
}
function ping() {
var now = time();
data$g = { gap: now - last };
encode$2(25 /* Event.Ping */);
if (data$g.gap < 300000 /* Setting.PingTimeout */) {
timeout$7 = setTimeout$1(ping, interval);
}
else {
suspend();
}
}
function stop$B() {
clearTimeout(timeout$7);
last = 0;
interval = 0;
}
var ping$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
get data () { return data$g; },
reset: reset$p,
start: start$E,
stop: stop$B
});
var data$f = null;
function start$D() {
data$f = {};
}
function stop$A() {
data$f = {};
}
function track$7(event, time) {
if (!(event in data$f)) {
data$f[event] = [[time, 0]];
}
else {
var e = data$f[event];
var last = e[e.length - 1];
// Add a new entry only if the new event occurs after configured interval
// Otherwise, extend the duration of the previous entry
if (time - last[0] > 100 /* Setting.SummaryInterval */) {
data$f[event].push([time, 0]);
}
else {
last[1] = time - last[0];
}
}
}
function compute$b() {
encode$2(36 /* Event.Summary */);
}
function reset$o() {
data$f = {};
}
var summary = /*#__PURE__*/Object.freeze({
__proto__: null,
compute: compute$b,
get data () { return data$f; },
reset: reset$o,
start: start$D,
stop: stop$A,
track: track$7
});
// Track the start time to be able to compute duration at the end of the task
var idleTimeout = 5000;
var tracker = {};
var queuedTasks = [];
var activeTask = null;
var pauseTask = null;
var resumeResolve = null;
function pause$1() {
if (pauseTask === null) {
pauseTask = new Promise(function (resolve) {
resumeResolve = resolve;
});
}
}
function resume$1() {
if (pauseTask) {
resumeResolve();
pauseTask = null;
if (activeTask === null) {
run();
}
}
}
function reset$n() {
tracker = {};
queuedTasks = [];
activeTask = null;
pauseTask = null;
}
function schedule$1(task, priority) {
if (priority === void 0) { priority = 0 /* Priority.Normal */; }
return __awaiter(this, void 0, void 0, function () {
var _i, queuedTasks_1, q, promise;
return __generator(this, function (_a) {
// If this task is already scheduled, skip it
for (_i = 0, queuedTasks_1 = queuedTasks; _i < queuedTasks_1.length; _i++) {
q = queuedTasks_1[_i];
if (q.task === task) {
return [2 /*return*/];
}
}
promise = new Promise(function (resolve) {
var insert = priority === 1 /* Priority.High */ ? "unshift" : "push";
// Queue this task for asynchronous execution later
// We also store a unique page identifier (id) along with the task to ensure
// ensure that we do not accidentally execute this task in context of a different page
queuedTasks[insert]({ task: task, resolve: resolve, id: id() });
});
// If there is no active task running, and Clarity is not in pause state,
// invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
if (activeTask === null && pauseTask === null) {
run();
}
return [2 /*return*/, promise];
});
});
}
function run() {
var entry = queuedTasks.shift();
if (entry) {
activeTask = entry;
entry
.task()
.then(function () {
// Bail out if the context in which this task was operating is different from the current page
// An example scenario where task could span across pages is Single Page Applications (SPA)
// A task that started on page #1, but completes on page #2
if (entry.id !== id()) {
return;
}
entry.resolve();
activeTask = null; // Reset active task back to null now that the promise is resolved
run();
})
.catch(function (error) {
// If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
if (entry.id !== id()) {
return;
}
if (error) {
log(0 /* Code.RunTask */, 1 /* Severity.Warning */, error.name, error.message, error.stack);
}
activeTask = null;
run();
});
}
}
function state$a(timer) {
var id = key(timer);
if (id in tracker) {
var elapsed = performance.now() - tracker[id].start;
return elapsed > tracker[id].yield ? 0 /* Task.Wait */ : 1 /* Task.Run */;
}
// If this task is no longer being tracked, send stop message to the caller
return 2 /* Task.Stop */;
}
function start$C(timer) {
tracker[key(timer)] = { start: performance.now(), calls: 0, yield: 30 /* Setting.LongTask */ };
}
function restart$2(timer) {
var id = key(timer);
if (tracker === null || tracker === void 0 ? void 0 : tracker[id]) {
var c = tracker[id].calls;
var y = tracker[id].yield;
start$C(timer);
tracker[id].calls = c + 1;
tracker[id].yield = y;
}
}
function stop$z(timer) {
var end = performance.now();
var id = key(timer);
var duration = end - tracker[id].start;
sum(timer.cost, duration);
count$1(5 /* Metric.InvokeCount */);
// For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
// However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
if (tracker[id].calls > 0) {
sum(4 /* Metric.TotalCost */, duration);
}
}
function suspend$1(timer) {
var _a;
return __awaiter(this, void 0, void 0, function () {
var id, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
id = key(timer);
if (!(id in tracker)) return [3 /*break*/, 2];
stop$z(timer);
// some customer polyfills for requestIdleCallback return null
_b = tracker[id];
return [4 /*yield*/, wait()];
case 1:
// some customer polyfills for requestIdleCallback return null
_b.yield = ((_a = (_c.sent())) === null || _a === void 0 ? void 0 : _a.timeRemaining()) || 30 /* Setting.LongTask */;
restart$2(timer);
_c.label = 2;
case 2:
// After we are done with suspending task, ensure that we are still operating in the right context
// If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
return [2 /*return*/, id in tracker ? 1 /* Task.Run */ : 2 /* Task.Stop */];
}
});
});
}
function key(timer) {
return "".concat(timer.id, ".").concat(timer.cost);
}
function wait() {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!pauseTask) return [3 /*break*/, 2];
return [4 /*yield*/, pauseTask];
case 1:
_a.sent();
_a.label = 2;
case 2: return [2 /*return*/, new Promise(function (resolve) {
requestIdleCallback(resolve, { timeout: idleTimeout });
})];
}
});
});
}
// Use native implementation of requestIdleCallback if it exists.
// Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
// While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
// Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
// This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
// For non-blocking behavior, We need to know when browser has finished painting. This can be accomplished in two different ways (hacks):
// (1) Use MessageChannel to pass the message, and browser will receive the message right after paint event has occured.
// (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
// Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
// https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
function requestIdleCallbackPolyfill(callback, options) {
var startTime = performance.now();
var channel = new MessageChannel();
var incoming = channel.port1;
var outgoing = channel.port2;
incoming.onmessage = function (event) {
var currentTime = performance.now();
var elapsed = currentTime - startTime;
var duration = currentTime - event.data;
if (duration > 30 /* Setting.LongTask */ && elapsed < options.timeout) {
requestAnimationFrame(function () {
outgoing.postMessage(currentTime);
});
}
else {
var didTimeout_1 = elapsed > options.timeout;
callback({
didTimeout: didTimeout_1,
timeRemaining: function () { return (didTimeout_1 ? 30 /* Setting.LongTask */ : Math.max(0, 30 /* Setting.LongTask */ - duration)); },
});
}
};
requestAnimationFrame(function () {
outgoing.postMessage(performance.now());
});
}
var requestIdleCallback = window.requestIdleCallback || requestIdleCallbackPolyfill;
/**
* Creates a throttled version of the provided function that only executes at most once
* every specified duration in milliseconds, ensuring the last event is not lost.
* @param func - The function to throttle.
* @param duration - The duration in milliseconds to wait before allowing the next execution.
* @returns A throttled version of the provided function.
*/
function throttle(func, duration) {
var lastExecutionTime = 0;
var timeoutId = null;
var lastArgs = null;
return function () {
var _this = this;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var now = performance.now();
var timeSinceLastExecution = now - lastExecutionTime;
// If the function is called during the throttling period, store the arguments to ensure we don't drop the last event
if (lastExecutionTime !== 0 && timeSinceLastExecution < duration) {
lastArgs = args;
if (timeoutId)
return;
timeoutId = setTimeout(function () {
lastExecutionTime = performance.now();
func.apply(_this, lastArgs);
lastArgs = null;
timeoutId = null;
}, duration - timeSinceLastExecution);
}
else {
// Execute immediately if outside the throttling period (including the first run)
lastExecutionTime = now;
func.apply(this, args);
}
};
}
var history$5 = [];
var data$e;
function start$B() {
history$5 = [];
max(26 /* Metric.Automation */, navigator.webdriver ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */);
try {
// some sites (unintentionally) overwrite the window.self property, so we also check for the main window object
max(31 /* Metric.Iframed */, window.top === window.self || window.top === window ? 1 /* IframeStatus.TopFrame */ : 2 /* IframeStatus.Iframe */);
}
catch (ex) {
max(31 /* Metric.Iframed */, 0 /* IframeStatus.Unknown */);
}
}
function check$4(id, target, input) {
// Compute hash for fraud detection, if enabled. Hash is computed only if input meets the minimum length criteria
if (config$2.fraud && id !== null && input && input.length >= 5 /* Setting.WordLength */) {
data$e = { id: id, target: target, checksum: hash(input, 28 /* Setting.ChecksumPrecision */) };
// Only encode this event if we haven't already reported this hash
if (history$5.indexOf(data$e.checksum) < 0) {
history$5.push(data$e.checksum);
encode$1(41 /* Event.Fraud */);
}
}
}
// Following code takes an array of tokens and transforms it to optimize for repeating tokens and make it efficient to send over the wire
// 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
// If so, it replaces the token with its reference (index). This helps us save bytes by not repeating the same value twice.
// E.g. If tokens array is: ["hello", "world", "coding", "language", "world", "language", "example"]
// Then the resulting tokens array after following code execution would be: ["hello", "world", "coding", "language", [1, 3], "example"]
// Where [1,3] points to tokens[1] => "world" and tokens[3] => "language"
function tokenize (tokens) {
var output = [];
var lookup = {};
var pointer = 0;
var reference = null;
for (var i = 0; i < tokens.length; i++) {
// Only optimize for string values
if (typeof tokens[i] === "string") {
var token = tokens[i];
var index = lookup[token] || -1;
if (index >= 0) {
if (reference) {
reference.push(index);
}
else {
reference = [index];
output.push(reference);
pointer++;
}
}
else {
reference = null;
output.push(token);
lookup[token] = pointer++;
}
}
else {
// If the value is anything other than string, append it as it is to the output array
// And, also increment the pointer to stay in sync with output array
reference = null;
output.push(tokens[i]);
pointer++;
}
}
return output;
}
var state$9 = [];
var elementAnimate = null;
var animationPlay = null;
var animationPause = null;
var animationCommitStyles = null;
var animationCancel = null;
var animationFinish = null;
var animationId = "clarityAnimationId";
var operationCount = "clarityOperationCount";
var maxOperations = 20;
function start$A() {
var _a;
if (((_a = window.Animation) === null || _a === void 0 ? void 0 : _a.prototype) &&
window.KeyframeEffect &&
window.KeyframeEffect.prototype &&
window.KeyframeEffect.prototype.getKeyframes &&
window.KeyframeEffect.prototype.getTiming) {
reset$m();
overrideAnimationHelper(animationPlay, "play");
overrideAnimationHelper(animationPause, "pause");
overrideAnimationHelper(animationCommitStyles, "commitStyles");
overrideAnimationHelper(animationCancel, "cancel");
overrideAnimationHelper(animationFinish, "finish");
if (elementAnimate === null) {
elementAnimate = Element.prototype.animate;
Element.prototype.animate = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var createdAnimation = elementAnimate.apply(this, args);
trackAnimationOperation(createdAnimation, "play");
return createdAnimation;
};
}
if (document.getAnimations) {
for (var _i = 0, _b = document.getAnimations(); _i < _b.length; _i++) {
var animation = _b[_i];
if (animation.playState === "finished") {
trackAnimationOperation(animation, "finish");
}
else if (animation.playState === "paused" || animation.playState === "idle") {
trackAnimationOperation(animation, "pause");
}
else if (animation.playState === "running") {
trackAnimationOperation(animation, "play");
}
}
}
}
}
function reset$m() {
state$9 = [];
}
function track$6(time, id, operation, keyFrames, timing, targetId, timeline) {
state$9.push({
time: time,
event: 44 /* Event.Animation */,
data: {
id: id,
operation: operation,
keyFrames: keyFrames,
timing: timing,
targetId: targetId,
timeline: timeline,
},
});
encode$4(44 /* Event.Animation */);
}
function stop$y() {
reset$m();
}
function overrideAnimationHelper(functionToOverride, name) {
if (functionToOverride === null) {
// biome-ignore lint/style/noParameterAssign: function intentionally reassigns parameter for shimming
functionToOverride = Animation.prototype[name];
Animation.prototype[name] = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
trackAnimationOperation(this, name);
functionToOverride.apply(this, args);
};
}
}
function trackAnimationOperation(animation, name) {
if (active()) {
var effect = animation.effect;
var target = (effect === null || effect === void 0 ? void 0 : effect.target) ? getId(effect.target) : null;
if (target !== null && effect.getKeyframes && effect.getTiming) {
if (!animation[animationId]) {
animation[animationId] = shortid();
animation[operationCount] = 0;
var keyframes = effect.getKeyframes();
var timing = effect.getTiming();
track$6(time(), animation[animationId], 0 /* AnimationOperation.Create */, JSON.stringify(keyframes), JSON.stringify(timing), target);
}
if (animation[operationCount]++ < maxOperations) {
var operation = null;
switch (name) {
case "play":
operation = 1 /* AnimationOperation.Play */;
break;
case "pause":
operation = 2 /* AnimationOperation.Pause */;
break;
case "cancel":
operation = 3 /* AnimationOperation.Cancel */;
break;
case "finish":
operation = 4 /* AnimationOperation.Finish */;
break;
case "commitStyles":
operation = 5 /* AnimationOperation.CommitStyles */;
break;
}
if (operation) {
track$6(time(), a