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