diff --git a/CHANGELOG.md b/CHANGELOG.md index 48ba0eea..0e21f58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 25.4.4 + +- Improved user property recording order with respect to sessions and events. + ## 25.4.3 - Added filtering capability to `content` interface through `enterContentZone(contentFilterCallback)`. diff --git a/cypress/e2e/bridged_utils.cy.js b/cypress/e2e/bridged_utils.cy.js index 01cfbdf5..0cd59308 100644 --- a/cypress/e2e/bridged_utils.cy.js +++ b/cypress/e2e/bridged_utils.cy.js @@ -15,7 +15,7 @@ function initMain(name, version) { } const SDK_NAME = "javascript_native_web"; -const SDK_VERSION = "25.4.3"; +const SDK_VERSION = "25.4.4"; // tests describe("Bridged SDK Utilities Tests", () => { diff --git a/cypress/e2e/up_order.cy.js b/cypress/e2e/up_order.cy.js new file mode 100644 index 00000000..ad900bee --- /dev/null +++ b/cypress/e2e/up_order.cy.js @@ -0,0 +1,139 @@ +/* eslint-disable cypress/no-unnecessary-waiting */ +/* eslint-disable require-jsdoc */ +var Countly = require("../../lib/countly"); +var hp = require("../support/helper"); + +Countly.q = Countly.q || []; + +function initMain() { + Countly.init({ + app_key: "YOUR_APP_KEY", + url: "https://your.domain.count.ly", + test_mode_eq: true, + test_mode: true + }); +} + +const userDetailObj = hp.userDetailObj; + +// an event object to use +const eventObj = { + key: "in_app_purchase", + count: 3, + sum: 2.97, + dur: 300, + segmentation: { + app_version: "1.0", + country: "Tahiti" + } +}; +const custUP = { + custom: { "name": "John Doe"} +} + +describe("User properties order test", () => { + it("User details order test", () => { + hp.haltAndClearStorage(() => { + initMain(); + Countly.begin_session(); + Countly.add_event(eventObj); + Countly.user_details(userDetailObj); + Countly.add_event(eventObj); + Countly.add_event(eventObj); + Countly.user_details(userDetailObj); + cy.fetch_local_event_queue().then((eq) => { + expect(eq.length).to.equal(0); + }); + cy.wait(500).then(() => { + cy.fetch_local_request_queue(hp.appKey).then((rq) => { + expect(rq.length).to.equal(5); + cy.log(rq); + cy.check_session(rq[0]); + cy.check_event(JSON.parse(rq[1].events)[1], eventObj, undefined, false); + cy.check_user_details(rq[2], userDetailObj); + cy.check_event(JSON.parse(rq[3].events)[0], eventObj, undefined, false); + cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false); + cy.check_user_details(rq[4], userDetailObj); + }); + }); + }); + }); + it("User details order test, async", () => { + hp.haltAndClearStorage(() => { + initMain(); + Countly.q.push(['track_sessions']); + Countly.q.push(['add_event', eventObj]); + Countly.q.push(['user_details', userDetailObj]); + Countly.q.push(['add_event', eventObj]); + Countly.q.push(['add_event', eventObj]); + Countly.q.push(['user_details', userDetailObj]); + cy.fetch_local_event_queue().then((eq) => { + expect(eq.length).to.equal(0); + }); + cy.wait(500).then(() => { + cy.fetch_local_request_queue(hp.appKey).then((rq) => { + expect(rq.length).to.equal(5); + cy.log(rq); + cy.check_session(rq[0]); + cy.check_event(JSON.parse(rq[1].events)[1], eventObj, undefined, false); + cy.check_user_details(rq[2], userDetailObj); + cy.check_event(JSON.parse(rq[3].events)[0], eventObj, undefined, false); + cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false); + cy.check_user_details(rq[4], userDetailObj); + }); + }); + }); + }); + it("User data order test", () => { + hp.haltAndClearStorage(() => { + initMain(); + Countly.userData.set("name", "John Doe"); + Countly.begin_session(); + Countly.userData.set("name", "John Doe"); + Countly.add_event(eventObj); + Countly.userData.set("name", "John Doe"); + Countly.user_details(userDetailObj); + cy.fetch_local_event_queue().then((eq) => { + expect(eq.length).to.equal(0); + }); + cy.wait(500).then(() => { + cy.fetch_local_request_queue(hp.appKey).then((rq) => { + expect(rq.length).to.equal(6); + cy.log(rq); + cy.check_user_details(rq[0], custUP); + cy.check_session(rq[1]); + cy.check_user_details(rq[2], custUP); + cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false); + cy.check_user_details(rq[4], custUP); + cy.check_user_details(rq[5], userDetailObj); + }); + }); + }); + }); + it("User data order test, async", () => { + hp.haltAndClearStorage(() => { + initMain(); + Countly.q.push(['userData.set', "name", "John Doe"]); + Countly.q.push(['track_sessions']); + Countly.q.push(['userData.set', "name", "John Doe"]); + Countly.q.push(['add_event', eventObj]); + Countly.q.push(['userData.set', "name", "John Doe"]); + Countly.q.push(['user_details', userDetailObj]); + cy.fetch_local_event_queue().then((eq) => { + expect(eq.length).to.equal(0); + }); + cy.wait(500).then(() => { + cy.fetch_local_request_queue(hp.appKey).then((rq) => { + expect(rq.length).to.equal(6); + cy.log(rq); + cy.check_user_details(rq[0], custUP); + cy.check_session(rq[1]); + cy.check_user_details(rq[2], custUP); + cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false); + cy.check_user_details(rq[4], custUP); + cy.check_user_details(rq[5], userDetailObj); + }); + }); + }); + }); +}); diff --git a/lib/countly.js b/lib/countly.js index ff2e85af..4fc69621 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -217,7 +217,7 @@ backoffCount: "cly_hc_backoff_count", consecutiveBackoffCount: "cly_hc_consecutive_backoff_count" }); - var SDK_VERSION = "25.4.3"; + var SDK_VERSION = "25.4.4"; 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) @@ -945,6 +945,7 @@ var _testModeTime = /*#__PURE__*/new WeakMap(); var _requestTimeoutDuration = /*#__PURE__*/new WeakMap(); var _contentFilterCallback = /*#__PURE__*/new WeakMap(); + var _isProcessingAsyncFromUserDataSave = /*#__PURE__*/new WeakMap(); var _getAndSetServerConfig = /*#__PURE__*/new WeakMap(); var _populateServerConfig = /*#__PURE__*/new WeakMap(); var _initialize = /*#__PURE__*/new WeakMap(); @@ -1101,6 +1102,7 @@ _classPrivateFieldInitSpec(this, _testModeTime, void 0); _classPrivateFieldInitSpec(this, _requestTimeoutDuration, void 0); _classPrivateFieldInitSpec(this, _contentFilterCallback, void 0); + _classPrivateFieldInitSpec(this, _isProcessingAsyncFromUserDataSave, void 0); _classPrivateFieldInitSpec(this, _getAndSetServerConfig, function () { if (_this.device_id === "[CLY]_temp_id") { _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "server_config, Device ID is temporary, not fetching server config"); @@ -2059,6 +2061,7 @@ var req = {}; req.begin_session = 1; req.metrics = JSON.stringify(_classPrivateFieldGet2(_getMetrics, _this).call(_this)); + _this.userData.save(true); // ensure user data is saved before session start _classPrivateFieldGet2(_toRequestQueue, _this).call(_this, req); } _classPrivateFieldGet2(_setValueInStorage, _this).call(_this, "cly_session", getTimestamp() + _classPrivateFieldGet2(_sessionCookieTimeout, _this) * 60); @@ -2085,6 +2088,7 @@ return; } _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "session_duration, Session extended: [" + sec + "]"); + _this.userData.save(true); // ensure user data is saved before session update _classPrivateFieldGet2(_toRequestQueue, _this).call(_this, { session_duration: sec }); @@ -2107,6 +2111,7 @@ _classPrivateFieldGet2(_reportViewDuration, _this).call(_this); if (!_classPrivateFieldGet2(_useSessionCookie, _this) || force) { _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "end_session, Session ended"); + _this.userData.save(true); // ensure user data is saved before session end _classPrivateFieldGet2(_toRequestQueue, _this).call(_this, { end_session: 1, session_duration: sec @@ -2267,6 +2272,9 @@ _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.ERROR, "Adding event failed. Event must have a key property"); return; } + if (!_classPrivateFieldGet2(_isProcessingAsyncFromUserDataSave, _this)) { + _this.userData.save(true); // ensure cached user data is saved before adding event + } if (!event.count) { event.count = 1; } @@ -2441,6 +2449,7 @@ user.byear = truncateSingleValue(user.byear, _classPrivateFieldGet2(_SCLimitValueSize, _this), "user_details", _classPrivateFieldGet2(_log, _this)); user.custom = truncateObject(user.custom, _classPrivateFieldGet2(_SCLimitKeyLength, _this), _classPrivateFieldGet2(_SCLimitValueSize, _this), _classPrivateFieldGet2(_SCLimitSegmentationValues, _this), "user_details", _classPrivateFieldGet2(_log, _this)); var props = ["name", "username", "email", "organization", "phone", "picture", "gender", "byear", "custom"]; + _this.userData.save(); // ensure user data (and events) is saved before sending user details _classPrivateFieldGet2(_toRequestQueue, _this).call(_this, { user_details: JSON.stringify(createNewObjectFromProperties(user, props)) }); @@ -2627,20 +2636,30 @@ * Save changes made to user's custom properties object and send them to server * @memberof Countly.userData * */ - save: function save() { - _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property"); - if (_this.check_consent(featureEnums.USERS)) { - // process async queue before sending events - _classPrivateFieldGet2(_processAsyncQueue, _this).call(_this); - // flush events to event queue to prevent a drill issue + save: function save(forEvents) { + _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property. forEvents:[" + forEvents + "]"); + if (!_this.check_consent(featureEnums.USERS) || Object.keys(_classPrivateFieldGet2(_customData, _this)).length === 0) { + return; + } + if (!forEvents) { + _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.DEBUG, "[userData] save, flushing async queue and event queue before sending custom user data"); + _classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, _this, true); + try { + // process async queue before sending events + _classPrivateFieldGet2(_processAsyncQueue, _this).call(_this); + } finally { + _classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, _this, false); + } + + // flush events to request queue _classPrivateFieldGet2(_sendEventsForced, _this).call(_this); - _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "user_details, flushed the event queue"); - _classPrivateFieldGet2(_toRequestQueue, _this).call(_this, { - user_details: JSON.stringify({ - custom: _classPrivateFieldGet2(_customData, _this) - }) - }); } + _classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, will send the following custom data to server: [" + JSON.stringify(_classPrivateFieldGet2(_customData, _this)) + "]"); + _classPrivateFieldGet2(_toRequestQueue, _this).call(_this, { + user_details: JSON.stringify({ + custom: _classPrivateFieldGet2(_customData, _this) + }) + }); _classPrivateFieldSet2(_customData, _this, {}); } }); @@ -6516,6 +6535,7 @@ _classPrivateFieldSet2(_SCBackoffDuration, this, 60); // 60 seconds _classPrivateFieldSet2(_requestTimeoutDuration, this, 30000); // 30 seconds _classPrivateFieldSet2(_contentFilterCallback, this, null); + _classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, this, false); this.app_key = getConfig("app_key", _ob, null); this.url = stripTrailingSlash(getConfig("url", _ob, "")); this.serialize = getConfig("serialize", _ob, Countly.serialize); diff --git a/lib/countly.min.js b/lib/countly.min.js index f1543377..ce2b11f8 100644 --- a/lib/countly.min.js +++ b/lib/countly.min.js @@ -1,43 +1,43 @@ (function(La,bb){"object"===typeof exports&&"undefined"!==typeof module?bb(exports):"function"===typeof define&&define.amd?define(["exports"],bb):(La="undefined"!==typeof globalThis?globalThis:La||self,bb(La.Countly=La.Countly||{}))})(this,function(La){function bb(m,q,v){if("function"==typeof m?m===q:m.has(q))return 3>arguments.length?q:v;throw new TypeError("Private element is not present on this object");}function b(m,q){return m.get(bb(m,q))}function p(m,q,v){if(q.has(m))throw new TypeError("Cannot initialize the same private elements twice on an object"); -q.set(m,v)}function k(m,q,v){return m.set(bb(m,q),v),v}function Pc(m,q){for(var v=0;v=m?Jb++:Jb=m;return Jb}function x(m,q,v){if(q&&Object.keys(q).length){if("undefined"!==typeof q[m])return q[m]}else if("undefined"!== -typeof t[m])return t[m];return v}function rc(m,q,v){for(var a in t.i)t.i[a].tracking_crashes&&t.i[a].recordError(m,q,v)}function sc(m,q){for(var v=[],a=Object.keys(m||{}).sort(),K=0;Ka){var g={},h=0,n;for(n in m)hq&&(K=m.substring(0,q),a(f.DEBUG,v+", Key: [ "+m+" ] is longer than accepted length. It will be truncated."));return K}function Tc(m,q){m=(new TextEncoder).encode(m+ -q);return crypto.subtle.digest("SHA-256",m).then(function(v){return Array.from(new Uint8Array(v)).map(function(a){return a.toString(16).padStart(2,"0")}).join("").toUpperCase()})}function F(m,q,v){A&&(null===m||"undefined"===typeof m?Kb()&&console.warn("[WARNING] [Countly] add_event_listener, Can't bind ["+q+"] event to nonexisting element"):"undefined"!==typeof m.addEventListener?m.addEventListener(q,v,!1):m.attachEvent("on"+q,v))}function Lb(m){return m?"undefined"!==typeof m.target?m.target:m.srcElement: -window.event.srcElement}function db(m){if(m)return m;(m=navigator.userAgent)||(m=tc());return m}function tc(m){if(m)return m;m="";navigator.userAgentData&&(m=navigator.userAgentData.brands.map(function(q){return q.brand+":"+q.version}).join(),m+=navigator.userAgentData.mobile?" mobi ":" ",m+=navigator.userAgentData.platform);return m}function uc(m){if(!m){if(navigator.userAgentData&&navigator.userAgentData.mobile)return"phone";m=db()}m=m.toLowerCase();var q="desktop",v=/(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|nintendo ds|archos|skyfire|puffin|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard)/; -/(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(m)?q="tablet":v.test(m)&&(q="phone");return q}function Uc(m){var q=/(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)/; -if(m)return q.test(m);m=q.test(db());q=q.test(tc());return m||q}function vc(m){"undefined"===typeof m.pageY&&"number"===typeof m.clientX&&document.documentElement&&(m.pageX=m.clientX+document.body.scrollLeft+document.documentElement.scrollLeft,m.pageY=m.clientY+document.body.scrollTop+document.documentElement.scrollTop);return m}function Mb(){var m=document;return Math.max(Math.max(m.body.scrollHeight,m.documentElement.scrollHeight),Math.max(m.body.offsetHeight,m.documentElement.offsetHeight),Math.max(m.body.clientHeight, -m.documentElement.clientHeight))}function wc(){var m=document;return Math.max(Math.max(m.body.scrollWidth,m.documentElement.scrollWidth),Math.max(m.body.offsetWidth,m.documentElement.offsetWidth),Math.max(m.body.clientWidth,m.documentElement.clientWidth))}function Vc(){var m=document;return Math.min(Math.min(m.body.clientHeight,m.documentElement.clientHeight),Math.min(m.body.offsetHeight,m.documentElement.offsetHeight),window.innerHeight)}function Wc(m,q,v,a,K,c){m=document.createElement(m);var d; -m.setAttribute(q,v);m.setAttribute(a,K);q=function(){d||c();d=!0};c&&(m.onreadystatechange=q,m.onload=q);document.getElementsByTagName("head")[0].appendChild(m)}function Xc(m,q){Wc("script","type","text/javascript","src",m,q)}function Nb(m,q){Wc("link","rel","stylesheet","href",m,q)}function Yc(){if(A){var m=document.getElementById("cly-loader");if(!m){var q=document.head||document.getElementsByTagName("head")[0],v=document.createElement("style");v.type="text/css";v.styleSheet?v.styleSheet.cssText= +q.set(m,v)}function k(m,q,v){return m.set(bb(m,q),v),v}function Qc(m,q){for(var v=0;v=m?Kb++:Kb=m;return Kb}function x(m,q,v){if(q&&Object.keys(q).length){if("undefined"!==typeof q[m])return q[m]}else if("undefined"!== +typeof t[m])return t[m];return v}function sc(m,q,v){for(var a in t.i)t.i[a].tracking_crashes&&t.i[a].recordError(m,q,v)}function tc(m,q){for(var v=[],a=Object.keys(m||{}).sort(),K=0;Ka){var g={},h=0,n;for(n in m)hq&&(K=m.substring(0,q),a(f.DEBUG,v+", Key: [ "+m+" ] is longer than accepted length. It will be truncated."));return K}function Uc(m,q){m=(new TextEncoder).encode(m+ +q);return crypto.subtle.digest("SHA-256",m).then(function(v){return Array.from(new Uint8Array(v)).map(function(a){return a.toString(16).padStart(2,"0")}).join("").toUpperCase()})}function F(m,q,v){A&&(null===m||"undefined"===typeof m?Lb()&&console.warn("[WARNING] [Countly] add_event_listener, Can't bind ["+q+"] event to nonexisting element"):"undefined"!==typeof m.addEventListener?m.addEventListener(q,v,!1):m.attachEvent("on"+q,v))}function Mb(m){return m?"undefined"!==typeof m.target?m.target:m.srcElement: +window.event.srcElement}function db(m){if(m)return m;(m=navigator.userAgent)||(m=uc());return m}function uc(m){if(m)return m;m="";navigator.userAgentData&&(m=navigator.userAgentData.brands.map(function(q){return q.brand+":"+q.version}).join(),m+=navigator.userAgentData.mobile?" mobi ":" ",m+=navigator.userAgentData.platform);return m}function vc(m){if(!m){if(navigator.userAgentData&&navigator.userAgentData.mobile)return"phone";m=db()}m=m.toLowerCase();var q="desktop",v=/(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|nintendo ds|archos|skyfire|puffin|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard)/; +/(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(m)?q="tablet":v.test(m)&&(q="phone");return q}function Vc(m){var q=/(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)/; +if(m)return q.test(m);m=q.test(db());q=q.test(uc());return m||q}function wc(m){"undefined"===typeof m.pageY&&"number"===typeof m.clientX&&document.documentElement&&(m.pageX=m.clientX+document.body.scrollLeft+document.documentElement.scrollLeft,m.pageY=m.clientY+document.body.scrollTop+document.documentElement.scrollTop);return m}function Nb(){var m=document;return Math.max(Math.max(m.body.scrollHeight,m.documentElement.scrollHeight),Math.max(m.body.offsetHeight,m.documentElement.offsetHeight),Math.max(m.body.clientHeight, +m.documentElement.clientHeight))}function xc(){var m=document;return Math.max(Math.max(m.body.scrollWidth,m.documentElement.scrollWidth),Math.max(m.body.offsetWidth,m.documentElement.offsetWidth),Math.max(m.body.clientWidth,m.documentElement.clientWidth))}function Wc(){var m=document;return Math.min(Math.min(m.body.clientHeight,m.documentElement.clientHeight),Math.min(m.body.offsetHeight,m.documentElement.offsetHeight),window.innerHeight)}function Xc(m,q,v,a,K,c){m=document.createElement(m);var d; +m.setAttribute(q,v);m.setAttribute(a,K);q=function(){d||c();d=!0};c&&(m.onreadystatechange=q,m.onload=q);document.getElementsByTagName("head")[0].appendChild(m)}function Yc(m,q){Xc("script","type","text/javascript","src",m,q)}function Ob(m,q){Xc("link","rel","stylesheet","href",m,q)}function Zc(){if(A){var m=document.getElementById("cly-loader");if(!m){var q=document.head||document.getElementsByTagName("head")[0],v=document.createElement("style");v.type="text/css";v.styleSheet?v.styleSheet.cssText= "#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%;}}":v.appendChild(document.createTextNode("#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%;}}")); -q.appendChild(v);m=document.createElement("div");m.setAttribute("id","cly-loader");window.addEventListener("load",function(){if(t.showLoaderProtection)Kb()&&console.warn("[WARNING] [Countly] showLoader, Loader is already on");else try{document.body.appendChild(m)}catch(a){Kb()&&console.error("[ERROR] [Countly] showLoader, Body is not loaded for loader to append: "+a)}})}m.style.display="block"}}function Kb(){return t&&t.debug&&"undefined"!==typeof console?!0:!1}function Zc(){if(A){t.showLoaderProtection= -!0;var m=document.getElementById("cly-loader");m&&(m.style.display="none")}}function xd(m){var q=document.createElement("script"),v=document.createElement("script");q.async=!0;v.async=!0;q.src=t.customSourceBoomerang||$c.BOOMERANG_SRC;v.src=t.customSourceCountlyBoomerang||$c.CLY_BOOMERANG_SRC;document.getElementsByTagName("head")[0].appendChild(q);document.getElementsByTagName("head")[0].appendChild(v);var a=!1,K=!1;q.onload=function(){a=!0};v.onload=function(){K=!0};var c=0,d=setInterval(function(){c+= -50;if(a&&K||1500<=c){if(t.debug){var g="BoomerangJS loaded:["+a+"], countly_boomerang loaded:["+K+"].";a&&K?console.log("[DEBUG] "+g):console.warn("[WARNING] "+g+" Initializing without APM.")}t.init(m);clearInterval(d)}},50)}var U={NPS:"[CLY]_nps",SURVEY:"[CLY]_survey",STAR_RATING:"[CLY]_star_rating",VIEW:"[CLY]_view",ORIENTATION:"[CLY]_orientation",ACTION:"[CLY]_action"},yd=Object.values(U),f={ERROR:"[ERROR] ",WARNING:"[WARNING] ",INFO:"[INFO] ",DEBUG:"[DEBUG] ",VERBOSE:"[VERBOSE] "},$c={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"},S=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"}),ad=/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?::([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?::([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/, -A="undefined"!==typeof window,t=globalThis.Countly||{},Jb=0,sb=new WeakMap,bd=new WeakMap,eb=new WeakMap,fa=new WeakMap,tb=new WeakMap,Na=new WeakMap,fb=new WeakMap,L=new WeakMap,R=new WeakMap,Z=new WeakMap,va=new WeakMap,ja=new WeakMap,ka=new WeakMap,ub=new WeakMap,Oa=new WeakMap,oa=new WeakMap,vb=new WeakMap,ba=new WeakMap,Pa=new WeakMap,Qa=new WeakMap,wb=new WeakMap,gb=new WeakMap,Ra=new WeakMap,Fa=new WeakMap,pa=new WeakMap,Sa=new WeakMap,hb=new WeakMap,ib=new WeakMap,N=new WeakMap,la=new WeakMap, -wa=new WeakMap,Ob=new WeakMap,xa=new WeakMap,Ga=new WeakMap,I=new WeakMap,Ta=new WeakMap,Ua=new WeakMap,xb=new WeakMap,ya=new WeakMap,jb=new WeakMap,qa=new WeakMap,za=new WeakMap,ra=new WeakMap,yb=new WeakMap,M=new WeakMap,zb=new WeakMap,xc=new WeakMap,Va=new WeakMap,kb=new WeakMap,lb=new WeakMap,mb=new WeakMap,Aa=new WeakMap,Pb=new WeakMap,sa=new WeakMap,Ha=new WeakMap,Wa=new WeakMap,nb=new WeakMap,Qb=new WeakMap,ob=new WeakMap,pb=new WeakMap,Ab=new WeakMap,Bb=new WeakMap,Rb=new WeakMap,Sb=new WeakMap, -qb=new WeakMap,Xa=new WeakMap,Tb=new WeakMap,J=new WeakMap,G=new WeakMap,ca=new WeakMap,Ya=new WeakMap,Ba=new WeakMap,Ca=new WeakMap,Ub=new WeakMap,Vb=new WeakMap,Wb=new WeakMap,Xb=new WeakMap,Yb=new WeakMap,Zb=new WeakMap,$b=new WeakMap,yc=new WeakMap,Za=new WeakMap,Ia=new WeakMap,$a=new WeakMap,Cb=new WeakMap,Db=new WeakMap,Eb=new WeakMap,ac=new WeakMap,Fb=new WeakMap,bc=new WeakMap,cc=new WeakMap,cd=new WeakMap,zc=new WeakMap,ha=new WeakMap,Ac=new WeakMap,ma=new WeakMap,ta=new WeakMap,Bc=new WeakMap, -Cc=new WeakMap,dc=new WeakMap,ec=new WeakMap,Dc=new WeakMap,Ja=new WeakMap,dd=new WeakMap,fc=new WeakMap,ed=new WeakMap,Ec=new WeakMap,fd=new WeakMap,gd=new WeakMap,gc=new WeakMap,Da=new WeakMap,hc=new WeakMap,Fc=new WeakMap,ic=new WeakMap,hd=new WeakMap,Gc=new WeakMap,ab=new WeakMap,V=new WeakMap,Gb=new WeakMap,id=new WeakMap,jd=new WeakMap,Ea=new WeakMap,jc=new WeakMap,kd=new WeakMap,Hc=new WeakMap,Hb=new WeakMap,Ib=new WeakMap,kc=new WeakMap,e=new WeakMap,ia=new WeakMap,Ic=new WeakMap,ld=new WeakMap, -Jc=new WeakMap,Kc=new WeakMap,lc=new WeakMap,mc=new WeakMap,Lc=new WeakMap,nc=new WeakMap,md=new WeakMap,Mc=new WeakMap,nd=new WeakMap,od=new WeakMap,pd=new WeakMap,qd=new WeakMap,Nc=new WeakMap,B=new WeakMap,z=new WeakMap,da=new WeakMap,rd=new WeakMap,sd=new WeakMap,td=new WeakMap,ud=new WeakMap,na=new WeakMap,vd=function(m,q,v){return q&&Pc(m.prototype,q),v&&Pc(m,v),Object.defineProperty(m,"prototype",{writable:!1}),m}(function v(q){var a=this;if(!(this instanceof v))throw new TypeError("Cannot call a class as a function"); -p(this,sb,void 0);p(this,bd,void 0);p(this,eb,void 0);p(this,fa,void 0);p(this,tb,void 0);p(this,Na,void 0);p(this,fb,void 0);p(this,L,void 0);p(this,R,void 0);p(this,Z,void 0);p(this,va,void 0);p(this,ja,void 0);p(this,ka,void 0);p(this,ub,void 0);p(this,Oa,void 0);p(this,oa,void 0);p(this,vb,void 0);p(this,ba,void 0);p(this,Pa,void 0);p(this,Qa,void 0);p(this,wb,void 0);p(this,gb,void 0);p(this,Ra,void 0);p(this,Fa,void 0);p(this,pa,void 0);p(this,Sa,void 0);p(this,hb,void 0);p(this,ib,void 0); -p(this,N,void 0);p(this,la,void 0);p(this,wa,void 0);p(this,Ob,void 0);p(this,xa,void 0);p(this,Ga,void 0);p(this,I,void 0);p(this,Ta,void 0);p(this,Ua,void 0);p(this,xb,void 0);p(this,ya,void 0);p(this,jb,void 0);p(this,qa,void 0);p(this,za,void 0);p(this,ra,void 0);p(this,yb,void 0);p(this,M,void 0);p(this,zb,void 0);p(this,xc,void 0);p(this,Va,void 0);p(this,kb,void 0);p(this,lb,void 0);p(this,mb,void 0);p(this,Aa,void 0);p(this,Pb,void 0);p(this,sa,void 0);p(this,Ha,void 0);p(this,Wa,void 0); -p(this,nb,void 0);p(this,Qb,void 0);p(this,ob,void 0);p(this,pb,void 0);p(this,Ab,void 0);p(this,Bb,void 0);p(this,Rb,void 0);p(this,Sb,void 0);p(this,qb,void 0);p(this,Xa,void 0);p(this,Tb,void 0);p(this,J,void 0);p(this,G,void 0);p(this,ca,void 0);p(this,Ya,void 0);p(this,Ba,void 0);p(this,Ca,void 0);p(this,Ub,void 0);p(this,Vb,void 0);p(this,Wb,void 0);p(this,Xb,void 0);p(this,Yb,void 0);p(this,Zb,void 0);p(this,$b,void 0);p(this,yc,void 0);p(this,Za,void 0);p(this,Ia,void 0);p(this,$a,void 0); -p(this,Cb,void 0);p(this,Db,void 0);p(this,Eb,void 0);p(this,ac,void 0);p(this,Fb,void 0);p(this,bc,function(){if("[CLY]_temp_id"===a.device_id)b(e,a).call(a,f.INFO,"server_config, Device ID is temporary, not fetching server config");else if(b(yc,a))b(e,a).call(a,f.INFO,"server_config, SDK behavior sync is disabled, not fetching server config");else{b(e,a).call(a,f.INFO,"server_config, Fetching server config");var c={};c.app_key=a.app_key;c.device_id=a.device_id;c.sdk_version=b(ra,a);c.sdk_name=b(za, -a);c.t=b(I,a);c.timestamp=cb();var d=new Date;c.hour=d.getHours();c.dow=d.getDay();c.av=a.app_version;c.method="sc";b(ia,a).call(a,"server_config",a.url+b(Na,a),c,function(g,h,n){if(!g)try{var l=JSON.parse(n);b(e,a).call(a,f.INFO,"server_config, Config fetched successfully:["+JSON.stringify(l)+"]");l&&b(cc,a).call(a,l);b(z,a).call(a,"cly_config",JSON.stringify(l))}catch(r){b(e,a).call(a,f.ERROR,"server_config, Had an issue while parsing the response: "+r)}},!0,!0);setTimeout(function(){b(bc,a).call(a)}, -36E5*b(Qb,a))}});p(this,cc,function(c){c&&c.c&&"object"===T(c.c)&&(c=c.c,c.hasOwnProperty("networking")&&k(Pb,a,c.networking),c.hasOwnProperty("tracking")&&k(ob,a,c.tracking),c.hasOwnProperty("rqs")&&k(sa,a,c.rqs),c.hasOwnProperty("eqs")&&k(Ha,a,c.eqs),c.hasOwnProperty("sui")&&k(Wa,a,c.sui),c.hasOwnProperty("czi")&&14window.innerHeight?"landscape":"portrait")}})});w(this,"report_conversion",function(c,d){b(e,a).call(a,f.WARNING,"report_conversion, Deprecated function call! Use 'recordDirectAttribution' in place of this call. Call will be redirected now!");a.recordDirectAttribution(c,d)});w(this,"recordDirectAttribution",function(c,d){b(e,a).call(a,f.INFO,"recordDirectAttribution, Recording the attribution for campaign ID: ["+ -c+"] and the user ID: ["+d+"]");a.check_consent("attribution")&&(c=c||b(B,a).call(a,"cly_cmp_id")||"cly_organic",(d=d||b(B,a).call(a,"cly_cmp_uid"))?b(V,a).call(a,{campaign_id:c,campaign_user:d}):b(V,a).call(a,{campaign_id:c}))});w(this,"user_details",function(c){b(e,a).call(a,f.INFO,"user_details, Trying to add user details: ",c);a.check_consent("users")&&(b(Ea,a).call(a),b(Da,a).call(a),b(e,a).call(a,f.INFO,"user_details, flushed the event queue"),c.name=D(c.name,b(G,a),"user_details",b(e,a)),c.username= -D(c.username,b(G,a),"user_details",b(e,a)),c.email=D(c.email,b(G,a),"user_details",b(e,a)),c.organization=D(c.organization,b(G,a),"user_details",b(e,a)),c.phone=D(c.phone,b(G,a),"user_details",b(e,a)),c.picture=D(c.picture,4096,"user_details",b(e,a)),c.gender=D(c.gender,b(G,a),"user_details",b(e,a)),c.byear=D(c.byear,b(G,a),"user_details",b(e,a)),c.custom=ua(c.custom,b(J,a),b(G,a),b(ca,a),"user_details",b(e,a)),b(V,a).call(a,{user_details:JSON.stringify(rb(c,"name username email organization phone picture gender byear custom".split(" ")))}))}); -p(this,ma,{});p(this,ta,function(c,d,g){a.check_consent("users")&&(b(ma,a)[c]||(b(ma,a)[c]={}),"$push"===g||"$pull"===g||"$addToSet"===g?(b(ma,a)[c][g]||(b(ma,a)[c][g]=[]),b(ma,a)[c][g].push(d)):b(ma,a)[c][g]=d)});w(this,"userData",{set:function(c,d){b(e,a).call(a,f.INFO,"[userData] set, Setting user's custom property value: ["+d+"] under the key: ["+c+"]");c=D(c,b(J,a),"userData set",b(e,a));d=D(d,b(G,a),"userData set",b(e,a));b(ma,a)[c]=d},unset:function(c){b(e,a).call(a,f.INFO,"[userData] unset, Resetting user's custom property with key: ["+ -c+"] ");b(ma,a)[c]=""},set_once:function(c,d){b(e,a).call(a,f.INFO,"[userData] set_once, Setting user's unique custom property value: ["+d+"] under the key: ["+c+"] ");c=D(c,b(J,a),"userData set_once",b(e,a));d=D(d,b(G,a),"userData set_once",b(e,a));b(ta,a).call(a,c,d,"$setOnce")},increment:function(c){b(e,a).call(a,f.INFO,"[userData] increment, Increasing user's custom property value under the key: ["+c+"] by one");c=D(c,b(J,a),"userData increment",b(e,a));b(ta,a).call(a,c,1,"$inc")},increment_by:function(c, -d){b(e,a).call(a,f.INFO,"[userData] increment_by, Increasing user's custom property value under the key: ["+c+"] by: ["+d+"]");c=D(c,b(J,a),"userData increment_by",b(e,a));d=D(d,b(G,a),"userData increment_by",b(e,a));b(ta,a).call(a,c,d,"$inc")},multiply:function(c,d){b(e,a).call(a,f.INFO,"[userData] multiply, Multiplying user's custom property value under the key: ["+c+"] by: ["+d+"]");c=D(c,b(J,a),"userData multiply",b(e,a));d=D(d,b(G,a),"userData multiply",b(e,a));b(ta,a).call(a,c,d,"$mul")},max:function(c, -d){b(e,a).call(a,f.INFO,"[userData] max, Saving user's maximum custom property value compared to the value: ["+d+"] under the key: ["+c+"]");c=D(c,b(J,a),"userData max",b(e,a));d=D(d,b(G,a),"userData max",b(e,a));b(ta,a).call(a,c,d,"$max")},min:function(c,d){b(e,a).call(a,f.INFO,"[userData] min, Saving user's minimum custom property value compared to the value: ["+d+"] under the key: ["+c+"]");c=D(c,b(J,a),"userData min",b(e,a));d=D(d,b(G,a),"userData min",b(e,a));b(ta,a).call(a,c,d,"$min")},push:function(c, -d){b(e,a).call(a,f.INFO,"[userData] push, Pushing a value: ["+d+"] under the key: ["+c+"] to user's custom property array");c=D(c,b(J,a),"userData push",b(e,a));d=D(d,b(G,a),"userData push",b(e,a));b(ta,a).call(a,c,d,"$push")},push_unique:function(c,d){b(e,a).call(a,f.INFO,"[userData] push_unique, Pushing a unique value: ["+d+"] under the key: ["+c+"] to user's custom property array");c=D(c,b(J,a),"userData push_unique",b(e,a));d=D(d,b(G,a),"userData push_unique",b(e,a));b(ta,a).call(a,c,d,"$addToSet")}, -pull:function(c,d){b(e,a).call(a,f.INFO,"[userData] pull, Removing the value: ["+d+"] under the key: ["+c+"] from user's custom property array");b(ta,a).call(a,c,d,"$pull")},save:function(){b(e,a).call(a,f.INFO,"[userData] save, Saving changes to user's custom property");a.check_consent("users")&&(b(Ea,a).call(a),b(Da,a).call(a),b(e,a).call(a,f.INFO,"user_details, flushed the event queue"),b(V,a).call(a,{user_details:JSON.stringify({custom:b(ma,a)})}));k(ma,a,{})}});w(this,"report_trace",function(c){b(e, -a).call(a,f.INFO,"report_trace, Reporting performance trace");if(a.check_consent("apm")){for(var d="type name stz etz apm_metrics apm_attr".split(" "),g=0;g=b(Ya,a);)b(va,a).shift(),b(e,a).call(a,f.WARNING,"add_log, Reached maximum crashLogs size. Will erase the oldest one.");b(va,a).push(c)}});w(this,"fetch_remote_config",function(c,d,g){var h=null,n=null,l=null;c&&(g||"function"!==typeof c?Array.isArray(c)&&(h=c):l=c);d&&(g||"function"!==typeof d?Array.isArray(d)&& -(n=d):l=d);l||"function"!==typeof g||(l=g);a.useExplicitRcApi?(b(e,a).call(a,f.INFO,"fetch_remote_config, Fetching remote config"),c=a.rcAutoOptinAb?1:0,b(Bc,a).call(a,h,n,c,null,l)):(b(e,a).call(a,f.WARNING,"fetch_remote_config, Fetching remote config, with legacy API"),b(Bc,a).call(a,h,n,null,"legacy",l))});p(this,Bc,function(c,d,g,h,n){b(e,a).call(a,f.INFO,"fetch_remote_config_explicit, Fetching sequence initiated");var l={method:"rc",av:a.app_version};c&&(l.keys=JSON.stringify(c));d&&(l.omit_keys= -JSON.stringify(d));var r;"legacy"===h&&(l.method="fetch_remote_config");0===g&&(l.oi=0);1===g&&(l.oi=1);"function"===typeof n&&(r=n);a.check_consent("sessions")&&(l.metrics=JSON.stringify(b(Hb,a).call(a)));a.check_consent("remote-config")?(b(ab,a).call(a,l),b(ia,a).call(a,"fetch_remote_config_explicit",a.url+b(Na,a),l,function(u,y,C){if(!u){try{var O=JSON.parse(C);if(l.keys||l.omit_keys)for(var P in O)b(Z,a)[P]=O[P];else k(Z,a,O);b(z,a).call(a,"cly_remote_configs",b(Z,a))}catch(Q){b(e,a).call(a,f.ERROR, -"fetch_remote_config_explicit, Had an issue while parsing the response: "+Q)}r&&(b(e,a).call(a,f.INFO,"fetch_remote_config_explicit, Callback function is provided"),r(u,b(Z,a)))}},!0)):(b(e,a).call(a,f.ERROR,"fetch_remote_config_explicit, Remote config requires explicit consent"),r&&r(Error("Remote config requires explicit consent"),b(Z,a)))});w(this,"enrollUserToAb",function(c){b(e,a).call(a,f.INFO,"enrollUserToAb, Providing AB test keys to opt in for");c&&Array.isArray(c)&&0!==c.length?(c={method:"ab", -keys:JSON.stringify(c),av:a.app_version},b(ab,a).call(a,c),b(ia,a).call(a,"enrollUserToAb",a.url+b(Na,a),c,function(d,g,h){if(!d)try{var n=JSON.parse(h);b(e,a).call(a,f.DEBUG,"enrollUserToAb, Parsed the response's result: ["+n.result+"]")}catch(l){b(e,a).call(a,f.ERROR,"enrollUserToAb, Had an issue while parsing the response: "+l)}},!0)):b(e,a).call(a,f.ERROR,"enrollUserToAb, No keys provided")});w(this,"get_remote_config",function(c){b(e,a).call(a,f.INFO,"get_remote_config, Getting remote config from storage"); -return"undefined"!==typeof c?b(Z,a)[c]:b(Z,a)});p(this,Cc,function(){b(e,a).call(a,f.INFO,"stop_time, Stopping tracking duration");b(wa,a)&&(k(wa,a,!1),k(vb,a,H()-b(oa,a)),k(Qa,a,H()-b(Pa,a)))});p(this,dc,function(){b(e,a).call(a,f.INFO,"start_time, Starting tracking duration");b(wa,a)||(k(wa,a,!0),k(oa,a,H()-b(vb,a)),k(Pa,a,H()-b(Qa,a)),k(Qa,a,0),b(Gc,a).call(a))});w(this,"track_sessions",function(){if(A){b(e,a).call(a,f.INFO,"track_session, Starting tracking user session");a.begin_session();b(dc, -a).call(a);F(window,"beforeunload",function(){b(Ea,a).call(a);b(Da,a).call(a);a.end_session()});var c="hidden",d=function(){document[c]||!document.hasFocus()?b(Cc,a).call(a):b(dc,a).call(a)};F(window,"focus",d);F(window,"blur",d);F(window,"pageshow",d);F(window,"pagehide",d);"onfocusin"in document&&(F(window,"focusin",d),F(window,"focusout",d));c in document?document.addEventListener("visibilitychange",d):"mozHidden"in document?(c="mozHidden",document.addEventListener("mozvisibilitychange",d)):"webkitHidden"in -document?(c="webkitHidden",document.addEventListener("webkitvisibilitychange",d)):"msHidden"in document&&(c="msHidden",document.addEventListener("msvisibilitychange",d));d=function(){b(Fa,a)>=b(Ra,a)&&b(dc,a).call(a);k(Fa,a,0)};F(window,"mousemove",d);F(window,"click",d);F(window,"keydown",d);F(window,"scroll",d);setInterval(function(){var g;k(Fa,a,(g=b(Fa,a),g++,g));b(Fa,a)>=b(Ra,a)&&b(Cc,a).call(a)},6E4)}else b(e,a).call(a,f.WARNING,"track_sessions, window object is not available. Not tracking sessions.")}); -w(this,"track_pageview",function(c,d,g){if(A||c)if(b(Ab,a))if(b(e,a).call(a,f.INFO,"track_pageview, Tracking page views"),b(e,a).call(a,f.VERBOSE,"track_pageview, last view is:["+b(ba,a)+"], current view ID is:["+b(ya,a)+"], previous view ID is:["+b(jb,a)+"]"),b(ba,a)&&b(xb,a)&&(b(e,a).call(a,f.DEBUG,"track_pageview, Scroll registry triggered"),b(nc,a).call(a),k(Ta,a,!0),k(Ua,a,0)),b(ic,a).call(a),k(jb,a,b(ya,a)),k(ya,a,pc()),(c=D(c,b(J,a),"track_pageview",b(e,a)))&&Array.isArray(c)&&(d=c,c=null), -c||(c=a.getViewName()),void 0===c||""===c)b(e,a).call(a,f.ERROR,"track_pageview, No page name to track (it is either undefined or empty string). No page view can be tracked.");else if(null===c)b(e,a).call(a,f.ERROR,"track_pageview, View name returned as null. Page view will be ignored.");else{if(d&&d.length)for(var h=0;h