UNPKG

countly-sdk-web

Version:
1,210 lines (1,160 loc) 456 kB
/** ********** * Countly Web SDK * https://github.com/Countly/countly-sdk-web *********** */ /** * Countly object to manage the internal queue and send requests to Countly server. More information on {@link https://resources.count.ly/docs/countly-sdk-for-web} * @name Countly * @global * @namespace Countly * @example <caption>SDK integration</caption> * <script type="text/javascript"> * * //some default pre init * var Countly = Countly || {}; * Countly.q = Countly.q || []; * * //provide your app key that you retrieved from Countly dashboard * Countly.app_key = "YOUR_APP_KEY"; * * //provide your server IP or name. Use try.count.ly for EE trial server. * //if you use your own server, make sure you have https enabled if you use * //https below. * Countly.url = "https://yourdomain.com"; * * //start pushing function calls to queue * //track sessions automatically * Countly.q.push(["track_sessions"]); * * //track sessions automatically * Countly.q.push(["track_pageview"]); * * //load countly script asynchronously * (function() { * var cly = document.createElement("script"); cly.type = "text/javascript"; * cly.async = true; * //enter url of script here * cly.src = "https://cdn.jsdelivr.net/countly-sdk-web/latest/countly.min.js"; * cly.onload = function(){Countly.init()}; * var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(cly, s); * })(); * </script> */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Countly = global.Countly || {})); })(this, (function (exports) { 'use strict'; function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _classPrivateFieldGet2(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _classPrivateFieldSet2(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _readOnlyError(r) { throw new TypeError('"' + r + '" is read-only'); } function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function (t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() { } function GeneratorFunction() { } function GeneratorFunctionPrototype() { } t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine(u), _regeneratorDefine(u, o, "Generator"), _regeneratorDefine(u, n, function () { return this; }), _regeneratorDefine(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function () { return { w: i, m: f }; })(); } function _regeneratorDefine(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine = function (e, r, n, t) { function o(r, n) { _regeneratorDefine(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine(e, r, n, t); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } // Feature ENUMS var featureEnums = { SESSIONS: "sessions", EVENTS: "events", VIEWS: "views", SCROLLS: "scrolls", CLICKS: "clicks", FORMS: "forms", CRASHES: "crashes", ATTRIBUTION: "attribution", USERS: "users", STAR_RATING: "star-rating", LOCATION: "location", APM: "apm", FEEDBACK: "feedback", REMOTE_CONFIG: "remote-config" }; /** * At the current moment there are following internal events and their respective required consent: [CLY]_nps - "feedback" consent [CLY]_survey - "feedback" consent [CLY]_star_rating - "star_rating" consent [CLY]_view - "views" consent [CLY]_orientation - "users" consent [CLY]_push_action - "push" consent [CLY]_action - "clicks" or "scroll" consent */ var internalEventKeyEnums = { NPS: "[CLY]_nps", SURVEY: "[CLY]_survey", STAR_RATING: "[CLY]_star_rating", VIEW: "[CLY]_view", ORIENTATION: "[CLY]_orientation", ACTION: "[CLY]_action" }; var internalEventKeyEnumsArray = Object.values(internalEventKeyEnums); /** * *log level Enums: *Error - this is a issues that needs attention right now. *Warning - this is something that is potentially a issue. Maybe a deprecated usage of something, maybe consent is enabled but consent is not given. *Info - All publicly exposed functions should log a call at this level to indicate that they were called. These calls should include the function name. *Debug - this should contain logs from the internal workings of the SDK and it's important calls. This should include things like the SDK configuration options, success or fail of the current network request, "request queue is full" and the oldest request get's dropped, etc. *Verbose - this should give a even deeper look into the SDK's inner working and should contain things that are more noisy and happen often. */ var logLevelEnums = { ERROR: "[ERROR] ", WARNING: "[WARNING] ", INFO: "[INFO] ", DEBUG: "[DEBUG] ", VERBOSE: "[VERBOSE] " }; /** * *device ID type: *0 - device ID was set by the developer during init *1 - device ID was auto generated by Countly *2 - device ID was temporarily given by Countly *3 - device ID was provided from location.search */ var DeviceIdTypeInternalEnums = { DEVELOPER_SUPPLIED: 0, SDK_GENERATED: 1, TEMPORARY_ID: 2, URL_PROVIDED: 3 }; /** * to be used as a default value for certain configuration key values */ var configurationDefaultValues = { BEAT_INTERVAL: 500, QUEUE_SIZE: 1000, FAIL_TIMEOUT_AMOUNT: 60, INACTIVITY_TIME: 20, SESSION_UPDATE: 60, MAX_EVENT_BATCH: 100, SESSION_COOKIE_TIMEOUT: 30, MAX_KEY_LENGTH: 128, MAX_VALUE_SIZE: 256, MAX_SEGMENTATION_VALUES: 100, MAX_BREADCRUMB_COUNT: 100, MAX_STACKTRACE_LINES_PER_THREAD: 30, MAX_STACKTRACE_LINE_LENGTH: 200 }; /** * BoomerangJS and countly */ var CDN = { BOOMERANG_SRC: "https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/plugin/boomerang/boomerang.min.js", CLY_BOOMERANG_SRC: "https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/plugin/boomerang/countly_boomerang.js" }; /** * Health check counters' local storage keys */ var healthCheckCounterEnum = Object.freeze({ errorCount: "cly_hc_error_count", warningCount: "cly_hc_warning_count", statusCode: "cly_hc_status_code", errorMessage: "cly_hc_error_message", backoffCount: "cly_hc_backoff_count", consecutiveBackoffCount: "cly_hc_consecutive_backoff_count" }); var SDK_VERSION = "26.1.2"; var SDK_NAME = "javascript_native_web"; // Using this on document.referrer would return an array with 17 elements in it. The 12th element (array[11]) would be the path we are looking for. Others would be things like password and such (use https://regex101.com/ to check more) // an example URL: // http://user:pass@host:8080/path/to/resource?query=value#fragment // this url would yield the following result for matches = urlParseRE.exec(document.referrer); // // 0: "http://user:pass@host:8080/path/to/resource?query=value#fragment" // 1: "http://user:pass@host:8080/path/to/resource?query=value" // 2: "http://user:pass@host:8080/path/to/resource" // 3: "http://user:pass@host:8080" // 4: "http:" // 5: "//" // 6: "user:pass@host:8080" // 7: "user:pass" // 8: "user" // 9: "pass" // 10: "host:8080" // 11: "host" // 12: "8080" // 13: "/path/to/resource" // 14: "/path/to/" // 15: "resource" // 16: "?query=value" // 17: "#fragment" var urlParseRE = /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/; var isBrowser = typeof window !== "undefined"; var Countly = globalThis.Countly || {}; /** * Get selected values from multi select input * @param {HTMLElement} input - select with multi true option * @returns {String} coma concatenated values */ function getMultiSelectValues(input) { var values = []; if (typeof input.options !== "undefined") { for (var j = 0; j < input.options.length; j++) { if (input.options[j].selected) { values.push(input.options[j].value); } } } return values.join(", "); } /** * Return a crypto-safe random string * @memberof Countly._internals * @returns {string} - random string */ function secureRandom() { var id = "xxxxxxxx"; id = replacePatternWithRandomValues(id, "[x]"); // timestamp in milliseconds var timestamp = Date.now().toString(); return id + timestamp; } /** * Generate random UUID value * @memberof Countly._internals * @returns {String} random UUID value */ function generateUUID() { var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; uuid = replacePatternWithRandomValues(uuid, "[xy]"); return uuid; } /** * Generate random value based on pattern * * @param {string} str - string to replace * @param {string} pattern - pattern to replace * @returns {string} - replaced string */ function replacePatternWithRandomValues(str, pattern) { var d = new Date().getTime(); var regex = new RegExp(pattern, "g"); return str.replace(regex, function (c) { var r = (d + Math.random() * 16) % 16 | 0; return (c === "x" ? r : r & 0x3 | 0x8).toString(16); }); } /** * Get unix timestamp * @memberof Countly._internals * @returns {Number} unix timestamp */ function getTimestamp() { return Math.floor(new Date().getTime() / 1000); } var lastMsTs = 0; /** * Get unique timestamp in milliseconds * @memberof Countly._internals * @returns {Number} milliseconds timestamp */ function getMsTimestamp() { var ts = new Date().getTime(); if (lastMsTs >= ts) { lastMsTs++; } else { lastMsTs = ts; } return lastMsTs; } /** * Get config value from multiple sources * like config object, global object or fallback value * @param {String} key - config key * @param {Object} ob - config object * @param {Varies} override - fallback value * @returns {Varies} value to be used as config */ function getConfig(key, ob, override) { if (ob && Object.keys(ob).length) { if (typeof ob[key] !== "undefined") { return ob[key]; } } else if (typeof Countly[key] !== "undefined") { return Countly[key]; } return override; } /** * Dispatch errors to instances that lister to errors * @param {Error} error - Error object * @param {Boolean} fatality - fatal if false and nonfatal if true * @param {Object} segments - custom crash segments */ function dispatchErrors(error, fatality, segments) { // Check each instance like Countly.i[app_key_1], Countly.i[app_key_2] ... for (var app_key in Countly.i) { // If track_errors is enabled for that instance if (Countly.i[app_key].tracking_crashes) { // Trigger recordError function for that instance Countly.i[app_key].recordError(error, fatality, segments); } } } /** * Convert JSON object to URL encoded query parameter string * @memberof Countly._internals * @param {Object} params - object with query parameters * @param {String} salt - salt to be used for checksum calculation * @returns {String} URL encode query string */ function prepareParams(params, salt) { var str = []; // deterministic ordering for checksum stability var keys = Object.keys(params || {}).sort(); for (var k = 0; k < keys.length; k++) { var i = keys[k]; str.push(i + "=" + encodeURIComponent(params[i])); } var data = str.join("&"); if (salt) { return calculateChecksum(data, salt).then(function (checksum) { data += "&checksum256=" + checksum.toUpperCase(); return data; }); } return Promise.resolve(data); } /** * Removing trailing slashes * @memberof Countly._internals * @param {String} str - string from which to remove trailing slash * @returns {String} modified string */ function stripTrailingSlash(str) { if (typeof str === "string") { if (str.substring(str.length - 1) === "/") { return str.substring(0, str.length - 1); } } return str; } /** * Retrieve only specific properties from object * @memberof Countly._internals * @param {Object} orig - original object * @param {Array} props - array with properties to get from object * @returns {Object} new object with requested properties */ function createNewObjectFromProperties(orig, props) { var ob = {}; var prop; for (var i = 0, len = props.length; i < len; i++) { prop = props[i]; if (typeof orig[prop] !== "undefined") { ob[prop] = orig[prop]; } } return ob; } /** * Add specified properties to an object from another object * @memberof Countly._internals * @param {Object} orig - original object * @param {Object} transferOb - object to copy values from * @param {Array} props - array with properties to get from object * @returns {Object} original object with additional requested properties */ function addNewProperties(orig, transferOb, props) { if (!props) { return; } var prop; for (var i = 0, len = props.length; i < len; i++) { prop = props[i]; if (typeof transferOb[prop] !== "undefined") { orig[prop] = transferOb[prop]; } } return orig; } /** * Truncates an object's key/value pairs to a certain length * @param {Object} obj - original object to be truncated * @param {Number} keyLimit - limit for key length * @param {Number} valueLimit - limit for value length * @param {Number} segmentLimit - limit for segments pairs * @param {string} errorLog - prefix for error log * @param {function} logCall - internal logging function * @returns {Object} - the new truncated object */ function truncateObject(obj, keyLimit, valueLimit, segmentLimit, errorLog, logCall) { var ob = {}; if (obj) { if (Object.keys(obj).length > segmentLimit) { var resizedObj = {}; var i = 0; for (var e in obj) { if (i < segmentLimit) { resizedObj[e] = obj[e]; i++; } } obj = resizedObj; } for (var key in obj) { var newKey = truncateSingleValue(key, keyLimit, errorLog, logCall); var newValue = truncateSingleValue(obj[key], valueLimit, errorLog, logCall); ob[newKey] = newValue; } } return ob; } /** * Truncates a single value to a certain length * @param {string|number} str - original value to be truncated * @param {Number} limit - limit length * @param {string} errorLog - prefix for error log * @param {function} logCall - internal logging function * @returns {string|number} - the new truncated value */ function truncateSingleValue(str, limit, errorLog, logCall) { var newStr = str; if (typeof str === "number") { str = str.toString(); } if (typeof str === "string") { if (str.length > limit) { newStr = str.substring(0, limit); logCall(logLevelEnums.DEBUG, errorLog + ", Key: [ " + str + " ] is longer than accepted length. It will be truncated."); } } return newStr; } /** * Calculates the checksum of the data with the given salt * Uses SHA-256 algorithm with web crypto API * Implementation based on https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest * TODO: Turn to async function when we drop support for older browsers * @param {string} data - data to be used for checksum calculation (concatenated query parameters) * @param {string} salt - salt to be used for checksum calculation * @returns {string} checksum in hex format */ function calculateChecksum(data, salt) { var msgUint8 = new TextEncoder().encode(data + salt); // encode as (utf-8) Uint8Array return crypto.subtle.digest("SHA-256", msgUint8).then(function (hashBuffer) { // hash the message var hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array var hashHex = hashArray.map(function (b) { return b.toString(16).padStart(2, "0"); }).join(""); // convert bytes to hex string (lowercase) return hashHex.toUpperCase(); }); } /** * Polyfill to get closest parent matching nodeName * @param {HTMLElement} el - element from which to search * @param {String} nodeName - tag/node name * @returns {HTMLElement} closest parent element */ function get_closest_element(el, nodeName) { nodeName = nodeName.toUpperCase(); while (el) { if (el.nodeName.toUpperCase() === nodeName) { return el; } el = el.parentElement; } } /** * Listen to specific browser event * @memberof Countly._internals * @param {HTMLElement} element - HTML element that should listen to event * @param {String} type - event name or action * @param {Function} listener - callback when event is fired */ function add_event_listener(element, type, listener) { if (!isBrowser) { return; } if (element === null || typeof element === "undefined") { // element can be null so lets check it first if (checkIfLoggingIsOn()) { // eslint-disable-next-line no-console console.warn("[WARNING] [Countly] add_event_listener, Can't bind [" + type + "] event to nonexisting element"); } return; } if (typeof element.addEventListener !== "undefined") { element.addEventListener(type, listener, false); } // for old browser use attachEvent instead else { element.attachEvent("on" + type, listener); } } /** * Get element that fired event * @memberof Countly._internals * @param {Event} event - event that was filed * @returns {HTMLElement} HTML element that caused event to fire */ function get_event_target(event) { if (!event) { return window.event.srcElement; } if (typeof event.target !== "undefined") { return event.target; } return event.srcElement; } /** * Returns raw user agent string * @memberof Countly._internals * @param {string} uaOverride - a string value to pass instead of ua value * @returns {string} currentUserAgentString - raw user agent string */ function currentUserAgentString(uaOverride) { if (uaOverride) { return uaOverride; } var ua_raw = navigator.userAgent; // check if userAgentData is supported and userAgent is not available, then use it if (!ua_raw) { ua_raw = currentUserAgentDataString(); } // RAW USER AGENT STRING return ua_raw; } /** * Forms user agent string from userAgentData by concatenating brand, version, mobile and platform * @memberof Countly._internals * @param {string} uaOverride - a string value to pass instead of ua value * @returns {string} currentUserAgentString - user agent string from userAgentData */ function currentUserAgentDataString(uaOverride) { if (uaOverride) { return uaOverride; } var ua = ""; if (navigator.userAgentData) { // turn brands array into string ua = navigator.userAgentData.brands.map(function (e) { return e.brand + ":" + e.version; }).join(); // add mobile info ua += navigator.userAgentData.mobile ? " mobi " : " "; // add platform info ua += navigator.userAgentData.platform; } return ua; } /** * Parse Windows version from UA-CH platformVersion * Chromium based browsers use platformVersion major >= 13 for Windows 11 and lower values for Windows 10. * * @param {string} platformVersion - platformVersion value from userAgentData.getHighEntropyValues * @returns {string|null} "11", "10" or null if not parsable */ function parseWindowsVersionFromPlatformVersion(platformVersion) { if (typeof platformVersion !== "string" || !platformVersion) { return null; } var major = parseInt(platformVersion.split(".")[0], 10); if (isNaN(major)) { return null; } return major >= 13 ? "11" : "10"; } /** * Retrieve User-Agent Client Hints (high entropy values) when available. * * @param {Object} uaDataOverride - optional userAgentData override for testing * @returns {Promise<Object|null>} resolved hints object or null */ function getUserAgentClientHintsInternal(uaData) { if (!uaData || typeof uaData.getHighEntropyValues !== "function") { return Promise.resolve(null); } return uaData.getHighEntropyValues(["platform", "platformVersion", "architecture", "bitness", "model", "uaFullVersion", "fullVersionList"]).then(function (values) { if (!values || _typeof(values) !== "object") { return null; } var browserName = null; var browserVersion = null; var versionList = Array.isArray(values.fullVersionList) ? values.fullVersionList : []; var fallbackList = Array.isArray(uaData.brands) ? uaData.brands : []; var normalizeBrand = function normalizeBrand(brand) { return typeof brand === "string" ? brand.toLowerCase().replace(/[^a-z0-9]/g, "") : ""; }; var isGreaseBrand = function isGreaseBrand(brand) { var normalized = normalizeBrand(brand); return normalized === "notabrand" || normalized === "notabrand99" || normalized.indexOf("notabrand") === 0; }; var isChromiumBrand = function isChromiumBrand(brand) { return normalizeBrand(brand) === "chromium"; }; var pickEntry = function pickEntry(list, allowChromiumFallback) { return list.find(function (entry) { if (!entry || !entry.brand) { return false; } if (isGreaseBrand(entry.brand)) { return false; } if (!allowChromiumFallback && isChromiumBrand(entry.brand)) { return false; } return true; }); }; var browserEntry = pickEntry(versionList, false) || pickEntry(fallbackList, false) || pickEntry(versionList, true) || pickEntry(fallbackList, true); if (browserEntry) { browserName = browserEntry.brand || null; browserVersion = browserEntry.version || null; } var parsed = { platform: values.platform || uaData.platform || null, platformVersion: values.platformVersion || null, architecture: values.architecture || null, bitness: values.bitness || null, model: values.model || null, uaFullVersion: values.uaFullVersion || null, fullVersionList: values.fullVersionList || null, browserName: browserName, browserVersion: browserVersion, windowsVersion: null }; if (parsed.platform && parsed.platform.toLowerCase() === "windows") { parsed.windowsVersion = parseWindowsVersionFromPlatformVersion(parsed.platformVersion); } return parsed; })["catch"](function () { return null; }); } var prefetchedUserAgentClientHintsPromise = null; if (isBrowser && typeof navigator !== "undefined" && navigator.userAgentData && typeof navigator.userAgentData.getHighEntropyValues === "function") { prefetchedUserAgentClientHintsPromise = getUserAgentClientHintsInternal(navigator.userAgentData); } function getUserAgentClientHints(uaDataOverride) { if (uaDataOverride) { return getUserAgentClientHintsInternal(uaDataOverride); } if (prefetchedUserAgentClientHintsPromise) { return prefetchedUserAgentClientHintsPromise; } if (isBrowser && typeof navigator !== "undefined" && navigator.userAgentData && typeof navigator.userAgentData.getHighEntropyValues === "function") { prefetchedUserAgentClientHintsPromise = getUserAgentClientHintsInternal(navigator.userAgentData); return prefetchedUserAgentClientHintsPromise; } return Promise.resolve(null); } /** * Returns device type information according to user agent string * @memberof Countly._internals * @param {string} uaOverride - a string value to pass instead of ua value * @returns {string} userAgentDeviceDetection - current device type (desktop, tablet, phone) */ function userAgentDeviceDetection(uaOverride) { var userAgent; // TODO: refactor here if (uaOverride) { userAgent = uaOverride; } else if (navigator.userAgentData && navigator.userAgentData.mobile) { return "phone"; } else { userAgent = currentUserAgentString(); } // make it lowercase for regex to work properly userAgent = userAgent.toLowerCase(); // assign the default device var device = "desktop"; // regexps corresponding to tablets or phones that can be found in userAgent string var tabletCheck = /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/; var phoneCheck = /(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|nintendo ds|archos|skyfire|puffin|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard)/; // check whether the regexp values corresponds to something in the user agent string if (tabletCheck.test(userAgent)) { device = "tablet"; } else if (phoneCheck.test(userAgent)) { device = "phone"; } // set the device type return device; } /** * Returns information regarding if the current user is a search bot or not * @memberof Countly._internals * @param {string} uaOverride - a string value to pass instead of ua value * @returns {boolean} userAgentSearchBotDetection - if a search bot is reaching the site or not */ function userAgentSearchBotDetection(uaOverride) { // search bot regexp var searchBotRE = /(CountlySiteBot|nuhk|Googlebot|GoogleSecurityScanner|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver|bingbot|Google Web Preview|Mediapartners-Google|AdsBot-Google|Baiduspider|Ezooms|YahooSeeker|AltaVista|AVSearch|Mercator|Scooter|InfoSeek|Ultraseek|Lycos|Wget|YandexBot|Yandex|YaDirectFetcher|SiteBot|Exabot|AhrefsBot|MJ12bot|TurnitinBot|magpie-crawler|Nutch Crawler|CMS Crawler|rogerbot|Domnutch|ssearch_bot|XoviBot|netseer|digincore|fr-crawler|wesee|AliasIO|contxbot|PingdomBot|BingPreview|HeadlessChrome|Lighthouse)/; // check override first if (uaOverride) { return searchBotRE.test(uaOverride); } // check both userAgent and userAgentData, as one of them might be containing the information we are looking for var ua_bot = searchBotRE.test(currentUserAgentString()); var uaData_bot = searchBotRE.test(currentUserAgentDataString()); return ua_bot || uaData_bot; } /** * Modify event to set standard coordinate properties if they are not available * @memberof Countly._internals * @param {Event} e - event object * @returns {Event} modified event object */ function get_page_coord(e) { // checking if pageY and pageX is already available if (typeof e.pageY === "undefined" && typeof e.clientX === "number" && document.documentElement) { // if not, then add scrolling positions e.pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; e.pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } // return e which now contains pageX and pageY attributes return e; } /** * Get height of whole document * @memberof Countly._internals * @returns {Number} height in pixels */ function getDocHeight() { var D = document; return Math.max(Math.max(D.body.scrollHeight, D.documentElement.scrollHeight), Math.max(D.body.offsetHeight, D.documentElement.offsetHeight), Math.max(D.body.clientHeight, D.documentElement.clientHeight)); } /** * Get width of whole document * @memberof Countly._internals * @returns {Number} width in pixels */ function getDocWidth() { var D = document; return Math.max(Math.max(D.body.scrollWidth, D.documentElement.scrollWidth), Math.max(D.body.offsetWidth, D.documentElement.offsetWidth), Math.max(D.body.clientWidth, D.documentElement.clientWidth)); } /** * Get height of viewable area * @memberof Countly._internals * @returns {Number} height in pixels */ function getViewportHeight() { var D = document; return Math.min(Math.min(D.body.clientHeight, D.documentElement.clientHeight), Math.min(D.body.offsetHeight, D.documentElement.offsetHeight), window.innerHeight); } /** * Get device's orientation * @returns {String} device orientation */ function getOrientation() { return window.innerWidth > window.innerHeight ? "landscape" : "portrait"; } /** * Load external js files * @param {String} tag - Tag/node name to load file in * @param {String} attr - Attribute name for type * @param {String} type - Type value * @param {String} src - Attribute name for file path * @param {String} data - File path * @param {Function} callback - callback when done */ function loadFile(tag, attr, type, src, data, callback) { var fileRef = document.createElement(tag); var loaded; fileRef.setAttribute(attr, type); fileRef.setAttribute(src, data); var callbackFunction = function callbackFunction() { if (!loaded) { callback(); } loaded = true; }; if (callback) { fileRef.onreadystatechange = callbackFunction; fileRef.onload = callbackFunction; } document.getElementsByTagName("head")[0].appendChild(fileRef); } /** * Load external js files * @memberof Countly._internals * @param {String} js - path to JS file * @param {Function} callback - callback when done */ function loadJS(js, callback) { loadFile("script", "type", "text/javascript", "src", js, callback); } /** * Load external css files * @memberof Countly._internals * @param {String} css - path to CSS file * @param {Function} callback - callback when done */ function loadCSS(css, callback) { loadFile("link", "rel", "stylesheet", "href", css, callback); } /** * Show loader UI when loading external data * @memberof Countly._internals */ function showLoader() { if (!isBrowser) { return; } var loader = document.getElementById("cly-loader"); if (!loader) { var css = "#cly-loader {height: 4px; width: 100%; position: absolute; z-index: 99999; overflow: hidden; background-color: #fff; top:0px; left:0px;}" + "#cly-loader:before{display: block; position: absolute; content: ''; left: -200px; width: 200px; height: 4px; background-color: #2EB52B; animation: cly-loading 2s linear infinite;}" + "@keyframes cly-loading { from {left: -200px; width: 30%;} 50% {width: 30%;} 70% {width: 70%;} 80% { left: 50%;} 95% {left: 120%;} to {left: 100%;}}"; var head = document.head || document.getElementsByTagName("head")[0]; var style = document.createElement("style"); style.type = "text/css"; if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } head.appendChild(style); loader = document.createElement("div"); loader.setAttribute("id", "cly-loader"); window.addEventListener("load", function () { if (Countly.showLoaderProtection) { if (checkIfLoggingIsOn()) { console.warn("[WARNING] [Countly] showLoader, Loader is already on"); } return; } try { document.body.appendChild(loader); } catch (e) { if (checkIfLoggingIsOn()) { console.error("[ERROR] [Countly] showLoader, Body is not loaded for loader to append: " + e); } } }); } loader.style.display = "block"; } /** * Checks if debug is true and console is available in Countly object * @memberof Countly._internals * @returns {Boolean} true if debug is true and console is available in Countly object */ function checkIfLoggingIsOn() { // check if logging is enabled if (Countly && Countly.debug && typeof console !== "undefined") { return true; } return false; } /** * Hide loader UI * @memberof Countly._internals */ function hideLoader() { if (!isBrowser) { return; } // Inform showLoader that it should not append the loader Countly.showLoaderProtection = true; var loader = document.getElementById("cly-loader"); if (loader) { loader.style.display = "none"; } } var _consentTimer = /*#__PURE__*/new WeakMap(); var _self = /*#__PURE__*/new WeakMap(); var _global = /*#__PURE__*/new WeakMap(); var _sessionStarted = /*#__PURE__*/new WeakMap(); var _apiPath = /*#__PURE__*/new WeakMap(); var _readPath = /*#__PURE__*/new WeakMap(); var _beatInterval = /*#__PURE__*/new WeakMap(); var _requestQueue = /*#__PURE__*/new WeakMap(); var _eventQueue = /*#__PURE__*/new WeakMap(); var _remoteConfigs = /*#__PURE__*/new WeakMap(); var _crashLogs = /*#__PURE__*/new WeakMap(); var _timedEvents = /*#__PURE__*/new WeakMap(); var _ignoreReferrers = /*#__PURE__*/new WeakMap(); var _crashSegments = /*#__PURE__*/new WeakMap(); var _autoExtend = /*#__PURE__*/new WeakMap(); var _lastBeat = /*#__PURE__*/new WeakMap(); var _storedDuration = /*#__PURE__*/new WeakMap(); var _lastView = /*#__PURE__*/new WeakMap(); var _lastViewTime = /*#__PURE__*/new WeakMap(); var _lastViewStoredDuration = /*#__PURE__*/new WeakMap(); var _failTimeout = /*#__PURE__*/new WeakMap(); var _failTimeoutAmount = /*#__PURE__*/new WeakMap(); var _inactivityTime = /*#__PURE__*/new WeakMap(); var _inactivityCounter = /*#__PURE__*/new WeakMap(); var _useSessionCookie = /*#__PURE__*/new WeakMap(); var _sessionCookieTimeout = /*#__PURE__*/new WeakMap(); var _readyToProcess = /*#__PURE__*/new WeakMap(); var _hasPulse = /*#__PURE__*/new WeakMap(); var _offlineMode = /*#__PURE__*/new WeakMap(); var _lastParams = /*#__PURE__*/new WeakMap(); var _trackTime = /*#__PURE__*/new WeakMap(); var _startTime = /*#__PURE__*/new WeakMap(); var _lsSupport = /*#__PURE__*/new WeakMap(); var _firstView = /*#__PURE__*/new WeakMap(); var _deviceIdType = /*#__PURE__*/new WeakMap(); var _isScrollRegistryOpen = /*#__PURE__*/new WeakMap(); var _scrollRegistryTopPosition = /*#__PURE__*/new WeakMap(); var _trackingScrolls = /*#__PURE__*/new WeakMap(); var _currentViewId = /*#__PURE__*/new WeakMap(); var _previousViewId = /*#__PURE__*/new WeakMap(); var _freshUTMTags = /*#__PURE__*/new WeakMap(); var _sdkName = /*#__PURE__*/new WeakMap(); var _sdkVersion = /*#__PURE__*/new WeakMap(); var _shouldSendHC = /*#__PURE__*/new WeakMap(); var _consents = /*#__PURE__*/new WeakMap(); var _generatedRequests = /*#__PURE__*/new WeakMap(); var _contentEndPoint = /*#__PURE__*/new WeakMap(); var _inContentZone = /*#__PURE__*/new WeakMap(); var _contentZoneTimer = /*#__PURE__*/new WeakMap(); var _contentIframeID = /*#__PURE__*/new WeakMap(); var _crashFilterCallback = /*#__PURE__*/new WeakMap(); var _serverConfigCache = /*#__PURE__*/new WeakMap(); var _SCNetwork = /*#__PURE__*/new WeakMap(); var _SCSizeReqQueue = /*#__PURE__*/new WeakMap(); var _SCSizeEventBatch = /*#__PURE__*/new WeakMap(); var _SCIntervalSessionUpdate = /*#__PURE__*/new WeakMap(); var _SCIntervalContent = /*#__PURE__*/new WeakMap(); var _SCInterval = /*#__PURE__*/new WeakMap(); var _SCTrackingAll = /*#__PURE__*/new WeakMap(); var _SCTrackingSession = /*#__PURE__*/new WeakMap(); var _SCTrackingViews = /*#__PURE__*/new WeakMap(); var _SCTrackingCrashes = /*#__PURE__*/new WeakMap(); var _SCTrackingEvents = /*#__PURE__*/new WeakMap(); var _SCTrackingLocation = /*#__PURE__*/new WeakMap(); var _SCEnableContent = /*#__PURE__*/new WeakMap(); var _SCEnableConsentRequired = /*#__PURE__*/new WeakMap(); var _SCEnableRefreshContentZone = /*#__PURE__*/new WeakMap(); var _SCLimitKeyLength = /*#__PURE__*/new WeakMap(); var _SCLimitValueSize = /*#__PURE__*/new WeakMap(); var _SCLimitSegmentationValues = /*#__PURE__*/new WeakMap(); var _SCLimitBreadcrumbCount = /*#__PURE__*/new WeakMap(); var _SCLimitStackTraceLinesPerThread = /*#__PURE__*/new WeakMap(); var _SCLimitStackTraceLineLength = /*#__PURE__*/new WeakMap(); var _SCBackoffMechanismEnabled = /*#__PURE__*/new WeakMap(); var _SCBackoffAcceptedTimeout = /*#__PURE__*/new WeakMap(); var _SCBackoffRQPercentage = /*#__PURE__*/new WeakMap(); var _SCBackoffRequestAge = /*#__PURE__*/new WeakMap(); var _SCBackoffDuration = /*#__PURE__*/new WeakMap(); var _SCEventBlacklist = /*#__PURE__*/new WeakMap(); var _SCEventWhitelist = /*#__PURE__*/new WeakMap(); var _SCUserPropertyBlacklist = /*#__PURE__*/new WeakMap(); var _SCUserPropertyWhitelist = /*#__PURE__*/new WeakMap(); var _SCSegmentationBlacklist = /*#__PURE__*/new WeakMap(); var _SCSegmentationWhitelist = /*#__PURE__*/new WeakMap(); var _SCEventSegmentationBlacklist = /*#__PURE__*/new WeakMap(); var _SCEventSegmentationWhitelist = /*#__PURE__*/new WeakMap(); var _SCJourneyTriggerEvents = /*#__PURE__*/new WeakMap(); var _initContentSent = /*#__PURE__*/new WeakMap(); var _initTimestamp = /*#__PURE__*/new WeakMap(); var _isSCDisabled = /*#__PURE__*/new WeakMap(); var _lastRequestDuration = /*#__PURE__*/new WeakMap(); var _backoffEndTime = /*#__PURE__*/new WeakMap(); var _isInBackoff = /*#__PURE__*/new WeakMap(); var _backOffLogShown = /*#__PURE__*/new WeakMap(); var _lastRequestWasBackoff = /*#__PURE__*/new WeakMap(); var _testModeTime = /*#__PURE__*/new WeakMap(); var _requestTimeoutDuration = /*#__PURE__*/new WeakMap(); var _contentFilterCallback = /*#__PURE__*/new WeakMap(); var _isProcessingAsyncFromUserDataSave = /*#__PURE__*/new WeakMap(); var _journeyTriggerInProgress = /*#__PURE__*/new WeakMap(); var _journeyTriggerPending = /*#__PURE__*/new WeakMap(); var _journeyPendingEventIds = /*#__PURE__*/new WeakMap(); var _fakeRequestHandler = /*#__PURE__*/new WeakMap(); var _uaClientHints = /*#__PURE__*/new WeakMap(); var _uaClientHintsStatus = /*#__PURE__*/new WeakMap(); var _clientHintsPromise = /*#__PURE__*/new WeakMap(); var _pendingRequestBuffer = /*#__PURE__*/new WeakMap(); var _clientHintsBufferTimeoutId = /*#__PURE__*/new WeakMap(); var _changeIdRemoteConfigTimeoutId = /*#__PURE__*/new WeakMap(); var _initializeClientHints = /*#__PURE__*/new WeakMap(); var _flushPendingRequestBuffer = /*#__PURE__*/new WeakMap(); var _getAndSetServerConfig = /*#__PURE__*/new WeakMap(); var _populateServerConfig = /*#__PURE__*/new WeakMap(); var _initialize = /*#__PURE__*/new WeakMap(); var _updateConsent = /*#__PURE__*/new WeakMap(); var _add_cly_events = /*#__PURE__*/new W