@zoho_apptics/apptics-js-sdk 2.0.0-beta.1 → 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 +80 -16
- package/README.md +187 -100
- 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 +1 -2
- package/dist/cjs/core/consent.d.ts +13 -3
- package/dist/cjs/core/consent.js +24 -10
- package/dist/cjs/core/constants.d.ts +22 -17
- package/dist/cjs/core/constants.js +22 -19
- package/dist/cjs/core/db.js +1 -1
- 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.js +1 -1
- package/dist/cjs/core/hooks.d.ts +5 -0
- package/dist/cjs/core/hooks.js +2 -1
- package/dist/cjs/core/locks.js +1 -1
- package/dist/cjs/core/logger.js +1 -1
- package/dist/cjs/core/state.d.ts +6 -4
- package/dist/cjs/core/state.js +2 -4
- package/dist/cjs/core/storage.js +1 -4
- package/dist/cjs/core/stylesheet.js +1 -1
- package/dist/cjs/core/transport.js +13 -16
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +1 -1
- package/dist/cjs/device/appversion.d.ts +10 -1
- package/dist/cjs/device/appversion.js +26 -3
- package/dist/cjs/device/index.d.ts +17 -2
- package/dist/cjs/device/index.js +195 -27
- package/dist/cjs/engagement/api-rules.js +1 -1
- package/dist/cjs/engagement/auto-api.js +1 -1
- package/dist/cjs/engagement/auto-screen.js +1 -1
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +7 -3
- package/dist/cjs/engagement/session.d.ts +17 -0
- package/dist/cjs/engagement/session.js +95 -4
- package/dist/cjs/engagement/track.d.ts +4 -4
- package/dist/cjs/engagement/track.js +15 -12
- package/dist/cjs/errors/send.js +2 -2
- package/dist/cjs/errors/track.d.ts +2 -1
- package/dist/cjs/errors/track.js +6 -4
- package/dist/cjs/feedback/controls.d.ts +6 -0
- package/dist/cjs/feedback/controls.js +74 -25
- package/dist/cjs/feedback/index.js +1 -1
- package/dist/cjs/feedback/logs.d.ts +3 -6
- package/dist/cjs/feedback/logs.js +8 -18
- package/dist/cjs/feedback/quartz.js +1 -1
- package/dist/cjs/feedback/send.d.ts +2 -2
- package/dist/cjs/feedback/send.js +4 -27
- package/dist/cjs/feedback/styles.js +2 -2
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +9 -1
- package/dist/cjs/feedback/ui.d.ts +7 -0
- package/dist/cjs/feedback/ui.js +160 -25
- package/dist/cjs/heatmap/send.d.ts +2 -0
- package/dist/cjs/heatmap/send.js +3 -5
- package/dist/cjs/heatmap/track.js +1 -1
- package/dist/cjs/index.d.ts +1 -2
- package/dist/cjs/index.js +54 -17
- package/dist/cjs/lifecycle.d.ts +13 -2
- package/dist/cjs/lifecycle.js +122 -54
- package/dist/cjs/remoteconfig/country.js +1 -1
- package/dist/cjs/remoteconfig/custom.js +1 -1
- package/dist/cjs/remoteconfig/evaluate.js +1 -1
- package/dist/cjs/remoteconfig/fetch.d.ts +17 -10
- package/dist/cjs/remoteconfig/fetch.js +126 -31
- package/dist/cjs/remoteconfig/index.d.ts +13 -4
- package/dist/cjs/remoteconfig/index.js +36 -20
- package/dist/cjs/remoteconfig/store.d.ts +15 -7
- package/dist/cjs/remoteconfig/store.js +68 -35
- package/dist/cjs/remoteconfig/tz-countries.js +1 -1
- package/dist/cjs/{core → remotelog}/callsite.d.ts +5 -4
- package/dist/cjs/{core → remotelog}/callsite.js +6 -5
- package/dist/cjs/remotelog/gate.js +1 -1
- package/dist/cjs/remotelog/logger.js +2 -2
- package/dist/cjs/remotelog/send.js +2 -2
- package/dist/cjs/remotelogger/index.js +1 -1
- package/dist/cjs/scheduler.d.ts +3 -1
- package/dist/cjs/scheduler.js +24 -21
- package/dist/cjs/types.d.ts +61 -16
- package/dist/cjs/user/index.js +6 -2
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +1 -2
- package/dist/esm/core/consent.d.ts +13 -3
- package/dist/esm/core/consent.js +24 -11
- package/dist/esm/core/constants.d.ts +22 -17
- package/dist/esm/core/constants.js +22 -19
- package/dist/esm/core/db.js +1 -1
- 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.js +1 -1
- package/dist/esm/core/hooks.d.ts +5 -0
- package/dist/esm/core/hooks.js +2 -1
- package/dist/esm/core/locks.js +1 -1
- package/dist/esm/core/logger.js +1 -1
- package/dist/esm/core/state.d.ts +6 -4
- package/dist/esm/core/state.js +2 -4
- package/dist/esm/core/storage.js +1 -4
- package/dist/esm/core/stylesheet.js +1 -1
- package/dist/esm/core/transport.js +13 -16
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +1 -1
- package/dist/esm/device/appversion.d.ts +10 -1
- package/dist/esm/device/appversion.js +26 -4
- package/dist/esm/device/index.d.ts +17 -2
- package/dist/esm/device/index.js +195 -30
- package/dist/esm/engagement/api-rules.js +1 -1
- package/dist/esm/engagement/auto-api.js +1 -1
- package/dist/esm/engagement/auto-screen.js +1 -1
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +7 -3
- package/dist/esm/engagement/session.d.ts +17 -0
- package/dist/esm/engagement/session.js +93 -5
- package/dist/esm/engagement/track.d.ts +4 -4
- package/dist/esm/engagement/track.js +16 -13
- package/dist/esm/errors/send.js +2 -2
- package/dist/esm/errors/track.d.ts +2 -1
- package/dist/esm/errors/track.js +6 -4
- package/dist/esm/feedback/controls.d.ts +6 -0
- package/dist/esm/feedback/controls.js +74 -26
- package/dist/esm/feedback/index.js +1 -1
- package/dist/esm/feedback/logs.d.ts +3 -6
- package/dist/esm/feedback/logs.js +8 -18
- package/dist/esm/feedback/quartz.js +1 -1
- package/dist/esm/feedback/send.d.ts +2 -2
- package/dist/esm/feedback/send.js +6 -29
- package/dist/esm/feedback/styles.js +2 -2
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +9 -1
- package/dist/esm/feedback/ui.d.ts +7 -0
- package/dist/esm/feedback/ui.js +161 -26
- package/dist/esm/heatmap/send.d.ts +2 -0
- package/dist/esm/heatmap/send.js +3 -5
- package/dist/esm/heatmap/track.js +1 -1
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +57 -20
- package/dist/esm/lifecycle.d.ts +13 -2
- package/dist/esm/lifecycle.js +126 -60
- package/dist/esm/remoteconfig/country.js +1 -1
- package/dist/esm/remoteconfig/custom.js +1 -1
- package/dist/esm/remoteconfig/evaluate.js +1 -1
- package/dist/esm/remoteconfig/fetch.d.ts +17 -10
- package/dist/esm/remoteconfig/fetch.js +127 -32
- package/dist/esm/remoteconfig/index.d.ts +13 -4
- package/dist/esm/remoteconfig/index.js +36 -20
- package/dist/esm/remoteconfig/store.d.ts +15 -7
- package/dist/esm/remoteconfig/store.js +68 -36
- package/dist/esm/remoteconfig/tz-countries.js +1 -1
- package/dist/esm/{core → remotelog}/callsite.d.ts +5 -4
- package/dist/esm/{core → remotelog}/callsite.js +6 -5
- package/dist/esm/remotelog/gate.js +1 -1
- package/dist/esm/remotelog/logger.js +2 -2
- package/dist/esm/remotelog/send.js +2 -2
- package/dist/esm/remotelogger/index.js +1 -1
- package/dist/esm/scheduler.d.ts +3 -1
- package/dist/esm/scheduler.js +25 -23
- package/dist/esm/types.d.ts +61 -16
- package/dist/esm/user/index.js +7 -3
- 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 +3 -2
- package/dist/chunks/lifecycle-VYr43KcB.min.js +0 -3
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +0 -1
- package/dist/cjs/auto-init.d.ts +0 -8
- package/dist/cjs/auto-init.js +0 -32
- package/dist/esm/auto-init.d.ts +0 -8
- package/dist/esm/auto-init.js +0 -29
|
@@ -28,7 +28,22 @@ export declare function getUpdatesFromServer(): Promise<Dict>;
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function restoreServerSettings(): void;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* next_check_after as a time: the server's value is milliseconds, either a timestamp or a wait
|
|
32
|
+
* from now. Anything below 1e12 (2001 as a timestamp) can only be a wait.
|
|
33
|
+
*/
|
|
34
|
+
export declare function nextCheckTime(value: unknown, now: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* Asks for the server settings now if the device is registered, then schedules the next ask
|
|
37
|
+
* for the time the answer gave in next_check_after. On a first visit the ids don't exist yet;
|
|
38
|
+
* registerDeviceWithApptics calls this as soon as device/add succeeds.
|
|
33
39
|
*/
|
|
34
40
|
export declare function refreshServerSettings(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Start-up's getsdkconfig. Asked on a first visit and on the first page load in each tab; any
|
|
43
|
+
* other load (a reload, a link to another page of the site, back/forward) uses the saved answer
|
|
44
|
+
* and waits for its next_check_after, unless that has already passed. Nothing happens if one has
|
|
45
|
+
* gone out already on this load (after device/add).
|
|
46
|
+
*/
|
|
47
|
+
export declare function startServerSettingChecks(): void;
|
|
48
|
+
/** reset(): no more checks, and the next init() asks again straight away. */
|
|
49
|
+
export declare function stopServerSettingChecks(): void;
|
package/dist/cjs/device/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
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');
|
|
@@ -108,10 +108,18 @@ function checkAndUpdateDeviceInfo() {
|
|
|
108
108
|
storage.user_defaults.add('device_info', state._ZAP._device_info); // no i18n
|
|
109
109
|
addOrUpdateDeviceID(state._ZAP._device_info, previous);
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether device/add has registered this device: it has a deviceid. anonymousid is not required:
|
|
113
|
+
* not every Apptics server sends one, nothing the SDK sends carries it, and requiring it had a
|
|
114
|
+
* server that leaves it out register the same device again on every page load.
|
|
115
|
+
*/
|
|
116
|
+
function isRegistered(info) {
|
|
117
|
+
return !!info && !utils.isEmpty(info.deviceid);
|
|
118
|
+
}
|
|
111
119
|
function addOrUpdateDeviceID(device_info, previous) {
|
|
112
120
|
var old_device_info = previous !== undefined ? previous : storage.user_defaults.item('device_info') || {}; // no i18n
|
|
113
121
|
// Compare the old and new device infos and update the server if anything changed in the new.
|
|
114
|
-
if (device_info
|
|
122
|
+
if (!isRegistered(device_info) || device_info.frameworkid == null) {
|
|
115
123
|
device_info.frameworkid = state._ZAP._frameworkID;
|
|
116
124
|
logger.Console.log('addOrUpdateDeviceID: Initiated register the device'); // no i18n
|
|
117
125
|
registerDeviceWithApptics().then(function (result_device_info) {
|
|
@@ -188,7 +196,7 @@ function useStoredUuid() {
|
|
|
188
196
|
function adoptRegisteredDevice() {
|
|
189
197
|
return storage.reloadSettings(['device_info']).then(function () {
|
|
190
198
|
var stored = storage.user_defaults.item('device_info') || {}; // no i18n
|
|
191
|
-
if (
|
|
199
|
+
if (!isRegistered(stored)) {
|
|
192
200
|
return null;
|
|
193
201
|
}
|
|
194
202
|
// the device is registered under the uuid the other tab sent
|
|
@@ -213,7 +221,7 @@ function adoptRegisteredDevice() {
|
|
|
213
221
|
*/
|
|
214
222
|
function prepareDevice() {
|
|
215
223
|
var cached = storage.user_defaults.item('device_info') || {}; // no i18n
|
|
216
|
-
if (
|
|
224
|
+
if (isRegistered(cached)) {
|
|
217
225
|
useStoredUuid();
|
|
218
226
|
checkAndUpdateDeviceInfo();
|
|
219
227
|
return Promise.resolve();
|
|
@@ -278,7 +286,7 @@ function registerDeviceWithApptics() {
|
|
|
278
286
|
if (offline) {
|
|
279
287
|
return offline;
|
|
280
288
|
}
|
|
281
|
-
if (state._ZAP._device_info
|
|
289
|
+
if (isRegistered(state._ZAP._device_info)) {
|
|
282
290
|
return Promise.resolve(state._ZAP._device_info);
|
|
283
291
|
}
|
|
284
292
|
return new Promise((resolve, reject) => {
|
|
@@ -306,8 +314,10 @@ function registerDeviceWithApptics() {
|
|
|
306
314
|
}).then(function (result) {
|
|
307
315
|
settleRegistration(true, result);
|
|
308
316
|
resolve(result);
|
|
309
|
-
// first visit: the ids exist now, so fetch the server settings right away
|
|
310
|
-
|
|
317
|
+
// first visit: the ids exist now, so fetch the server settings right away. A device
|
|
318
|
+
// registered again later (its details or the framework id changed) asks nothing more:
|
|
319
|
+
// start-up has already asked on this page load
|
|
320
|
+
startServerSettingChecks();
|
|
311
321
|
}).catch(function (err) {
|
|
312
322
|
settleRegistration(false, err);
|
|
313
323
|
reject(err);
|
|
@@ -339,6 +349,12 @@ function getUpdatesFromServer() {
|
|
|
339
349
|
return transport.postToApptics('sdkconfig', '', transport.apiHeaders(), label).then(function (result) {
|
|
340
350
|
var data = result.data || {};
|
|
341
351
|
applyServerFlags(data);
|
|
352
|
+
if (state._ZAP._nextConfigCheck === 0) {
|
|
353
|
+
// no next_check_after: getupdates_interval from now, stored so a later page load
|
|
354
|
+
// can tell whether the answer is still current
|
|
355
|
+
state._ZAP._nextConfigCheck = utils.nowMs() + state.getConfig('getupdates_interval'); // no i18n
|
|
356
|
+
state._ZAP._trackinfo.next_check_after = state._ZAP._nextConfigCheck;
|
|
357
|
+
}
|
|
342
358
|
// the visitor's tracking status lives in trackinfo too, and is not the server's to replace
|
|
343
359
|
var saved = storage.storedTrackingStatus();
|
|
344
360
|
storage.user_defaults.add('trackinfo', saved === undefined ? state._ZAP._trackinfo : Object.assign({}, state._ZAP._trackinfo, { tracking_status: saved })); // no i18n
|
|
@@ -374,6 +390,10 @@ function applyServerFlags(data) {
|
|
|
374
390
|
if (data.hasOwnProperty('tracking_enabled')) { // no i18n
|
|
375
391
|
state._ZAP._shouldTrack = data.tracking_enabled !== false;
|
|
376
392
|
}
|
|
393
|
+
else if (data.hasOwnProperty('shouldtrack')) { // no i18n
|
|
394
|
+
// what this SDK saves, read back on the next visit
|
|
395
|
+
state._ZAP._shouldTrack = data.shouldtrack !== false;
|
|
396
|
+
}
|
|
377
397
|
else {
|
|
378
398
|
// the old shape: archived versions and untracked customer groups
|
|
379
399
|
var versionArchived = !data.hasOwnProperty('versionarchivestatus') || utils.isEmpty(data.versionarchivestatus)
|
|
@@ -383,9 +403,14 @@ function applyServerFlags(data) {
|
|
|
383
403
|
state._ZAP._shouldTrack = versionArchived || !customerGroupTracking;
|
|
384
404
|
}
|
|
385
405
|
if (hasModules) {
|
|
386
|
-
//
|
|
387
|
-
state._ZAP._engagementTracking =
|
|
388
|
-
|
|
406
|
+
// each kind of engagement item has its own module, so the server can turn one off alone
|
|
407
|
+
state._ZAP._engagementTracking = {
|
|
408
|
+
event: moduleEnabled(status, constants.SDK_MODULE.EVENT),
|
|
409
|
+
api: moduleEnabled(status, constants.SDK_MODULE.API),
|
|
410
|
+
screen: moduleEnabled(status, constants.SDK_MODULE.SCREEN),
|
|
411
|
+
session: moduleEnabled(status, constants.SDK_MODULE.SESSION),
|
|
412
|
+
};
|
|
413
|
+
state._ZAP._errorTracking = moduleEnabled(status, constants.SDK_MODULE.JS_ERROR);
|
|
389
414
|
state._ZAP._heatmapTracking = moduleEnabled(status, constants.SDK_MODULE.HEATMAP);
|
|
390
415
|
state._ZAP._remoteLoggerEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_LOGGER);
|
|
391
416
|
state._ZAP._feedbackEnabled = moduleEnabled(status, constants.SDK_MODULE.FEEDBACK);
|
|
@@ -394,11 +419,22 @@ function applyServerFlags(data) {
|
|
|
394
419
|
state._ZAP._remoteConfigEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_CONFIG);
|
|
395
420
|
}
|
|
396
421
|
else {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
422
|
+
// this SDK saves one flag per kind under engagement; an earlier build saved one
|
|
423
|
+
// engagementtracking flag for all four
|
|
424
|
+
var saved = (data.engagement && typeof data.engagement === 'object' // no i18n
|
|
425
|
+
? data.engagement : null);
|
|
426
|
+
var kindOn = function (kind) {
|
|
427
|
+
return saved ? saved[kind] !== false : data.engagementtracking !== false;
|
|
428
|
+
};
|
|
429
|
+
state._ZAP._engagementTracking = { event: kindOn('event'), api: kindOn('api'), screen: kindOn('screen'), session: kindOn('session') }; // no i18n
|
|
430
|
+
// the same for the others: read the short key this SDK saves, else the earlier build's
|
|
431
|
+
var flagOn = function (key, oldKey) {
|
|
432
|
+
return (data.hasOwnProperty(key) ? data[key] : data[oldKey]) !== false;
|
|
433
|
+
};
|
|
434
|
+
state._ZAP._errorTracking = flagOn('error', 'errortracking'); // no i18n
|
|
435
|
+
state._ZAP._heatmapTracking = flagOn('heatmap', 'heatmaptracking'); // no i18n
|
|
436
|
+
state._ZAP._remoteLoggerEnabled = flagOn('remotelogger', 'remoteloggerenabled'); // no i18n
|
|
437
|
+
state._ZAP._feedbackEnabled = flagOn('feedback', 'feedbackenabled'); // no i18n
|
|
402
438
|
}
|
|
403
439
|
// screen recording, set up in the console: everything the form needs comes from here
|
|
404
440
|
var quartz = (data.quartz && typeof data.quartz === 'object' ? data.quartz : {});
|
|
@@ -412,16 +448,15 @@ function applyServerFlags(data) {
|
|
|
412
448
|
domain: text(quartz.quartz_domain),
|
|
413
449
|
cdnUrl: text(quartz.quartz_cdn_url),
|
|
414
450
|
};
|
|
415
|
-
// when to ask again, which replaces polling on a fixed interval
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
451
|
+
// when to ask again, which replaces polling on a fixed interval. Kept as a timestamp, so the
|
|
452
|
+
// stored copy reads back the same. An answer without one falls back to getupdates_interval
|
|
453
|
+
state._ZAP._nextConfigCheck = nextCheckTime(data.next_check_after, utils.nowMs());
|
|
419
454
|
state._ZAP._trackinfo = {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
455
|
+
engagement: state._ZAP._engagementTracking,
|
|
456
|
+
error: state._ZAP._errorTracking,
|
|
457
|
+
heatmap: state._ZAP._heatmapTracking,
|
|
458
|
+
remotelogger: state._ZAP._remoteLoggerEnabled,
|
|
459
|
+
feedback: state._ZAP._feedbackEnabled,
|
|
425
460
|
// in the server's own shape, so restoring it on the next visit reads it the same way
|
|
426
461
|
quartz: {
|
|
427
462
|
quartz_enabled: state._ZAP._quartz.enabled,
|
|
@@ -442,31 +477,164 @@ function restoreServerSettings() {
|
|
|
442
477
|
var saved = storage.user_defaults.item('trackinfo'); // no i18n
|
|
443
478
|
if (saved && typeof saved === 'object') { // no i18n
|
|
444
479
|
applyServerFlags(saved);
|
|
480
|
+
// saved by an earlier build under the old names: store it again in this SDK's shape now,
|
|
481
|
+
// rather than at the next getsdkconfig, which may be a while off. The visitor's tracking
|
|
482
|
+
// status is kept as it is
|
|
483
|
+
if (OLD_TRACKINFO_KEYS.some(function (key) { return saved.hasOwnProperty(key); })) {
|
|
484
|
+
storage.user_defaults.add('trackinfo', saved.tracking_status === undefined ? state._ZAP._trackinfo // no i18n
|
|
485
|
+
: Object.assign({}, state._ZAP._trackinfo, { tracking_status: saved.tracking_status }));
|
|
486
|
+
}
|
|
445
487
|
logger.Console.log('restoreServerSettings: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
|
|
446
488
|
}
|
|
447
489
|
}
|
|
490
|
+
/** trackinfo keys an earlier build saved, which this SDK saves under shorter names. */
|
|
491
|
+
var OLD_TRACKINFO_KEYS = ['engagementtracking', 'errortracking', 'heatmaptracking', 'remoteloggerenabled', 'feedbackenabled']; // no i18n
|
|
492
|
+
/**
|
|
493
|
+
* next_check_after as a time: the server's value is milliseconds, either a timestamp or a wait
|
|
494
|
+
* from now. Anything below 1e12 (2001 as a timestamp) can only be a wait.
|
|
495
|
+
*/
|
|
496
|
+
function nextCheckTime(value, now) {
|
|
497
|
+
if (typeof value !== 'number' || !(value > 0)) { // no i18n
|
|
498
|
+
return 0;
|
|
499
|
+
}
|
|
500
|
+
return value >= 1e12 ? value : now + value;
|
|
501
|
+
}
|
|
502
|
+
/** setTimeout's longest wait; a longer one fires straight away. */
|
|
503
|
+
var MAX_TIMEOUT = 2147483647;
|
|
504
|
+
var configTimer = null;
|
|
505
|
+
var configInFlight = false;
|
|
506
|
+
/** Bumped by stopServerSettingChecks(), so an answer to an ask from before a reset schedules nothing. */
|
|
507
|
+
var configRound = 0;
|
|
508
|
+
/** Whether this page load has asked, or set the timer from the saved answer instead. */
|
|
509
|
+
var configAsked = false;
|
|
510
|
+
/** A user registered, or getupdates reported a change, while a getsdkconfig was in flight: asked again when it answers. */
|
|
511
|
+
var configAskAgain = false;
|
|
512
|
+
/** When the next getsdkconfig is due, as a timestamp. 0 until the first answer. */
|
|
513
|
+
var configDueAt = 0;
|
|
448
514
|
/**
|
|
449
|
-
*
|
|
450
|
-
*
|
|
515
|
+
* Asks for the server settings now if the device is registered, then schedules the next ask
|
|
516
|
+
* for the time the answer gave in next_check_after. On a first visit the ids don't exist yet;
|
|
517
|
+
* registerDeviceWithApptics calls this as soon as device/add succeeds.
|
|
451
518
|
*/
|
|
452
519
|
function refreshServerSettings() {
|
|
453
|
-
if (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid'))) { // no i18n
|
|
520
|
+
if (configInFlight || !transport.hasDeviceId() || (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid')))) { // no i18n
|
|
454
521
|
return;
|
|
455
522
|
}
|
|
523
|
+
if (configTimer !== null) {
|
|
524
|
+
clearTimeout(configTimer);
|
|
525
|
+
configTimer = null;
|
|
526
|
+
}
|
|
527
|
+
configInFlight = true;
|
|
528
|
+
configAsked = true;
|
|
529
|
+
markTabAsked();
|
|
530
|
+
var round = configRound;
|
|
531
|
+
var askedAt = utils.nowMs();
|
|
456
532
|
getUpdatesFromServer().catch(function (err) {
|
|
457
533
|
logger.Console.log(err);
|
|
534
|
+
}).then(function () {
|
|
535
|
+
if (round !== configRound) {
|
|
536
|
+
// stopServerSettingChecks() ran while this was in flight
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
configInFlight = false;
|
|
540
|
+
if (configAskAgain) {
|
|
541
|
+
configAskAgain = false;
|
|
542
|
+
refreshServerSettings();
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
// an answer without a usable next_check_after, or a failed call, waits getupdates_interval
|
|
546
|
+
configDueAt = state._ZAP._nextConfigCheck > utils.nowMs() ? state._ZAP._nextConfigCheck : askedAt + state.getConfig('getupdates_interval'); // no i18n
|
|
547
|
+
scheduleServerSettingCheck();
|
|
458
548
|
});
|
|
459
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* getsdkconfig now, not at the next check: setUser registered a user, or getupdates reported a
|
|
552
|
+
* module changed.
|
|
553
|
+
*/
|
|
554
|
+
function askServerSettings() {
|
|
555
|
+
if (configInFlight) {
|
|
556
|
+
// the one in flight went out before the change: ask again once it answers
|
|
557
|
+
configAskAgain = true;
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
refreshServerSettings();
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Start-up's getsdkconfig. Asked on a first visit and on the first page load in each tab; any
|
|
564
|
+
* other load (a reload, a link to another page of the site, back/forward) uses the saved answer
|
|
565
|
+
* and waits for its next_check_after, unless that has already passed. Nothing happens if one has
|
|
566
|
+
* gone out already on this load (after device/add).
|
|
567
|
+
*/
|
|
568
|
+
function startServerSettingChecks() {
|
|
569
|
+
if (configAsked) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (tabHasAsked() && state._ZAP._nextConfigCheck > utils.nowMs()) {
|
|
573
|
+
logger.Console.log('getsdkconfig: the saved answer is still current'); // no i18n
|
|
574
|
+
configAsked = true;
|
|
575
|
+
configDueAt = state._ZAP._nextConfigCheck;
|
|
576
|
+
scheduleServerSettingCheck();
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
refreshServerSettings();
|
|
580
|
+
}
|
|
581
|
+
/** sessionStorage: per tab, and kept across a reload and the site's own links. */
|
|
582
|
+
var TAB_ASKED_KEY = '_apptics_sdkconfig_asked'; // no i18n
|
|
583
|
+
/** Whether getsdkconfig has gone out in this tab before, on an earlier page load. */
|
|
584
|
+
function tabHasAsked() {
|
|
585
|
+
try {
|
|
586
|
+
return sessionStorage.getItem(TAB_ASKED_KEY) === '1'; // no i18n
|
|
587
|
+
}
|
|
588
|
+
catch (e) {
|
|
589
|
+
return false; // no sessionStorage (private mode, sandboxed frame): every load asks
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function markTabAsked() {
|
|
593
|
+
try {
|
|
594
|
+
sessionStorage.setItem(TAB_ASKED_KEY, '1'); // no i18n
|
|
595
|
+
}
|
|
596
|
+
catch (e) {
|
|
597
|
+
// as above: without it, every load asks
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/** Sets the timer for the next getsdkconfig, at configDueAt. */
|
|
601
|
+
function scheduleServerSettingCheck() {
|
|
602
|
+
if (configTimer !== null) {
|
|
603
|
+
clearTimeout(configTimer);
|
|
604
|
+
}
|
|
605
|
+
var wait = Math.min(Math.max(configDueAt - utils.nowMs(), 0), MAX_TIMEOUT);
|
|
606
|
+
logger.Console.log('getsdkconfig: next check in ' + wait + ' ms'); // no i18n
|
|
607
|
+
configTimer = setTimeout(function () {
|
|
608
|
+
configTimer = null;
|
|
609
|
+
refreshServerSettings();
|
|
610
|
+
}, wait);
|
|
611
|
+
}
|
|
612
|
+
/** reset(): no more checks, and the next init() asks again straight away. */
|
|
613
|
+
function stopServerSettingChecks() {
|
|
614
|
+
if (configTimer !== null) {
|
|
615
|
+
clearTimeout(configTimer);
|
|
616
|
+
}
|
|
617
|
+
configTimer = null;
|
|
618
|
+
configRound++;
|
|
619
|
+
configInFlight = false;
|
|
620
|
+
configAsked = false;
|
|
621
|
+
configAskAgain = false;
|
|
622
|
+
configDueAt = 0;
|
|
623
|
+
}
|
|
460
624
|
hooks.hooks.updateDevice = updateDeviceWithApptics;
|
|
461
625
|
hooks.hooks.refreshDeviceInfo = checkAndUpdateDeviceInfo;
|
|
626
|
+
hooks.hooks.askServerSettings = askServerSettings;
|
|
462
627
|
|
|
463
628
|
exports.addOrUpdateDeviceID = addOrUpdateDeviceID;
|
|
464
629
|
exports.checkAndUpdateDeviceInfo = checkAndUpdateDeviceInfo;
|
|
465
630
|
exports.deviceReady = deviceReady;
|
|
466
631
|
exports.getUpdatesFromServer = getUpdatesFromServer;
|
|
467
632
|
exports.needsDevice = needsDevice;
|
|
633
|
+
exports.nextCheckTime = nextCheckTime;
|
|
468
634
|
exports.prepareDevice = prepareDevice;
|
|
469
635
|
exports.refreshServerSettings = refreshServerSettings;
|
|
470
636
|
exports.registerDeviceWithApptics = registerDeviceWithApptics;
|
|
471
637
|
exports.restoreServerSettings = restoreServerSettings;
|
|
638
|
+
exports.startServerSettingChecks = startServerSettingChecks;
|
|
639
|
+
exports.stopServerSettingChecks = stopServerSettingChecks;
|
|
472
640
|
exports.updateDeviceWithApptics = updateDeviceWithApptics;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
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');
|
|
@@ -15,8 +15,7 @@ var transport = require('../core/transport.js');
|
|
|
15
15
|
* Sends pending engagements to the Apptics API.
|
|
16
16
|
*/
|
|
17
17
|
function sendEngagementData(forget) {
|
|
18
|
-
|
|
19
|
-
if (env.is_apptics_disabled() || !consent.shouldTrackOrSendData() || !state._ZAP._engagementTracking) {
|
|
18
|
+
if (env.is_apptics_disabled() || !consent.shouldTrackOrSendData()) {
|
|
20
19
|
return Promise.resolve();
|
|
21
20
|
}
|
|
22
21
|
if (navigator.onLine == false) {
|
|
@@ -39,6 +38,11 @@ function sendEngagementData(forget) {
|
|
|
39
38
|
var _payload = {};
|
|
40
39
|
var sent = [];
|
|
41
40
|
utils.cloneObject(engagement_items).forEach(function (item) {
|
|
41
|
+
// a kind the server has turned off (getsdkconfig's event, api, screen or session
|
|
42
|
+
// module) stays queued until it is back on or the item expires
|
|
43
|
+
if (state._ZAP._engagementTracking[item.type] === false) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
42
46
|
const itemWithId = { ...item };
|
|
43
47
|
if (item.type == constants.AP_CONSTANTS.EVENT) {
|
|
44
48
|
eventObj.push(item);
|
|
@@ -35,10 +35,27 @@ export declare function dropPendingSession(): boolean;
|
|
|
35
35
|
/** Whether a session is waiting to be written. Used by tests and by the page-closing path. */
|
|
36
36
|
export declare function hasPendingSession(): boolean;
|
|
37
37
|
export declare function startNewSession(time: number): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* The tab went inactive. That moment becomes the session's lastactivetime, the time it ends at if
|
|
40
|
+
* the user does not come back within inactiveduration. If they do not, the session is ended at it
|
|
41
|
+
* then, and `onEnded` sends it, rather than staying open until they return or the tab closes.
|
|
42
|
+
* Going inactive twice (a window blur, then the tab hidden) keeps the first moment.
|
|
43
|
+
*/
|
|
44
|
+
export declare function markTabInactive(onEnded: () => void): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* The tab is active again. The caller then settles the session (checkAndUpdateSession): back
|
|
47
|
+
* within inactiveduration it continues, later it has ended and a new one starts.
|
|
48
|
+
*/
|
|
49
|
+
export declare function markTabActive(): void;
|
|
50
|
+
/** reset(): no timer left running, and the tab counts as active. */
|
|
51
|
+
export declare function resetInactivity(): void;
|
|
38
52
|
/**
|
|
39
53
|
* This tab is closing, or the page is being replaced. The session ends here when this is the last
|
|
40
54
|
* tab using it, so it can be sent; while another tab of the site is still on it, this tab only
|
|
41
55
|
* leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
|
|
56
|
+
*
|
|
57
|
+
* A tab closed while inactive ends the session when the user left (its lastactivetime), not when
|
|
58
|
+
* the tab closed: the time it sat hidden or in the background is not session time.
|
|
42
59
|
*/
|
|
43
60
|
export declare function endCurrentSession(): Promise<void>;
|
|
44
61
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
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');
|
|
@@ -64,7 +64,7 @@ function closeScreensOf(session, time) {
|
|
|
64
64
|
* @param sessionData A set of properties belonging to the session.
|
|
65
65
|
*/
|
|
66
66
|
function trackSession(sessionData) {
|
|
67
|
-
if (!consent.canTrackEngagement('trackSession')) { // no i18n
|
|
67
|
+
if (!consent.canTrackEngagement('trackSession', constants.AP_CONSTANTS.SESSION)) { // no i18n
|
|
68
68
|
return Promise.resolve();
|
|
69
69
|
}
|
|
70
70
|
if (sessionData && typeof sessionData !== 'object') { // no i18n
|
|
@@ -320,26 +320,114 @@ function commitNow(session) {
|
|
|
320
320
|
}
|
|
321
321
|
return trackSession(session);
|
|
322
322
|
}
|
|
323
|
+
/** Whether this tab is inactive: hidden, minimised, or its window has lost focus. */
|
|
324
|
+
var inactive = false;
|
|
325
|
+
var inactivityTimer = null;
|
|
326
|
+
/**
|
|
327
|
+
* The tab went inactive. That moment becomes the session's lastactivetime, the time it ends at if
|
|
328
|
+
* the user does not come back within inactiveduration. If they do not, the session is ended at it
|
|
329
|
+
* then, and `onEnded` sends it, rather than staying open until they return or the tab closes.
|
|
330
|
+
* Going inactive twice (a window blur, then the tab hidden) keeps the first moment.
|
|
331
|
+
*/
|
|
332
|
+
function markTabInactive(onEnded) {
|
|
333
|
+
var first = !inactive;
|
|
334
|
+
inactive = true;
|
|
335
|
+
if (inactivityTimer !== null) {
|
|
336
|
+
clearTimeout(inactivityTimer);
|
|
337
|
+
}
|
|
338
|
+
inactivityTimer = setTimeout(function () {
|
|
339
|
+
inactivityTimer = null;
|
|
340
|
+
endInactiveSession().then(function (ended) {
|
|
341
|
+
if (ended) {
|
|
342
|
+
onEnded();
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}, state._ZAP.inactiveduration * 60 * 1000 + 1000);
|
|
346
|
+
return first ? updateLastActiveTimeOfSession() : Promise.resolve();
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* The tab is active again. The caller then settles the session (checkAndUpdateSession): back
|
|
350
|
+
* within inactiveduration it continues, later it has ended and a new one starts.
|
|
351
|
+
*/
|
|
352
|
+
function markTabActive() {
|
|
353
|
+
inactive = false;
|
|
354
|
+
if (inactivityTimer !== null) {
|
|
355
|
+
clearTimeout(inactivityTimer);
|
|
356
|
+
inactivityTimer = null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** reset(): no timer left running, and the tab counts as active. */
|
|
360
|
+
function resetInactivity() {
|
|
361
|
+
markTabActive();
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* inactiveduration has passed with the tab inactive: the session ends at its lastactivetime, when
|
|
365
|
+
* the user left, so the time away is not counted. Resolves true when it ended one. While another
|
|
366
|
+
* tab of the site is still on the session, this tab only leaves it; one whose lastactivetime is
|
|
367
|
+
* recent is in use elsewhere and is left alone.
|
|
368
|
+
*/
|
|
369
|
+
function endInactiveSession() {
|
|
370
|
+
if (!state._ZAP._token || env.is_apptics_disabled()) {
|
|
371
|
+
return Promise.resolve(false);
|
|
372
|
+
}
|
|
373
|
+
var ended = false;
|
|
374
|
+
return withSessionLock(function () {
|
|
375
|
+
return currentSession().then(function (_currentSession) {
|
|
376
|
+
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
var lastActive = Number(_currentSession.lastactivetime) || utils.nowMs();
|
|
380
|
+
if (utils.nowMs() - lastActive < state._ZAP.inactiveduration * 60 * 1000) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
var others = tabsOf(_currentSession).filter(function (tab) { return tab !== tabId(); });
|
|
384
|
+
if (others.length > 0) {
|
|
385
|
+
var left = Object.assign({}, _currentSession, { tabs: others });
|
|
386
|
+
state._ZAP._current_session = left;
|
|
387
|
+
return storage.engagement_transactions.update(left);
|
|
388
|
+
}
|
|
389
|
+
var endAt = Math.max(lastActive, Number(_currentSession.starttime) || lastActive);
|
|
390
|
+
var closed = Object.assign({}, _currentSession, { endtime: endAt, tabs: [] });
|
|
391
|
+
state._ZAP._current_session = closed;
|
|
392
|
+
ended = true;
|
|
393
|
+
logger.Console.log('session: inactive for ' + state._ZAP.inactiveduration + ' minutes, so it ends when the user left, ' + endAt); // no i18n
|
|
394
|
+
return storage.engagement_transactions.update(closed).then(function () {
|
|
395
|
+
return closeScreensOf(closed, endAt);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
}).then(function () {
|
|
399
|
+
return ended;
|
|
400
|
+
});
|
|
401
|
+
}
|
|
323
402
|
/**
|
|
324
403
|
* This tab is closing, or the page is being replaced. The session ends here when this is the last
|
|
325
404
|
* tab using it, so it can be sent; while another tab of the site is still on it, this tab only
|
|
326
405
|
* leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
|
|
406
|
+
*
|
|
407
|
+
* A tab closed while inactive ends the session when the user left (its lastactivetime), not when
|
|
408
|
+
* the tab closed: the time it sat hidden or in the background is not session time.
|
|
327
409
|
*/
|
|
328
410
|
function endCurrentSession() {
|
|
329
411
|
var time = utils.nowMs();
|
|
412
|
+
var wasInactive = inactive;
|
|
413
|
+
markTabActive();
|
|
330
414
|
// nothing was tracked and the minimum was never reached: this visit is not reported at all
|
|
331
415
|
if (dropPendingSession()) {
|
|
332
416
|
return Promise.resolve();
|
|
333
417
|
}
|
|
334
418
|
return withSessionLock(function () {
|
|
335
|
-
return closeOrLeaveSession(time);
|
|
419
|
+
return closeOrLeaveSession(time, wasInactive);
|
|
336
420
|
});
|
|
337
421
|
}
|
|
338
|
-
function closeOrLeaveSession(
|
|
422
|
+
function closeOrLeaveSession(now, wasInactive) {
|
|
339
423
|
return currentSession().then(function (_currentSession) {
|
|
340
424
|
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
341
425
|
return;
|
|
342
426
|
}
|
|
427
|
+
var lastActive = Number(_currentSession.lastactivetime) || 0;
|
|
428
|
+
var time = wasInactive && lastActive > 0
|
|
429
|
+
? Math.max(Math.min(lastActive, now), Number(_currentSession.starttime) || 0)
|
|
430
|
+
: now;
|
|
343
431
|
var others = tabsOf(_currentSession).filter(function (tab) { return tab !== tabId(); });
|
|
344
432
|
if (others.length > 0) {
|
|
345
433
|
var left = Object.assign({}, _currentSession, { tabs: others, lastactivetime: time });
|
|
@@ -456,6 +544,9 @@ exports.currentSession = currentSession;
|
|
|
456
544
|
exports.dropPendingSession = dropPendingSession;
|
|
457
545
|
exports.endCurrentSession = endCurrentSession;
|
|
458
546
|
exports.holdTabLock = holdTabLock;
|
|
547
|
+
exports.markTabActive = markTabActive;
|
|
548
|
+
exports.markTabInactive = markTabInactive;
|
|
549
|
+
exports.resetInactivity = resetInactivity;
|
|
459
550
|
exports.startNewSession = startNewSession;
|
|
460
551
|
exports.tabId = tabId;
|
|
461
552
|
exports.trackSession = trackSession;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Dict, Properties } from '../types.js';
|
|
2
2
|
import { type DefinedEvent } from './defined-events.js';
|
|
3
3
|
/**
|
|
4
|
-
* Records an event: `trackEvent(name, group
|
|
4
|
+
* Records an event: `trackEvent(name, group, properties?)`, or a defined event with
|
|
5
5
|
* `trackEvent(DefinedEvents.AP_USER_LOGIN, properties?)`, which brings its own group.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The group is required. An event without one, or with an invalid name or group, is not logged,
|
|
8
|
+
* and the console says why (debug or not): filing it under a group the app never named would hide
|
|
9
|
+
* the mistake.
|
|
10
10
|
*/
|
|
11
11
|
export declare function trackEvent(eventName: string | DefinedEvent, groupName?: string | Properties, jsonObj?: Properties): void;
|
|
12
12
|
/** The app's own screen view. Automatic screen tracking calls recordScreen() instead. */
|