countly-sdk-web 22.6.1 → 22.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.markdownlint.json +1 -1
- package/CHANGELOG.md +12 -0
- package/README.md +35 -50
- package/countly-sdk-web-22.06.3.tgz +0 -0
- package/cypress/integration/crashes.js +36 -0
- package/cypress/support/commands.js +26 -0
- package/examples/example_remote_config.html +11 -3
- package/examples/react/package.json +1 -1
- package/examples/symbolication/package.json +2 -2
- package/lib/countly.js +182 -47
- package/lib/countly.min.js +126 -124
- package/package.json +1 -1
- package/countly-sdk-web-22.06.1.tgz +0 -0
package/.markdownlint.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 22.06.3
|
|
2
|
+
- Fixed an issue that arose when sending crashes through a gateway. User agent information is now sent as part of the request.
|
|
3
|
+
|
|
4
|
+
## 22.06.2
|
|
5
|
+
- ! Minor breaking change ! If no domain whitelist is provided for the heatmaps the SDK will fallback to your server url
|
|
6
|
+
- Fixed a bug where heatmap files were susceptible to DOM XSS
|
|
7
|
+
- Users can now input their domain whitelist for heatmaps feature during init
|
|
8
|
+
- Implementing new Remote Config/AB testing API:
|
|
9
|
+
- Added an init time flag to enable/disable new remote config API (default: disabled)
|
|
10
|
+
- Added a new call to opt in users to the A/B testing for the given keys
|
|
11
|
+
- Added an init time flag to enable/disable automatically opting in users for A/B testing while fetching remote config (with new RC API)(default: enabled)
|
|
12
|
+
|
|
1
13
|
## 22.06.1
|
|
2
14
|
- Added SDK calls to report Feedback widgets manually
|
|
3
15
|
|
package/README.md
CHANGED
|
@@ -1,76 +1,61 @@
|
|
|
1
|
+
[](https://www.codacy.com/gh/Countly/countly-sdk-web/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Countly/countly-sdk-web&utm_campaign=Badge_Grade)
|
|
2
|
+
[](https://badge.fury.io/js/countly-sdk-web)
|
|
3
|
+
[](https://www.jsdelivr.com/package/npm/countly-sdk-web)
|
|
4
|
+
|
|
1
5
|
# Countly Web SDK
|
|
2
|
-
|
|
6
|
+
|
|
7
|
+
This repository contains the Countly Web SDK, which can be integrated into websites and web applications. The Countly Web SDK is intended to be used with [Countly Community Edition](https://github.com/Countly/countly-server) or [Countly Enterprise Edition](https://count.ly/product).
|
|
3
8
|
|
|
4
9
|
## What is Countly?
|
|
5
|
-
[Countly](
|
|
10
|
+
[Countly](https://count.ly) is a product analytics solution and innovation enabler that helps teams track product performance and customer journey and behavior across [mobile](https://count.ly/mobile-analytics), [web](http://count.ly/web-analytics),
|
|
11
|
+
and [desktop](https://count.ly/desktop-analytics) applications. [Ensuring privacy by design](https://count.ly/privacy-by-design), Countly allows you to innovate and enhance your products to provide personalized and customized customer experiences, and meet key business and revenue goals.
|
|
6
12
|
|
|
7
13
|
Track, measure, and take action - all without leaving Countly.
|
|
8
14
|
|
|
9
|
-
* **Slack user?** [Join our
|
|
10
|
-
* **Questions?** [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
There are 3 ways to get Countly SDK.
|
|
14
|
-
|
|
15
|
-
### 1. Available with Countly server
|
|
16
|
-
Since Countly server 16.02, Countly Web SDK is available in your Countly server installation in `countly/frontend/express/public/sdk/web/countly.min.js` which should be available through URL as `https://yourserver.com/sdk/web/countly.min.js`
|
|
17
|
-
|
|
18
|
-
### 2. Installation using package managers
|
|
19
|
-
bower install countly-sdk-web
|
|
20
|
-
or
|
|
21
|
-
|
|
22
|
-
npm install countly-sdk-web
|
|
23
|
-
or
|
|
24
|
-
|
|
25
|
-
yarn add countly-sdk-web
|
|
15
|
+
* **Slack user?** [Join our Slack Community](https://slack.count.ly)
|
|
16
|
+
* **Questions or feature requests?** [Post in our Community Forum](https://support.count.ly/hc/en-us/community/topics)
|
|
17
|
+
* **Looking for the Countly Server?** [Countly Community Edition repository](https://github.com/Countly/countly-server)
|
|
18
|
+
* **Looking for other Countly SDKs?** [An overview of all Countly SDKs for mobile, web and desktop](https://support.count.ly/hc/en-us/articles/360037236571-Downloading-and-Installing-SDKs#officially-supported-sdks)
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
Countly web SDK is available on CDNJS. Use either
|
|
20
|
+
## Integrating Countly SDK in your projects
|
|
29
21
|
|
|
30
|
-
[https://
|
|
22
|
+
For a detailed description on how to use this SDK [check out our documentation](https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-).
|
|
31
23
|
|
|
32
|
-
|
|
24
|
+
For information about how to add the SDK to your project, please check [this section of the documentation](https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-#adding-the-sdk-to-the-project).
|
|
33
25
|
|
|
34
|
-
[https://
|
|
26
|
+
You can reach minimal integration info for your website from [this section of the documentation](https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-#minimal-setup).
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
Link to the script and call helper methods based on what you want to track: sessions, views, clicks, custom events, user data, etc. and for much more information check out our documentation at [https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-](https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-)
|
|
28
|
+
Countly Web SDK has JSDoc3 compatible comments, and you can generate documentation by running `npm run-script docs` or you can use [the online version](https://countly.github.io/countly-sdk-web/).
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
For an example integration of this SDK, you can have a look [here](https://github.com/Countly/countly-sdk-web/tree/master/examples).
|
|
40
31
|
|
|
41
|
-
|
|
32
|
+
This SDK supports the following features:
|
|
33
|
+
* [Analytics](https://support.count.ly/hc/en-us/articles/4431589003545-Analytics)
|
|
34
|
+
* [User Profiles](https://support.count.ly/hc/en-us/articles/4403281285913-User-Profiles)
|
|
35
|
+
* [Crash Reports](https://support.count.ly/hc/en-us/articles/4404213566105-Crashes-Errors)
|
|
36
|
+
* [A/B Testing](https://support.count.ly/hc/en-us/articles/4416496362393-A-B-Testing-)
|
|
37
|
+
* [Performance Monitoring](https://support.count.ly/hc/en-us/articles/4734457847705-Performance)
|
|
38
|
+
* [Feedback Widgets](https://support.count.ly/hc/en-us/articles/4652903481753-Feedback-Surveys-NPS-and-Ratings-)
|
|
42
39
|
|
|
43
40
|
## Security
|
|
44
41
|
Security is very important to us. If you discover any issue regarding security, please disclose the information responsibly by sending an email to security@count.ly and **not by creating a GitHub issue**.
|
|
45
42
|
|
|
46
|
-
## Other Github resources
|
|
47
|
-
Check Countly Community Edition source code here:
|
|
48
|
-
|
|
49
|
-
* [Countly Server](https://github.com/Countly/countly-server)
|
|
50
|
-
|
|
51
|
-
There are also other Countly SDK repositories below:
|
|
52
|
-
|
|
53
|
-
* [Countly iOS SDK](https://github.com/Countly/countly-sdk-ios)
|
|
54
|
-
* [Countly Android SDK](https://github.com/Countly/countly-sdk-android)
|
|
55
|
-
* [Countly Windows Phone SDK](https://github.com/Countly/countly-sdk-windows-phone)
|
|
56
|
-
* [Countly Appcelerator Titanium SDK](https://github.com/euforic/Titanium-Count.ly) (Community supported)
|
|
57
|
-
* [Countly Unity3D SDK](https://github.com/Countly/countly-sdk-unity) (Community supported)
|
|
58
|
-
|
|
59
|
-
## How can I help you with your efforts?
|
|
60
|
-
Glad you asked. We need ideas, feedbacks and constructive comments. All your suggestions will be taken care with upmost importance. We are on [Twitter](http://twitter.com/gocountly) and [Facebook](https://www.facebook.com/Countly) if you would like to keep up with our fast progress!
|
|
61
|
-
|
|
62
43
|
## Badges
|
|
63
|
-
If you like Countly, [why not use one of our badges](https://count.ly/brand-assets) and give a link back to us
|
|
44
|
+
If you like Countly, [why not use one of our badges](https://count.ly/brand-assets) and give a link back to us so others know about this wonderful platform?
|
|
64
45
|
|
|
65
46
|
<a href="https://count.ly/f/badge" rel="nofollow"><img style="width:145px;height:60px" src="https://count.ly/badges/dark.svg?v2" alt="Countly - Product Analytics" /></a>
|
|
66
47
|
|
|
67
|
-
|
|
48
|
+
```JS
|
|
49
|
+
<a href="https://count.ly/f/badge" rel="nofollow"><img style="width:145px;height:60px" src="https://count.ly/badges/dark.svg" alt="Countly - Product Analytics" /></a>
|
|
50
|
+
```
|
|
68
51
|
|
|
69
52
|
<a href="https://count.ly/f/badge" rel="nofollow"><img style="width:145px;height:60px" src="https://count.ly/badges/light.svg?v2" alt="Countly - Product Analytics" /></a>
|
|
70
53
|
|
|
71
|
-
|
|
54
|
+
```JS
|
|
55
|
+
<a href="https://count.ly/f/badge" rel="nofollow"><img style="width:145px;height:60px" src="https://count.ly/badges/light.svg" alt="Countly - Product Analytics" /></a>
|
|
56
|
+
```
|
|
72
57
|
|
|
73
|
-
##
|
|
74
|
-
For
|
|
58
|
+
## How can I help you with your efforts?
|
|
59
|
+
Glad you asked! We need ideas, feedback and constructive comments. All your suggestions will be taken care of with utmost importance. For feature requests and engaging with the community, join [our Slack Community](https://slack.count.ly) or [Community Forum](https://support.count.ly/hc/en-us/community/topics).
|
|
75
60
|
|
|
76
|
-
[
|
|
61
|
+
We are on [Twitter](http://twitter.com/gocountly), [Facebook](https://www.facebook.com/Countly) and [LinkedIn](https://www.linkedin.com/company/countly) if you would like to keep up with Countly related updates.
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* eslint-disable require-jsdoc */
|
|
2
|
+
var Countly = require("../../lib/countly");
|
|
3
|
+
var hp = require("../support/helper");
|
|
4
|
+
|
|
5
|
+
function initMain() {
|
|
6
|
+
Countly.init({
|
|
7
|
+
app_key: "YOUR_APP_KEY",
|
|
8
|
+
url: "https://try.count.ly",
|
|
9
|
+
test_mode: true
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function cause_error() {
|
|
14
|
+
// eslint-disable-next-line no-undef
|
|
15
|
+
undefined_function();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("Crashes tests ", () => {
|
|
19
|
+
it("Checks if a caught crash is reported correctly", () => {
|
|
20
|
+
hp.haltAndClearStorage(() => {
|
|
21
|
+
initMain();
|
|
22
|
+
Countly.track_errors();
|
|
23
|
+
try {
|
|
24
|
+
cause_error();
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
Countly.log_error(err);
|
|
28
|
+
}
|
|
29
|
+
cy.wait(3000).then(() => {
|
|
30
|
+
cy.fetch_local_request_queue().then((rq) => {
|
|
31
|
+
cy.check_crash(rq[0], hp.appKey);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -33,6 +33,32 @@ Cypress.Commands.add("check_request_commons", (testObject, appKey) => {
|
|
|
33
33
|
expect(testObject.sdk_version).to.be.ok;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Checks a crash request for valid/correct formation
|
|
38
|
+
* @param {Object} testObject - crash object to be checked
|
|
39
|
+
*/
|
|
40
|
+
Cypress.Commands.add("check_crash", (testObject, appKey) => {
|
|
41
|
+
appKey = appKey || hp.appKey;
|
|
42
|
+
const metrics = JSON.parse(testObject.metrics);
|
|
43
|
+
const crash = JSON.parse(testObject.crash);
|
|
44
|
+
const metricKeys = Object.keys(metrics);
|
|
45
|
+
cy.check_request_commons(testObject, appKey);
|
|
46
|
+
cy.check_commons(testObject);
|
|
47
|
+
expect(metrics._ua).to.be.exist;
|
|
48
|
+
expect(metricKeys.length).to.equal(1);
|
|
49
|
+
expect(crash._app_version).to.be.exist;
|
|
50
|
+
expect(crash._background).to.be.exist;
|
|
51
|
+
expect(crash._error).to.be.exist;
|
|
52
|
+
expect(crash._javascript).to.be.exist;
|
|
53
|
+
expect(crash._nonfatal).to.be.exist;
|
|
54
|
+
expect(crash._not_os_specific).to.be.exist;
|
|
55
|
+
expect(crash._online).to.be.exist;
|
|
56
|
+
expect(crash._opengl).to.be.exist;
|
|
57
|
+
expect(crash._resolution).to.be.exist;
|
|
58
|
+
expect(crash._run).to.be.exist;
|
|
59
|
+
expect(crash._view).to.be.exist;
|
|
60
|
+
});
|
|
61
|
+
|
|
36
62
|
/**
|
|
37
63
|
* Checks a queue object for valid/correct begin session, end session and session extension values
|
|
38
64
|
* @param {Object} queue - queue object to check
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
app_key: "YOUR_APP_KEY",
|
|
14
14
|
url: "https://try.count.ly", //your server goes here
|
|
15
15
|
debug: true,
|
|
16
|
+
rc_automatic_optin_for_ab: true, // set it to false for not opting in users for AB testing while fetching the remote config (only with latest API)
|
|
17
|
+
use_explicit_rc_api: false, // set it to true to use the latest API
|
|
16
18
|
remote_config: function (err, configs) {
|
|
17
19
|
//handle initial remote configs here
|
|
18
20
|
console.log(err, configs);
|
|
@@ -31,19 +33,25 @@
|
|
|
31
33
|
<center>
|
|
32
34
|
<img src="./images/team_countly.jpg" id="wallpaper" />
|
|
33
35
|
<br />
|
|
34
|
-
<input type="button" onclick="
|
|
36
|
+
<input type="button" onclick="fetchConfig()" value="Fetch Config">
|
|
35
37
|
<input type="button" onclick="getConfig()" value="Get Config">
|
|
36
38
|
<input type="button" onclick="getConfig('test')" value="Get config for key Test">
|
|
39
|
+
<input type="button" onclick="ab(['key1','key2'])" value="Enroll user to AB test">
|
|
37
40
|
<p><a href='http://count.ly/'>Count.ly</a></p>
|
|
38
41
|
</center>
|
|
39
42
|
<script type='text/javascript'>
|
|
40
|
-
//
|
|
41
|
-
function
|
|
43
|
+
// fetches all keys
|
|
44
|
+
function fetchConfig() {
|
|
42
45
|
Countly.fetch_remote_config(function (err, config) {
|
|
43
46
|
alert(JSON.stringify(config));
|
|
44
47
|
});
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
// enroll user
|
|
51
|
+
function ab(keyArray) {
|
|
52
|
+
Countly.enrollUserToAb(keyArray);
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
//get config
|
|
48
56
|
function getConfig(key) {
|
|
49
57
|
alert(JSON.stringify(Countly.get_remote_config(key)));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"@testing-library/jest-dom": "^5.16.4",
|
|
7
7
|
"@testing-library/react": "^13.3.0",
|
|
8
8
|
"@testing-library/user-event": "^14.2.1",
|
|
9
|
-
"countly-sdk-web": "^22.
|
|
9
|
+
"countly-sdk-web": "^22.06.1",
|
|
10
10
|
"react": "^18.2.0",
|
|
11
11
|
"react-dom": "^18.2.0",
|
|
12
12
|
"react-router-dom": "^5.3.3",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"build": "webpack"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@babel/core": "^7.
|
|
14
|
-
"@babel/preset-env": "^7.
|
|
13
|
+
"@babel/core": "^7.19.1",
|
|
14
|
+
"@babel/preset-env": "^7.19.1",
|
|
15
15
|
"@webpack-cli/init": "^1.1.3",
|
|
16
16
|
"babel-loader": "^8.2.5",
|
|
17
17
|
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
package/lib/countly.js
CHANGED
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
*/
|
|
176
176
|
Countly.onload = Countly.onload || [];
|
|
177
177
|
|
|
178
|
-
var SDK_VERSION = "22.06.
|
|
178
|
+
var SDK_VERSION = "22.06.3";
|
|
179
179
|
var SDK_NAME = "javascript_native_web";
|
|
180
180
|
|
|
181
181
|
var urlParseRE = /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/;
|
|
@@ -255,6 +255,8 @@
|
|
|
255
255
|
this.onload = getConfig("onload", ob, []);
|
|
256
256
|
this.utm = getConfig("utm", ob, { source: true, medium: true, campaign: true, term: true, content: true });
|
|
257
257
|
this.ignore_prefetch = getConfig("ignore_prefetch", ob, true);
|
|
258
|
+
this.rcAutoOptinAb = getConfig("rc_automatic_optin_for_ab", ob, true);
|
|
259
|
+
this.useExplicitRcApi = getConfig("use_explicit_rc_api", ob, false);
|
|
258
260
|
this.debug = getConfig("debug", ob, false);
|
|
259
261
|
this.test_mode = getConfig("test_mode", ob, false);
|
|
260
262
|
this.metrics = getConfig("metrics", ob, {});
|
|
@@ -278,6 +280,7 @@
|
|
|
278
280
|
this.maxBreadcrumbCount = getConfig("max_breadcrumb_count", ob, null);
|
|
279
281
|
this.maxStackTraceLinesPerThread = getConfig("max_stack_trace_lines_per_thread", ob, configurationDefaultValues.MAX_STACKTRACE_LINES_PER_THREAD);
|
|
280
282
|
this.maxStackTraceLineLength = getConfig("max_stack_trace_line_length", ob, configurationDefaultValues.MAX_STACKTRACE_LINE_LENGTH);
|
|
283
|
+
this.heatmapWhitelist = getConfig("heatmap_whitelist", ob, []);
|
|
281
284
|
|
|
282
285
|
if (maxCrashLogs && !this.maxBreadcrumbCount) {
|
|
283
286
|
this.maxBreadcrumbCount = maxCrashLogs;
|
|
@@ -291,6 +294,11 @@
|
|
|
291
294
|
lsSupport = false;
|
|
292
295
|
}
|
|
293
296
|
|
|
297
|
+
if (!this.rcAutoOptinAb && !this.useExplicitRcApi) {
|
|
298
|
+
log(logLevelEnums.WARNING, "initialize, Auto opting is disabled, switching to explicit RC API");
|
|
299
|
+
this.useExplicitRcApi = true;
|
|
300
|
+
}
|
|
301
|
+
|
|
294
302
|
if (!Array.isArray(ignoreReferrers)) {
|
|
295
303
|
ignoreReferrers = [];
|
|
296
304
|
}
|
|
@@ -341,11 +349,25 @@
|
|
|
341
349
|
setToken(this.passed_data.token);
|
|
342
350
|
setValueInStorage("cly_old_token", this.passed_data.token);
|
|
343
351
|
}
|
|
344
|
-
|
|
345
|
-
if
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
352
|
+
var strippedList = [];
|
|
353
|
+
// if whitelist is provided is an array
|
|
354
|
+
if (Array.isArray(this.heatmapWhitelist)) {
|
|
355
|
+
this.heatmapWhitelist.push(this.url);
|
|
356
|
+
strippedList = this.heatmapWhitelist.map(function(e) {
|
|
357
|
+
// remove trailing slashes from the entries
|
|
358
|
+
return stripTrailingSlash(e);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
strippedList = [this.url];
|
|
363
|
+
}
|
|
364
|
+
// if the passed url is in the whitelist proceed
|
|
365
|
+
if (strippedList.includes(this.passed_data.url)) {
|
|
366
|
+
if (this.passed_data.purpose === "heatmap") {
|
|
367
|
+
this.ignore_visitor = true;
|
|
368
|
+
showLoader();
|
|
369
|
+
loadJS(this.passed_data.url + "/views/heatmap.js", hideLoader);
|
|
370
|
+
}
|
|
349
371
|
}
|
|
350
372
|
}
|
|
351
373
|
}
|
|
@@ -394,6 +416,10 @@
|
|
|
394
416
|
if (this.test_mode) {
|
|
395
417
|
log(logLevelEnums.WARNING, "initialize, test_mode:[" + this.test_mode + "], queues won't be processed");
|
|
396
418
|
}
|
|
419
|
+
// if test mode is enabled warn the user
|
|
420
|
+
if (this.heatmapWhitelist) {
|
|
421
|
+
log(logLevelEnums.DEBUG, "initialize, heatmap whitelist:[" + JSON.stringify(this.heatmapWhitelist) + "], these domains will be whitelisted");
|
|
422
|
+
}
|
|
397
423
|
// if storage is se to something other than local storage
|
|
398
424
|
if (this.storage !== "default") {
|
|
399
425
|
log(logLevelEnums.DEBUG, "initialize, storage is set to:[" + this.storage + "]");
|
|
@@ -407,6 +433,12 @@
|
|
|
407
433
|
if (this.remote_config) {
|
|
408
434
|
log(logLevelEnums.DEBUG, "initialize, remote_config callback provided:[" + !!this.remote_config + "]");
|
|
409
435
|
}
|
|
436
|
+
if (typeof this.rcAutoOptinAb === "boolean") {
|
|
437
|
+
log(logLevelEnums.DEBUG, "initialize, automatic RC optin is enabled:[" + this.rcAutoOptinAb + "]");
|
|
438
|
+
}
|
|
439
|
+
if (!this.useExplicitRcApi) {
|
|
440
|
+
log(logLevelEnums.WARNING, "initialize, will use legacy RC API. Consider enabling new API during init with use_explicit_rc_api flag");
|
|
441
|
+
}
|
|
410
442
|
if (this.track_domains) {
|
|
411
443
|
log(logLevelEnums.DEBUG, "initialize, tracking domain info:[" + this.track_domains + "]");
|
|
412
444
|
}
|
|
@@ -419,6 +451,9 @@
|
|
|
419
451
|
if (offlineMode) {
|
|
420
452
|
log(logLevelEnums.DEBUG, "initialize, offline_mode:[" + offlineMode + "], user info won't be send to the servers");
|
|
421
453
|
}
|
|
454
|
+
if (offlineMode) {
|
|
455
|
+
log(logLevelEnums.DEBUG, "initialize, stored remote configs:[" + JSON.stringify(remoteConfigs) + "]");
|
|
456
|
+
}
|
|
422
457
|
// functions, if provided, would be printed as true without revealing their content
|
|
423
458
|
log(logLevelEnums.DEBUG, "initialize, 'getViewName' callback override provided:[" + !!this.getViewName + "]");
|
|
424
459
|
log(logLevelEnums.DEBUG, "initialize, 'getSearchQuery' callback override provided:[" + !!this.getSearchQuery + "]");
|
|
@@ -671,6 +706,8 @@
|
|
|
671
706
|
self.ip_address = undefined;
|
|
672
707
|
self.ignore_bots = undefined;
|
|
673
708
|
self.force_post = undefined;
|
|
709
|
+
self.rcAutoOptinAb = undefined;
|
|
710
|
+
self.useExplicitRcApi = undefined;
|
|
674
711
|
self.remote_config = undefined;
|
|
675
712
|
self.ignore_visitor = undefined;
|
|
676
713
|
self.require_consent = undefined;
|
|
@@ -1063,6 +1100,7 @@
|
|
|
1063
1100
|
// start new session for new ID
|
|
1064
1101
|
this.begin_session(!autoExtend, true);
|
|
1065
1102
|
}
|
|
1103
|
+
// if init time remote config was enabled with a callback function, remove currently stored remote configs and fetch remote config again
|
|
1066
1104
|
if (this.remote_config) {
|
|
1067
1105
|
remoteConfigs = {};
|
|
1068
1106
|
setValueInStorage("cly_remote_configs", remoteConfigs);
|
|
@@ -1579,46 +1617,102 @@
|
|
|
1579
1617
|
crashLogs.push(record);
|
|
1580
1618
|
}
|
|
1581
1619
|
};
|
|
1582
|
-
|
|
1583
1620
|
/**
|
|
1584
|
-
* Fetch remote config
|
|
1621
|
+
* Fetch remote config from the server (old one for method=fetch_remote_config API)
|
|
1585
1622
|
* @param {array=} keys - Array of keys to fetch, if not provided will fetch all keys
|
|
1586
1623
|
* @param {array=} omit_keys - Array of keys to omit, if provided will fetch all keys except provided ones
|
|
1587
1624
|
* @param {function=} callback - Callback to notify with first param error and second param remote config object
|
|
1588
1625
|
* */
|
|
1589
1626
|
this.fetch_remote_config = function(keys, omit_keys, callback) {
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1627
|
+
var keysFiltered = null;
|
|
1628
|
+
var omitKeysFiltered = null;
|
|
1629
|
+
var callbackFiltered = null;
|
|
1630
|
+
|
|
1631
|
+
// check first param is truthy
|
|
1632
|
+
if (keys) {
|
|
1633
|
+
// if third parameter is falsy and first param is a function assign it as the callback function
|
|
1634
|
+
if (!callback && typeof keys === "function") {
|
|
1635
|
+
callbackFiltered = keys;
|
|
1597
1636
|
}
|
|
1598
|
-
if
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
keys = null;
|
|
1602
|
-
}
|
|
1603
|
-
else if (Array.isArray(keys) && keys.length) {
|
|
1604
|
-
log(logLevelEnums.INFO, "fetch_remote_config, Keys to fetch: [ " + keys + " ]");
|
|
1605
|
-
request.keys = JSON.stringify(keys);
|
|
1606
|
-
}
|
|
1637
|
+
// else if first param is an array assign it as 'keys'
|
|
1638
|
+
else if (Array.isArray(keys)) {
|
|
1639
|
+
keysFiltered = keys;
|
|
1607
1640
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1641
|
+
}
|
|
1642
|
+
// check second param is truthy
|
|
1643
|
+
if (omit_keys) {
|
|
1644
|
+
// if third parameter is falsy and second param is a function assign it as the callback function
|
|
1645
|
+
if (!callback && typeof omit_keys === "function") {
|
|
1646
|
+
callbackFiltered = omit_keys;
|
|
1647
|
+
}
|
|
1648
|
+
// else if second param is an array assign it as 'omit_keys'
|
|
1649
|
+
else if (Array.isArray(omit_keys)) {
|
|
1650
|
+
omitKeysFiltered = omit_keys;
|
|
1617
1651
|
}
|
|
1652
|
+
}
|
|
1653
|
+
// assign third param as a callback function if it was not assigned yet in first two params
|
|
1654
|
+
if (!callbackFiltered && typeof callback === "function") {
|
|
1655
|
+
callbackFiltered = callback;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// use new RC API
|
|
1659
|
+
if (this.useExplicitRcApi) {
|
|
1660
|
+
log(logLevelEnums.INFO, "fetch_remote_config, Fetching remote config");
|
|
1661
|
+
// opt in is true(1) or false(0)
|
|
1662
|
+
var opt = this.rcAutoOptinAb ? 1 : 0;
|
|
1663
|
+
fetch_remote_config_explicit(keysFiltered, omitKeysFiltered, opt, null, callbackFiltered);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
log(logLevelEnums.WARNING, "fetch_remote_config, Fetching remote config, with legacy API");
|
|
1668
|
+
fetch_remote_config_explicit(keysFiltered, omitKeysFiltered, null, "legacy", callbackFiltered);
|
|
1669
|
+
};
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Fetch remote config from the server (new one with method=rc API)
|
|
1673
|
+
* @param {array=} keys - Array of keys to fetch, if not provided will fetch all keys
|
|
1674
|
+
* @param {array=} omit_keys - Array of keys to omit, if provided will fetch all keys except provided ones
|
|
1675
|
+
* @param {number=} optIn - an inter to indicate if the user is opted in for the AB testing or not (1 is opted in, 0 is opted out)
|
|
1676
|
+
* @param {string=} api - which API to use, if not provided would use default ("legacy" is for method="fetch_remote_config", default is method="rc")
|
|
1677
|
+
* @param {function=} callback - Callback to notify with first param error and second param remote config object
|
|
1678
|
+
* */
|
|
1679
|
+
function fetch_remote_config_explicit(keys, omit_keys, optIn, api, callback) {
|
|
1680
|
+
log(logLevelEnums.INFO, "fetch_remote_config_explicit, Fetching sequence initiated");
|
|
1681
|
+
var request = {
|
|
1682
|
+
method: "rc"
|
|
1683
|
+
};
|
|
1684
|
+
// check if keys were provided
|
|
1685
|
+
if (keys.length > 0) {
|
|
1686
|
+
request.keys = JSON.stringify(keys);
|
|
1687
|
+
}
|
|
1688
|
+
// check if omit_keys were provided
|
|
1689
|
+
if (omit_keys.length > 0) {
|
|
1690
|
+
request.omit_keys = JSON.stringify(omit_keys);
|
|
1691
|
+
}
|
|
1692
|
+
var providedCall;
|
|
1693
|
+
// legacy api prompt check
|
|
1694
|
+
if (api === "legacy") {
|
|
1695
|
+
request.method = "fetch_remote_config";
|
|
1696
|
+
}
|
|
1697
|
+
// opted out/in check
|
|
1698
|
+
if (optIn === 0) {
|
|
1699
|
+
request.oi = 0;
|
|
1700
|
+
}
|
|
1701
|
+
if (optIn === 1) {
|
|
1702
|
+
request.oi = 1;
|
|
1703
|
+
}
|
|
1704
|
+
// callback check
|
|
1705
|
+
if (typeof callback === "function") {
|
|
1706
|
+
providedCall = callback;
|
|
1707
|
+
}
|
|
1708
|
+
if (self.check_consent(featureEnums.SESSIONS)) {
|
|
1709
|
+
request.metrics = JSON.stringify(getMetrics());
|
|
1710
|
+
}
|
|
1711
|
+
if (self.check_consent(featureEnums.REMOTE_CONFIG)) {
|
|
1618
1712
|
prepareRequest(request);
|
|
1619
|
-
sendXmlHttpRequest("
|
|
1713
|
+
sendXmlHttpRequest("fetch_remote_config_explicit", self.url + readPath, request, function(err, params, responseText) {
|
|
1620
1714
|
if (err) {
|
|
1621
|
-
log(logLevelEnums.ERROR, "
|
|
1715
|
+
log(logLevelEnums.ERROR, "fetch_remote_config_explicit, An error occurred: " + err);
|
|
1622
1716
|
return;
|
|
1623
1717
|
}
|
|
1624
1718
|
try {
|
|
@@ -1636,30 +1730,61 @@
|
|
|
1636
1730
|
setValueInStorage("cly_remote_configs", remoteConfigs);
|
|
1637
1731
|
}
|
|
1638
1732
|
catch (ex) {
|
|
1639
|
-
log(logLevelEnums.ERROR, "
|
|
1733
|
+
log(logLevelEnums.ERROR, "fetch_remote_config_explicit, Had an issue while parsing the response: " + ex);
|
|
1640
1734
|
}
|
|
1641
|
-
if (
|
|
1642
|
-
log(logLevelEnums.INFO, "
|
|
1643
|
-
|
|
1735
|
+
if (providedCall) {
|
|
1736
|
+
log(logLevelEnums.INFO, "fetch_remote_config_explicit, Callback function is provided");
|
|
1737
|
+
providedCall(err, remoteConfigs);
|
|
1644
1738
|
}
|
|
1645
1739
|
// JSON array can pass
|
|
1646
1740
|
}, true);
|
|
1647
1741
|
}
|
|
1648
1742
|
else {
|
|
1649
|
-
log(logLevelEnums.ERROR, "
|
|
1650
|
-
if (
|
|
1651
|
-
|
|
1743
|
+
log(logLevelEnums.ERROR, "fetch_remote_config_explicit, Remote config requires explicit consent");
|
|
1744
|
+
if (providedCall) {
|
|
1745
|
+
providedCall(new Error("Remote config requires explicit consent"), remoteConfigs);
|
|
1652
1746
|
}
|
|
1653
1747
|
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
/**
|
|
1751
|
+
* AB testing key provider, opts the user in for the selected keys
|
|
1752
|
+
* @param {array=} keys - Array of keys opt in FOR
|
|
1753
|
+
* */
|
|
1754
|
+
this.enrollUserToAb = function(keys) {
|
|
1755
|
+
log(logLevelEnums.INFO, "enrollUserToAb, Providing AB test keys to opt in for");
|
|
1756
|
+
if (!keys || !Array.isArray(keys) || keys.length === 0) {
|
|
1757
|
+
log(logLevelEnums.ERROR, "enrollUserToAb, No keys provided");
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
var request = {
|
|
1761
|
+
method: "ab",
|
|
1762
|
+
keys: JSON.stringify(keys)
|
|
1763
|
+
};
|
|
1764
|
+
prepareRequest(request);
|
|
1765
|
+
sendXmlHttpRequest("enrollUserToAb", this.url + readPath, request, function(err, params, responseText) {
|
|
1766
|
+
if (err) {
|
|
1767
|
+
log(logLevelEnums.ERROR, "enrollUserToAb, An error occurred: " + err);
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
try {
|
|
1771
|
+
var resp = JSON.parse(responseText);
|
|
1772
|
+
log(logLevelEnums.DEBUG, "enrollUserToAb, Parsed the response's result: [" + resp.result + "]");
|
|
1773
|
+
}
|
|
1774
|
+
catch (ex) {
|
|
1775
|
+
log(logLevelEnums.ERROR, "enrollUserToAb, Had an issue while parsing the response: " + ex);
|
|
1776
|
+
}
|
|
1777
|
+
// JSON array can pass
|
|
1778
|
+
}, true);
|
|
1654
1779
|
};
|
|
1655
1780
|
|
|
1656
1781
|
/**
|
|
1657
|
-
*
|
|
1782
|
+
* Gets remote config object (all key/value pairs) or specific value for provided key from the storage
|
|
1658
1783
|
* @param {string=} key - if provided, will return value for key, or return whole object
|
|
1659
1784
|
* @returns {object} remote configs
|
|
1660
1785
|
* */
|
|
1661
1786
|
this.get_remote_config = function(key) {
|
|
1662
|
-
log(logLevelEnums.INFO, "get_remote_config, Getting remote config");
|
|
1787
|
+
log(logLevelEnums.INFO, "get_remote_config, Getting remote config from storage");
|
|
1663
1788
|
if (typeof key !== "undefined") {
|
|
1664
1789
|
return remoteConfigs[key];
|
|
1665
1790
|
}
|
|
@@ -3227,7 +3352,12 @@
|
|
|
3227
3352
|
log(logLevelEnums.ERROR, "Could not get the experimental-webgl context: " + ex);
|
|
3228
3353
|
}
|
|
3229
3354
|
|
|
3230
|
-
|
|
3355
|
+
// send userAgent string with the crash object incase it gets removed by a gateway
|
|
3356
|
+
var req = {};
|
|
3357
|
+
req.crash = JSON.stringify(obj);
|
|
3358
|
+
req.metrics = JSON.stringify({ _ua: metrics._ua });
|
|
3359
|
+
|
|
3360
|
+
toRequestQueue(req);
|
|
3231
3361
|
}
|
|
3232
3362
|
};
|
|
3233
3363
|
|
|
@@ -4279,12 +4409,15 @@
|
|
|
4279
4409
|
* @param {number} [conf.max_stack_trace_line_length=200] - maximum amount of characters are allowed per stack trace line. This limits also the crash message length
|
|
4280
4410
|
* @param {array=} conf.ignore_referrers - array with referrers to ignore
|
|
4281
4411
|
* @param {boolean} [conf.ignore_prefetch=true] - ignore prefetching and pre rendering from counting as real website visits
|
|
4412
|
+
* @param {boolean} [conf.rc_automatic_optin_for_ab=true] - opts in the user for A/B testing while fetching the remote config (if true)
|
|
4413
|
+
* @param {boolean} [conf.use_explicit_rc_api=false] - set it to true to use the new remote config API
|
|
4282
4414
|
* @param {boolean} [conf.force_post=false] - force using post method for all requests
|
|
4283
4415
|
* @param {boolean} [conf.ignore_visitor=false] - ignore this current visitor
|
|
4284
4416
|
* @param {boolean} [conf.require_consent=false] - Pass true if you are implementing GDPR compatible consent management. It would prevent running any functionality without proper consent
|
|
4285
4417
|
* @param {boolean} [conf.utm={"source":true, "medium":true, "campaign":true, "term":true, "content":true}] - Object instructing which UTM parameters to track
|
|
4286
4418
|
* @param {boolean} [conf.use_session_cookie=true] - Use cookie to track session
|
|
4287
4419
|
* @param {boolean} [conf.enable_orientation_tracking=true] - Enables orientation tracking at the start of a session
|
|
4420
|
+
* @param {array=} [conf.heatmap_whitelist=[]] - array with trustable domains for heatmap reporting
|
|
4288
4421
|
* @param {number} [conf.session_cookie_timeout=30] - How long till cookie session should expire in minutes
|
|
4289
4422
|
* @param {boolean|function} [conf.remote_config=false] - Enable automatic remote config fetching, provide callback function to be notified when fetching done
|
|
4290
4423
|
* @param {string=} [conf.namespace=""] - Have separate namespace of of persistent data
|
|
@@ -4448,8 +4581,10 @@
|
|
|
4448
4581
|
* @returns {String} modified string
|
|
4449
4582
|
*/
|
|
4450
4583
|
function stripTrailingSlash(str) {
|
|
4451
|
-
if (str
|
|
4452
|
-
|
|
4584
|
+
if (typeof str === "string") {
|
|
4585
|
+
if (str.substring(str.length - 1) === "/") {
|
|
4586
|
+
return str.substring(0, str.length - 1);
|
|
4587
|
+
}
|
|
4453
4588
|
}
|
|
4454
4589
|
return str;
|
|
4455
4590
|
}
|