clarity-js
Version:
An analytics library that uses web page interactions to generate aggregated insights
1,265 lines (1,233 loc) • 262 kB
JavaScript
'use strict';
var clarity = /*#__PURE__*/Object.freeze({
__proto__: null,
get consent () { return consent; },
get consentv2 () { return consentv2; },
get dlog () { return log; },
get event () { return event$1; },
get hashText () { return hashText; },
get identify () { return identify; },
get maxMetric () { return max; },
get measure () { return measure; },
get metadata () { return metadata; },
get pause () { return pause; },
get queue () { return queue; },
get register () { return register; },
get resume () { return resume; },
get schedule () { return schedule; },
get set () { return set; },
get signal () { return signal; },
get start () { return start; },
get stop () { return stop; },
get time () { return time; },
get upgrade () { return upgrade; },
get version () { return version$1; }
});
var w = window;
var c = "clarity" /* Constant.Clarity */;
function setup() {
// Start queuing up calls while Clarity is inactive and we are in a browser enviornment
if (typeof w !== "undefined") {
w[c] = function () {
(w[c].q = w[c].q || []).push(arguments);
// if the start function was called, don't queue it and instead process the queue
arguments[0] === "start" && w[c].q.unshift(w[c].q.pop()) && process$8();
};
}
}
function process$8() {
if (typeof w !== "undefined") {
// Do not execute or reset global "clarity" variable if a version of Clarity is already running on the page
if (w[c] && w[c].v) {
return console.warn("Error CL001: Multiple Clarity tags detected.");
}
// Expose clarity in a browser environment
// To be efficient about queuing up operations while Clarity is wiring up, we expose clarity.*(args) => clarity(*, args);
// This allows us to reprocess any calls that we missed once Clarity is available on the page
// Once Clarity script bundle is loaded on the page, we also initialize a "v" property that holds current version
// We use the presence or absence of "v" to determine if we are attempting to run a duplicate instance
var queue = w[c] ? (w[c].q || []) : [];
w[c] = function (method) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
return clarity[method].apply(clarity, args);
};
w[c].v = version$1;
while (queue.length > 0) {
w[c].apply(w, queue.shift());
}
}
}
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,
modules: [],
diagnostics: false,
};
function api(method) {
// Zone.js, a popular package for Angular, overrides native browser APIs which can lead to inconsistent state for single page applications.
// Example issue: https://github.com/angular/angular/issues/31712
// As a work around, we ensuring Clarity access APIs outside of Zone (and use native implementation instead)
return window["Zone" /* Constant.Zone */] && "__symbol__" /* Constant.Symbol */ in window["Zone" /* Constant.Zone */] ? window["Zone" /* Constant.Zone */]["__symbol__" /* Constant.Symbol */](method) : method;
}
var startTime = 0;
function computeStartTime() {
return performance.now() + performance.timeOrigin;
}
function start$O() {
startTime = computeStartTime();
}
// 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; }
// If startTime is 0, Clarity hasn't been started or has been stopped
// Use a local baseline to maintain relative timing semantics without affecting global state
var baseline = startTime === 0 ? computeStartTime() : startTime;
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 - baseline), 0);
}
function stop$K() {
startTime = 0;
}
var version$1 = "0.8.64";
// tslint:disable: no-bitwise
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 catchallRegex = /\S/gi;
var maxUrlLength = 2048;
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":
case "href":
case "xlink:href":
if (privacy === 3 /* Privacy.TextImage */) {
return (value === null || value === void 0 ? void 0 : value.startsWith('blob:')) ? 'blob:' : "" /* Data.Constant.Empty */;
}
return 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 = "https://" /* Data.Constant.HTTPS */ + "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 = path + "?" + query.split("&").map(function (p) { return drop_1.some(function (x) { return p.indexOf(x + "=") === 0; }) ? (p.split("=")[0] + "=" + 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 prefix + trimmed.length.toString(36) + 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 = new RegExp("\\p{N}", "gu");
letterRegex = new RegExp("\\p{L}", "gu");
currencyRegex = new RegExp("\\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 state$b = null;
var buffer = null;
var update$2 = false;
function start$N() {
update$2 = false;
reset$u();
}
function reset$u() {
// 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: Object.assign({}, buffer) };
}
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,
modules: null,
};
}
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;
updatePointer(x, y, time);
break;
case 13 /* Event.MouseDown */:
buffer.downX = x;
buffer.downY = y;
buffer.downTime = time;
updatePointer(x, y, time);
break;
case 14 /* Event.MouseUp */:
buffer.upX = x;
buffer.upY = y;
buffer.upTime = time;
updatePointer(x, y, time);
break;
default:
updatePointer(x, y, time);
break;
}
update$2 = true;
}
function updatePointer(x, y, time) {
buffer.pointerPrevX = buffer.pointerX;
buffer.pointerPrevY = buffer.pointerY;
buffer.pointerPrevTime = buffer.pointerTime;
buffer.pointerX = x;
buffer.pointerY = y;
buffer.pointerTime = time;
}
function activity(t) {
buffer.activityTime = t;
}
function visibility(t, visible) {
buffer.visible = visible;
if (!buffer.visible) {
activity(t);
}
update$2 = true;
}
function dynamic(modules) {
buffer.modules = Array.from(modules);
update$2 = true;
}
function compute$g() {
if (update$2) {
encode$1(4 /* Event.Baseline */);
}
}
function stop$J() {
reset$u();
}
var data$l = 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$1(a, b) {
if (active() &&
a &&
typeof a === "string" /* Constant.String */ &&
a.length < 255) {
if (b && typeof b === "string" /* Constant.String */ && b.length < 255) {
data$l = { key: a, value: b };
}
else {
data$l = { value: a };
}
encode$1(24 /* Event.Custom */);
}
}
var data$k = null;
var updates$3 = null;
function start$M() {
data$k = {};
updates$3 = {};
count$1(5 /* Metric.InvokeCount */);
}
function stop$I() {
data$k = {};
updates$3 = {};
}
function init(metric) {
if (!(metric in data$k)) {
data$k[metric] = 0;
}
if (!(metric in updates$3)) {
updates$3[metric] = 0;
}
}
function count$1(metric) {
init(metric);
data$k[metric]++;
updates$3[metric]++;
}
function sum(metric, value) {
if (value !== null) {
init(metric);
data$k[metric] += value;
updates$3[metric] += value;
}
}
function max(metric, value) {
// Ensure that we do not process null or NaN values
if (value !== null && isNaN(value) === false) {
if (!(metric in data$k)) {
data$k[metric] = 0;
}
if (value > data$k[metric] || data$k[metric] === 0) {
updates$3[metric] = value;
data$k[metric] = value;
}
}
}
function compute$f() {
encode$1(0 /* Event.Metric */);
}
function reset$t() {
updates$3 = {};
}
function setTimeout$1(handler, timeout, event) {
return window.setTimeout(measure(handler), timeout, event);
}
function clearTimeout$1(handle) {
return window.clearTimeout(handle);
}
var data$j;
var last = 0;
var interval = 0;
var timeout$7 = null;
function start$L() {
interval = 60000 /* Setting.PingInterval */;
last = 0;
}
function reset$s() {
if (timeout$7) {
clearTimeout$1(timeout$7);
}
timeout$7 = setTimeout$1(ping, interval);
last = time();
}
function ping() {
var now = time();
data$j = { gap: now - last };
encode$1(25 /* Event.Ping */);
if (data$j.gap < 300000 /* Setting.PingTimeout */) {
timeout$7 = setTimeout$1(ping, interval);
}
else {
suspend();
}
}
function stop$H() {
clearTimeout$1(timeout$7);
last = 0;
interval = 0;
}
var data$i = null;
function start$K() {
data$i = {};
}
function stop$G() {
data$i = {};
}
function track$7(event, time) {
if (!(event in data$i)) {
data$i[event] = [[time, 0]];
}
else {
var e = data$i[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$i[event].push([time, 0]);
}
else {
last[1] = time - last[0];
}
}
}
function compute$e() {
encode$1(36 /* Event.Summary */);
}
function reset$r() {
data$i = {};
}
/******************************************************************************
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 supported$1 = "CompressionStream" /* Constant.CompressionStream */ in window;
function compress (input) {
return __awaiter(this, void 0, void 0, function () {
var stream, _a;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_c.trys.push([0, 3, , 4]);
if (!supported$1) return [3 /*break*/, 2];
stream = new ReadableStream({ start: function (controller) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
controller.enqueue(input);
controller.close();
return [2 /*return*/];
});
});
} }).pipeThrough(new TextEncoderStream()).pipeThrough(new window["CompressionStream" /* Constant.CompressionStream */]("gzip"));
_a = Uint8Array.bind;
return [4 /*yield*/, new Response(stream).arrayBuffer()];
case 1: return [2 /*return*/, new (_a.apply(Uint8Array, [void 0, _c.sent()]))()];
case 2: return [3 /*break*/, 4];
case 3:
_c.sent();
return [3 /*break*/, 4];
case 4: return [2 /*return*/, null];
}
});
});
}
var data$h = null;
function start$J() {
reset$q();
}
function set(variable, value) {
var values = typeof value === "string" /* Constant.String */ ? [value] : value;
log$2(variable, values);
}
function identify(userId, sessionId, pageId, userHint) {
if (sessionId === void 0) { sessionId = null; }
if (pageId === void 0) { pageId = null; }
if (userHint === void 0) { userHint = null; }
return __awaiter(this, void 0, void 0, function () {
var output;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = {};
return [4 /*yield*/, sha256(userId)];
case 1:
output = (_a.userId = _b.sent(), _a.userHint = userHint || redact(userId), _a);
// By default, hash custom userId using SHA256 algorithm on the client to preserve privacy
log$2("userId" /* Constant.UserId */, [output.userId]);
// Optional non-identifying name for the user
// If name is not explicitly provided, we automatically generate a redacted version of the userId
log$2("userHint" /* Constant.UserHint */, [output.userHint]);
log$2("userType" /* Constant.UserType */, [detect(userId)]);
// Log sessionId and pageId if provided
if (sessionId) {
log$2("sessionId" /* Constant.SessionId */, [sessionId]);
output.sessionId = sessionId;
}
if (pageId) {
log$2("pageId" /* Constant.PageId */, [pageId]);
output.pageId = pageId;
}
return [2 /*return*/, output];
}
});
});
}
function log$2(variable, value) {
if (active() &&
variable &&
value &&
typeof variable === "string" /* Constant.String */ &&
variable.length < 255) {
var validValues = variable in data$h ? data$h[variable] : [];
for (var i = 0; i < value.length; i++) {
if (typeof value[i] === "string" /* Constant.String */ && value[i].length < 255) {
validValues.push(value[i]);
}
}
data$h[variable] = validValues;
}
}
function compute$d() {
encode$1(34 /* Event.Variable */);
}
function reset$q() {
data$h = {};
}
function stop$F() {
reset$q();
}
function redact(input) {
return input && input.length >= 5 /* Setting.WordLength */ ?
input.substring(0, 2) + scrub(input.substring(2), "*" /* Constant.Asterix */, "*" /* Constant.Asterix */) : scrub(input, "*" /* Constant.Asterix */, "*" /* Constant.Asterix */);
}
function sha256(input) {
return __awaiter(this, void 0, void 0, function () {
var buffer;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 4, , 5]);
if (!(crypto && input)) return [3 /*break*/, 2];
return [4 /*yield*/, crypto.subtle.digest("SHA-256" /* Constant.SHA256 */, new TextEncoder().encode(input))];
case 1:
buffer = _b.sent();
return [2 /*return*/, Array.prototype.map.call(new Uint8Array(buffer), function (x) { return (('00' + x.toString(16)).slice(-2)); }).join('')];
case 2: return [2 /*return*/, "" /* Constant.Empty */];
case 3: return [3 /*break*/, 5];
case 4:
_b.sent();
return [2 /*return*/, "" /* Constant.Empty */];
case 5: return [2 /*return*/];
}
});
});
}
function detect(input) {
return input && input.indexOf("@" /* Constant.At */) > 0 ? "email" /* Constant.Email */ : "string" /* Constant.String */;
}
var data$g = {};
var keys = new Set();
var variables = {};
var selectors = {};
var hashes = {};
var validation = {};
function start$I() {
reset$p();
}
// 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 = 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 = 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$1(8 /* Code.Config */, 1 /* Severity.Warning */, e ? e.name : null);
}
}
function clone$1(v) {
return JSON.parse(JSON.stringify(v));
}
function compute$c() {
try {
for (var v in variables) {
var key = parseInt(v);
if (validation[key] == "" /* Constant.Empty */ || document.querySelector(validation[key])) {
var variableData = variables[key];
for (var v_1 in variableData) {
var variableKey = parseInt(v_1);
var value = str(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 = 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) {
if (e.tagName === "IMG") {
var img = e;
return img.src || img.currentSrc || "" /* Constant.Empty */;
}
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 = parseInt(h);
var content = hashText(hashData[hashKey]).trim().slice(0, 10000 /* Setting.ExtractLimit */);
update$1(key, hashKey, content);
}
}
}
if (keys.size > 0) {
encode$1(40 /* Event.Extract */);
}
}
catch (e) {
log$1(5 /* Code.Selector */, 1 /* Severity.Warning */, e ? e.name : null);
}
}
function reset$p() {
keys.clear();
}
function update$1(key, subkey, value) {
var update = false;
if (!(key in data$g)) {
data$g[key] = {};
update = true;
}
if (!isEmpty(hashes[key])
&& (!(subkey in data$g[key]) || data$g[key][subkey] != value)) {
update = true;
}
data$g[key][subkey] = value;
if (update) {
keys.add(key);
}
return;
}
function stop$E() {
reset$p();
}
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"].
function evaluate(variable, base) {
if (base === void 0) { base = window; }
if (variable.length == 0) {
return base;
}
var part = variable.shift();
var output;
if (base && 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(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;
}
function match(base, condition) {
if (condition) {
var prop = condition.split(":");
return prop.length > 1 ? base[prop[0]] == prop[1] : base[prop[0]];
}
return true;
}
function isEmpty(obj) {
return Object.keys(obj).length == 0;
}
function supported(target, api) {
try {
return !!target[api];
}
catch (_a) {
return false;
}
}
function encodeCookieValue(value) {
return encodeURIComponent(value);
}
function decodeCookieValue(value) {
try {
var decodedValue = decodeURIComponent(value);
return [decodedValue != value, decodedValue];
}
catch (_a) {
}
return [false, value];
}
var rootDomain = null;
var COOKIE_SEP = "^" /* Constant.Caret */;
function start$H() {
rootDomain = null;
}
function stop$D() {
rootDomain = null;
}
function getCookie(key, limit) {
var _a;
if (limit === void 0) { limit = false; }
if (supported(document, "cookie" /* Constant.Cookie */)) {
var cookies = document.cookie.split(";" /* Constant.Semicolon */);
if (cookies) {
for (var i = 0; i < cookies.length; i++) {
var pair = cookies[i].split("=" /* Constant.Equals */);
if (pair.length > 1 && pair[0] && pair[0].trim() === key) {
// Some browsers automatically url encode cookie values if they are not url encoded.
// We therefore encode and decode cookie values ourselves.
// For backwards compatability we need to consider 3 cases:
// * Cookie was previously not encoded by Clarity and browser did not encode it
// * Cookie was previously not encoded by Clarity and browser encoded it once or more
// * Cookie was previously encoded by Clarity and browser did not encode it
var _b = decodeCookieValue(pair[1]), isEncoded = _b[0], decodedValue = _b[1];
while (isEncoded) {
_a = decodeCookieValue(decodedValue), isEncoded = _a[0], decodedValue = _a[1];
}
// If we are limiting cookies, check if the cookie value is limited
if (limit) {
return decodedValue.endsWith("~" /* Constant.Tilde */ + "1") ? decodedValue.substring(0, decodedValue.length - 2) : null;
}
return decodedValue;
}
}
}
}
return null;
}
function setCookie(key, value, time) {
// only write cookies if we are currently in a cookie writing mode (and they are supported)
// OR if we are trying to write an empty cookie (i.e. clear the cookie value out)
if ((config$2.track || value == "" /* Constant.Empty */) && ((navigator && navigator.cookieEnabled) || supported(document, "cookie" /* Constant.Cookie */))) {
// Some browsers automatically url encode cookie values if they are not url encoded.
// We therefore encode and decode cookie values ourselves.
var encodedValue = encodeCookieValue(value);
var expiry = new Date();
expiry.setDate(expiry.getDate() + time);
var expires = expiry ? "expires=" /* Constant.Expires */ + expiry.toUTCString() : "" /* Constant.Empty */;
var cookie = key + "=" + encodedValue + ";" /* Constant.Semicolon */ + expires + ";path=/" /* Constant.Path */;
try {
// Attempt to get the root domain only once and fall back to writing cookie on the current domain.
if (rootDomain === null) {
var hostname = location.hostname ? location.hostname.split("." /* Constant.Dot */) : [];
// Walk backwards on a domain and attempt to set a cookie, until successful
for (var i = hostname.length - 1; i >= 0; i--) {
rootDomain = "." + hostname[i] + (rootDomain ? rootDomain : "" /* Constant.Empty */);
// We do not wish to attempt writing a cookie on the absolute last part of the domain, e.g. .com or .net.
// So we start attempting after second-last part, e.g. .domain.com (PASS) or .co.uk (FAIL)
if (i < hostname.length - 1) {
// Write the cookie on the current computed top level domain
document.cookie = cookie + ";" /* Constant.Semicolon */ + "domain=" /* Constant.Domain */ + rootDomain;
// Once written, check if the cookie exists and its value matches exactly with what we intended to set
// Checking for exact value match helps us eliminate a corner case where the cookie may already be present with a different value
// If the check is successful, no more action is required and we can return from the function since rootDomain cookie is already set
// If the check fails, continue with the for loop until we can successfully set and verify the cookie
if (getCookie(key) === value) {
return;
}
}
}
// Finally, if we were not successful and gone through all the options, play it safe and reset rootDomain to be empty
// This forces our code to fall back to always writing cookie to the current domain
rootDomain = "" /* Constant.Empty */;
}
}
catch (_a) {
rootDomain = "" /* Constant.Empty */;
}
document.cookie = rootDomain ? cookie + ";" /* Constant.Semicolon */ + "domain=" /* Constant.Domain */ + rootDomain : cookie;
}
}
var signalCallback = null;
function signal(cb) {
signalCallback = cb;
}
function signalsEvent(signalsPayload) {
try {
if (signalCallback) {
JSON.parse(signalsPayload).forEach(function (s) { return signalCallback(s); });
}
}
catch (_a) {
//do nothing
}
}
var modules$2 = [
{ start: start$N, stop: stop$J },
{ start: start$a, stop: stop$a },
{ start: start$J, stop: stop$F },
{ start: start$b, stop: stop$b },
{ start: start$K, stop: stop$G },
{ start: start$H, stop: stop$D },
{ start: start$9, stop: stop$9 },
{ start: start$8, stop: stop$8 },
{ start: start$7, stop: stop$7 },
{ start: start$h, stop: stop$f },
{ start: start$L, stop: stop$H },
{ start: start$c, stop: stop$c },
{ start: start$I, stop: stop$E },
];
function start$G() {
// Metric needs to be initialized before we can start measuring. so metric is not wrapped in measure
start$M();
modules$2.forEach(function (x) { return measure(x.start)(); });
}
function stop$C() {
// Stop modules in the reverse order of their initialization
// The ordering below should respect inter-module dependency.
// E.g. if upgrade depends on upload, then upgrade needs to end before upload.
// Similarly, if upload depends on metadata, upload needs to end before metadata.
modules$2.slice().reverse().forEach(function (x) { return measure(x.stop)(); });
stop$I();
}
function compute$b() {
compute$d();
compute$g();
compute$3();
compute$f();
compute$e();
compute$4();
compute$c();
compute$2();
}
var history$5 = [];
var data$f;
function start$F() {
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$5(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$f = { 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.includes(data$f.checksum)) {
history$5.push(data$f.checksum);
encode$2(41 /* Event.Fraud */);
}
}
}
/**
* Pre-computed arrays for better minification and performance
* These replace comma-separated strings that were previously split at runtime
*/
// Pre-computed mask arrays (replaces Mask.Text.split(), etc.)
var MaskTextList = ["address", "password", "contact"];
var MaskDisableList = ["radio", "checkbox", "range", "button", "reset", "submit"];
var MaskExcludeList = ["password", "secret", "pass", "social", "ssn", "code", "hidden"];
var MaskTagsList = ["INPUT", "SELECT", "TEXTAREA"];
// Pre-computed exclude class names (replaces Constant.ExcludeClassNames.split())
var ExcludeClassNamesList = ["load", "active", "fixed", "visible", "focus", "show", "collaps", "animat"];
var state$a = [];
function start$E() {
reset$o();
}
function observe$c(root) {
bind(root, "change", recompute$8, true);
}
function recompute$8(evt) {
var element = target(evt);
if (element) {
var value = element.value;
var checksum = value && value.length >= 5 /* Setting.WordLength */ && config$2.fraud && !MaskExcludeList.includes(element.type) ? hash(value, 28 /* Setting.ChecksumPrecision */) : "" /* Constant.Empty */;
state$a.push({ time: time(evt), event: 42 /* Event.Change */, data: { target: target(evt), type: element.type, value: value, checksum: checksum } });
schedule(encode$4.bind(this, 42 /* Event.Change */));
}
}
function reset$o() {
state$a = [];
}
function stop$B() {
reset$o();
}
function offset(element) {
var output = { x: 0, y: 0 };
// Walk up the chain to ensure we compute offset distance correctly
// In case where we may have nested IFRAMEs, we keep walking up until we get to the top most parent page
if (element && element.offsetParent) {
do {
var parent_1 = element.offsetParent;
var frame = parent_1 === null ? iframe(element.ownerDocument) : null;
output.x += element.offsetLeft;
output.y += element.offsetTop;
element = frame ? frame : parent_1;
} while (element);
}
return output;
}
var UserInputTags = ["input", "textarea", "radio", "button", "canvas", "select"];
var VM_PATTERN = /VM\d/;
var state$9 = [];
function start$D() {
reset$n();
}
function observe$b(root) {
bind(root, "click", handler$4.bind(this, 9 /* Event.Click */, root), true);
bind(root, "contextmenu", handler$4.bind(this, 48 /* Event.ContextMenu */, root), true);
}
function handler$4(event, root, evt) {
var frame = iframe(root);
var d = frame && frame.contentDocument ? frame.contentDocument.documentElement : document.documentElement;
var x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
var y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
// In case of iframe, we adjust (x,y) to be relative to top parent's origin
if (frame) {
var distance = offset(frame);
x = x ? x + Math.round(distance.x) : x;
y = y ? y + Math.round(distance.y) : y;
}
var t = target(evt);
// Find nearest anchor tag (<a/>) parent if current target node is part of one
// If present, we use the returned link element to populate text and link properties below
var a = link(t);
// Get layout rectangle for the target element
var l = layout(t);
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
// This property helps differentiate between a keyboard navigation vs. pointer click
// In case of a keyboard navigation, we use center of target element as (x,y)
if (evt.detail === 0 && l) {
x = Math.round(l.x + (l.w / 2));
y = Math.round(l.y + (l.h / 2));
}
var relativeCoords = computeRelativeCoordinates(t, x, y, l);
var eX = relativeCoords.eX;
var eY = relativeCoords.eY;
// Check for null values before processing this event
if (x !== null && y !== null) {
var textInfo = text(t);
state$9.push({
time: time(evt),
event: event,
data: {
target: t,
x: x,
y: y,
eX: eX,
eY: eY,
button: evt.button,
reaction: reaction(t),
context: context(a),
text: textInfo.text,
link: a ? a.href : null,
hash: null,
trust: evt.isTrusted ? 1 /* BooleanFlag.True */ : 0 /* BooleanFlag.False */,
isFullText: textInfo.isFullText,
w: l ? l.w : 0,
h: l ? l.h : 0,
tag: getElementAttribute(t, "tagName").substring(0, 10 /* Setting.ClickTag */),
class: getElementAttribute(t, "className").substring(0, 50 /* Setting.ClickClass */),
id: getElementAttribute(t, "id").substring(0, 25 /* Setting.ClickId */),
source: config$2.diagnostics && !evt.isTrusted ? source() : 0 /* ClickSource.Undefined */
}
});
schedule(encode$4.bind(t