From d09a7d9ab78bd53f52ed0516ec889244caf5af37 Mon Sep 17 00:00:00 2001 From: turtledreams <62231246+turtledreams@users.noreply.github.com> Date: Wed, 13 Nov 2024 03:17:54 +0900 Subject: [PATCH] SDK update and fixes --- CHANGELOG.md | 13 + cypress/e2e/bridged_utils.cy.js | 2 +- cypress/e2e/device_id_init_scenarios.cy.js | 254 ++++++++---- cypress/e2e/health_check.cy.js | 34 +- cypress/e2e/remaining_requests.cy.js | 8 +- cypress/support/helper.js | 44 +- examples/examples_feedback_widgets.html | 16 +- lib/countly.d.ts | 27 ++ lib/countly.js | 443 ++++++++++++--------- lib/countly.min.js | 320 +++++++-------- package-lock.json | 4 +- package.json | 2 +- 12 files changed, 711 insertions(+), 456 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56f4a142..1ac9b9c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## 24.11.0 +- Mitigated an issue where SDK could try to send old stored offline mode data during init if `clear_stored_id` was true +- Mitigated an issue where the SDK could stayed on offline mode after the first init with `offline_mode` set to true +- Mitigated an issue where old Rating widget stickers were not cleared when a new one was presented + +- Improved view tracking logic +- Default request method is now set to "POST" +- Healtchecks won't be sent in offline mode anymore +- Added a new interface 'feedback' which includes convenience methods to show feedback widgets: + - showNPS([String nameIDorTag]) - for displaying the first available NPS widget or one with the given name, Tag or ID value + - showSurvey([String nameIDorTag]) - for displaying the first available Survey widget or one with the given name, Tag or ID value + - showRating([String nameIDorTag]) - for displaying the first available Rating widget or one with the given name, Tag or ID value + ## 24.4.1 - Added types for the SDK - Added a new method `set_id(newDeviceId)` for managing device ID changes according to the device ID Type diff --git a/cypress/e2e/bridged_utils.cy.js b/cypress/e2e/bridged_utils.cy.js index c6673198..df184f57 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 = "24.4.1"; +const SDK_VERSION = "24.11.0"; // tests describe("Bridged SDK Utilities Tests", () => { diff --git a/cypress/e2e/device_id_init_scenarios.cy.js b/cypress/e2e/device_id_init_scenarios.cy.js index 4ff80b68..4a75fa7e 100644 --- a/cypress/e2e/device_id_init_scenarios.cy.js +++ b/cypress/e2e/device_id_init_scenarios.cy.js @@ -8,21 +8,21 @@ * | device ID | generated | mode was | device ID | device ID | | not | | * | was set | ID | enabled | provided | enabled | | set | set | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | - | - | - | 1 | - | + * | First init | - | - | - | 1 | 65 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | x | - | - | 2 | - | + * | First init | x | - | - | 2 | 66 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | - | x | - | 3 | - | + * | First init | - | x | - | 3 | 67 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | - | - | x | 4 | - | + * | First init | - | - | x | 4 | 68 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | x | x | - | 5 | - | + * | First init | x | x | - | 5 | 69 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | x | - | x | 6 | - | + * | First init | x | - | x | 6 | 70 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | - | x | x | 7 | - | + * | First init | - | x | x | 7 | 71 | * +--------------------------------------------------+------------------------------------+----------------------+ - * | First init | x | x | x | 8 | - | + * | First init | x | x | x | 8 | 72 | * +--------------------------------------------------+------------------------------------+----------------------+ * | x | - | - | - | - | - | 17 | 33 | * +--------------------------------------------------+------------------------------------+----------------------+ @@ -130,11 +130,11 @@ var DeviceIdTypeInternalEnumsTest = { TEMPORARY_ID: 2, URL_PROVIDED: 3 }; -describe("Device Id tests during first init", ()=>{ +describe("Device Id tests during first init", () => { // sdk is initialized w/o custom device id, w/o offline mode, w/o utm device id // we provide no device id information sdk should generate the id - it("1-SDK is initialized without custom device id, without offline mode, without utm device id", ()=>{ + it("1-SDK is initialized without custom device id, without offline mode, without utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); expect(Countly.get_device_id_type()).to.eq(Countly.DeviceIdType.SDK_GENERATED); @@ -147,7 +147,7 @@ describe("Device Id tests during first init", ()=>{ }); }); // we provide device id information sdk should use it - it("2-SDK is initialized with custom device id, without offline mode, without utm device id", ()=>{ + it("2-SDK is initialized with custom device id, without offline mode, without utm device id", () => { hp.haltAndClearStorage(() => { initMain("gerwutztreimer", false, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -160,7 +160,7 @@ describe("Device Id tests during first init", ()=>{ }); }); // we provide no device id information sdk should generate the id - it("3-SDK is initialized without custom device id, with offline mode, without utm device id", ()=>{ + it("3-SDK is initialized without custom device id, with offline mode, without utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, true, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.TEMPORARY_ID); @@ -172,7 +172,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("4-SDK is initialized without custom device id, without offline mode, with utm device id", ()=>{ + it("4-SDK is initialized without custom device id, without offline mode, with utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, "?cly_device_id=abab"); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -184,7 +184,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("5-SDK is initialized with custom device id, with offline mode, without utm device id", ()=>{ + it("5-SDK is initialized with custom device id, with offline mode, without utm device id", () => { hp.haltAndClearStorage(() => { initMain("customID", true, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -196,7 +196,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("6-SDK is initialized with custom device id, without offline mode, with utm device id", ()=>{ + it("6-SDK is initialized with custom device id, without offline mode, with utm device id", () => { hp.haltAndClearStorage(() => { initMain("customID2", false, "?cly_device_id=someID"); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -208,7 +208,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("7-SDK is initialized without custom device id, with offline mode, with utm device id", ()=>{ + it("7-SDK is initialized without custom device id, with offline mode, with utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, true, "?cly_device_id=someID"); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -220,7 +220,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("8-SDK is initialized with custom device id, with offline mode, with utm device id", ()=>{ + it("8-SDK is initialized with custom device id, with offline mode, with utm device id", () => { hp.haltAndClearStorage(() => { initMain("customID3", true, "?cly_device_id=someID2"); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -235,7 +235,7 @@ describe("Device Id tests during first init", ()=>{ // Here tests focus the device id change and offline mode // first pair - it("9-SDK is initialized with no device id, not offline mode, not utm device id", ()=>{ + it("9-SDK is initialized with no device id, not offline mode, not utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.SDK_GENERATED); @@ -251,7 +251,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("10-SDK is initialized with no device id, not offline mode, not utm device id, but then offline", ()=>{ + it("10-SDK is initialized with no device id, not offline mode, not utm device id, but then offline", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.SDK_GENERATED); @@ -269,7 +269,7 @@ describe("Device Id tests during first init", ()=>{ }); }); // second pair - it("11-SDK is initialized with user defined device id, not offline mode, not utm device id", ()=>{ + it("11-SDK is initialized with user defined device id, not offline mode, not utm device id", () => { hp.haltAndClearStorage(() => { initMain("userID", false, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -285,7 +285,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("12-SDK is initialized with user defined device id, not offline mode, not utm device id, but then offline", ()=>{ + it("12-SDK is initialized with user defined device id, not offline mode, not utm device id, but then offline", () => { hp.haltAndClearStorage(() => { initMain("userID", false, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -303,7 +303,7 @@ describe("Device Id tests during first init", ()=>{ }); }); // third pair - it("13-SDK is initialized with no device id, not offline mode, with utm device id", ()=>{ + it("13-SDK is initialized with no device id, not offline mode, with utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, "?cly_device_id=abab"); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -319,7 +319,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("14-SDK is initialized with no device id, not offline mode, with utm device id, but then offline", ()=>{ + it("14-SDK is initialized with no device id, not offline mode, with utm device id, but then offline", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, "?cly_device_id=abab"); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); @@ -337,7 +337,7 @@ describe("Device Id tests during first init", ()=>{ }); }); // fourth pair - it("15-SDK is initialized with no device id, with offline mode, no utm device id", ()=>{ + it("15-SDK is initialized with no device id, with offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, true, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.TEMPORARY_ID); @@ -353,7 +353,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("16-SDK is initialized with no device id, with offline mode, no utm device id, but then offline", ()=>{ + it("16-SDK is initialized with no device id, with offline mode, no utm device id, but then offline", () => { hp.haltAndClearStorage(() => { initMain(undefined, true, undefined); expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.TEMPORARY_ID); @@ -372,7 +372,7 @@ describe("Device Id tests during first init", ()=>{ }); // Auto generated or developer set device ID was present in the local storage before initialization - it("17-Stored ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", ()=>{ + it("17-Stored ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -386,7 +386,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("18-Stored ID precedence, SDK is initialized with device id, not offline mode, no utm device id", ()=>{ + it("18-Stored ID precedence, SDK is initialized with device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -400,7 +400,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("19-Stored ID precedence, SDK is initialized with no device id, offline mode, no utm device id", ()=>{ + it("19-Stored ID precedence, SDK is initialized with no device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -414,7 +414,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("20-Stored ID precedence, SDK is initialized with no device id, no offline mode, utm device id", ()=>{ + it("20-Stored ID precedence, SDK is initialized with no device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -428,7 +428,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("21-Stored ID precedence, SDK is initialized with device id, offline mode, no utm device id", ()=>{ + it("21-Stored ID precedence, SDK is initialized with device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -442,7 +442,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("22-Stored ID precedence, SDK is initialized with device id, no offline mode, utm device id", ()=>{ + it("22-Stored ID precedence, SDK is initialized with device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -456,7 +456,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("23-Stored ID precedence, SDK is initialized no device id, offline mode, utm device id", ()=>{ + it("23-Stored ID precedence, SDK is initialized no device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -470,7 +470,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("24-Stored ID precedence, SDK is initialized with device id, offline mode, utm device id", ()=>{ + it("24-Stored ID precedence, SDK is initialized with device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -486,7 +486,7 @@ describe("Device Id tests during first init", ()=>{ }); // Temporary ID was present in the local storage before initialization - it("25-Stored temp ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", ()=>{ + it("25-Stored temp ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -500,7 +500,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("26-Stored temp ID precedence, SDK is initialized with device id, not offline mode, no utm device id", ()=>{ + it("26-Stored temp ID precedence, SDK is initialized with device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -514,7 +514,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("27-Stored temp ID precedence, SDK is initialized with no device id, offline mode, no utm device id", ()=>{ + it("27-Stored temp ID precedence, SDK is initialized with no device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -528,7 +528,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("28-Stored temp ID precedence, SDK is initialized with no device id, no offline mode, utm device id", ()=>{ + it("28-Stored temp ID precedence, SDK is initialized with no device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -542,7 +542,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("29-Stored temp ID precedence, SDK is initialized with device id, offline mode, no utm device id", ()=>{ + it("29-Stored temp ID precedence, SDK is initialized with device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -556,7 +556,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("30-Stored temp ID precedence, SDK is initialized with device id, no offline mode, utm device id", ()=>{ + it("30-Stored temp ID precedence, SDK is initialized with device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -570,7 +570,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("31-Stored temp ID precedence, SDK is initialized with no device id, offline mode, utm device id", ()=>{ + it("31-Stored temp ID precedence, SDK is initialized with no device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -584,7 +584,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("32-Stored temp ID precedence, SDK is initialized with device id, offline mode, utm device id", ()=>{ + it("32-Stored temp ID precedence, SDK is initialized with device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -601,7 +601,7 @@ describe("Device Id tests during first init", ()=>{ // Same tests with clear device ID flag set to true // Auto generated or developer set device ID was present in the local storage before initialization - it("33-Cleared ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", ()=>{ + it("33-Cleared ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -615,7 +615,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("34-Cleared ID precedence, SDK is initialized with device id, not offline mode, no utm device id", ()=>{ + it("34-Cleared ID precedence, SDK is initialized with device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -629,7 +629,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("35-Cleared ID precedence, SDK is initialized with no device id, offline mode, no utm device id", ()=>{ + it("35-Cleared ID precedence, SDK is initialized with no device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -643,7 +643,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("36-Cleared ID precedence, SDK is initialized with no device id, no offline mode, utm device id", ()=>{ + it("36-Cleared ID precedence, SDK is initialized with no device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -657,7 +657,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("37-Cleared ID precedence, SDK is initialized with device id, offline mode, no utm device id", ()=>{ + it("37-Cleared ID precedence, SDK is initialized with device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -671,7 +671,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("38-Cleared ID precedence, SDK is initialized with device id, no offline mode, utm device id", ()=>{ + it("38-Cleared ID precedence, SDK is initialized with device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -685,7 +685,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("39-Cleared ID precedence, SDK is initialized with no device id, offline mode, utm device id", ()=>{ + it("39-Cleared ID precedence, SDK is initialized with no device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -699,7 +699,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("40-Cleared ID precedence, SDK is initialized with device id, offline mode, utm device id", ()=>{ + it("40-Cleared ID precedence, SDK is initialized with device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("storedID", false, undefined); Countly.halt(); @@ -715,21 +715,21 @@ describe("Device Id tests during first init", ()=>{ }); // Temporary ID was present in the local storage before initialization - it("41-Cleared temp ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", ()=>{ + it("41-Cleared temp ID precedence, SDK is initialized with no device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); initMain(undefined, false, undefined, true); - expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.SDK_GENERATED); - validateSdkGeneratedId(Countly.get_device_id()); - validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.SDK_GENERATED); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.TEMPORARY_ID); + expect(Countly.get_device_id()).to.eq("[CLY]_temp_id"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.TEMPORARY_ID); Countly.begin_session(); cy.fetch_local_request_queue().then((eq) => { - checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.SDK_GENERATED); + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.TEMPORARY_ID); }); }); }); - it("42-Cleared temp ID precedence, SDK is initialized with device id, not offline mode, no utm device id", ()=>{ + it("42-Cleared temp ID precedence, SDK is initialized with device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -743,7 +743,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("43-Cleared temp ID precedence, SDK is initialized with no device id, offline mode, no utm device id", ()=>{ + it("43-Cleared temp ID precedence, SDK is initialized with no device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -757,7 +757,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("44-Cleared temp ID precedence, SDK is initialized with no device id, no offline mode, utm device id", ()=>{ + it("44-Cleared temp ID precedence, SDK is initialized with no device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -771,7 +771,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("45-Cleared temp ID precedence, SDK is initialized with device id, offline mode, no utm device id", ()=>{ + it("45-Cleared temp ID precedence, SDK is initialized with device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -785,7 +785,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("46-Cleared temp ID precedence, SDK is initialized with device id, no offline mode, utm device id", ()=>{ + it("46-Cleared temp ID precedence, SDK is initialized with device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -799,7 +799,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("47-Cleared temp ID precedence, SDK is initialized with no device id, offline mode, utm device id", ()=>{ + it("47-Cleared temp ID precedence, SDK is initialized with no device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -813,7 +813,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("48-Cleared temp ID precedence, SDK is initialized with device id, offline mode, utm device id", ()=>{ + it("48-Cleared temp ID precedence, SDK is initialized with device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain("[CLY]_temp_id", false, undefined); Countly.halt(); @@ -829,7 +829,7 @@ describe("Device Id tests during first init", ()=>{ }); // SDK generated ID was present prior the second init - it("49-Stored UUID precedence, SDK is initialized with no device id, not offline mode, no utm device id", ()=>{ + it("49-Stored UUID precedence, SDK is initialized with no device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -845,7 +845,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("50-Stored UUID precedence, SDK is initialized with device id, not offline mode, no utm device id", ()=>{ + it("50-Stored UUID precedence, SDK is initialized with device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -861,7 +861,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("51-Stored UUID precedence, SDK is initialized with no device id, offline mode, no utm device id", ()=>{ + it("51-Stored UUID precedence, SDK is initialized with no device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -877,7 +877,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("52-Stored UUID precedence, SDK is initialized with no device id, no offline mode, utm device id", ()=>{ + it("52-Stored UUID precedence, SDK is initialized with no device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -893,7 +893,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("53-Stored UUID precedence, SDK is initialized with device id, offline mode, no utm device id", ()=>{ + it("53-Stored UUID precedence, SDK is initialized with device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -909,7 +909,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("54-Stored UUID precedence, SDK is initialized with device id, no offline mode, utm device id", ()=>{ + it("54-Stored UUID precedence, SDK is initialized with device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -925,7 +925,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("55-Stored UUID precedence, SDK is initialized no device id, offline mode, utm device id", ()=>{ + it("55-Stored UUID precedence, SDK is initialized no device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -941,7 +941,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("56-Stored UUID precedence, SDK is initialized with device id, offline mode, utm device id", ()=>{ + it("56-Stored UUID precedence, SDK is initialized with device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -959,7 +959,7 @@ describe("Device Id tests during first init", ()=>{ }); // SDK generated ID was present prior the second init (same tests with flag set to true) - it("57-Stored UUID precedence, SDK is initialized with no device id, not offline mode, no utm device id", ()=>{ + it("57-Stored UUID precedence, SDK is initialized with no device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -975,7 +975,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("58-Stored UUID precedence, SDK is initialized with device id, not offline mode, no utm device id", ()=>{ + it("58-Stored UUID precedence, SDK is initialized with device id, not offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -990,7 +990,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("59-Stored UUID precedence, SDK is initialized with no device id, offline mode, no utm device id", ()=>{ + it("59-Stored UUID precedence, SDK is initialized with no device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -1005,7 +1005,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("60-Stored UUID precedence, SDK is initialized with no device id, no offline mode, utm device id", ()=>{ + it("60-Stored UUID precedence, SDK is initialized with no device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -1020,7 +1020,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("61-Stored UUID precedence, SDK is initialized with device id, offline mode, no utm device id", ()=>{ + it("61-Stored UUID precedence, SDK is initialized with device id, offline mode, no utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -1035,7 +1035,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("62-Stored UUID precedence, SDK is initialized with device id, no offline mode, utm device id", ()=>{ + it("62-Stored UUID precedence, SDK is initialized with device id, no offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -1050,7 +1050,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("63-Stored UUID precedence, SDK is initialized no device id, offline mode, utm device id", ()=>{ + it("63-Stored UUID precedence, SDK is initialized no device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -1065,7 +1065,7 @@ describe("Device Id tests during first init", ()=>{ }); }); }); - it("64-Stored UUID precedence, SDK is initialized with device id, offline mode, utm device id", ()=>{ + it("64-Stored UUID precedence, SDK is initialized with device id, offline mode, utm device id", () => { hp.haltAndClearStorage(() => { initMain(undefined, false, undefined); var oldUUID = Countly.get_device_id(); @@ -1080,4 +1080,104 @@ describe("Device Id tests during first init", ()=>{ }); }); }); + + // testing first 8 tests with clear_stored_id flag set to true + it("65-SDK is initialized without custom device id, without offline mode, without utm device id", () => { + hp.haltAndClearStorage(() => { + initMain(undefined, false, undefined, true); + expect(Countly.get_device_id_type()).to.eq(Countly.DeviceIdType.SDK_GENERATED); + validateSdkGeneratedId(Countly.get_device_id()); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.SDK_GENERATED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.SDK_GENERATED); + }); + }); + }); + // we provide device id information sdk should use it + it("66-SDK is initialized with custom device id, without offline mode, without utm device id", () => { + hp.haltAndClearStorage(() => { + initMain("gerwutztreimer", false, undefined, true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); + expect(Countly.get_device_id()).to.eq("gerwutztreimer"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.DEVELOPER_SUPPLIED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.DEVELOPER_SUPPLIED); + }); + }); + }); + // we provide no device id information sdk should generate the id + it("67-SDK is initialized without custom device id, with offline mode, without utm device id", () => { + hp.haltAndClearStorage(() => { + initMain(undefined, true, undefined, true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.TEMPORARY_ID); + expect(Countly.get_device_id()).to.eq("[CLY]_temp_id"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.TEMPORARY_ID); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.TEMPORARY_ID); + }); + }); + }); + it("68-SDK is initialized without custom device id, without offline mode, with utm device id", () => { + hp.haltAndClearStorage(() => { + initMain(undefined, false, "?cly_device_id=abab", true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); + expect(Countly.get_device_id()).to.eq("abab"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + }); + }); + }); + it("69-SDK is initialized with custom device id, with offline mode, without utm device id", () => { + hp.haltAndClearStorage(() => { + initMain("customID", true, undefined, true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); + expect(Countly.get_device_id()).to.eq("customID"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.DEVELOPER_SUPPLIED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.DEVELOPER_SUPPLIED); + }); + }); + }); + it("70-SDK is initialized with custom device id, without offline mode, with utm device id", () => { + hp.haltAndClearStorage(() => { + initMain("customID2", false, "?cly_device_id=someID", true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); + expect(Countly.get_device_id()).to.eq("someID"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + }); + }); + }); + it("71-SDK is initialized without custom device id, with offline mode, with utm device id", () => { + hp.haltAndClearStorage(() => { + initMain(undefined, true, "?cly_device_id=someID", true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); + expect(Countly.get_device_id()).to.eq("someID"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + }); + }); + }); + it("72-SDK is initialized with custom device id, with offline mode, with utm device id", () => { + hp.haltAndClearStorage(() => { + initMain("customID3", true, "?cly_device_id=someID2", true); + expect(Countly.get_device_id_type()).to.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED); + expect(Countly.get_device_id()).to.eq("someID2"); + validateInternalDeviceIdType(DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + Countly.begin_session(); + cy.fetch_local_request_queue().then((eq) => { + checkRequestsForT(eq, DeviceIdTypeInternalEnumsTest.URL_PROVIDED); + }); + }); + }); }); diff --git a/cypress/e2e/health_check.cy.js b/cypress/e2e/health_check.cy.js index d3d1babc..69d41ca3 100644 --- a/cypress/e2e/health_check.cy.js +++ b/cypress/e2e/health_check.cy.js @@ -10,28 +10,42 @@ function initMain() { }); } -describe("Health Check tests ", () => { +describe("Health Check tests", () => { it("Check if health check is sent at the beginning", () => { hp.haltAndClearStorage(() => { initMain(); - cy.intercept("https://your.domain.count.ly/i?*").as("getXhr"); - cy.wait("@getXhr").then((xhr) => { - const url = new URL(xhr.request.url); + cy.intercept("POST", "https://your.domain.count.ly/i").as("postXhr"); + cy.wait("@postXhr").then((xhr) => { + const body = xhr.request.body; + const params = new URLSearchParams(body); - // Test the 'hc' parameter - const hcParam = url.searchParams.get("hc"); - const hcParamObj = JSON.parse(hcParam); + const hcParam = params.get("hc"); + const hcParamObj = JSON.parse(decodeURIComponent(hcParam)); expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "" }); - // Test the 'metrics' parameter - const metricsParam = url.searchParams.get("metrics"); + const metricsParam = params.get("metrics"); expect(metricsParam).to.equal("{\"_app_version\":\"0.0\",\"_ua\":\"abcd\"}"); - // check nothing in the request queue cy.fetch_local_request_queue().then((rq) => { expect(rq.length).to.equal(0); }); }); }); }); + it("Check no health check is sent in offline mode", () => { + hp.haltAndClearStorage(() => { + Countly.init({ + app_key: "YOUR_APP_KEY", + url: "https://your.domain.count.ly", + test_mode: true, + offline_mode: true + }); + + cy.intercept("POST", "https://your.domain.count.ly/i").as("postXhr"); + cy.get('@postXhr').should('not.exist'); + cy.fetch_local_request_queue().then((rq) => { + expect(rq.length).to.equal(0); + }); + }); + }); }); diff --git a/cypress/e2e/remaining_requests.cy.js b/cypress/e2e/remaining_requests.cy.js index 08745c9f..abf33dad 100644 --- a/cypress/e2e/remaining_requests.cy.js +++ b/cypress/e2e/remaining_requests.cy.js @@ -18,7 +18,7 @@ describe("Remaining requests tests ", () => { initMain(false); // We will expect 4 requests: health check, begin_session, end_session, orientation - hp.interceptAndCheckRequests(undefined, undefined, undefined, "?hc=*", "hc", (requestParams) => { + hp.interceptAndCheckRequests("POST", undefined, undefined, "?hc=*", "hc", (requestParams) => { const params = JSON.parse(requestParams.get("hc")); assert.isTrue(typeof params.el === "number"); assert.isTrue(typeof params.wl === "number"); @@ -29,19 +29,19 @@ describe("Remaining requests tests ", () => { cy.wait(1000).then(() => { // Create a session Countly.begin_session(); - hp.interceptAndCheckRequests(undefined, undefined, undefined, "?begin_session=*", "begin_session", (requestParams) => { + hp.interceptAndCheckRequests("POST", undefined, undefined, "?begin_session=*", "begin_session", (requestParams) => { expect(requestParams.get("begin_session")).to.equal("1"); expect(requestParams.get("rr")).to.equal("3"); expect(requestParams.get("av")).to.equal(av); }); // End the session Countly.end_session(undefined, true); - hp.interceptAndCheckRequests(undefined, undefined, undefined, "?end_session=*", "end", (requestParams) => { + hp.interceptAndCheckRequests("POST", undefined, undefined, "?end_session=*", "end", (requestParams) => { expect(requestParams.get("end_session")).to.equal("1"); expect(requestParams.get("rr")).to.equal("2"); expect(requestParams.get("av")).to.equal(av); }); - hp.interceptAndCheckRequests(undefined, undefined, undefined, undefined, "orientation", (requestParams) => { + hp.interceptAndCheckRequests("POST", undefined, undefined, undefined, "orientation", (requestParams) => { expect(JSON.parse(requestParams.get("events"))[0].key).to.equal("[CLY]_orientation"); expect(requestParams.get("rr")).to.equal("1"); expect(requestParams.get("av")).to.equal(av); diff --git a/cypress/support/helper.js b/cypress/support/helper.js index e976bccd..2096aaad 100644 --- a/cypress/support/helper.js +++ b/cypress/support/helper.js @@ -68,31 +68,43 @@ var waitFunction = function(startTime, waitTime, waitIncrement, continueCallback }; /** - * This intercepts the request the SDK makes and returns the request parameters to the callback function - * @param {String} requestType - GET, POST, PUT, DELETE - * @param {String} requestUrl - request url (https://your.domain.count.ly) - * @param {String} endPoint - endpoint (/i) - * @param {String} requestParams - request parameters (?begin_session=**) + * Intercepts SDK requests and returns request parameters to the callback function. + * @param {String} requestType - GET or POST + * @param {String} requestUrl - base URL (e.g., https://your.domain.count.ly) + * @param {String} endPoint - endpoint (e.g., /i) + * @param {String} aliasParam - parameter to match in requests (e.g., "hc", "begin_session") * @param {String} alias - alias for the request - * @param {Function} callback - callback function + * @param {Function} callback - callback function for parsed parameters */ -function interceptAndCheckRequests(requestType, requestUrl, endPoint, requestParams, alias, callback) { - // requestType = requestType || "GET"; - requestUrl = requestUrl || "https://your.domain.count.ly"; // TODO: might be needed in the future but not yet +function interceptAndCheckRequests(requestType, requestUrl, endPoint, aliasParam, alias, callback) { + requestType = requestType || "GET"; + requestUrl = requestUrl || "https://your.domain.count.ly"; endPoint = endPoint || "/i"; - requestParams = requestParams || "?*"; alias = alias || "getXhr"; - cy.intercept(requestUrl + endPoint + requestParams, (req) => { - // const { url } = req; - req.reply(200, {result: "Success"}, { + // Intercept requests + cy.intercept(requestType, requestUrl + endPoint + "*", (req) => { + if (requestType === "POST" && req.body) { + // Parse URL-encoded body for POST requests + const params = new URLSearchParams(req.body); + callback(params); + } else { + // Parse URL parameters for GET requests + const url = new URL(req.url); + const params = url.searchParams; + callback(params); + } + req.reply(200, { result: "Success" }, { "x-countly-rr": "2" }); }).as(alias); + + // Wait for the request alias to be triggered cy.wait("@" + alias).then((xhr) => { - const url = new URL(xhr.request.url); - const searchParams = url.searchParams; - callback(searchParams); + const params = requestType === "POST" && xhr.request.body + ? new URLSearchParams(xhr.request.body) + : new URL(xhr.request.url).searchParams; + callback(params); }); } diff --git a/examples/examples_feedback_widgets.html b/examples/examples_feedback_widgets.html index 36dab0e3..8585fbdb 100644 --- a/examples/examples_feedback_widgets.html +++ b/examples/examples_feedback_widgets.html @@ -22,7 +22,21 @@ }); //================================================= - // Fetching and displaying feedback widgets + // Displaying feedback widgets + //================================================= + Countly.feedback.showNPS(); + + // OR with a specific ID, name or tag + // const id = 'ID_from_server'; + // Countly.feedback.showNPS(id); + + // Other feedback widgets + // Countly.feedback.showSurvey(); + // Countly.feedback.showRating(); + + + //================================================= + // ADVANCED: Fetching and displaying feedback widgets //================================================= function fetchAndDisplayWidget() { // Fetch user's feedbacks widgets from the server (must have been created at server first) diff --git a/lib/countly.d.ts b/lib/countly.d.ts index 7d9f5aec..26a0d0c1 100644 --- a/lib/countly.d.ts +++ b/lib/countly.d.ts @@ -456,6 +456,33 @@ declare module "countly-sdk-web" { | null ): void; + /** + * Feedback interface with convenience methods for feedback widgets: + * - showNPS([nameIDorTag]) - shows an NPS widget by name, id, or tag, or a random one if not provided + * - showSurvey([nameIDorTag]) - shows a Survey widget by name, id, or tag, or a random one if not provided + * - showRating([nameIDorTag]) - shows a Rating widget by name, id, or tag, or a random one if not provided + */ + const feedback: Feedback; + interface Feedback { + /** + * Displays the first available NPS widget or the one with the provided name, id, or tag. + * @param nameIDorTag - Optional name, id, or tag of the NPS widget to display. + */ + showNPS(nameIDorTag?: string): void; + + /** + * Displays the first available Survey widget or the one with the provided name, id, or tag. + * @param nameIDorTag - Optional name, id, or tag of the Survey widget to display. + */ + showSurvey(nameIDorTag?: string): void; + + /** + * Displays the first available Rating widget or the one with the provided name, id, or tag. + * @param nameIDorTag - Optional name, id, or tag of the Rating widget to display. + */ + showRating(nameIDorTag?: string): void; + } + /** * This function retrieves all associated widget information (IDs, type, name etc in an array/list of objects) of your app * @param {Function} callback - Callback function with two parameters, 1st for returned list, 2nd for error diff --git a/lib/countly.js b/lib/countly.js index 88f70657..6a6d0969 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -47,6 +47,30 @@ })(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 _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 _createClass(e, r, t) { + return Object.defineProperty(e, "prototype", { + writable: !1 + }), e; + } function _typeof(o) { "@babel/helpers - typeof"; @@ -56,42 +80,6 @@ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); - } - } - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; - } - function _toPrimitive(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); - } - function _toPropertyKey(arg) { - var key = _toPrimitive(arg, "string"); - return typeof key === "symbol" ? key : String(key); - } // Feature ENUMS var featureEnums = { @@ -196,7 +184,7 @@ statusCode: "cly_hc_status_code", errorMessage: "cly_hc_error_message" }); - var SDK_VERSION = "24.4.1"; + var SDK_VERSION = "24.11.0"; 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) @@ -835,8 +823,11 @@ } } + var _showWidgetInternal = /*#__PURE__*/new WeakMap(); var CountlyClass = /*#__PURE__*/_createClass(function CountlyClass(ob) { + var _this = this; _classCallCheck(this, CountlyClass); + _classPrivateFieldInitSpec(this, _showWidgetInternal, void 0); var self = this; var global = !Countly.i; var sessionStarted = false; @@ -883,6 +874,7 @@ var freshUTMTags = null; var sdkName = getConfig("sdk_name", ob, SDK_NAME); var sdkVersion = getConfig("sdk_version", ob, SDK_VERSION); + var shouldSendHC = false; try { localStorage.setItem("cly_testLocal", true); // clean up test @@ -898,88 +890,91 @@ consents[Countly.features[it]] = {}; } this.initialize = function () { - this.serialize = getConfig("serialize", ob, Countly.serialize); - this.deserialize = getConfig("deserialize", ob, Countly.deserialize); - this.getViewName = getConfig("getViewName", ob, Countly.getViewName); - this.getViewUrl = getConfig("getViewUrl", ob, Countly.getViewUrl); - this.getSearchQuery = getConfig("getSearchQuery", ob, Countly.getSearchQuery); - this.DeviceIdType = Countly.DeviceIdType; // it is Countly device Id type Enums for clients to use - this.namespace = getConfig("namespace", ob, ""); - this.clearStoredId = getConfig("clear_stored_id", ob, false); - this.app_key = getConfig("app_key", ob, null); - this.onload = getConfig("onload", ob, []); - this.utm = getConfig("utm", ob, { + _this.serialize = getConfig("serialize", ob, Countly.serialize); + _this.deserialize = getConfig("deserialize", ob, Countly.deserialize); + _this.getViewName = getConfig("getViewName", ob, Countly.getViewName); + _this.getViewUrl = getConfig("getViewUrl", ob, Countly.getViewUrl); + _this.getSearchQuery = getConfig("getSearchQuery", ob, Countly.getSearchQuery); + _this.DeviceIdType = Countly.DeviceIdType; // it is Countly device Id type Enums for clients to use + _this.namespace = getConfig("namespace", ob, ""); + _this.clearStoredId = getConfig("clear_stored_id", ob, false); + _this.app_key = getConfig("app_key", ob, null); + _this.onload = getConfig("onload", ob, []); + _this.utm = getConfig("utm", ob, { source: true, medium: true, campaign: true, term: true, content: true }); - this.ignore_prefetch = getConfig("ignore_prefetch", ob, true); - this.rcAutoOptinAb = getConfig("rc_automatic_optin_for_ab", ob, true); - this.useExplicitRcApi = getConfig("use_explicit_rc_api", ob, false); - this.debug = getConfig("debug", ob, false); - this.test_mode = getConfig("test_mode", ob, false); - this.test_mode_eq = getConfig("test_mode_eq", ob, false); - this.metrics = getConfig("metrics", ob, {}); - this.headers = getConfig("headers", ob, {}); - this.url = stripTrailingSlash(getConfig("url", ob, "")); - this.app_version = getConfig("app_version", ob, "0.0"); - this.country_code = getConfig("country_code", ob, null); - this.city = getConfig("city", ob, null); - this.ip_address = getConfig("ip_address", ob, null); - this.ignore_bots = getConfig("ignore_bots", ob, true); - this.force_post = getConfig("force_post", ob, false); - this.remote_config = getConfig("remote_config", ob, false); - this.ignore_visitor = getConfig("ignore_visitor", ob, false); - this.require_consent = getConfig("require_consent", ob, false); - this.track_domains = !isBrowser ? undefined : getConfig("track_domains", ob, true); - this.storage = getConfig("storage", ob, "default"); - this.enableOrientationTracking = !isBrowser ? undefined : getConfig("enable_orientation_tracking", ob, true); - this.maxKeyLength = getConfig("max_key_length", ob, configurationDefaultValues.MAX_KEY_LENGTH); - this.maxValueSize = getConfig("max_value_size", ob, configurationDefaultValues.MAX_VALUE_SIZE); - this.maxSegmentationValues = getConfig("max_segmentation_values", ob, configurationDefaultValues.MAX_SEGMENTATION_VALUES); - this.maxBreadcrumbCount = getConfig("max_breadcrumb_count", ob, null); - this.maxStackTraceLinesPerThread = getConfig("max_stack_trace_lines_per_thread", ob, configurationDefaultValues.MAX_STACKTRACE_LINES_PER_THREAD); - this.maxStackTraceLineLength = getConfig("max_stack_trace_line_length", ob, configurationDefaultValues.MAX_STACKTRACE_LINE_LENGTH); - this.heatmapWhitelist = getConfig("heatmap_whitelist", ob, []); - self.salt = getConfig("salt", ob, null); - self.hcErrorCount = getValueFromStorage(healthCheckCounterEnum.errorCount) || 0; - self.hcWarningCount = getValueFromStorage(healthCheckCounterEnum.warningCount) || 0; - self.hcStatusCode = getValueFromStorage(healthCheckCounterEnum.statusCode) || -1; - self.hcErrorMessage = getValueFromStorage(healthCheckCounterEnum.errorMessage) || ""; - if (maxCrashLogs && !this.maxBreadcrumbCount) { - this.maxBreadcrumbCount = maxCrashLogs; + _this.ignore_prefetch = getConfig("ignore_prefetch", ob, true); + _this.rcAutoOptinAb = getConfig("rc_automatic_optin_for_ab", ob, true); + _this.useExplicitRcApi = getConfig("use_explicit_rc_api", ob, false); + _this.debug = getConfig("debug", ob, false); + _this.test_mode = getConfig("test_mode", ob, false); + _this.test_mode_eq = getConfig("test_mode_eq", ob, false); + _this.metrics = getConfig("metrics", ob, {}); + _this.headers = getConfig("headers", ob, {}); + _this.url = stripTrailingSlash(getConfig("url", ob, "")); + _this.app_version = getConfig("app_version", ob, "0.0"); + _this.country_code = getConfig("country_code", ob, null); + _this.city = getConfig("city", ob, null); + _this.ip_address = getConfig("ip_address", ob, null); + _this.ignore_bots = getConfig("ignore_bots", ob, true); + _this.force_post = getConfig("force_post", ob, false); + _this.remote_config = getConfig("remote_config", ob, false); + _this.ignore_visitor = getConfig("ignore_visitor", ob, false); + _this.require_consent = getConfig("require_consent", ob, false); + _this.track_domains = !isBrowser ? undefined : getConfig("track_domains", ob, true); + _this.storage = getConfig("storage", ob, "default"); + _this.enableOrientationTracking = !isBrowser ? undefined : getConfig("enable_orientation_tracking", ob, true); + _this.maxKeyLength = getConfig("max_key_length", ob, configurationDefaultValues.MAX_KEY_LENGTH); + _this.maxValueSize = getConfig("max_value_size", ob, configurationDefaultValues.MAX_VALUE_SIZE); + _this.maxSegmentationValues = getConfig("max_segmentation_values", ob, configurationDefaultValues.MAX_SEGMENTATION_VALUES); + _this.maxBreadcrumbCount = getConfig("max_breadcrumb_count", ob, null); + _this.maxStackTraceLinesPerThread = getConfig("max_stack_trace_lines_per_thread", ob, configurationDefaultValues.MAX_STACKTRACE_LINES_PER_THREAD); + _this.maxStackTraceLineLength = getConfig("max_stack_trace_line_length", ob, configurationDefaultValues.MAX_STACKTRACE_LINE_LENGTH); + _this.heatmapWhitelist = getConfig("heatmap_whitelist", ob, []); + _this.salt = getConfig("salt", ob, null); + _this.hcErrorCount = getValueFromStorage(healthCheckCounterEnum.errorCount) || 0; + _this.hcWarningCount = getValueFromStorage(healthCheckCounterEnum.warningCount) || 0; + _this.hcStatusCode = getValueFromStorage(healthCheckCounterEnum.statusCode) || -1; + _this.hcErrorMessage = getValueFromStorage(healthCheckCounterEnum.errorMessage) || ""; + if (maxCrashLogs && !_this.maxBreadcrumbCount) { + _this.maxBreadcrumbCount = maxCrashLogs; log(logLevelEnums.WARNING, "initialize, 'maxCrashLogs' is deprecated. Use 'maxBreadcrumbCount' instead!"); - } else if (!maxCrashLogs && !this.maxBreadcrumbCount) { - this.maxBreadcrumbCount = 100; + } else if (!maxCrashLogs && !_this.maxBreadcrumbCount) { + _this.maxBreadcrumbCount = 100; } - if (this.storage === "cookie") { + if (_this.storage === "cookie") { lsSupport = false; } - if (!this.rcAutoOptinAb && !this.useExplicitRcApi) { + if (!_this.rcAutoOptinAb && !_this.useExplicitRcApi) { log(logLevelEnums.WARNING, "initialize, Auto opting is disabled, switching to explicit RC API"); - this.useExplicitRcApi = true; + _this.useExplicitRcApi = true; } if (!Array.isArray(ignoreReferrers)) { ignoreReferrers = []; } - if (this.url === "") { + if (_this.url === "") { log(logLevelEnums.ERROR, "initialize, Please provide server URL"); - this.ignore_visitor = true; + _this.ignore_visitor = true; } if (getValueFromStorage("cly_ignore")) { // opted out user - this.ignore_visitor = true; + _this.ignore_visitor = true; } migrate(); requestQueue = getValueFromStorage("cly_queue") || []; eventQueue = getValueFromStorage("cly_event") || []; remoteConfigs = getValueFromStorage("cly_remote_configs") || {}; - if (this.clearStoredId) { + + // flag that indicates that the offline mode was enabled at the end of the previous app session + var tempIdModeWasEnabled = getValueFromStorage("cly_id") === "[CLY]_temp_id"; + if (_this.clearStoredId) { // retrieve stored device ID and type from local storage and use it to flush existing events if (getValueFromStorage("cly_id") && !tempIdModeWasEnabled) { - this.device_id = getValueFromStorage("cly_id"); + _this.device_id = getValueFromStorage("cly_id"); log(logLevelEnums.DEBUG, "initialize, temporarily using the previous device ID to flush existing events"); deviceIdType = getValueFromStorage("cly_id_type"); if (!deviceIdType) { @@ -989,7 +984,7 @@ // don't process async queue here, just send the events (most likely async data is for the new user) sendEventsForced(); // set them back to their initial values - this.device_id = undefined; + _this.device_id = undefined; deviceIdType = DeviceIdTypeInternalEnums.SDK_GENERATED; } // then clear the storage so that a new device ID is set again later @@ -1002,47 +997,47 @@ if (isBrowser) { if (window.name && window.name.indexOf("cly:") === 0) { try { - this.passed_data = JSON.parse(window.name.replace("cly:", "")); + _this.passed_data = JSON.parse(window.name.replace("cly:", "")); } catch (ex) { log(logLevelEnums.ERROR, "initialize, Could not parse name: " + window.name + ", error: " + ex); } } else if (location.hash && location.hash.indexOf("#cly:") === 0) { try { - this.passed_data = JSON.parse(location.hash.replace("#cly:", "")); + _this.passed_data = JSON.parse(location.hash.replace("#cly:", "")); } catch (ex) { log(logLevelEnums.ERROR, "initialize, Could not parse hash: " + location.hash + ", error: " + ex); } } } - if (this.passed_data && this.passed_data.app_key && this.passed_data.app_key === this.app_key || this.passed_data && !this.passed_data.app_key && global) { - if (this.passed_data.token && this.passed_data.purpose) { - if (this.passed_data.token !== getValueFromStorage("cly_old_token")) { - setToken(this.passed_data.token); - setValueInStorage("cly_old_token", this.passed_data.token); + if (_this.passed_data && _this.passed_data.app_key && _this.passed_data.app_key === _this.app_key || _this.passed_data && !_this.passed_data.app_key && global) { + if (_this.passed_data.token && _this.passed_data.purpose) { + if (_this.passed_data.token !== getValueFromStorage("cly_old_token")) { + setToken(_this.passed_data.token); + setValueInStorage("cly_old_token", _this.passed_data.token); } var strippedList = []; // if whitelist is provided is an array - if (Array.isArray(this.heatmapWhitelist)) { - this.heatmapWhitelist.push(this.url); - strippedList = this.heatmapWhitelist.map(function (e) { + if (Array.isArray(_this.heatmapWhitelist)) { + _this.heatmapWhitelist.push(_this.url); + strippedList = _this.heatmapWhitelist.map(function (e) { // remove trailing slashes from the entries return stripTrailingSlash(e); }); } else { - strippedList = [this.url]; + strippedList = [_this.url]; } // if the passed url is in the whitelist proceed - if (strippedList.includes(this.passed_data.url)) { - if (this.passed_data.purpose === "heatmap") { - this.ignore_visitor = true; + if (strippedList.includes(_this.passed_data.url)) { + if (_this.passed_data.purpose === "heatmap") { + _this.ignore_visitor = true; showLoader(); - loadJS(this.passed_data.url + "/views/heatmap.js", hideLoader); + loadJS(_this.passed_data.url + "/views/heatmap.js", hideLoader); } } } } - if (this.ignore_visitor) { - log(logLevelEnums.WARNING, "initialize, ignore_visitor:[" + this.ignore_visitor + "], this user will not be tracked"); + if (_this.ignore_visitor) { + log(logLevelEnums.WARNING, "initialize, ignore_visitor:[" + _this.ignore_visitor + "], this user will not be tracked"); return; } @@ -1053,74 +1048,74 @@ } else { log(logLevelEnums.DEBUG, "initialize, SDK name:[" + sdkName + "], version:[" + sdkVersion + "], default name:[" + SDK_NAME + "] and default version:[" + SDK_VERSION + "]"); } - log(logLevelEnums.DEBUG, "initialize, app_key:[" + this.app_key + "], url:[" + this.url + "]"); + log(logLevelEnums.DEBUG, "initialize, app_key:[" + _this.app_key + "], url:[" + _this.url + "]"); log(logLevelEnums.DEBUG, "initialize, device_id:[" + getConfig("device_id", ob, undefined) + "]"); - log(logLevelEnums.DEBUG, "initialize, require_consent is enabled:[" + this.require_consent + "]"); + log(logLevelEnums.DEBUG, "initialize, require_consent is enabled:[" + _this.require_consent + "]"); try { - log(logLevelEnums.DEBUG, "initialize, metric override:[" + JSON.stringify(this.metrics) + "]"); - log(logLevelEnums.DEBUG, "initialize, header override:[" + JSON.stringify(this.headers) + "]"); + log(logLevelEnums.DEBUG, "initialize, metric override:[" + JSON.stringify(_this.metrics) + "]"); + log(logLevelEnums.DEBUG, "initialize, header override:[" + JSON.stringify(_this.headers) + "]"); // empty array is truthy and so would be printed if provided - log(logLevelEnums.DEBUG, "initialize, number of onload callbacks provided:[" + this.onload.length + "]"); + log(logLevelEnums.DEBUG, "initialize, number of onload callbacks provided:[" + _this.onload.length + "]"); // if the utm object is different to default utm object print it here - log(logLevelEnums.DEBUG, "initialize, utm tags:[" + JSON.stringify(this.utm) + "]"); + log(logLevelEnums.DEBUG, "initialize, utm tags:[" + JSON.stringify(_this.utm) + "]"); // empty array printed if non provided if (ignoreReferrers) { log(logLevelEnums.DEBUG, "initialize, referrers to ignore :[" + JSON.stringify(ignoreReferrers) + "]"); } - log(logLevelEnums.DEBUG, "initialize, salt given:[" + !!self.salt + "]"); + log(logLevelEnums.DEBUG, "initialize, salt given:[" + !!_this.salt + "]"); } catch (e) { log(logLevelEnums.ERROR, "initialize, Could not stringify some config object values"); } - log(logLevelEnums.DEBUG, "initialize, app_version:[" + this.app_version + "]"); + log(logLevelEnums.DEBUG, "initialize, app_version:[" + _this.app_version + "]"); // location info printed here - log(logLevelEnums.DEBUG, "initialize, provided location info; country_code:[" + this.country_code + "], city:[" + this.city + "], ip_address:[" + this.ip_address + "]"); + log(logLevelEnums.DEBUG, "initialize, provided location info; country_code:[" + _this.country_code + "], city:[" + _this.city + "], ip_address:[" + _this.ip_address + "]"); // print non vital values only if provided by the developer or differs from the default value - if (this.namespace !== "") { - log(logLevelEnums.DEBUG, "initialize, namespace given:[" + this.namespace + "]"); + if (_this.namespace !== "") { + log(logLevelEnums.DEBUG, "initialize, namespace given:[" + _this.namespace + "]"); } - if (this.clearStoredId) { - log(logLevelEnums.DEBUG, "initialize, clearStoredId flag set to:[" + this.clearStoredId + "]"); + if (_this.clearStoredId) { + log(logLevelEnums.DEBUG, "initialize, clearStoredId flag set to:[" + _this.clearStoredId + "]"); } - if (this.ignore_prefetch) { - log(logLevelEnums.DEBUG, "initialize, ignoring pre-fetching and pre-rendering from counting as real website visits :[" + this.ignore_prefetch + "]"); + if (_this.ignore_prefetch) { + log(logLevelEnums.DEBUG, "initialize, ignoring pre-fetching and pre-rendering from counting as real website visits :[" + _this.ignore_prefetch + "]"); } // if test mode is enabled warn the user - if (this.test_mode) { - log(logLevelEnums.WARNING, "initialize, test_mode:[" + this.test_mode + "], request queue won't be processed"); + if (_this.test_mode) { + log(logLevelEnums.WARNING, "initialize, test_mode:[" + _this.test_mode + "], request queue won't be processed"); } - if (this.test_mode_eq) { - log(logLevelEnums.WARNING, "initialize, test_mode_eq:[" + this.test_mode_eq + "], event queue won't be processed"); + if (_this.test_mode_eq) { + log(logLevelEnums.WARNING, "initialize, test_mode_eq:[" + _this.test_mode_eq + "], event queue won't be processed"); } // if test mode is enabled warn the user - if (this.heatmapWhitelist) { - log(logLevelEnums.DEBUG, "initialize, heatmap whitelist:[" + JSON.stringify(this.heatmapWhitelist) + "], these domains will be whitelisted"); + if (_this.heatmapWhitelist) { + log(logLevelEnums.DEBUG, "initialize, heatmap whitelist:[" + JSON.stringify(_this.heatmapWhitelist) + "], these domains will be whitelisted"); } // if storage is se to something other than local storage - if (this.storage !== "default") { - log(logLevelEnums.DEBUG, "initialize, storage is set to:[" + this.storage + "]"); + if (_this.storage !== "default") { + log(logLevelEnums.DEBUG, "initialize, storage is set to:[" + _this.storage + "]"); } - if (this.ignore_bots) { - log(logLevelEnums.DEBUG, "initialize, ignore traffic from bots :[" + this.ignore_bots + "]"); + if (_this.ignore_bots) { + log(logLevelEnums.DEBUG, "initialize, ignore traffic from bots :[" + _this.ignore_bots + "]"); } - if (this.force_post) { - log(logLevelEnums.DEBUG, "initialize, forced post method for all requests:[" + this.force_post + "]"); + if (_this.force_post) { + log(logLevelEnums.DEBUG, "initialize, forced post method for all requests:[" + _this.force_post + "]"); } - if (this.remote_config) { - log(logLevelEnums.DEBUG, "initialize, remote_config callback provided:[" + !!this.remote_config + "]"); + if (_this.remote_config) { + log(logLevelEnums.DEBUG, "initialize, remote_config callback provided:[" + !!_this.remote_config + "]"); } - if (typeof this.rcAutoOptinAb === "boolean") { - log(logLevelEnums.DEBUG, "initialize, automatic RC optin is enabled:[" + this.rcAutoOptinAb + "]"); + if (typeof _this.rcAutoOptinAb === "boolean") { + log(logLevelEnums.DEBUG, "initialize, automatic RC optin is enabled:[" + _this.rcAutoOptinAb + "]"); } - if (!this.useExplicitRcApi) { + if (!_this.useExplicitRcApi) { log(logLevelEnums.WARNING, "initialize, will use legacy RC API. Consider enabling new API during init with use_explicit_rc_api flag"); } - if (this.track_domains) { - log(logLevelEnums.DEBUG, "initialize, tracking domain info:[" + this.track_domains + "]"); + if (_this.track_domains) { + log(logLevelEnums.DEBUG, "initialize, tracking domain info:[" + _this.track_domains + "]"); } - if (this.enableOrientationTracking) { - log(logLevelEnums.DEBUG, "initialize, enableOrientationTracking:[" + this.enableOrientationTracking + "]"); + if (_this.enableOrientationTracking) { + log(logLevelEnums.DEBUG, "initialize, enableOrientationTracking:[" + _this.enableOrientationTracking + "]"); } if (!useSessionCookie) { log(logLevelEnums.WARNING, "initialize, use_session_cookie is enabled:[" + useSessionCookie + "]"); @@ -1128,31 +1123,31 @@ if (offlineMode) { log(logLevelEnums.DEBUG, "initialize, offline_mode:[" + offlineMode + "], user info won't be send to the servers"); } - if (offlineMode) { + if (remoteConfigs) { log(logLevelEnums.DEBUG, "initialize, stored remote configs:[" + JSON.stringify(remoteConfigs) + "]"); } // functions, if provided, would be printed as true without revealing their content - log(logLevelEnums.DEBUG, "initialize, 'getViewName' callback override provided:[" + (this.getViewName !== Countly.getViewName) + "]"); - log(logLevelEnums.DEBUG, "initialize, 'getSearchQuery' callback override provided:[" + (this.getSearchQuery !== Countly.getSearchQuery) + "]"); + log(logLevelEnums.DEBUG, "initialize, 'getViewName' callback override provided:[" + (_this.getViewName !== Countly.getViewName) + "]"); + log(logLevelEnums.DEBUG, "initialize, 'getSearchQuery' callback override provided:[" + (_this.getSearchQuery !== Countly.getSearchQuery) + "]"); // limits are printed here if they were modified - if (this.maxKeyLength !== configurationDefaultValues.MAX_KEY_LENGTH) { - log(logLevelEnums.DEBUG, "initialize, maxKeyLength set to:[" + this.maxKeyLength + "] characters"); + if (_this.maxKeyLength !== configurationDefaultValues.MAX_KEY_LENGTH) { + log(logLevelEnums.DEBUG, "initialize, maxKeyLength set to:[" + _this.maxKeyLength + "] characters"); } - if (this.maxValueSize !== configurationDefaultValues.MAX_VALUE_SIZE) { - log(logLevelEnums.DEBUG, "initialize, maxValueSize set to:[" + this.maxValueSize + "] characters"); + if (_this.maxValueSize !== configurationDefaultValues.MAX_VALUE_SIZE) { + log(logLevelEnums.DEBUG, "initialize, maxValueSize set to:[" + _this.maxValueSize + "] characters"); } - if (this.maxSegmentationValues !== configurationDefaultValues.MAX_SEGMENTATION_VALUES) { - log(logLevelEnums.DEBUG, "initialize, maxSegmentationValues set to:[" + this.maxSegmentationValues + "] key/value pairs"); + if (_this.maxSegmentationValues !== configurationDefaultValues.MAX_SEGMENTATION_VALUES) { + log(logLevelEnums.DEBUG, "initialize, maxSegmentationValues set to:[" + _this.maxSegmentationValues + "] key/value pairs"); } - if (this.maxBreadcrumbCount !== configurationDefaultValues.MAX_BREADCRUMB_COUNT) { - log(logLevelEnums.DEBUG, "initialize, maxBreadcrumbCount for custom logs set to:[" + this.maxBreadcrumbCount + "] entries"); + if (_this.maxBreadcrumbCount !== configurationDefaultValues.MAX_BREADCRUMB_COUNT) { + log(logLevelEnums.DEBUG, "initialize, maxBreadcrumbCount for custom logs set to:[" + _this.maxBreadcrumbCount + "] entries"); } - if (this.maxStackTraceLinesPerThread !== configurationDefaultValues.MAX_STACKTRACE_LINES_PER_THREAD) { - log(logLevelEnums.DEBUG, "initialize, maxStackTraceLinesPerThread set to:[" + this.maxStackTraceLinesPerThread + "] lines"); + if (_this.maxStackTraceLinesPerThread !== configurationDefaultValues.MAX_STACKTRACE_LINES_PER_THREAD) { + log(logLevelEnums.DEBUG, "initialize, maxStackTraceLinesPerThread set to:[" + _this.maxStackTraceLinesPerThread + "] lines"); } - if (this.maxStackTraceLineLength !== configurationDefaultValues.MAX_STACKTRACE_LINE_LENGTH) { - log(logLevelEnums.DEBUG, "initialize, maxStackTraceLineLength set to:[" + this.maxStackTraceLineLength + "] characters"); + if (_this.maxStackTraceLineLength !== configurationDefaultValues.MAX_STACKTRACE_LINE_LENGTH) { + log(logLevelEnums.DEBUG, "initialize, maxStackTraceLineLength set to:[" + _this.maxStackTraceLineLength + "] characters"); } if (beatInterval !== configurationDefaultValues.BEAT_INTERVAL) { log(logLevelEnums.DEBUG, "initialize, interval for heartbeats set to:[" + beatInterval + "] milliseconds"); @@ -1179,7 +1174,7 @@ log(logLevelEnums.DEBUG, "initialize, session_cookie_timeout set to:[" + sessionCookieTimeout + "] minutes to expire a cookies session"); } var deviceIdParamValue = null; - var searchQuery = self.getSearchQuery(); + var searchQuery = _this.getSearchQuery(); var hasUTM = false; var utms = {}; if (searchQuery) { @@ -1196,15 +1191,12 @@ setValueInStorage("cly_cmp_uid", nv[1]); } else if (nv[0] === "cly_device_id") { deviceIdParamValue = nv[1]; - } else if ((nv[0] + "").indexOf("utm_") === 0 && this.utm[nv[0].replace("utm_", "")]) { + } else if ((nv[0] + "").indexOf("utm_") === 0 && _this.utm[nv[0].replace("utm_", "")]) { utms[nv[0].replace("utm_", "")] = nv[1]; hasUTM = true; } } } - - // flag that indicates that the offline mode was enabled at the end of the previous app session - var tempIdModeWasEnabled = getValueFromStorage("cly_id") === "[CLY]_temp_id"; var developerSetDeviceId = getConfig("device_id", ob, undefined); if (typeof developerSetDeviceId === "number") { // device ID should always be string @@ -1213,7 +1205,7 @@ // check if there wqs stored ID if (getValueFromStorage("cly_id") && !tempIdModeWasEnabled) { - this.device_id = getValueFromStorage("cly_id"); + _this.device_id = getValueFromStorage("cly_id"); log(logLevelEnums.INFO, "initialize, Set the stored device ID"); deviceIdType = getValueFromStorage("cly_id_type"); if (!deviceIdType) { @@ -1221,17 +1213,19 @@ // there is a device ID saved but there is no device ID information saved deviceIdType = DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED; } + offlineMode = false; } // if not check if device ID was provided with URL else if (deviceIdParamValue !== null) { log(logLevelEnums.INFO, "initialize, Device ID set by URL"); - this.device_id = deviceIdParamValue; + _this.device_id = deviceIdParamValue; deviceIdType = DeviceIdTypeInternalEnums.URL_PROVIDED; + offlineMode = false; } // if not check if developer provided any ID else if (developerSetDeviceId) { log(logLevelEnums.INFO, "initialize, Device ID set by developer"); - this.device_id = developerSetDeviceId; + _this.device_id = developerSetDeviceId; if (ob && Object.keys(ob).length) { if (ob.device_id !== undefined) { deviceIdType = DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED; @@ -1239,10 +1233,11 @@ } else if (Countly.device_id !== undefined) { deviceIdType = DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED; } + offlineMode = false; } // if not check if offline mode is on else if (offlineMode || tempIdModeWasEnabled) { - this.device_id = "[CLY]_temp_id"; + _this.device_id = "[CLY]_temp_id"; deviceIdType = DeviceIdTypeInternalEnums.TEMPORARY_ID; if (offlineMode && tempIdModeWasEnabled) { log(logLevelEnums.INFO, "initialize, Temp ID set, continuing offline mode from previous app session"); @@ -1258,7 +1253,7 @@ // if all fails generate an ID else { log(logLevelEnums.INFO, "initialize, Generating the device ID"); - this.device_id = getConfig("device_id", ob, getStoredIdOrGenerateId()); + _this.device_id = getConfig("device_id", ob, getStoredIdOrGenerateId()); if (ob && Object.keys(ob).length) { if (ob.device_id !== undefined) { deviceIdType = DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED; @@ -1269,23 +1264,23 @@ } // Store the device ID and device ID type - setValueInStorage("cly_id", this.device_id); + setValueInStorage("cly_id", _this.device_id); setValueInStorage("cly_id_type", deviceIdType); // as we have assigned the device ID now we can save the tags if (hasUTM) { freshUTMTags = {}; - for (var tag in this.utm) { + for (var tag in _this.utm) { // this.utm is a filter for allowed tags if (utms[tag]) { // utms is the tags that were passed in the URL - this.userData.set("utm_" + tag, utms[tag]); + _this.userData.set("utm_" + tag, utms[tag]); freshUTMTags[tag] = utms[tag]; } else { - this.userData.unset("utm_" + tag); + _this.userData.unset("utm_" + tag); } } - this.userData.save(); + _this.userData.save(); } notifyLoaders(); setTimeout(function () { @@ -1294,8 +1289,8 @@ } else { log(logLevelEnums.WARNING, "initialize, Heartbeat disabled. This is for testing purposes only!"); } - if (self.remote_config) { - self.fetch_remote_config(self.remote_config); + if (_this.remote_config) { + _this.fetch_remote_config(_this.remote_config); } }, 1); if (isBrowser) { @@ -1663,6 +1658,10 @@ if (needResync) { setValueInStorage("cly_queue", requestQueue, true); } + if (shouldSendHC) { + HealthCheck.sendInstantHCRequest(); + shouldSendHC = false; + } }; /** @@ -2735,9 +2734,6 @@ } } } - lastView = page; - lastViewTime = getTimestamp(); - log(logLevelEnums.VERBOSE, "track_pageview, last view is assigned:[" + lastView + "], current view ID is:[" + currentViewId + "], previous view ID is:[" + previousViewId + "]"); var segments = { name: page, visit: 1, @@ -2790,7 +2786,6 @@ log(logLevelEnums.INFO, "track_pageview, Adding referrer to segmentation:[" + document.referrer + "]"); segments.referrer = document.referrer; // add referrer } - if (viewSegments) { viewSegments = truncateObject(viewSegments, self.maxKeyLength, self.maxValueSize, self.maxSegmentationValues, "track_pageview", log); for (var key in viewSegments) { @@ -2806,6 +2801,9 @@ key: internalEventKeyEnums.VIEW, segmentation: segments }, currentViewId); + lastView = page; + lastViewTime = getTimestamp(); + log(logLevelEnums.VERBOSE, "track_pageview, last view is assigned:[" + lastView + "]"); } else { lastParams.track_pageview = arguments; } @@ -3595,6 +3593,69 @@ } }; + /** + * Internal method to display a feedback widget of a specific type. + * @param {String} widgetType - The type of widget ("nps", "survey", "rating"). + * @param {String} [nameIDorTag] - The name, id, or tag of the widget to display. + */ + _classPrivateFieldSet2(_showWidgetInternal, this, function (widgetType, nameIDorTag) { + log(logLevelEnums.INFO, "showWidget, Showing ".concat(widgetType, " widget, nameIDorTag:[").concat(nameIDorTag, "]")); + _this.get_available_feedback_widgets(function (feedbackWidgetArray, error) { + if (error) { + log(logLevelEnums.ERROR, "showWidget, Error while getting feedback widgets list: ".concat(error)); + return; + } + + // Find the first widget of the specified type, or match by name, id, or tag if provided + var widget = feedbackWidgetArray.find(function (w) { + return w.type === widgetType; + }); + if (nameIDorTag && typeof nameIDorTag === 'string') { + var matchedWidget = feedbackWidgetArray.find(function (w) { + return w.type === widgetType && (w.name === nameIDorTag || w._id === nameIDorTag || w.tg.includes(nameIDorTag)); + }); + if (matchedWidget) { + widget = matchedWidget; + log(logLevelEnums.VERBOSE, "showWidget, Found ".concat(widgetType, " widget by name, id, or tag: [").concat(JSON.stringify(matchedWidget), "]")); + } + } + if (!widget) { + log(logLevelEnums.ERROR, "showWidget, No ".concat(widgetType, " widget found.")); + return; + } + _this.present_feedback_widget(widget, null, null, null); + }); + }), + /** + * Feedback interface with convenience methods for feedback widgets: + * - showNPS([String nameIDorTag]) - shows an NPS widget by name, id or tag, or a random one if not provided + * - showSurvey([String nameIDorTag]) - shows a Survey widget by name, id or tag, or a random one if not provided + * - showRating([String nameIDorTag]) - shows a Rating widget by name, id or tag, or a random one if not provided + */ + this.feedback = { + /** + * Displays the first available NPS widget or the one with the provided name, id, or tag. + * @param {String} [nameIDorTag] - Name, id, or tag of the NPS widget to display. + */ + showNPS: function showNPS(nameIDorTag) { + return _classPrivateFieldGet2(_showWidgetInternal, _this).call(_this, "nps", nameIDorTag); + }, + /** + * Displays the first available Survey widget or the one with the provided name, id, or tag. + * @param {String} [nameIDorTag] - Name, id, or tag of the Survey widget to display. + */ + showSurvey: function showSurvey(nameIDorTag) { + return _classPrivateFieldGet2(_showWidgetInternal, _this).call(_this, "survey", nameIDorTag); + }, + /** + * Displays the first available Rating widget or the one with the provided name, id, or tag. + * @param {String} [nameIDorTag] - Name, id, or tag of the Rating widget to display. + */ + showRating: function showRating(nameIDorTag) { + return _classPrivateFieldGet2(_showWidgetInternal, _this).call(_this, "rating", nameIDorTag); + } + }; + /** * This function retrieves all associated widget information (IDs, type, name etc in an array/list of objects) of your app * @param {Function} callback - Callback function with two parameters, 1st for returned list, 2nd for error @@ -3990,6 +4051,13 @@ * @param {Object} feedback - feedback object */ function showRatingForFeedbackWidget(feedback) { + // remove old stickers if exists + var stickers = document.getElementsByClassName("countly-feedback-sticker"); + while (stickers.length > 0) { + log(logLevelEnums.VERBOSE, "present_feedback_widget, Removing old stickers"); + stickers[0].remove(); + } + // render sticker if hide sticker property isn't set if (!feedback.appearance.hideS) { log(logLevelEnums.DEBUG, "present_feedback_widget, handling the sticker as it was not set to hidden"); @@ -4702,7 +4770,7 @@ var xhr = new XMLHttpRequest(); params = params || {}; prepareParams(params, self.salt).then(function (saltedData) { - var method = "GET"; + var method = "POST"; if (self.force_post || saltedData.length >= 2000) { method = "POST"; } @@ -4774,7 +4842,7 @@ log(logLevelEnums.DEBUG, "Sending Fetch request"); // Prepare request options - var method = "GET"; + var method = "POST"; var headers = { "Content-type": "application/x-www-form-urlencoded" }; @@ -5391,6 +5459,11 @@ * Countly health check request sender */ function sendInstantHCRequest() { + if (offlineMode) { + log(logLevelEnums.DEBUG, "sendInstantHCRequest, Offline mode is active. Not sending health check request."); + shouldSendHC = true; + return; + } // truncate error message to 1000 characters var curbedMessage = truncateSingleValue(self.hcErrorMessage, 1000, "healthCheck", log); // due to some server issues we pass empty string as is diff --git a/lib/countly.min.js b/lib/countly.min.js index 38710243..0402d66f 100644 --- a/lib/countly.min.js +++ b/lib/countly.min.js @@ -1,159 +1,161 @@ -(function(ma,L){"object"===typeof exports&&"undefined"!==typeof module?L(exports):"function"===typeof define&&define.amd?define(["exports"],L):(ma="undefined"!==typeof globalThis?globalThis:ma||self,L(ma.Countly=ma.Countly||{}))})(this,function(ma){function L(l){"@babel/helpers - typeof";return L="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(k){return typeof k}:function(k){return k&&"function"==typeof Symbol&&k.constructor===Symbol&&k!==Symbol.prototype?"symbol":typeof k},L(l)} -function zb(l,k){for(var q=0;q=l?Pa++:Pa=l;return Pa}function u(l,k,q){if(k&&Object.keys(k).length){if("undefined"!==typeof k[l])return k[l]}else if("undefined"!==typeof p[l])return p[l];return q}function ib(l,k,q){for(var t in p.i)p.i[t].tracking_crashes&&p.i[t].recordError(l,k,q)}function jb(l,k){var q=[],t;for(t in l)q.push(t+"="+encodeURIComponent(l[t]));var C=q.join("&");return k?Cb(C,k).then(function(O){return C+="&checksum256="+O}):Promise.resolve(C)}function ta(l){return"string"===typeof l&&"/"===l.substring(l.length- -1)?l.substring(0,l.length-1):l}function za(l,k){for(var q={},t,C=0,O=k.length;Ct){var W={},Aa=0,na;for(na in l)Aak&&(C=l.substring(0,k),t(c.DEBUG,q+", Key: [ "+l+" ] is longer than accepted length. It will be truncated.")); -return C}function Cb(l,k){l=(new TextEncoder).encode(l+k);return crypto.subtle.digest("SHA-256",l).then(function(q){return Array.from(new Uint8Array(q)).map(function(t){return t.toString(16).padStart(2,"0")}).join("")})}function A(l,k,q){x&&(null===l||"undefined"===typeof l?Ca()&&console.warn("[WARNING] [Countly] add_event_listener, Can't bind ["+k+"] event to nonexisting element"):"undefined"!==typeof l.addEventListener?l.addEventListener(k,q,!1):l.attachEvent("on"+k,q))}function Qa(l){return l? -"undefined"!==typeof l.target?l.target:l.srcElement:window.event.srcElement}function ua(l){if(l)return l;(l=navigator.userAgent)||(l=kb());return l}function kb(l){if(l)return l;l="";navigator.userAgentData&&(l=navigator.userAgentData.brands.map(function(k){return k.brand+":"+k.version}).join(),l+=navigator.userAgentData.mobile?" mobi ":" ",l+=navigator.userAgentData.platform);return l}function Db(l){if(!l){if(navigator.userAgentData&&navigator.userAgentData.mobile)return"phone";l=ua()}l=l.toLowerCase(); -var k="desktop",q=/(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(l)?k="tablet":q.test(l)&&(k="phone");return k}function Eb(l){var k=/(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(l)return k.test(l);l=k.test(ua());k=k.test(kb());return l||k}function lb(l){"undefined"===typeof l.pageY&&"number"===typeof l.clientX&&document.documentElement&&(l.pageX=l.clientX+document.body.scrollLeft+document.documentElement.scrollLeft,l.pageY=l.clientY+document.body.scrollTop+document.documentElement.scrollTop);return l}function Ra(){var l=document;return Math.max(Math.max(l.body.scrollHeight,l.documentElement.scrollHeight),Math.max(l.body.offsetHeight,l.documentElement.offsetHeight),Math.max(l.body.clientHeight, -l.documentElement.clientHeight))}function mb(){var l=document;return Math.max(Math.max(l.body.scrollWidth,l.documentElement.scrollWidth),Math.max(l.body.offsetWidth,l.documentElement.offsetWidth),Math.max(l.body.clientWidth,l.documentElement.clientWidth))}function Fb(){var l=document;return Math.min(Math.min(l.body.clientHeight,l.documentElement.clientHeight),Math.min(l.body.offsetHeight,l.documentElement.offsetHeight),window.innerHeight)}function Gb(l,k,q,t,C,O){l=document.createElement(l);var Q; -l.setAttribute(k,q);l.setAttribute(t,C);k=function(){Q||O();Q=!0};O&&(l.onreadystatechange=k,l.onload=k);document.getElementsByTagName("head")[0].appendChild(l)}function Hb(l,k){Gb("script","type","text/javascript","src",l,k)}function Sa(l,k){Gb("link","rel","stylesheet","href",l,k)}function Ib(){if(x){var l=document.getElementById("cly-loader");if(!l){var k=document.head||document.getElementsByTagName("head")[0],q=document.createElement("style");q.type="text/css";q.styleSheet?q.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%;}}":q.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%;}}")); -k.appendChild(q);l=document.createElement("div");l.setAttribute("id","cly-loader");document.body.onload=function(){if(p.showLoaderProtection)Ca()&&console.warn("[WARNING] [Countly] showloader, Loader is already on");else try{document.body.appendChild(l)}catch(t){Ca()&&console.error("[ERROR] [Countly] showLoader, Body is not loaded for loader to append: "+t)}}}l.style.display="block"}}function Ca(){return p&&p.debug&&"undefined"!==typeof console?!0:!1}function Jb(){if(x){p.showLoaderProtection=!0; -var l=document.getElementById("cly-loader");l&&(l.style.display="none")}}function Xb(l){var k=document.createElement("script"),q=document.createElement("script");k.async=!0;q.async=!0;k.src=p.customSourceBoomerang||Kb.BOOMERANG_SRC;q.src=p.customSourceCountlyBoomerang||Kb.CLY_BOOMERANG_SRC;document.getElementsByTagName("head")[0].appendChild(k);document.getElementsByTagName("head")[0].appendChild(q);var t=!1,C=!1;k.onload=function(){t=!0};q.onload=function(){C=!0};var O=0,Q=setInterval(function(){O+= -50;if(t&&C||1500<=O){if(p.debug){var W="BoomerangJS loaded:["+t+"], countly_boomerang loaded:["+C+"].";t&&C?console.log("[DEBUG] "+W):console.warn("[WARNING] "+W+" Initializing without APM.")}p.init(l);clearInterval(Q)}},50)}var M={NPS:"[CLY]_nps",SURVEY:"[CLY]_survey",STAR_RATING:"[CLY]_star_rating",VIEW:"[CLY]_view",ORIENTATION:"[CLY]_orientation",ACTION:"[CLY]_action"},Yb=Object.values(M),c={ERROR:"[ERROR] ",WARNING:"[WARNING] ",INFO:"[INFO] ",DEBUG:"[DEBUG] ",VERBOSE:"[VERBOSE] "},Kb={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"},U=Object.freeze({errorCount:"cly_hc_error_count",warningCount:"cly_hc_warning_count",statusCode:"cly_hc_status_code",errorMessage:"cly_hc_error_message"}),Lb=/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?::([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?::([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,x="undefined"!==typeof window,p=globalThis.Countly||{}, -Pa=0,Ub=function(l,k,q){k&&zb(l.prototype,k);q&&zb(l,q);Object.defineProperty(l,"prototype",{writable:!1});return l}(function q(k){function t(a,d){if(f.ignore_visitor)b(c.ERROR,"Adding event failed. Possible bot or user opt out");else if(a.key){a.count||(a.count=1);Yb.includes(a.key)||(a.key=z(a.key,f.maxKeyLength,"add_cly_event",b));a.segmentation=ba(a.segmentation,f.maxKeyLength,f.maxValueSize,f.maxSegmentationValues,"add_cly_event",b);a=za(a,["key","count","sum","dur","segmentation"]);a.timestamp= -hb();var e=new Date;a.hour=e.getHours();a.dow=e.getDay();a.id=d||fb();a.key===M.VIEW?a.pvid=Da||"":a.cvid=ia||"";I.push(a);w("cly_event",I);b(c.INFO,"With event ID: ["+a.id+"], successfully adding the last event:",a)}else b(c.ERROR,"Adding event failed. Event must have a key property")}function C(a,d,e,h,m){b(c.INFO,"fetch_remote_config_explicit, Fetching sequence initiated");var g={method:"rc",av:f.app_version};a&&(g.keys=JSON.stringify(a));d&&(g.omit_keys=JSON.stringify(d));var n;"legacy"===h&& -(g.method="fetch_remote_config");0===e&&(g.oi=0);1===e&&(g.oi=1);"function"===typeof m&&(n=m);f.check_consent("sessions")&&(g.metrics=JSON.stringify(Ta()));f.check_consent("remote-config")?(Ea(g),ca("fetch_remote_config_explicit",f.url+Ua,g,function(r,v,G){if(!r){try{var N=JSON.parse(G);if(g.keys||g.omit_keys)for(var J in N)S[J]=N[J];else S=N;w("cly_remote_configs",S)}catch(Fa){b(c.ERROR,"fetch_remote_config_explicit, Had an issue while parsing the response: "+Fa)}n&&(b(c.INFO,"fetch_remote_config_explicit, Callback function is provided"), -n(r,S))}},!0)):(b(c.ERROR,"fetch_remote_config_explicit, Remote config requires explicit consent"),n&&n(Error("Remote config requires explicit consent"),S))}function O(){b(c.INFO,"checkIgnore, Checking if user or visit should be ignored");f.ignore_prefetch&&x&&"undefined"!==typeof document.visibilityState&&"prerender"===document.visibilityState&&(f.ignore_visitor=!0,b(c.DEBUG,"checkIgnore, Ignoring visit due to prerendering"));f.ignore_bots&&Eb()&&(f.ignore_visitor=!0,b(c.DEBUG,"checkIgnore, Ignoring visit due to bot"))} -function Q(){0a.trigger_font_color.length?"#"+a.trigger_font_color:a.trigger_font_color;v.style.backgroundColor=7>a.trigger_bg_color.length?"#"+a.trigger_bg_color:a.trigger_bg_color;v.className="countly-feedback-sticker "+a.trigger_position+"-"+a.trigger_size;v.id="countly-feedback-sticker-"+a._id;g.appendChild(n);v.appendChild(g);v.appendChild(r);document.body.appendChild(v);var G=document.getElementById("smileyPathInStickerSvg"); -G&&(G.style.fill=7>a.trigger_font_color.length?"#"+a.trigger_font_color:a.trigger_font_color);A(document.getElementById("countly-feedback-sticker-"+a._id),"click",function(){document.getElementById("countly-iframe-wrapper-"+a._id).style.display="block";document.getElementById("cfbg").style.display="block"})}else document.getElementById("countly-iframe-wrapper-"+a._id).style.display="block",document.getElementById("cfbg").style.display="block"}catch(N){b(c.ERROR,"Somethings went wrong while element injecting process: "+ -N)}else b(c.WARNING,"processWidget, window object is not available. Not processing widget.")}function Aa(){var a;if("undefined"!==typeof f.onload&&0Va&&H.shift(),H.push(a),w("cly_queue",H,!0)):b(c.ERROR,"app_key or device_id is missing ",f.app_key,f.device_id)}function Wa(){Aa();if(f.ignore_visitor)Xa=!1,b(c.WARNING,"User opt_out, no heartbeat");else{Xa=!0;Ya&&"undefined"!==typeof p.q&&0Za&&(f.session_duration(a-ka),ka=a,0nb&&($a=!1,a=H[0],a.rr=H.length,b(c.DEBUG,"Processing request",a),w("cly_queue",H,!0),f.test_mode||ca("send_request_queue",f.url+ob,a,function(d,e){d?nb=F()+ab:H.shift();w("cly_queue",H,!0);$a=!0},!1));setTimeout(Wa,bb)}}function wa(){if("undefined"===typeof p||"undefined"===typeof p.i)b(c.DEBUG,"Countly is not finished initialization yet, will process the queue after initialization is done"); -else{var a=p.q;p.q=[];for(var d=0;da))return b(c.ERROR,"Http response status code:["+ -a+"] is not within the expected range"),!1;try{var e=JSON.parse(d);return"[object Object]"!==Object.prototype.toString.call(e)?(b(c.ERROR,"Http response is not JSON Object"),!1):!!e.result}catch(h){return b(c.ERROR,"Http response is not JSON: "+h),!1}}function rb(a,d){if(!(200<=a&&300>a))return b(c.ERROR,"Http response status code:["+a+"] is not within the expected range"),!1;try{var e=JSON.parse(d);return"[object Object]"===Object.prototype.toString.call(e)||Array.isArray(e)?!0:(b(c.ERROR,"Http response is not JSON Object nor JSON Array"), -!1)}catch(h){return b(c.ERROR,"Http response is not JSON: "+h),!1}}function Pb(){x?La=Math.max(La,window.scrollY,document.body.scrollTop,document.documentElement.scrollTop):b(c.WARNING,"processScroll, window object is not available. Not processing scroll.")}function tb(){if(!x)b(c.WARNING,"processScrollView, window object is not available. Not processing scroll view.");else if(Ma){Ma=!1;var a=Ra(),d=mb(),e=Fb();f.check_consent("scrolls")&&(a={type:"scroll",y:La+e,width:d,height:a,view:f.getViewUrl()}, -a=ba(a,f.maxKeyLength,f.maxValueSize,f.maxSegmentationValues,"processScrollView",b),f.track_domains&&(a.domain=window.location.hostname),t({key:M.ACTION,segmentation:a}))}}function Qb(a){w("cly_token",a)}function Rb(a,d,e){var h=new Date;h.setTime(h.getTime()+864E5*e);e="; expires="+h.toGMTString();document.cookie=a+"="+d+e+"; path=/"}function y(a,d,e){if("none"===f.storage||"object"!==L(f.storage)&&!x)b(c.DEBUG,"Storage is disabled. Value with key: ["+a+"] won't be retrieved");else{e||(a=f.app_key+ -"/"+a,f.namespace&&(a=ta(f.namespace)+"/"+a));if("object"===L(f.storage)&&"function"===typeof f.storage.getItem){var h=f.storage.getItem(a);return a.endsWith("cly_id")?h:f.deserialize(h)}void 0===d&&(d=ra);if(d)h=localStorage.getItem(a);else if("localstorage"!==f.storage)a:{d=a+"=";e=document.cookie.split(";");h=0;for(var m=e.length;hwindow.innerHeight?"landscape":"portrait")}})};this.report_conversion=function(a,d){b(c.WARNING, -"report_conversion, Deprecated function call! Use 'recordDirectAttribution' in place of this call. Call will be redirected now!");this.recordDirectAttribution(a,d)};this.recordDirectAttribution=function(a,d){b(c.INFO,"recordDirectAttribution, Recording the attribution for campaign ID: ["+a+"] and the user ID: ["+d+"]");this.check_consent("attribution")&&(a=a||y("cly_cmp_id")||"cly_organic",(d=d||y("cly_cmp_uid"))?P({campaign_id:a,campaign_user:d}):P({campaign_id:a}))};this.user_details=function(a){b(c.INFO, -"user_details, Trying to add user details: ",a);this.check_consent("users")&&(wa(),Q(),b(c.INFO,"user_details, flushed the event queue"),a.name=z(a.name,f.maxValueSize,"user_details",b),a.username=z(a.username,f.maxValueSize,"user_details",b),a.email=z(a.email,f.maxValueSize,"user_details",b),a.organization=z(a.organization,f.maxValueSize,"user_details",b),a.phone=z(a.phone,f.maxValueSize,"user_details",b),a.picture=z(a.picture,4096,"user_details",b),a.gender=z(a.gender,f.maxValueSize,"user_details", -b),a.byear=z(a.byear,f.maxValueSize,"user_details",b),a.custom=ba(a.custom,f.maxKeyLength,f.maxValueSize,f.maxSegmentationValues,"user_details",b),P({user_details:JSON.stringify(za(a,"name username email organization phone picture gender byear custom".split(" ")))}))};var Z={},ha=function(a,d,e){f.check_consent("users")&&(Z[a]||(Z[a]={}),"$push"===e||"$pull"===e||"$addToSet"===e?(Z[a][e]||(Z[a][e]=[]),Z[a][e].push(d)):Z[a][e]=d)};this.userData={set:function(a,d){b(c.INFO,"[userData] set, Setting user's custom property value: ["+ -d+"] under the key: ["+a+"]");a=z(a,f.maxKeyLength,"userData set",b);d=z(d,f.maxValueSize,"userData set",b);Z[a]=d},unset:function(a){b(c.INFO,"[userData] unset, Resetting user's custom property with key: ["+a+"] ");Z[a]=""},set_once:function(a,d){b(c.INFO,"[userData] set_once, Setting user's unique custom property value: ["+d+"] under the key: ["+a+"] ");a=z(a,f.maxKeyLength,"userData set_once",b);d=z(d,f.maxValueSize,"userData set_once",b);ha(a,d,"$setOnce")},increment:function(a){b(c.INFO,"[userData] increment, Increasing user's custom property value under the key: ["+ -a+"] by one");a=z(a,f.maxKeyLength,"userData increment",b);ha(a,1,"$inc")},increment_by:function(a,d){b(c.INFO,"[userData] increment_by, Increasing user's custom property value under the key: ["+a+"] by: ["+d+"]");a=z(a,f.maxKeyLength,"userData increment_by",b);d=z(d,f.maxValueSize,"userData increment_by",b);ha(a,d,"$inc")},multiply:function(a,d){b(c.INFO,"[userData] multiply, Multiplying user's custom property value under the key: ["+a+"] by: ["+d+"]");a=z(a,f.maxKeyLength,"userData multiply",b); -d=z(d,f.maxValueSize,"userData multiply",b);ha(a,d,"$mul")},max:function(a,d){b(c.INFO,"[userData] max, Saving user's maximum custom property value compared to the value: ["+d+"] under the key: ["+a+"]");a=z(a,f.maxKeyLength,"userData max",b);d=z(d,f.maxValueSize,"userData max",b);ha(a,d,"$max")},min:function(a,d){b(c.INFO,"[userData] min, Saving user's minimum custom property value compared to the value: ["+d+"] under the key: ["+a+"]");a=z(a,f.maxKeyLength,"userData min",b);d=z(d,f.maxValueSize, -"userData min",b);ha(a,d,"$min")},push:function(a,d){b(c.INFO,"[userData] push, Pushing a value: ["+d+"] under the key: ["+a+"] to user's custom property array");a=z(a,f.maxKeyLength,"userData push",b);d=z(d,f.maxValueSize,"userData push",b);ha(a,d,"$push")},push_unique:function(a,d){b(c.INFO,"[userData] push_unique, Pushing a unique value: ["+d+"] under the key: ["+a+"] to user's custom property array");a=z(a,f.maxKeyLength,"userData push_unique",b);d=z(d,f.maxValueSize,"userData push_unique",b); -ha(a,d,"$addToSet")},pull:function(a,d){b(c.INFO,"[userData] pull, Removing the value: ["+d+"] under the key: ["+a+"] from user's custom property array");ha(a,d,"$pull")},save:function(){b(c.INFO,"[userData] save, Saving changes to user's custom property");f.check_consent("users")&&(wa(),Q(),b(c.INFO,"user_details, flushed the event queue"),P({user_details:JSON.stringify({custom:Z})}));Z={}}};this.report_trace=function(a){b(c.INFO,"report_trace, Reporting performance trace");if(this.check_consent("apm")){for(var d= -"type name stz etz apm_metrics apm_attr".split(" "),e=0;e=f.maxBreadcrumbCount;)sa.shift(),b(c.WARNING,"add_log, Reached maximum crashLogs size. Will erase the oldest one.");sa.push(a)}};this.fetch_remote_config=function(a,d,e){var h=null,m=null,g=null;a&&(e||"function"!==typeof a?Array.isArray(a)&&(h=a):g=a);d&&(e||"function"!==typeof d?Array.isArray(d)&&(m=d):g=d);g||"function"!==typeof e||(g=e);this.useExplicitRcApi?(b(c.INFO,"fetch_remote_config, Fetching remote config"), -C(h,m,this.rcAutoOptinAb?1:0,null,g)):(b(c.WARNING,"fetch_remote_config, Fetching remote config, with legacy API"),C(h,m,null,"legacy",g))};this.enrollUserToAb=function(a){b(c.INFO,"enrollUserToAb, Providing AB test keys to opt in for");a&&Array.isArray(a)&&0!==a.length?(a={method:"ab",keys:JSON.stringify(a),av:f.app_version},Ea(a),ca("enrollUserToAb",this.url+Ua,a,function(d,e,h){if(!d)try{var m=JSON.parse(h);b(c.DEBUG,"enrollUserToAb, Parsed the response's result: ["+m.result+"]")}catch(g){b(c.ERROR, -"enrollUserToAb, Had an issue while parsing the response: "+g)}},!0)):b(c.ERROR,"enrollUserToAb, No keys provided")};this.get_remote_config=function(a){b(c.INFO,"get_remote_config, Getting remote config from storage");return"undefined"!==typeof a?S[a]:S};this.stop_time=function(){b(c.INFO,"stop_time, Stopping tracking duration");pa&&(pa=!1,vb=F()-ka,Ha=F()-Ga)};this.start_time=function(){b(c.INFO,"start_time, Starting tracking duration");pa||(pa=!0,ka=F()-vb,Ga=F()-Ha,Ha=0,Ba())};this.track_sessions= -function(){function a(){document[e]||!document.hasFocus()?f.stop_time():f.start_time()}function d(){Oa>=Na&&f.start_time();Oa=0}if(x){b(c.INFO,"track_session, Starting tracking user session");this.begin_session();this.start_time();A(window,"beforeunload",function(){wa();Q();f.end_session()});var e="hidden";A(window,"focus",a);A(window,"blur",a);A(window,"pageshow",a);A(window,"pagehide",a);"onfocusin"in document&&(A(window,"focusin",a),A(window,"focusout",a));e in document?document.addEventListener("visibilitychange", -a):"mozHidden"in document?(e="mozHidden",document.addEventListener("mozvisibilitychange",a)):"webkitHidden"in document?(e="webkitHidden",document.addEventListener("webkitvisibilitychange",a)):"msHidden"in document&&(e="msHidden",document.addEventListener("msvisibilitychange",a));A(window,"mousemove",d);A(window,"click",d);A(window,"keydown",d);A(window,"scroll",d);setInterval(function(){Oa++;Oa>=Na&&f.stop_time()},6E4)}else b(c.WARNING,"track_sessions, window object is not available. Not tracking sessions.")}; -this.track_pageview=function(a,d,e){if(x||a)if(b(c.INFO,"track_pageview, Tracking page views"),b(c.VERBOSE,"track_pageview, last view is:["+Y+"], current view ID is:["+ia+"], previous view ID is:["+Da+"]"),Y&&wb&&(b(c.DEBUG,"track_pageview, Scroll registry triggered"),tb(),Ma=!0,La=0),na(),Da=ia,ia=fb(),(a=z(a,f.maxKeyLength,"track_pageview",b))&&Array.isArray(a)&&(d=a,a=null),a||(a=this.getViewName()),void 0===a||""===a)b(c.ERROR,"track_pageview, No page name to track (it is either undefined or empty string). No page view can be tracked."); -else if(null===a)b(c.ERROR,"track_pageview, View name returned as null. Page view will be ignored.");else{if(d&&d.length)for(var h=0;h