@zoho_apptics/apptics-js-sdk 2.0.0-beta → 2.0.0-beta.2
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/CHANGELOG.md +191 -21
- package/README.md +519 -333
- package/dist/apptics.esm.min.js +2 -2
- package/dist/apptics.esm.min.js.map +1 -1
- package/dist/apptics.min.js +2 -2
- package/dist/apptics.min.js.map +1 -1
- package/dist/chunks/lifecycle-C6m1W7jo.min.js +3 -0
- package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
- package/dist/chunks/send-CgUO82ne.min.js +3 -0
- package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +2 -2
- package/dist/cjs/core/consent.d.ts +18 -4
- package/dist/cjs/core/consent.js +31 -15
- package/dist/cjs/core/constants.d.ts +57 -27
- package/dist/cjs/core/constants.js +52 -33
- package/dist/cjs/core/db.d.ts +12 -4
- package/dist/cjs/core/db.js +167 -34
- package/dist/cjs/core/debug.js +1 -1
- package/dist/cjs/core/env.d.ts +0 -3
- package/dist/cjs/core/env.js +1 -9
- package/dist/cjs/core/framework-id.d.ts +1 -1
- package/dist/cjs/core/framework-id.js +3 -3
- package/dist/cjs/core/guard.d.ts +9 -0
- package/dist/cjs/core/guard.js +27 -13
- package/dist/cjs/core/hooks.d.ts +7 -4
- package/dist/cjs/core/hooks.js +3 -3
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +3 -4
- package/dist/cjs/core/logger.js +4 -40
- package/dist/cjs/core/state.d.ts +9 -6
- package/dist/cjs/core/state.js +3 -5
- package/dist/cjs/core/storage.d.ts +20 -5
- package/dist/cjs/core/storage.js +115 -20
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +2 -0
- package/dist/cjs/core/transport.js +30 -18
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +2 -2
- package/dist/cjs/device/appversion.d.ts +10 -6
- package/dist/cjs/device/appversion.js +38 -3
- package/dist/cjs/device/index.d.ts +28 -2
- package/dist/cjs/device/index.js +312 -44
- package/dist/cjs/engagement/api-rules.d.ts +32 -67
- package/dist/cjs/engagement/api-rules.js +144 -157
- package/dist/cjs/engagement/auto-api.js +2 -2
- package/dist/cjs/engagement/auto-screen.d.ts +2 -0
- package/dist/cjs/engagement/auto-screen.js +67 -3
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +12 -3
- package/dist/cjs/engagement/session.d.ts +29 -3
- package/dist/cjs/engagement/session.js +253 -26
- package/dist/cjs/engagement/track.d.ts +4 -4
- package/dist/cjs/engagement/track.js +58 -30
- package/dist/cjs/errors/send.js +2 -2
- package/dist/cjs/errors/track.d.ts +2 -1
- package/dist/cjs/errors/track.js +49 -7
- package/dist/cjs/feedback/controls.d.ts +12 -0
- package/dist/cjs/feedback/controls.js +104 -25
- package/dist/cjs/feedback/index.d.ts +8 -7
- package/dist/cjs/feedback/index.js +32 -13
- package/dist/cjs/feedback/logs.d.ts +13 -11
- package/dist/cjs/feedback/logs.js +57 -28
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +5 -2
- package/dist/cjs/feedback/send.js +30 -28
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +20 -12
- package/dist/cjs/feedback/ui.d.ts +21 -6
- package/dist/cjs/feedback/ui.js +283 -70
- package/dist/cjs/heatmap/send.d.ts +2 -0
- package/dist/cjs/heatmap/send.js +3 -5
- package/dist/cjs/heatmap/track.d.ts +1 -1
- package/dist/cjs/heatmap/track.js +87 -16
- package/dist/cjs/index.d.ts +16 -63
- package/dist/cjs/index.js +144 -95
- package/dist/cjs/lifecycle.d.ts +21 -3
- package/dist/cjs/lifecycle.js +353 -89
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +7 -7
- package/dist/cjs/remoteconfig/custom.js +31 -51
- package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
- package/dist/cjs/remoteconfig/evaluate.js +56 -43
- package/dist/cjs/remoteconfig/fetch.d.ts +16 -9
- package/dist/cjs/remoteconfig/fetch.js +120 -39
- package/dist/cjs/remoteconfig/index.d.ts +19 -8
- package/dist/cjs/remoteconfig/index.js +81 -39
- package/dist/cjs/remoteconfig/store.d.ts +15 -7
- package/dist/cjs/remoteconfig/store.js +68 -35
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/{esm/core → cjs/remotelog}/callsite.d.ts +11 -4
- package/dist/cjs/{core → remotelog}/callsite.js +30 -7
- package/dist/cjs/remotelog/gate.d.ts +2 -11
- package/dist/cjs/remotelog/gate.js +9 -35
- package/dist/cjs/remotelog/index.d.ts +4 -21
- package/dist/cjs/remotelog/logger.d.ts +11 -17
- package/dist/cjs/remotelog/logger.js +31 -59
- package/dist/cjs/remotelog/send.js +11 -11
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.d.ts +3 -1
- package/dist/cjs/scheduler.js +25 -22
- package/dist/cjs/types.d.ts +208 -90
- package/dist/cjs/user/index.js +16 -7
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +2 -2
- package/dist/esm/core/consent.d.ts +18 -4
- package/dist/esm/core/consent.js +32 -17
- package/dist/esm/core/constants.d.ts +57 -27
- package/dist/esm/core/constants.js +51 -33
- package/dist/esm/core/db.d.ts +12 -4
- package/dist/esm/core/db.js +164 -35
- package/dist/esm/core/debug.js +1 -1
- package/dist/esm/core/env.d.ts +0 -3
- package/dist/esm/core/env.js +2 -9
- package/dist/esm/core/framework-id.d.ts +1 -1
- package/dist/esm/core/framework-id.js +3 -3
- package/dist/esm/core/guard.d.ts +9 -0
- package/dist/esm/core/guard.js +27 -14
- package/dist/esm/core/hooks.d.ts +7 -4
- package/dist/esm/core/hooks.js +3 -3
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +3 -4
- package/dist/esm/core/logger.js +4 -40
- package/dist/esm/core/state.d.ts +9 -6
- package/dist/esm/core/state.js +3 -5
- package/dist/esm/core/storage.d.ts +20 -5
- package/dist/esm/core/storage.js +113 -21
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +2 -0
- package/dist/esm/core/transport.js +30 -19
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +2 -2
- package/dist/esm/device/appversion.d.ts +10 -6
- package/dist/esm/device/appversion.js +37 -4
- package/dist/esm/device/index.d.ts +28 -2
- package/dist/esm/device/index.js +313 -49
- package/dist/esm/engagement/api-rules.d.ts +32 -67
- package/dist/esm/engagement/api-rules.js +143 -155
- package/dist/esm/engagement/auto-api.js +3 -3
- package/dist/esm/engagement/auto-screen.d.ts +2 -0
- package/dist/esm/engagement/auto-screen.js +67 -4
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +12 -3
- package/dist/esm/engagement/session.d.ts +29 -3
- package/dist/esm/engagement/session.js +251 -30
- package/dist/esm/engagement/track.d.ts +4 -4
- package/dist/esm/engagement/track.js +65 -37
- package/dist/esm/errors/send.js +2 -2
- package/dist/esm/errors/track.d.ts +2 -1
- package/dist/esm/errors/track.js +49 -7
- package/dist/esm/feedback/controls.d.ts +12 -0
- package/dist/esm/feedback/controls.js +103 -26
- package/dist/esm/feedback/index.d.ts +8 -7
- package/dist/esm/feedback/index.js +34 -13
- package/dist/esm/feedback/logs.d.ts +13 -11
- package/dist/esm/feedback/logs.js +59 -30
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +5 -2
- package/dist/esm/feedback/send.js +31 -31
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +20 -12
- package/dist/esm/feedback/ui.d.ts +21 -6
- package/dist/esm/feedback/ui.js +283 -71
- package/dist/esm/heatmap/send.d.ts +2 -0
- package/dist/esm/heatmap/send.js +3 -5
- package/dist/esm/heatmap/track.d.ts +1 -1
- package/dist/esm/heatmap/track.js +87 -16
- package/dist/esm/index.d.ts +16 -63
- package/dist/esm/index.js +148 -83
- package/dist/esm/lifecycle.d.ts +21 -3
- package/dist/esm/lifecycle.js +359 -98
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +7 -7
- package/dist/esm/remoteconfig/custom.js +32 -52
- package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
- package/dist/esm/remoteconfig/evaluate.js +57 -44
- package/dist/esm/remoteconfig/fetch.d.ts +16 -9
- package/dist/esm/remoteconfig/fetch.js +120 -39
- package/dist/esm/remoteconfig/index.d.ts +19 -8
- package/dist/esm/remoteconfig/index.js +82 -40
- package/dist/esm/remoteconfig/store.d.ts +15 -7
- package/dist/esm/remoteconfig/store.js +68 -36
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/{cjs/core → esm/remotelog}/callsite.d.ts +11 -4
- package/dist/esm/{core → remotelog}/callsite.js +30 -8
- package/dist/esm/remotelog/gate.d.ts +2 -11
- package/dist/esm/remotelog/gate.js +10 -32
- package/dist/esm/remotelog/index.d.ts +4 -21
- package/dist/esm/remotelog/logger.d.ts +11 -17
- package/dist/esm/remotelog/logger.js +33 -61
- package/dist/esm/remotelog/send.js +13 -13
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.d.ts +3 -1
- package/dist/esm/scheduler.js +26 -24
- package/dist/esm/types.d.ts +208 -90
- package/dist/esm/user/index.js +18 -8
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/dist/remoteconfig.esm.min.js +3 -0
- package/dist/remoteconfig.esm.min.js.map +1 -0
- package/dist/remotelogger.esm.min.js +3 -0
- package/dist/remotelogger.esm.min.js.map +1 -0
- package/package.json +16 -17
- package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
- package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
- package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
- package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
- package/dist/cjs/auto-init.d.ts +0 -8
- package/dist/cjs/auto-init.js +0 -32
- package/dist/cjs/heatmap/index.d.ts +0 -16
- package/dist/cjs/heatmap/index.js +0 -22
- package/dist/cjs/remotelog/index.js +0 -55
- package/dist/esm/auto-init.d.ts +0 -8
- package/dist/esm/auto-init.js +0 -29
- package/dist/esm/heatmap/index.d.ts +0 -16
- package/dist/esm/heatmap/index.js +0 -17
- package/dist/esm/remotelog/index.js +0 -51
- package/dist/heatmap.esm.min.js +0 -3
- package/dist/heatmap.esm.min.js.map +0 -1
- package/dist/style.css +0 -1
package/dist/esm/lifecycle.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
-
import { REQUIRED_CONFIG, AP_CONFIG,
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { REQUIRED_CONFIG, CONFIG_KEYS, AP_CONFIG, RETIRED_CONFIG_NAMES, DEFAULT_INACTIVE_MINUTES } from './core/constants.js';
|
|
3
3
|
import { _ZAP } from './core/state.js';
|
|
4
|
-
import {
|
|
5
|
-
import { isEmpty,
|
|
6
|
-
import { storageReady, user_defaults,
|
|
4
|
+
import { Console, warnAlways } from './core/logger.js';
|
|
5
|
+
import { isEmpty, isSuccess } from './core/utils.js';
|
|
6
|
+
import { storageReady, user_defaults, engagement_transactions, error_transactions, heatmap_transactions, remotelog_transactions, feedbacklog_transactions, tracking_preference } from './core/storage.js';
|
|
7
7
|
import { hooks } from './core/hooks.js';
|
|
8
8
|
import { applyDebugConfig } from './core/debug.js';
|
|
9
9
|
import { getConnectionType, getOrientation } from './core/env.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { checkAndUpdateSession, updateLastActiveTimeOfSession, updateScreenWithEndtime, endCurrentSession } from './engagement/session.js';
|
|
10
|
+
import { restoreServerSettings, prepareDevice, startServerSettingChecks, updateDeviceWithApptics, needsDevice, deviceReady } from './device/index.js';
|
|
11
|
+
import { clearAppVersion, keyFingerprint, registerAppVersion, appVersionFailedForNetwork } from './device/appversion.js';
|
|
12
|
+
import { holdTabLock, checkAndUpdateSession, markTabInactive, markTabActive, updateScreenWithEndtime, endCurrentSession, resetInactivity } from './engagement/session.js';
|
|
14
13
|
import { enableApiTracking } from './engagement/auto-api.js';
|
|
15
|
-
import { enableScreenTracking } from './engagement/auto-screen.js';
|
|
14
|
+
import { enableScreenTracking, routePath } from './engagement/auto-screen.js';
|
|
16
15
|
import { resumeScreen, trackEvent } from './engagement/track.js';
|
|
17
16
|
import { trackError } from './errors/track.js';
|
|
18
17
|
import { startTimers, flush, stopTimers } from './scheduler.js';
|
|
19
|
-
import './remotelog/
|
|
18
|
+
import { sendStoredLogs, forgetStoredLogs } from './remotelog/send.js';
|
|
20
19
|
import { getUserInfo } from './user/index.js';
|
|
21
20
|
import { clearAttempts } from './core/attempts.js';
|
|
22
|
-
import {
|
|
21
|
+
import { FRAMEWORK_ID } from './core/framework-id.js';
|
|
23
22
|
|
|
24
23
|
let listenersAttached = false;
|
|
25
24
|
/** The config init() was last called with; reset() starts again from it. */
|
|
@@ -38,16 +37,29 @@ function isBrowser() {
|
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* The config as the SDK keeps it: base_url, auth_key and app_domain under their inside names
|
|
41
|
+
* (serviceUrl, zak, identifier; see CONFIG_KEYS), and appversion as text. An earlier name of a
|
|
42
|
+
* key is still read when the key itself is not given, with a warning naming the key to use.
|
|
43
|
+
* The base url loses any trailing slash, because request paths add their own.
|
|
44
44
|
*/
|
|
45
45
|
function normaliseConfig(config) {
|
|
46
46
|
var settings = Object.assign({}, config);
|
|
47
|
-
Object.keys(
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
Object.keys(CONFIG_KEYS).forEach(function (key) {
|
|
48
|
+
var inside = CONFIG_KEYS[key];
|
|
49
|
+
if (!isEmpty(config[key])) {
|
|
50
|
+
settings[inside] = config[key];
|
|
51
|
+
return;
|
|
50
52
|
}
|
|
53
|
+
delete settings[inside];
|
|
54
|
+
Object.keys(RETIRED_CONFIG_NAMES).forEach(function (old) {
|
|
55
|
+
if (RETIRED_CONFIG_NAMES[old] !== key || isEmpty(config[old])) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
warnAlways('Apptics.init: ' + old + ' is now ' + key + '; rename it in your config'); // no i18n
|
|
59
|
+
if (isEmpty(settings[inside])) {
|
|
60
|
+
settings[inside] = config[old];
|
|
61
|
+
}
|
|
62
|
+
});
|
|
51
63
|
});
|
|
52
64
|
if (!isEmpty(settings.serviceUrl)) {
|
|
53
65
|
settings.serviceUrl = String(settings.serviceUrl).replace(/\/+$/, ''); // no i18n
|
|
@@ -73,21 +85,62 @@ const APPVERSION_PLACEHOLDERS = ['YOUR_APP_VERSION', 'YOUR_BUILD_OR_APP_VERSION'
|
|
|
73
85
|
function isAppVersionPlaceholder(version) {
|
|
74
86
|
return APPVERSION_PLACEHOLDERS.indexOf(version.toUpperCase()) !== -1;
|
|
75
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The framework id this SDK reports to serviceUrl. The id built into the package is the one
|
|
90
|
+
* sdk-apptics.zoho.com issued at publish time, and localzoho servers don't know it: any
|
|
91
|
+
* localzoho serviceUrl reports 5970 instead, the id apptics.localzoho.com issued for 2.0.0-beta
|
|
92
|
+
* (Browser), so the same package also runs there.
|
|
93
|
+
*/
|
|
94
|
+
const LOCALZOHO_FRAMEWORK_ID = 5970;
|
|
95
|
+
function frameworkIdFor(serviceUrl) {
|
|
96
|
+
var host = serviceUrl;
|
|
97
|
+
try {
|
|
98
|
+
host = new URL(serviceUrl).hostname;
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
// not a url: judge the text as it is
|
|
102
|
+
}
|
|
103
|
+
return host.indexOf('localzoho') !== -1 ? LOCALZOHO_FRAMEWORK_ID : FRAMEWORK_ID; // no i18n
|
|
104
|
+
}
|
|
76
105
|
/** Which app this browser's stored data belongs to, before app/addversion has named it. */
|
|
77
106
|
function appKey(settings) {
|
|
78
107
|
return settings.serviceUrl + '|' + settings.identifier; // no i18n
|
|
79
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The app version stored data belongs to, read before app/addversion replaces it: what tells a
|
|
111
|
+
* new release of the app from a reload. '' on a first visit.
|
|
112
|
+
*/
|
|
113
|
+
var previousAppVersion = ''; // no i18n
|
|
80
114
|
/**
|
|
81
115
|
* Data stored for another app on this origin is discarded before anything else reads it,
|
|
82
116
|
* including the stored app/addversion answer that would otherwise hand out that app's ids.
|
|
117
|
+
*
|
|
118
|
+
* Another app is a different base_url, app_domain or auth_key from the ones the stored data was
|
|
119
|
+
* registered with. Everything goes, the queues included, and start-up runs as on a first visit:
|
|
120
|
+
* app/addversion, device/add and the rest. Resolves once the store is empty.
|
|
83
121
|
*/
|
|
84
122
|
function discardOtherAppData(settings) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
123
|
+
// which app it is, and which version, are in app_version_info (serviceUrl, identifier, a
|
|
124
|
+
// fingerprint of zak, and appversion): no keys of their own. 2.0.0-beta's appkey and
|
|
125
|
+
// appversion are read this once
|
|
126
|
+
var info = user_defaults.item('app_version_info') || {}; // no i18n
|
|
127
|
+
var stored = !isEmpty(info.serviceUrl) && !isEmpty(info.identifier)
|
|
128
|
+
? appKey(info)
|
|
129
|
+
: user_defaults.item('appkey'); // no i18n
|
|
130
|
+
previousAppVersion = String((!isEmpty(info.appversion) ? info.appversion : user_defaults.item('appversion')) || ''); // no i18n
|
|
131
|
+
user_defaults.add('appkey', undefined); // no i18n
|
|
132
|
+
user_defaults.add('appversion', undefined); // no i18n
|
|
133
|
+
var otherApp = !isEmpty(stored) && stored !== appKey(settings);
|
|
134
|
+
// a build that kept no fingerprint cannot tell: hasAppVersion() registers again, and records one
|
|
135
|
+
var otherKey = !isEmpty(info.zakhash) && info.zakhash !== keyFingerprint(settings.zak);
|
|
136
|
+
if (!otherApp && !otherKey) {
|
|
137
|
+
return Promise.resolve();
|
|
89
138
|
}
|
|
90
|
-
|
|
139
|
+
Console.log('init: stored data was registered with another ' + (otherApp ? 'base_url or app_domain' : 'auth_key') + ', clearing it and starting as a first visit'); // no i18n
|
|
140
|
+
previousAppVersion = ''; // no i18n
|
|
141
|
+
// the remote config values read from the store, which is about to go
|
|
142
|
+
hooks.resetRemoteConfig();
|
|
143
|
+
return clearStorage();
|
|
91
144
|
}
|
|
92
145
|
/**
|
|
93
146
|
* Timing an app may tune in init(). Each has a range: a value outside it is ignored with a
|
|
@@ -131,27 +184,32 @@ function applyIntervals(config) {
|
|
|
131
184
|
}
|
|
132
185
|
}
|
|
133
186
|
}
|
|
187
|
+
/** Everything the SDK stores: settings, ids and every queue. Resolves once it is all gone. */
|
|
134
188
|
function clearStorage() {
|
|
135
189
|
clearAttempts();
|
|
136
190
|
user_defaults.clear();
|
|
137
|
-
|
|
138
|
-
|
|
191
|
+
return Promise.all([
|
|
192
|
+
engagement_transactions, error_transactions, heatmap_transactions, remotelog_transactions, feedbacklog_transactions,
|
|
193
|
+
].map(function (queue) {
|
|
194
|
+
return queue.clear();
|
|
195
|
+
})).then(function () {
|
|
196
|
+
return undefined;
|
|
197
|
+
});
|
|
139
198
|
}
|
|
140
199
|
/**
|
|
141
|
-
* Starts the SDK. The config object is required:
|
|
142
|
-
*
|
|
143
|
-
* Returns true when the config has serviceUrl, zak, identifier and appversion and start-up has
|
|
200
|
+
* Starts the SDK. The config object is required: the SDK starts only when the app calls this.
|
|
201
|
+
* Returns true when the config has base_url, auth_key, app_domain and appversion and start-up has
|
|
144
202
|
* begun (see ready()), false when it was already running, the config is missing or incomplete, or
|
|
145
203
|
* there is no browser. Start-up calls app/addversion with those four, which answers with aaid, the
|
|
146
204
|
* portal and project ids and the rest; then device/add registers the browser, as in 1.x.
|
|
147
205
|
*/
|
|
148
206
|
function init(config) {
|
|
149
207
|
if (!config || typeof config !== 'object') { // no i18n
|
|
150
|
-
warnAlways('Apptics.init: a config object is required, e.g.
|
|
208
|
+
warnAlways('Apptics.init: a config object is required, e.g. Apptics.init({ base_url, auth_key, app_domain, appversion })'); // no i18n
|
|
151
209
|
return false;
|
|
152
210
|
}
|
|
153
211
|
if (_ZAP._initialised) {
|
|
154
|
-
Console.warn('Apptics.init: already initialised; call
|
|
212
|
+
Console.warn('Apptics.init: already initialised; call Apptics.reset() to start again'); // no i18n
|
|
155
213
|
return false;
|
|
156
214
|
}
|
|
157
215
|
if (!isBrowser()) {
|
|
@@ -160,7 +218,7 @@ function init(config) {
|
|
|
160
218
|
}
|
|
161
219
|
var settings = normaliseConfig(config);
|
|
162
220
|
var missing = REQUIRED_CONFIG.filter(function (key) {
|
|
163
|
-
return isEmpty(settings[key]);
|
|
221
|
+
return isEmpty(settings[CONFIG_KEYS[key] || key]);
|
|
164
222
|
});
|
|
165
223
|
if (missing.length > 0) {
|
|
166
224
|
warnAlways('Apptics.init: missing required config: ' + missing.join(', ')); // no i18n
|
|
@@ -174,16 +232,14 @@ function init(config) {
|
|
|
174
232
|
_ZAP._defaultoptions = settings;
|
|
175
233
|
// every request goes to serviceUrl; there is no other host to learn about
|
|
176
234
|
_ZAP._defaultoptions.base_domain = settings.serviceUrl;
|
|
235
|
+
// decided per init(), so a reset() or a page on another host never keeps the last one
|
|
236
|
+
_ZAP._frameworkID = frameworkIdFor(settings.serviceUrl);
|
|
177
237
|
// marks the SDK as started for the checks that need it; app/addversion replaces it with aaid
|
|
178
238
|
_ZAP._token = settings.identifier;
|
|
179
239
|
// console_log: the SDK's own activity in the browser console
|
|
180
240
|
if (!isEmpty(config.console_log)) {
|
|
181
241
|
_ZAP._config.debug = !!config.console_log;
|
|
182
242
|
}
|
|
183
|
-
// track_session: false records no sessions, as Android's automaticSessionsTrackingStatus
|
|
184
|
-
_ZAP._config.track_session = typeof config.track_session === 'boolean' ? config.track_session : AP_CONFIG.track_session; // no i18n
|
|
185
|
-
// show_logs: the app's own logger lines in the browser console, as Android's showLogs
|
|
186
|
-
_ZAP._config.show_logs = typeof config.show_logs === 'boolean' ? config.show_logs : AP_CONFIG.show_logs; // no i18n
|
|
187
243
|
if (typeof config.ignore_do_not_track === 'boolean') { // no i18n
|
|
188
244
|
_ZAP._config.ignore_do_not_track = config.ignore_do_not_track;
|
|
189
245
|
}
|
|
@@ -196,10 +252,14 @@ function init(config) {
|
|
|
196
252
|
applyDebugConfig(_ZAP._config.debug_api);
|
|
197
253
|
// what to call this device in the debug stream; see devicename in core/transport.ts
|
|
198
254
|
_ZAP._config.device_name = typeof config.device_name === 'string' ? config.device_name : AP_CONFIG.device_name; // no i18n
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
|
|
255
|
+
// the SDK no longer reports its own failures to the Remote Logger page: only the lines an app
|
|
256
|
+
// logs with AppticsLogger go there
|
|
257
|
+
if (config.remote_log !== undefined) {
|
|
258
|
+
warnAlways('Apptics.init: remote_log is no longer read; the Remote Logger page shows the lines your app logs with AppticsLogger'); // no i18n
|
|
259
|
+
}
|
|
260
|
+
// AppticsLogger lines go to the Remote Logger page only, never to the browser console
|
|
261
|
+
if (config.show_logs !== undefined) {
|
|
262
|
+
warnAlways('Apptics.init: show_logs is no longer read; AppticsLogger lines are not printed to the browser console'); // no i18n
|
|
203
263
|
}
|
|
204
264
|
// automatic api tracking: the app's own requests, wrapped. Which hosts it records is
|
|
205
265
|
// configureApiTracking's to say; the two keys that once did it are refused out loud rather
|
|
@@ -207,22 +267,42 @@ function init(config) {
|
|
|
207
267
|
_ZAP._config.track_api = AP_CONFIG.track_api;
|
|
208
268
|
['track_api_hosts', 'track_api_exclude_hosts'].forEach(function (key) {
|
|
209
269
|
if (config[key] !== undefined) {
|
|
210
|
-
warnAlways('Apptics.init: ' + key + ' is no longer supported; use
|
|
270
|
+
warnAlways('Apptics.init: ' + key + ' is no longer supported; use AppticsApi.configure(new AppticsApi.ApiTrackingBuilder().' // no i18n
|
|
211
271
|
+ (key === 'track_api_hosts' ? 'allowOnlyDomains' : 'ignoreDomains') + '(...).build())'); // no i18n
|
|
212
272
|
}
|
|
213
273
|
});
|
|
274
|
+
// screen recording is set up in the console now, and arrives with getsdkconfig
|
|
275
|
+
['DC', 'quartz_workspace_key', 'quartz_department_key'].forEach(function (key) {
|
|
276
|
+
if (config[key] !== undefined) {
|
|
277
|
+
warnAlways('Apptics.init: ' + key + ' is no longer read; screen recording in the feedback form is set up from the Apptics console'); // no i18n
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
// sessions are what everything else is recorded in; an app that wants nothing tracked uses
|
|
281
|
+
// AppticsSettings.setTrackingStatus instead
|
|
282
|
+
if (config.track_session !== undefined) {
|
|
283
|
+
warnAlways('Apptics.init: track_session is no longer read; sessions are always recorded. To stop tracking, use AppticsSettings.setTrackingStatus(AppticsSettings.AP_TRACKING_STATUS.NO_TRACKING)'); // no i18n
|
|
284
|
+
}
|
|
214
285
|
if (typeof config.track_api === 'boolean') { // no i18n
|
|
215
286
|
_ZAP._config.track_api = config.track_api;
|
|
216
287
|
}
|
|
217
288
|
enableApiTracking(_ZAP._config.track_api);
|
|
218
|
-
// heatmap capture:
|
|
289
|
+
// heatmap capture: switched through the hook heatmap/track.ts sets
|
|
219
290
|
_ZAP._config.track_heatmap = typeof config.track_heatmap === 'boolean' // no i18n
|
|
220
291
|
? config.track_heatmap
|
|
221
292
|
: AP_CONFIG.track_heatmap;
|
|
222
|
-
hooks.
|
|
293
|
+
hooks.trackHeatmap(_ZAP._config.track_heatmap);
|
|
223
294
|
// automatic screen tracking, on unless the config says false: switched on by start(), once a
|
|
224
295
|
// screen view can be stored
|
|
225
296
|
_ZAP._config.track_screen = typeof config.track_screen === 'boolean' ? config.track_screen : AP_CONFIG.track_screen; // no i18n
|
|
297
|
+
_ZAP._config.screen_routes = screenRouteRules(config.screen_routes);
|
|
298
|
+
// uncaught errors and tagged clicks, on unless the config says false: the listeners check
|
|
299
|
+
// these as each error or click happens
|
|
300
|
+
_ZAP._config.track_error = typeof config.track_error === 'boolean' ? config.track_error : AP_CONFIG.track_error; // no i18n
|
|
301
|
+
_ZAP._config.track_click = typeof config.track_click === 'boolean' ? config.track_click : AP_CONFIG.track_click; // no i18n
|
|
302
|
+
// a theme in the config wins; without one, what AppticsFeedback.setFeedbackTheme set stays
|
|
303
|
+
if (config.feedback_theme === 'light' || config.feedback_theme === 'dark' || config.feedback_theme === 'auto') { // no i18n
|
|
304
|
+
_ZAP._config.feedback_theme = config.feedback_theme;
|
|
305
|
+
}
|
|
226
306
|
applyIntervals(config);
|
|
227
307
|
lastConfig = config;
|
|
228
308
|
_ZAP._initialised = true;
|
|
@@ -240,22 +320,11 @@ function init(config) {
|
|
|
240
320
|
_ZAP._pending = [];
|
|
241
321
|
return false;
|
|
242
322
|
}
|
|
243
|
-
discardOtherAppData(_ZAP._defaultoptions)
|
|
244
|
-
// app/addversion is the first call and everything waits on it: it is what turns the four
|
|
245
|
-
// values init() was given into aaid, the portal and project ids every url carries, and
|
|
246
|
-
// appreleaseversionid, which metaWebSDKV2 refuses a payload without
|
|
247
|
-
return registerAppVersion().then(function (registered) {
|
|
323
|
+
return discardOtherAppData(_ZAP._defaultoptions).then(function () {
|
|
248
324
|
if (myGeneration !== generation) {
|
|
249
325
|
return false;
|
|
250
326
|
}
|
|
251
|
-
|
|
252
|
-
warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
|
|
253
|
-
_ZAP._initialised = false;
|
|
254
|
-
_ZAP._token = ''; // no i18n
|
|
255
|
-
_ZAP._pending = [];
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
return start(config, myGeneration);
|
|
327
|
+
return startUp(config, myGeneration);
|
|
259
328
|
});
|
|
260
329
|
}).catch(function (err) {
|
|
261
330
|
Console.error('Apptics.init: start-up failed', err); // no i18n
|
|
@@ -263,42 +332,177 @@ function init(config) {
|
|
|
263
332
|
});
|
|
264
333
|
return true;
|
|
265
334
|
}
|
|
335
|
+
/** Start-up from app/addversion on, once the store holds only this app's data. */
|
|
336
|
+
function startUp(config, myGeneration) {
|
|
337
|
+
// app/addversion is the first call and everything waits on it: it is what turns the four
|
|
338
|
+
// values init() was given into aaid, the portal and project ids every url carries, and
|
|
339
|
+
// appreleaseversionid, which metaWebSDKV2 refuses a payload without
|
|
340
|
+
return registerAppVersion().then(function (registered) {
|
|
341
|
+
if (myGeneration !== generation) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
if (!registered && appVersionFailedForNetwork()) {
|
|
345
|
+
// a first visit with no network: start once it is back, keeping what the app
|
|
346
|
+
// tracks until then queued, rather than giving up for the rest of the page
|
|
347
|
+
warnAlways('Apptics.init: offline, so app/addversion is tried again when the browser is back online; calls made until then are kept'); // no i18n
|
|
348
|
+
return registerWhenOnline(myGeneration).then(function (ok) {
|
|
349
|
+
if (myGeneration !== generation) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
if (!ok) {
|
|
353
|
+
_ZAP._initialised = false;
|
|
354
|
+
_ZAP._token = ''; // no i18n
|
|
355
|
+
_ZAP._pending = [];
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
return start(config, myGeneration);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
if (!registered) {
|
|
362
|
+
warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
|
|
363
|
+
_ZAP._initialised = false;
|
|
364
|
+
_ZAP._token = ''; // no i18n
|
|
365
|
+
_ZAP._pending = [];
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
return start(config, myGeneration);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/** How often a start-up waiting on the network tries app/addversion again, besides on `online`. */
|
|
372
|
+
const OFFLINE_RETRY_MS = 60 * 1000;
|
|
373
|
+
/**
|
|
374
|
+
* app/addversion again once the network is back: on the `online` event, and every minute for a
|
|
375
|
+
* network that came back without one. Resolves true once registered, false when the server
|
|
376
|
+
* refuses it or a reset()/new init() took over.
|
|
377
|
+
*/
|
|
378
|
+
function registerWhenOnline(myGeneration) {
|
|
379
|
+
return new Promise(function (resolve) {
|
|
380
|
+
var timer = null;
|
|
381
|
+
var trying = false;
|
|
382
|
+
function done(ok) {
|
|
383
|
+
window.removeEventListener('online', attempt); // no i18n
|
|
384
|
+
if (timer !== null)
|
|
385
|
+
clearInterval(timer);
|
|
386
|
+
resolve(ok);
|
|
387
|
+
}
|
|
388
|
+
function attempt() {
|
|
389
|
+
if (myGeneration !== generation) {
|
|
390
|
+
done(false);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (trying || navigator.onLine === false) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
trying = true;
|
|
397
|
+
registerAppVersion().then(function (registered) {
|
|
398
|
+
trying = false;
|
|
399
|
+
if (registered) {
|
|
400
|
+
done(true);
|
|
401
|
+
}
|
|
402
|
+
else if (!appVersionFailedForNetwork()) {
|
|
403
|
+
warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
|
|
404
|
+
done(false);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
window.addEventListener('online', attempt); // no i18n
|
|
409
|
+
timer = setInterval(attempt, OFFLINE_RETRY_MS);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* screen_routes as init() keeps it: each entry checked, an exact path cleaned up, a regex given as
|
|
414
|
+
* a string compiled. An entry that cannot be used is left out, and said, rather than matching
|
|
415
|
+
* something its author did not mean.
|
|
416
|
+
*/
|
|
417
|
+
function screenRouteRules(routes) {
|
|
418
|
+
if (!Array.isArray(routes)) {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
var out = [];
|
|
422
|
+
routes.forEach(function (route) {
|
|
423
|
+
if (typeof route === 'string') { // no i18n
|
|
424
|
+
if (route.trim() !== '') { // no i18n
|
|
425
|
+
out.push({ kind: 'pattern', pattern: route.trim() }); // no i18n
|
|
426
|
+
}
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
var rule = route && typeof route === 'object' ? route : null; // no i18n
|
|
430
|
+
var name = rule && typeof rule.name === 'string' ? rule.name.trim() : ''; // no i18n
|
|
431
|
+
if (rule && name !== '' && typeof rule.equals === 'string' && rule.equals.trim() !== '') { // no i18n
|
|
432
|
+
out.push({ kind: 'equals', path: routePath(rule.equals.trim()), name: name }); // no i18n
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (rule && name !== '' && rule.regex !== undefined) {
|
|
436
|
+
var regex = null;
|
|
437
|
+
if (rule.regex instanceof RegExp) {
|
|
438
|
+
regex = rule.regex;
|
|
439
|
+
}
|
|
440
|
+
else if (typeof rule.regex === 'string' && rule.regex !== '') { // no i18n
|
|
441
|
+
try {
|
|
442
|
+
regex = new RegExp(rule.regex);
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
regex = null;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (regex) {
|
|
449
|
+
out.push({ kind: 'regex', regex: regex, name: name }); // no i18n
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
warnAlways('Apptics.init: screen_routes entry ' + safeJson(route) + ' is left out: give a route pattern, { equals, name } or { regex, name }'); // no i18n
|
|
454
|
+
});
|
|
455
|
+
return out;
|
|
456
|
+
}
|
|
457
|
+
/** An entry as the warning shows it: a RegExp as its source, which JSON.stringify would hide. */
|
|
458
|
+
function safeJson(value) {
|
|
459
|
+
try {
|
|
460
|
+
return JSON.stringify(value, function (_key, one) { return one instanceof RegExp ? String(one) : one; }) || String(value);
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
return String(value);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
266
466
|
/** Resolves true once start-up has finished, false if init() refused or storage is unusable. */
|
|
267
467
|
function ready() {
|
|
268
468
|
return _ZAP._readyPromise || Promise.resolve(false);
|
|
269
469
|
}
|
|
270
470
|
function start(config, myGeneration) {
|
|
471
|
+
// this tab's lock, before any screen view: it is how other tabs tell this one is still open
|
|
472
|
+
holdTabLock();
|
|
271
473
|
// app/addversion named the app; data stored for another one was discarded before it ran
|
|
272
474
|
_ZAP._token = String(_ZAP._defaultoptions.aaid);
|
|
273
|
-
user_defaults.add('token', _ZAP._token); // no i18n
|
|
274
475
|
// last known server settings (getupdates) apply before anything is tracked
|
|
275
476
|
restoreServerSettings();
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
uuid = getNewUUID();
|
|
282
|
-
user_defaults.add('uuid', uuid); // no i18n
|
|
283
|
-
}
|
|
284
|
-
_ZAP._uuid = uuid;
|
|
285
|
-
var stored_appversion = user_defaults.item('appversion'); // no i18n
|
|
286
|
-
if (isEmpty(stored_appversion)) {
|
|
287
|
-
user_defaults.add('appversion', _ZAP._defaultoptions.appversion); // no i18n
|
|
288
|
-
stored_appversion = _ZAP._defaultoptions.appversion;
|
|
289
|
-
}
|
|
290
|
-
var appVersionChanged = stored_appversion != _ZAP._defaultoptions.appversion;
|
|
477
|
+
// the user this browser was tracking as, before getsdkconfig is asked: it carries their ids
|
|
478
|
+
getUserInfo();
|
|
479
|
+
// a new release of the app, against the version the stored data was for (see
|
|
480
|
+
// discardOtherAppData): a first visit has none, and is no change
|
|
481
|
+
var appVersionChanged = previousAppVersion !== '' && previousAppVersion != _ZAP._defaultoptions.appversion; // no i18n
|
|
291
482
|
_ZAP._ignoreDnt = _ZAP._config.ignore_do_not_track;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
//
|
|
296
|
-
|
|
483
|
+
// the visitor's own stored choice, not getTrackingStatus(): that answers NO_TRACKING while Do
|
|
484
|
+
// Not Track is on, and storing it made one such visit switch tracking off for good
|
|
485
|
+
tracking_preference.update_status(tracking_preference.opt_in_status());
|
|
486
|
+
// the uuid and device info, and a registration on a first visit, under a lock shared with the
|
|
487
|
+
// site's other tabs (see prepareDevice)
|
|
488
|
+
return prepareDevice().then(function () {
|
|
489
|
+
if (myGeneration !== generation) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
return startTracking(appVersionChanged, myGeneration);
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
/** The rest of start-up, once the device is set up. */
|
|
496
|
+
function startTracking(appVersionChanged, myGeneration) {
|
|
497
|
+
// getsdkconfig, once per page load and then at each answer's next_check_after. A first visit
|
|
498
|
+
// asked as soon as device/add succeeded (registerDeviceWithApptics) and this does nothing; a
|
|
499
|
+
// returning visit, whose device is registered, asks now, after app/addversion. Until it
|
|
500
|
+
// answers, the settings saved by the last visit apply (restoreServerSettings, in start())
|
|
501
|
+
startServerSettingChecks();
|
|
297
502
|
if (appVersionChanged) {
|
|
298
503
|
_ZAP._device_info.appversionid = ''; // no i18n
|
|
299
504
|
updateDeviceWithApptics(_ZAP._device_info).then(function (result) {
|
|
300
505
|
if (isSuccess(result)) {
|
|
301
|
-
user_defaults.add('appversion', _ZAP._defaultoptions.appversion); // no i18n
|
|
302
506
|
Console.log('init: appversion updated to ' + _ZAP._defaultoptions.appversion); // no i18n
|
|
303
507
|
}
|
|
304
508
|
}).catch(function (err) {
|
|
@@ -309,7 +513,6 @@ function start(config, myGeneration) {
|
|
|
309
513
|
_ZAP._device_info.connectionType = getConnectionType();
|
|
310
514
|
attachListeners();
|
|
311
515
|
_ZAP._properties.orientation = getOrientation();
|
|
312
|
-
getUserInfo();
|
|
313
516
|
return checkAndUpdateSession().then(function () {
|
|
314
517
|
if (myGeneration !== generation) {
|
|
315
518
|
return false;
|
|
@@ -344,12 +547,13 @@ function start(config, myGeneration) {
|
|
|
344
547
|
function reset() {
|
|
345
548
|
generation++;
|
|
346
549
|
stopTimers();
|
|
550
|
+
resetInactivity();
|
|
347
551
|
// the wrappers come off with everything else; the init() below puts them back if the config
|
|
348
552
|
// asked for them, so a reset that has no config to restore leaves XHR and fetch untouched
|
|
349
553
|
enableApiTracking(false);
|
|
350
554
|
enableScreenTracking(false);
|
|
351
555
|
clearAppVersion();
|
|
352
|
-
hooks.
|
|
556
|
+
hooks.trackHeatmap(false);
|
|
353
557
|
hooks.resetHeatmap();
|
|
354
558
|
hooks.resetRemoteConfig();
|
|
355
559
|
clearStorage();
|
|
@@ -365,7 +569,7 @@ function reset() {
|
|
|
365
569
|
_ZAP._apid = ''; // no i18n
|
|
366
570
|
_ZAP._mapid = ''; // no i18n
|
|
367
571
|
_ZAP._sending = false;
|
|
368
|
-
Console.log('
|
|
572
|
+
Console.log('Apptics.reset()'); // no i18n
|
|
369
573
|
// start again with the config init() was called with
|
|
370
574
|
if (lastConfig) {
|
|
371
575
|
init(lastConfig);
|
|
@@ -374,9 +578,42 @@ function reset() {
|
|
|
374
578
|
function isActive() {
|
|
375
579
|
return _ZAP._ready;
|
|
376
580
|
}
|
|
377
|
-
|
|
581
|
+
/**
|
|
582
|
+
* AppticsEvent.setClickTracking: click tracking from markup on or off, as the `track_click`
|
|
583
|
+
* config sets it. The listener stays; it checks the switch on every click.
|
|
584
|
+
*/
|
|
585
|
+
function setClickTracking(enabled) {
|
|
586
|
+
_ZAP._config.track_click = typeof enabled === 'boolean' ? enabled : true; // no i18n
|
|
587
|
+
Console.log('click tracking: ' + (_ZAP._config.track_click ? 'on' : 'off')); // no i18n
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* AppticsScreen.setScreenTracking: automatic screen tracking on or off, as the `track_screen`
|
|
591
|
+
* config sets it. Applied now once the SDK is running; before that it is only noted, and start()
|
|
592
|
+
* switches tracking to it. Like the other switches, the next init() (a reset() too) goes back to
|
|
593
|
+
* what its config says.
|
|
594
|
+
*/
|
|
595
|
+
function setScreenTracking(enabled) {
|
|
596
|
+
_ZAP._config.track_screen = typeof enabled === 'boolean' ? enabled : true; // no i18n
|
|
597
|
+
if (isActive()) {
|
|
598
|
+
enableScreenTracking(_ZAP._config.track_screen);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* A click-tracking attribute, in either spelling: `apeventaction`, or `data-apeventaction`, which
|
|
603
|
+
* is valid HTML and what React and TypeScript accept without declaring the attribute.
|
|
604
|
+
*/
|
|
605
|
+
function trackingAttribute(el, name) {
|
|
606
|
+
var value = el.getAttribute(name);
|
|
607
|
+
return value !== null && value !== '' ? value : el.getAttribute('data-' + name); // no i18n
|
|
608
|
+
}
|
|
609
|
+
/** `[name]` and `[data-name]`, empty values left out, for closest(). */
|
|
610
|
+
function trackingSelector(name) {
|
|
611
|
+
return '[' + name + ']:not([' + name + '=""]),[data-' + name + ']:not([data-' + name + '=""])'; // no i18n
|
|
612
|
+
}
|
|
613
|
+
// Declarative click tracking: <el apeventaction="name" apeventprops='{"k":"v"}'> inside <el apgroupaction="group">,
|
|
614
|
+
// or the same attributes with a data- prefix
|
|
378
615
|
function onDocumentClick(event) {
|
|
379
|
-
if (!window.appticsReadyStatus || !isActive()) {
|
|
616
|
+
if (!window.appticsReadyStatus || !isActive() || !_ZAP._config.track_click) {
|
|
380
617
|
return;
|
|
381
618
|
}
|
|
382
619
|
var start = event.target;
|
|
@@ -386,15 +623,16 @@ function onDocumentClick(event) {
|
|
|
386
623
|
if (!start) {
|
|
387
624
|
return;
|
|
388
625
|
}
|
|
389
|
-
var targetElem = start.closest('
|
|
626
|
+
var targetElem = start.closest(trackingSelector('apeventaction')); // no i18n
|
|
390
627
|
if (!targetElem) {
|
|
391
628
|
return;
|
|
392
629
|
}
|
|
393
|
-
var eventName = targetElem
|
|
394
|
-
var groupElem = targetElem.closest('
|
|
395
|
-
|
|
630
|
+
var eventName = trackingAttribute(targetElem, 'apeventaction') || ''; // no i18n
|
|
631
|
+
var groupElem = targetElem.closest(trackingSelector('apgroupaction')); // no i18n
|
|
632
|
+
// no apgroupaction around it: trackEvent refuses it and says the group is missing
|
|
633
|
+
var groupName = (groupElem && trackingAttribute(groupElem, 'apgroupaction')) || undefined; // no i18n
|
|
396
634
|
var eventProps = {};
|
|
397
|
-
var rawProps = targetElem
|
|
635
|
+
var rawProps = trackingAttribute(targetElem, 'apeventprops'); // no i18n
|
|
398
636
|
if (!isEmpty(rawProps)) {
|
|
399
637
|
try {
|
|
400
638
|
eventProps = JSON.parse(rawProps);
|
|
@@ -416,10 +654,18 @@ function onDocumentClick(event) {
|
|
|
416
654
|
function onPageHidden() {
|
|
417
655
|
if (!isActive())
|
|
418
656
|
return;
|
|
657
|
+
// the moment the user left: the session ends at it if they are not back within inactiveduration
|
|
658
|
+
markTabInactive(sendEndedSession);
|
|
419
659
|
updateScreenWithEndtime();
|
|
420
660
|
flush(true);
|
|
421
661
|
Console.log('page hidden: closing the open screen and flushing'); // no i18n
|
|
422
662
|
}
|
|
663
|
+
/** A session that inactivity ended while the tab was away: sent now, not when the user returns. */
|
|
664
|
+
function sendEndedSession() {
|
|
665
|
+
if (!isActive())
|
|
666
|
+
return;
|
|
667
|
+
flush();
|
|
668
|
+
}
|
|
423
669
|
/**
|
|
424
670
|
* The tab is closing, or the page is being replaced or frozen into the back/forward cache.
|
|
425
671
|
* The session ends here, and everything queued goes out by keepalive, since a normal
|
|
@@ -451,14 +697,18 @@ function attachListeners() {
|
|
|
451
697
|
window.addEventListener('blur', function () {
|
|
452
698
|
if (!isActive())
|
|
453
699
|
return;
|
|
700
|
+
// focus moving into an iframe on the page blurs the window too, and the user is still here
|
|
701
|
+
if (document.activeElement && document.activeElement.tagName === 'IFRAME')
|
|
702
|
+
return; // no i18n
|
|
454
703
|
Console.log('Window lost focus, user switched to another window or application'); // no i18n
|
|
455
|
-
|
|
704
|
+
markTabInactive(sendEndedSession);
|
|
456
705
|
});
|
|
457
706
|
// Track when the window gains focus (user switches back to the window)
|
|
458
707
|
window.addEventListener('focus', function () {
|
|
459
708
|
if (!isActive())
|
|
460
709
|
return;
|
|
461
710
|
Console.log('Window regained focus, user switched back to the window'); // no i18n
|
|
711
|
+
markTabActive();
|
|
462
712
|
checkAndUpdateSession();
|
|
463
713
|
startTimers();
|
|
464
714
|
});
|
|
@@ -469,6 +719,7 @@ function attachListeners() {
|
|
|
469
719
|
if (!isActive() || !event.persisted)
|
|
470
720
|
return;
|
|
471
721
|
Console.log('restored from the back/forward cache: starting a new session'); // no i18n
|
|
722
|
+
markTabActive();
|
|
472
723
|
// the screen the page was left on starts a new view, in the new session
|
|
473
724
|
checkAndUpdateSession().then(resumeScreen);
|
|
474
725
|
startTimers();
|
|
@@ -480,12 +731,12 @@ function attachListeners() {
|
|
|
480
731
|
onPageHidden();
|
|
481
732
|
return;
|
|
482
733
|
}
|
|
483
|
-
// back in view
|
|
484
|
-
|
|
734
|
+
// back in view. getsdkconfig is not asked here: only at its next_check_after, on a tab's
|
|
735
|
+
// first page load, and when a user is registered or getupdates reports a change
|
|
736
|
+
markTabActive();
|
|
485
737
|
// hiding ended the screen view; the screen the user is back on starts a new one, after the
|
|
486
738
|
// session is settled so the view belongs to the right one
|
|
487
739
|
checkAndUpdateSession().then(resumeScreen);
|
|
488
|
-
refreshServerSettings();
|
|
489
740
|
});
|
|
490
741
|
// if we are running in cordova use ondeviceready otherwise onload
|
|
491
742
|
window.addEventListener(window.cordova ? 'deviceready' : 'load', function () {
|
|
@@ -500,8 +751,8 @@ function attachListeners() {
|
|
|
500
751
|
// until it has them: register before flushing, and flush once that has landed
|
|
501
752
|
if (needsDevice()) {
|
|
502
753
|
Console.log('back online: registering this device before sending'); // no i18n
|
|
503
|
-
|
|
504
|
-
|
|
754
|
+
// the same locked path as start-up, so two tabs coming back online register it once
|
|
755
|
+
prepareDevice().then(deviceReady).then(function () {
|
|
505
756
|
if (!isActive())
|
|
506
757
|
return;
|
|
507
758
|
flush();
|
|
@@ -519,16 +770,26 @@ function attachListeners() {
|
|
|
519
770
|
});
|
|
520
771
|
}
|
|
521
772
|
window.addEventListener('unhandledrejection', function (event) {
|
|
522
|
-
if (isActive() && _ZAP.
|
|
773
|
+
if (isActive() && _ZAP._config.track_error) {
|
|
523
774
|
trackError(event);
|
|
524
775
|
}
|
|
525
776
|
});
|
|
777
|
+
// in the capture phase: a <script>, <img> or <link> that fails to load fires `error` on the
|
|
778
|
+
// element, and it does not bubble, so a bubbling listener never hears of it. One listener
|
|
779
|
+
// hears both kinds, the window's own errors included, so none is counted twice
|
|
526
780
|
window.addEventListener('error', function (event) {
|
|
527
|
-
if (isActive()
|
|
528
|
-
|
|
781
|
+
if (!isActive() || !_ZAP._config.track_error) {
|
|
782
|
+
return;
|
|
529
783
|
}
|
|
530
|
-
|
|
784
|
+
var target = event.target;
|
|
785
|
+
if (target && target !== window && typeof target.tagName === 'string') { // no i18n
|
|
786
|
+
// named by describeTarget: Event "error" on <script src="...">
|
|
787
|
+
trackError({ type: 'error', reason: event }); // no i18n
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
trackError(event);
|
|
791
|
+
}, true);
|
|
531
792
|
window.addEventListener('click', onDocumentClick, true); // no i18n
|
|
532
793
|
}
|
|
533
794
|
|
|
534
|
-
export { init, ready, reset };
|
|
795
|
+
export { init, ready, reset, screenRouteRules, setClickTracking, setScreenTracking };
|