@zoho_apptics/apptics-js-sdk 1.0.2 → 2.0.0-beta.1
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 +193 -0
- package/LICENSE +13 -0
- package/README.md +846 -11
- package/dist/apptics.esm.min.js +3 -0
- package/dist/apptics.esm.min.js.map +1 -0
- package/dist/apptics.min.js +3 -0
- package/dist/apptics.min.js.map +1 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +8 -0
- package/dist/cjs/auto-init.js +32 -0
- package/dist/cjs/core/attempts.d.ts +28 -0
- package/dist/cjs/core/attempts.js +113 -0
- package/dist/cjs/core/batch.d.ts +32 -0
- package/dist/cjs/core/batch.js +116 -0
- package/dist/cjs/core/callsite.d.ts +34 -0
- package/dist/cjs/core/callsite.js +115 -0
- package/dist/cjs/core/consent.d.ts +27 -0
- package/dist/cjs/core/consent.js +132 -0
- package/dist/cjs/core/constants.d.ts +237 -0
- package/dist/cjs/core/constants.js +315 -0
- package/dist/cjs/core/db.d.ts +48 -0
- package/dist/cjs/core/db.js +467 -0
- package/dist/cjs/core/debug.d.ts +22 -0
- package/dist/cjs/core/debug.js +88 -0
- package/dist/cjs/core/env.d.ts +28 -0
- package/dist/cjs/core/env.js +267 -0
- package/dist/cjs/core/framework-id.d.ts +1 -0
- package/dist/cjs/core/framework-id.js +9 -0
- package/dist/cjs/core/guard.d.ts +11 -0
- package/dist/cjs/core/guard.js +86 -0
- package/dist/cjs/core/hooks.d.ts +39 -0
- package/dist/cjs/core/hooks.js +22 -0
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +15 -0
- package/dist/cjs/core/logger.js +50 -0
- package/dist/cjs/core/state.d.ts +70 -0
- package/dist/cjs/core/state.js +78 -0
- package/dist/cjs/core/storage.d.ts +61 -0
- package/dist/cjs/core/storage.js +327 -0
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +85 -0
- package/dist/cjs/core/transport.js +456 -0
- package/dist/cjs/core/utils.d.ts +26 -0
- package/dist/cjs/core/utils.js +175 -0
- package/dist/cjs/core/validate.d.ts +18 -0
- package/dist/cjs/core/validate.js +233 -0
- package/dist/cjs/device/appversion.d.ts +15 -0
- package/dist/cjs/device/appversion.js +170 -0
- package/dist/cjs/device/index.d.ts +34 -0
- package/dist/cjs/device/index.js +472 -0
- package/dist/cjs/engagement/api-rules.d.ts +49 -0
- package/dist/cjs/engagement/api-rules.js +206 -0
- package/dist/cjs/engagement/auto-api.d.ts +7 -0
- package/dist/cjs/engagement/auto-api.js +200 -0
- package/dist/cjs/engagement/auto-screen.d.ts +18 -0
- package/dist/cjs/engagement/auto-screen.js +305 -0
- package/dist/cjs/engagement/defined-events.d.ts +23 -0
- package/dist/cjs/engagement/defined-events.js +58 -0
- package/dist/cjs/engagement/send.d.ts +6 -0
- package/dist/cjs/engagement/send.js +104 -0
- package/dist/cjs/engagement/session.d.ts +51 -0
- package/dist/cjs/engagement/session.js +463 -0
- package/dist/cjs/engagement/track.d.ts +56 -0
- package/dist/cjs/engagement/track.js +375 -0
- package/dist/cjs/errors/send.d.ts +6 -0
- package/dist/cjs/errors/send.js +73 -0
- package/dist/cjs/errors/track.d.ts +4 -0
- package/dist/cjs/errors/track.js +167 -0
- package/dist/cjs/feedback/controls.d.ts +37 -0
- package/dist/cjs/feedback/controls.js +316 -0
- package/dist/cjs/feedback/index.d.ts +9 -0
- package/dist/cjs/feedback/index.js +56 -0
- package/dist/cjs/feedback/logs.d.ts +45 -0
- package/dist/cjs/feedback/logs.js +241 -0
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +28 -0
- package/dist/cjs/feedback/send.js +203 -0
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +2 -0
- package/dist/cjs/feedback/template.js +162 -0
- package/dist/cjs/feedback/ui.d.ts +63 -0
- package/dist/cjs/feedback/ui.js +709 -0
- package/dist/cjs/heatmap/send.d.ts +13 -0
- package/dist/cjs/heatmap/send.js +82 -0
- package/dist/cjs/heatmap/track.d.ts +4 -0
- package/dist/cjs/heatmap/track.js +448 -0
- package/dist/cjs/index.d.ts +22 -0
- package/dist/cjs/index.js +162 -0
- package/dist/cjs/lifecycle.d.ts +21 -0
- package/dist/cjs/lifecycle.js +734 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +10 -0
- package/dist/cjs/remoteconfig/custom.js +58 -0
- package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
- package/dist/cjs/remoteconfig/evaluate.js +184 -0
- package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
- package/dist/cjs/remoteconfig/fetch.js +200 -0
- package/dist/cjs/remoteconfig/index.d.ts +15 -0
- package/dist/cjs/remoteconfig/index.js +116 -0
- package/dist/cjs/remoteconfig/store.d.ts +18 -0
- package/dist/cjs/remoteconfig/store.js +98 -0
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/cjs/remotelog/gate.d.ts +2 -0
- package/dist/cjs/remotelog/gate.js +28 -0
- package/dist/cjs/remotelog/index.d.ts +8 -0
- package/dist/cjs/remotelog/logger.d.ts +22 -0
- package/dist/cjs/remotelog/logger.js +111 -0
- package/dist/cjs/remotelog/send.d.ts +13 -0
- package/dist/cjs/remotelog/send.js +153 -0
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.d.ts +8 -0
- package/dist/cjs/scheduler.js +78 -0
- package/dist/cjs/types.d.ts +385 -0
- package/dist/cjs/user/index.d.ts +33 -0
- package/dist/cjs/user/index.js +183 -0
- package/dist/esm/auto-init.d.ts +8 -0
- package/dist/esm/auto-init.js +29 -0
- package/dist/esm/core/attempts.d.ts +28 -0
- package/dist/esm/core/attempts.js +106 -0
- package/dist/esm/core/batch.d.ts +32 -0
- package/dist/esm/core/batch.js +109 -0
- package/dist/esm/core/callsite.d.ts +34 -0
- package/dist/esm/core/callsite.js +110 -0
- package/dist/esm/core/consent.d.ts +27 -0
- package/dist/esm/core/consent.js +122 -0
- package/dist/esm/core/constants.d.ts +237 -0
- package/dist/esm/core/constants.js +292 -0
- package/dist/esm/core/db.d.ts +48 -0
- package/dist/esm/core/db.js +453 -0
- package/dist/esm/core/debug.d.ts +22 -0
- package/dist/esm/core/debug.js +81 -0
- package/dist/esm/core/env.d.ts +28 -0
- package/dist/esm/core/env.js +251 -0
- package/dist/esm/core/framework-id.d.ts +1 -0
- package/dist/esm/core/framework-id.js +7 -0
- package/dist/esm/core/guard.d.ts +11 -0
- package/dist/esm/core/guard.js +83 -0
- package/dist/esm/core/hooks.d.ts +39 -0
- package/dist/esm/core/hooks.js +20 -0
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +15 -0
- package/dist/esm/core/logger.js +46 -0
- package/dist/esm/core/state.d.ts +70 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/storage.d.ts +61 -0
- package/dist/esm/core/storage.js +314 -0
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +85 -0
- package/dist/esm/core/transport.js +439 -0
- package/dist/esm/core/utils.d.ts +26 -0
- package/dist/esm/core/utils.js +161 -0
- package/dist/esm/core/validate.d.ts +18 -0
- package/dist/esm/core/validate.js +226 -0
- package/dist/esm/device/appversion.d.ts +15 -0
- package/dist/esm/device/appversion.js +164 -0
- package/dist/esm/device/index.d.ts +34 -0
- package/dist/esm/device/index.js +461 -0
- package/dist/esm/engagement/api-rules.d.ts +49 -0
- package/dist/esm/engagement/api-rules.js +199 -0
- package/dist/esm/engagement/auto-api.d.ts +7 -0
- package/dist/esm/engagement/auto-api.js +198 -0
- package/dist/esm/engagement/auto-screen.d.ts +18 -0
- package/dist/esm/engagement/auto-screen.js +301 -0
- package/dist/esm/engagement/defined-events.d.ts +23 -0
- package/dist/esm/engagement/defined-events.js +55 -0
- package/dist/esm/engagement/send.d.ts +6 -0
- package/dist/esm/engagement/send.js +101 -0
- package/dist/esm/engagement/session.d.ts +51 -0
- package/dist/esm/engagement/session.js +448 -0
- package/dist/esm/engagement/track.d.ts +56 -0
- package/dist/esm/engagement/track.js +365 -0
- package/dist/esm/errors/send.d.ts +6 -0
- package/dist/esm/errors/send.js +70 -0
- package/dist/esm/errors/track.d.ts +4 -0
- package/dist/esm/errors/track.js +163 -0
- package/dist/esm/feedback/controls.d.ts +37 -0
- package/dist/esm/feedback/controls.js +309 -0
- package/dist/esm/feedback/index.d.ts +9 -0
- package/dist/esm/feedback/index.js +51 -0
- package/dist/esm/feedback/logs.d.ts +45 -0
- package/dist/esm/feedback/logs.js +227 -0
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +28 -0
- package/dist/esm/feedback/send.js +198 -0
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +2 -0
- package/dist/esm/feedback/template.js +159 -0
- package/dist/esm/feedback/ui.d.ts +63 -0
- package/dist/esm/feedback/ui.js +703 -0
- package/dist/esm/heatmap/send.d.ts +13 -0
- package/dist/esm/heatmap/send.js +79 -0
- package/dist/esm/heatmap/track.d.ts +4 -0
- package/dist/esm/heatmap/track.js +445 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.js +150 -0
- package/dist/esm/lifecycle.d.ts +21 -0
- package/dist/esm/lifecycle.js +729 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +10 -0
- package/dist/esm/remoteconfig/custom.js +54 -0
- package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
- package/dist/esm/remoteconfig/evaluate.js +179 -0
- package/dist/esm/remoteconfig/fetch.d.ts +24 -0
- package/dist/esm/remoteconfig/fetch.js +196 -0
- package/dist/esm/remoteconfig/index.d.ts +15 -0
- package/dist/esm/remoteconfig/index.js +111 -0
- package/dist/esm/remoteconfig/store.d.ts +18 -0
- package/dist/esm/remoteconfig/store.js +91 -0
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/esm/remotelog/gate.d.ts +2 -0
- package/dist/esm/remotelog/gate.js +26 -0
- package/dist/esm/remotelog/index.d.ts +8 -0
- package/dist/esm/remotelog/logger.d.ts +22 -0
- package/dist/esm/remotelog/logger.js +108 -0
- package/dist/esm/remotelog/send.d.ts +13 -0
- package/dist/esm/remotelog/send.js +149 -0
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.d.ts +8 -0
- package/dist/esm/scheduler.js +74 -0
- package/dist/esm/types.d.ts +385 -0
- package/dist/esm/user/index.d.ts +33 -0
- package/dist/esm/user/index.js +176 -0
- package/dist/feedback.esm.min.js +3 -0
- package/dist/feedback.esm.min.js.map +1 -0
- package/package.json +122 -7
- package/src/appticssdk.js +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Dict, ServerResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sends queued heatmap records to heatmap/add.
|
|
4
|
+
*
|
|
5
|
+
* Shaped like the error and engagement senders, because the queue behaves the same way: records
|
|
6
|
+
* are stored first and sent on the flush timer, a failed send leaves them for the next one, and
|
|
7
|
+
* `forget` is used on page hide so the last batch survives the page going away.
|
|
8
|
+
*
|
|
9
|
+
* Gated on the server's own heatmap flag (getsdkconfig module 18), not on engagement: an app may
|
|
10
|
+
* want events while declining to have every click's coordinates recorded.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sendHeatmapData(forget?: boolean): Promise<void>;
|
|
13
|
+
export declare function requestToSendHeatmaps(_payload: Dict, forget?: boolean): Promise<ServerResult>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { _ZAP } from '../core/state.js';
|
|
3
|
+
import { Console, warnAlways } from '../core/logger.js';
|
|
4
|
+
import { cloneObject } from '../core/utils.js';
|
|
5
|
+
import { heatmap_transactions } from '../core/storage.js';
|
|
6
|
+
import { is_apptics_disabled } from '../core/env.js';
|
|
7
|
+
import { shouldTrackOrSendData } from '../core/consent.js';
|
|
8
|
+
import { defer, clearDefer, withSendLock, freshItems, awaitUnlessClosing, sendBatch } from '../core/batch.js';
|
|
9
|
+
import { isPermanentRefusal } from '../core/transport.js';
|
|
10
|
+
import { hooks } from '../core/hooks.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sends queued heatmap records to heatmap/add.
|
|
14
|
+
*
|
|
15
|
+
* Shaped like the error and engagement senders, because the queue behaves the same way: records
|
|
16
|
+
* are stored first and sent on the flush timer, a failed send leaves them for the next one, and
|
|
17
|
+
* `forget` is used on page hide so the last batch survives the page going away.
|
|
18
|
+
*
|
|
19
|
+
* Gated on the server's own heatmap flag (getsdkconfig module 18), not on engagement: an app may
|
|
20
|
+
* want events while declining to have every click's coordinates recorded.
|
|
21
|
+
*/
|
|
22
|
+
function sendHeatmapData(forget) {
|
|
23
|
+
if (is_apptics_disabled() || !shouldTrackOrSendData() || !_ZAP._heatmapTracking) {
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
}
|
|
26
|
+
if (navigator.onLine == false) {
|
|
27
|
+
Console.warn('sendHeatmapData: No Network'); // no i18n
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
if (_ZAP._sending) {
|
|
31
|
+
defer(sendHeatmapData);
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
_ZAP._sending = true;
|
|
35
|
+
clearDefer();
|
|
36
|
+
return withSendLock('heatmaps', function () {
|
|
37
|
+
return freshItems(heatmap_transactions, function () { return true; },
|
|
38
|
+
// aged by when the record happened, not when its session began: a tab left open for
|
|
39
|
+
// days would otherwise make every new record look older than the retention window
|
|
40
|
+
// and have it dropped unsent
|
|
41
|
+
function (item) { return (item.triggered_time || item.session_start_time); }).then(function (items) {
|
|
42
|
+
if (items.length == 0) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
var data = [];
|
|
46
|
+
var sent = [];
|
|
47
|
+
cloneObject(items).forEach(function (item) {
|
|
48
|
+
sent.push({ ...item });
|
|
49
|
+
// `id` is the queue's own key, not something the endpoint declares
|
|
50
|
+
delete item.id;
|
|
51
|
+
data.push(item);
|
|
52
|
+
});
|
|
53
|
+
return awaitUnlessClosing(requestToSendHeatmaps({ data: data }, forget).then(function () {
|
|
54
|
+
return heatmap_transactions.remove(sent);
|
|
55
|
+
}, function (err) {
|
|
56
|
+
// the server will never accept this batch. Keeping it would block every later
|
|
57
|
+
// batch behind it for good, so it is dropped and said out loud
|
|
58
|
+
if (isPermanentRefusal(err)) {
|
|
59
|
+
warnAlways('apptics: the server refused these heatmap records, so they were dropped: ' + String(err && err.message)); // no i18n
|
|
60
|
+
return heatmap_transactions.remove(sent);
|
|
61
|
+
}
|
|
62
|
+
return Promise.reject(err);
|
|
63
|
+
}), forget, 'sendHeatmapData'); // no i18n
|
|
64
|
+
});
|
|
65
|
+
}).catch(function (err) {
|
|
66
|
+
Console.log('sendHeatmapData:' + err); // no i18n
|
|
67
|
+
}).then(function () {
|
|
68
|
+
_ZAP._sending = false;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function requestToSendHeatmaps(_payload, forget) {
|
|
72
|
+
var label = 'requestToSendHeatmaps'; // no i18n
|
|
73
|
+
return sendBatch('heatmap', label, _payload, forget); // no i18n
|
|
74
|
+
}
|
|
75
|
+
// the scheduler flushes through this, so importing the heatmap entry is what puts heatmap
|
|
76
|
+
// records on the flush timer at all
|
|
77
|
+
hooks.sendHeatmaps = sendHeatmapData;
|
|
78
|
+
|
|
79
|
+
export { requestToSendHeatmaps, sendHeatmapData };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Turns capture on or off. Listeners are passive, so they never delay a click or a scroll. */
|
|
2
|
+
export declare function trackHeatmap(status?: boolean): void;
|
|
3
|
+
/** Forgets what this visit recorded. reset() calls it; the cap starts again from zero. */
|
|
4
|
+
export declare function resetHeatmapSession(): void;
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { AP_LOG_MODULE, AP_CONFIG, AP_TRACKING_STATUS } from '../core/constants.js';
|
|
3
|
+
import { getConfig, _ZAP } from '../core/state.js';
|
|
4
|
+
import { Console, logFailure } from '../core/logger.js';
|
|
5
|
+
import { nowMs, isEmpty, getNewUUID } from '../core/utils.js';
|
|
6
|
+
import { heatmap_transactions } from '../core/storage.js';
|
|
7
|
+
import { commitPendingSession } from '../engagement/session.js';
|
|
8
|
+
import { getConnectionType, getNetworkStatus, getOrientation } from '../core/env.js';
|
|
9
|
+
import { canTrackHeatmap, getTrackingStatus } from '../core/consent.js';
|
|
10
|
+
import { screenForRecord } from '../engagement/track.js';
|
|
11
|
+
import { hooks } from '../core/hooks.js';
|
|
12
|
+
import { noteDebugActivity } from '../core/debug.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Where people click and how far they scroll.
|
|
16
|
+
*
|
|
17
|
+
* Two shapes of record, and they behave very differently. A click is one discrete thing and is
|
|
18
|
+
* recorded as it happens. Scrolling fires continuously — hundreds of events for one flick of a
|
|
19
|
+
* wheel — so it is coalesced into one record per gesture: a burst of scrolling that ends when the
|
|
20
|
+
* page has been still for a moment. Recording every scroll event would swamp the queue, the
|
|
21
|
+
* batch and the user's bandwidth, and would say nothing a single record per gesture does not.
|
|
22
|
+
*/
|
|
23
|
+
/** How long the page must be still before a scroll burst counts as one finished gesture. */
|
|
24
|
+
const SCROLL_IDLE_MS = 150;
|
|
25
|
+
/**
|
|
26
|
+
* How much of the viewport, each way, an element must cover for its scrolling to be the page's.
|
|
27
|
+
* An app whose layout scrolls a content pane instead of the window (most dashboards do) still
|
|
28
|
+
* gets a scroll map; a dropdown list or a small table scrolling on its own is not the page moving.
|
|
29
|
+
*/
|
|
30
|
+
const PAGE_SCROLLER_SHARE = 0.5;
|
|
31
|
+
/** Clicks closer than this in time and space count as one angry repetition. */
|
|
32
|
+
const RAGE_WINDOW_MS = 1000;
|
|
33
|
+
const RAGE_RADIUS_PX = 30;
|
|
34
|
+
const RAGE_COUNT = 3;
|
|
35
|
+
/** How long a click has to change something before it is called dead. */
|
|
36
|
+
const DEAD_CLICK_MS = 1500;
|
|
37
|
+
/** Longest element label we keep; enough to identify a control, short enough not to be content. */
|
|
38
|
+
const LABEL_MAX = 80;
|
|
39
|
+
/** heatMapObjectSDK caps screen, url_path and element_label at 500 characters. */
|
|
40
|
+
const FIELD_MAX = 500;
|
|
41
|
+
var installed = false;
|
|
42
|
+
var recorded = 0;
|
|
43
|
+
/** Recent clicks, for spotting a rage burst. */
|
|
44
|
+
var recentClicks = [];
|
|
45
|
+
var scrollStart = 0;
|
|
46
|
+
var scrollStartX = 0;
|
|
47
|
+
var scrollStartY = 0;
|
|
48
|
+
var scrollTimer = null;
|
|
49
|
+
/** What the gesture in progress scrolls: null for the window, else the page's content pane. */
|
|
50
|
+
var scrolling = null;
|
|
51
|
+
/** Where each content pane was after its last gesture, as lastX / lastY are for the window. */
|
|
52
|
+
var paneAt = new WeakMap();
|
|
53
|
+
/**
|
|
54
|
+
* Where the page was before the current gesture began.
|
|
55
|
+
*
|
|
56
|
+
* A scroll event fires *after* the position has already changed, so the first event of a burst
|
|
57
|
+
* reports somewhere the page has moved to, not where it started. Measuring from that loses the
|
|
58
|
+
* first increment of every gesture, and loses a single-event jump — an anchor link or a "back to
|
|
59
|
+
* top" button — completely, which is exactly the movement worth seeing on a scroll map.
|
|
60
|
+
*/
|
|
61
|
+
var lastX = 0;
|
|
62
|
+
var lastY = 0;
|
|
63
|
+
/** Whether a record may still be stored, or the visit has produced enough. */
|
|
64
|
+
function underCap() {
|
|
65
|
+
var cap = getConfig('heatmap_max_per_session');
|
|
66
|
+
if (typeof cap !== 'number' || cap <= 0) {
|
|
67
|
+
cap = AP_CONFIG.heatmap_max_per_session;
|
|
68
|
+
}
|
|
69
|
+
if (recorded >= cap) {
|
|
70
|
+
if (recorded === cap) {
|
|
71
|
+
recorded++; // said once, not on every event for the rest of the visit
|
|
72
|
+
Console.log('apptics.heatmap: reached ' + cap + ' records for this session, pausing capture'); // no i18n
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
/** The page's full scrollable height, which is what a scroll depth is a fraction of. */
|
|
79
|
+
function contentHeight() {
|
|
80
|
+
var doc = document.documentElement;
|
|
81
|
+
var body = document.body;
|
|
82
|
+
return Math.max(doc ? doc.scrollHeight : 0, body ? body.scrollHeight : 0, doc ? doc.clientHeight : 0);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A 0-1 fraction, to three decimals.
|
|
86
|
+
*
|
|
87
|
+
* Clamped because the inputs can exceed each other: a page that has not laid out yet reports a
|
|
88
|
+
* content height of 0, and a rubber-band scroll puts the viewport bottom past the document end.
|
|
89
|
+
* Both produce a "fraction" far outside 0-1, which is meaningless on a heatmap.
|
|
90
|
+
*/
|
|
91
|
+
function fraction(part, whole) {
|
|
92
|
+
if (!whole || whole <= 0) {
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
var value = part / whole;
|
|
96
|
+
if (value < 0) {
|
|
97
|
+
value = 0;
|
|
98
|
+
}
|
|
99
|
+
if (value > 1) {
|
|
100
|
+
value = 1;
|
|
101
|
+
}
|
|
102
|
+
return Math.round(value * 1000) / 1000;
|
|
103
|
+
}
|
|
104
|
+
function viewportWidth() {
|
|
105
|
+
return window.innerWidth || (document.documentElement ? document.documentElement.clientWidth : 0) || 1;
|
|
106
|
+
}
|
|
107
|
+
/** Whether the element, or something it sits inside, is meant to be clicked. */
|
|
108
|
+
function clickableAncestor(node) {
|
|
109
|
+
var depth = 0;
|
|
110
|
+
while (node && depth < 10) {
|
|
111
|
+
var tag = node.tagName ? node.tagName.toLowerCase() : ''; // no i18n
|
|
112
|
+
var role = node.getAttribute ? node.getAttribute('role') : null; // no i18n
|
|
113
|
+
if (tag === 'a' || tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea' // no i18n
|
|
114
|
+
|| tag === 'summary' || tag === 'label' // no i18n
|
|
115
|
+
|| role === 'button' || role === 'link' || role === 'tab' || role === 'menuitem' // no i18n
|
|
116
|
+
|| (node.hasAttribute && node.hasAttribute('onclick'))) { // no i18n
|
|
117
|
+
return node;
|
|
118
|
+
}
|
|
119
|
+
node = node.parentElement;
|
|
120
|
+
depth++;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A name for the element that was clicked.
|
|
126
|
+
*
|
|
127
|
+
* Attributes an author chose come first, because they are deliberate and stable. Visible text is
|
|
128
|
+
* the last resort and is skipped entirely when the user has opted out of PII: the text of a
|
|
129
|
+
* control can be a person's name, an email address or the contents of their cart.
|
|
130
|
+
*/
|
|
131
|
+
function labelFor(node) {
|
|
132
|
+
if (!node) {
|
|
133
|
+
return ''; // no i18n
|
|
134
|
+
}
|
|
135
|
+
var attributes = ['data-ap-label', 'apeventaction', 'data-apeventaction', 'aria-label', 'name', 'id', 'title']; // no i18n
|
|
136
|
+
for (var i = 0; i < attributes.length; i++) {
|
|
137
|
+
var value = node.getAttribute ? node.getAttribute(attributes[i]) : null;
|
|
138
|
+
if (!isEmpty(value)) {
|
|
139
|
+
return String(value).slice(0, LABEL_MAX);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
var tag = node.tagName ? node.tagName.toLowerCase() : 'element'; // no i18n
|
|
143
|
+
if (getTrackingStatus() !== AP_TRACKING_STATUS.USAGE_TRACKING_WITH_PII) {
|
|
144
|
+
// without consent for PII the shape of the control is all we say
|
|
145
|
+
var role = node.getAttribute ? node.getAttribute('role') : null; // no i18n
|
|
146
|
+
return isEmpty(role) ? tag : tag + '[' + role + ']'; // no i18n
|
|
147
|
+
}
|
|
148
|
+
var text = (node.textContent || '').replace(/\s+/g, ' ').trim(); // no i18n
|
|
149
|
+
return isEmpty(text) ? tag : text.slice(0, LABEL_MAX);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A stable number for the element, so the console can group every click on one control together.
|
|
153
|
+
* Derived from its position in the tree rather than from its text, so it survives a label change
|
|
154
|
+
* and carries nothing a person typed.
|
|
155
|
+
*/
|
|
156
|
+
function triggeredId(node) {
|
|
157
|
+
if (!node) {
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
var path = ''; // no i18n
|
|
161
|
+
var current = node;
|
|
162
|
+
var depth = 0;
|
|
163
|
+
while (current && depth < 12) {
|
|
164
|
+
var parent = current.parentElement;
|
|
165
|
+
var index = 0;
|
|
166
|
+
if (parent) {
|
|
167
|
+
var children = parent.children;
|
|
168
|
+
for (var i = 0; i < children.length; i++) {
|
|
169
|
+
if (children[i] === current) {
|
|
170
|
+
index = i;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
path = '/' + (current.tagName || '?') + ':' + index + path; // no i18n
|
|
176
|
+
current = parent;
|
|
177
|
+
depth++;
|
|
178
|
+
}
|
|
179
|
+
// FNV-1a, kept positive: a short stable number, not a cryptographic digest
|
|
180
|
+
var hash = 2166136261;
|
|
181
|
+
for (var c = 0; c < path.length; c++) {
|
|
182
|
+
hash ^= path.charCodeAt(c);
|
|
183
|
+
hash = (hash * 16777619) >>> 0;
|
|
184
|
+
}
|
|
185
|
+
return hash;
|
|
186
|
+
}
|
|
187
|
+
/** Whether this click is part of a burst of repeated clicks in the same spot. */
|
|
188
|
+
function isRageClick(x, y, at) {
|
|
189
|
+
recentClicks = recentClicks.filter(function (click) { return at - click.at <= RAGE_WINDOW_MS; });
|
|
190
|
+
recentClicks.push({ at: at, x: x, y: y });
|
|
191
|
+
var near = recentClicks.filter(function (click) {
|
|
192
|
+
return Math.abs(click.x - x) <= RAGE_RADIUS_PX && Math.abs(click.y - y) <= RAGE_RADIUS_PX;
|
|
193
|
+
});
|
|
194
|
+
return near.length >= RAGE_COUNT;
|
|
195
|
+
}
|
|
196
|
+
/** The fields every record carries, whatever kind it is. */
|
|
197
|
+
function common() {
|
|
198
|
+
return {
|
|
199
|
+
id: getNewUUID(),
|
|
200
|
+
session_start_time: _ZAP._current_session.starttime || nowMs(),
|
|
201
|
+
// without this the server has no way to order records within a session: session_start_time
|
|
202
|
+
// is the same for every one of them
|
|
203
|
+
triggered_time: nowMs(),
|
|
204
|
+
// the screen inScreen() or automatic screen tracking last named, or else the page, named
|
|
205
|
+
// as automatic tracking would name it and recorded as a screen view so engagement/add has it
|
|
206
|
+
screen: String(screenForRecord()).slice(0, FIELD_MAX),
|
|
207
|
+
url_path: String(window.location.pathname).slice(0, FIELD_MAX),
|
|
208
|
+
screen_width: window.screen ? window.screen.width : 0,
|
|
209
|
+
screen_height: window.screen ? window.screen.height : 0,
|
|
210
|
+
content_height: contentHeight(),
|
|
211
|
+
orientation: getOrientation(),
|
|
212
|
+
network_status: getNetworkStatus(),
|
|
213
|
+
edge_type: _ZAP._device_info.connectionType || getConnectionType(),
|
|
214
|
+
scroll_offset_x: Math.round(window.scrollX || window.pageXOffset || 0),
|
|
215
|
+
scroll_offset_y: Math.round(window.scrollY || window.pageYOffset || 0),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function store(record) {
|
|
219
|
+
recorded++;
|
|
220
|
+
noteDebugActivity();
|
|
221
|
+
// a heatmap record is worth a session in the same way an event is
|
|
222
|
+
commitPendingSession();
|
|
223
|
+
heatmap_transactions.add(record);
|
|
224
|
+
Console.dir(record);
|
|
225
|
+
}
|
|
226
|
+
function onPointerDown(event) {
|
|
227
|
+
if (!canTrackHeatmap('heatmap') || !underCap()) { // no i18n
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
var at = nowMs();
|
|
232
|
+
var target = event.target;
|
|
233
|
+
var clickable = clickableAncestor(target);
|
|
234
|
+
var named = clickable || target;
|
|
235
|
+
var x = Math.round(event.clientX);
|
|
236
|
+
var y = Math.round(event.clientY);
|
|
237
|
+
var height = contentHeight() || 1;
|
|
238
|
+
var pointerType = event.pointerType;
|
|
239
|
+
var record = Object.assign(common(), {
|
|
240
|
+
// TODO: send 'buttonclick' for a clickable target once the server accepts it. Held at
|
|
241
|
+
// 'default' for now while heatmap/add is still refusing payloads; `is_clickable`
|
|
242
|
+
// already carries the same information, so nothing is lost meanwhile.
|
|
243
|
+
triggered_type: 'default', // no i18n
|
|
244
|
+
gesture_type: 'click', // no i18n
|
|
245
|
+
gesture_duration: 0,
|
|
246
|
+
input_type: isEmpty(pointerType) ? 'mouse' : String(pointerType), // no i18n
|
|
247
|
+
element_label: labelFor(named),
|
|
248
|
+
triggered_id: triggeredId(named),
|
|
249
|
+
coordinate_x: x,
|
|
250
|
+
coordinate_y: y,
|
|
251
|
+
normalized_x: fraction(x, viewportWidth()),
|
|
252
|
+
normalized_y: fraction((window.scrollY || 0) + y, height),
|
|
253
|
+
velocity: 0,
|
|
254
|
+
is_clickable: clickable ? 1 : 0,
|
|
255
|
+
is_rage_click: isRageClick(x, y, at) ? 1 : 0,
|
|
256
|
+
is_dead_click: 0,
|
|
257
|
+
});
|
|
258
|
+
store(record);
|
|
259
|
+
watchForDeadClick(record, target);
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
logFailure(AP_LOG_MODULE.HEATMAP, 'apptics.heatmap: click capture failed: ' + String(err)); // no i18n
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* A click on something that was not clickable and changed nothing is a click someone expected to
|
|
267
|
+
* work. Decided after the fact: the record is already stored, and is updated in place if nothing
|
|
268
|
+
* happened, so the click is never lost waiting to find out.
|
|
269
|
+
*/
|
|
270
|
+
function watchForDeadClick(record, target) {
|
|
271
|
+
if (record.is_clickable === 1 || typeof MutationObserver !== 'function') { // no i18n
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
var changed = false;
|
|
275
|
+
var url = window.location.href;
|
|
276
|
+
var observer = new MutationObserver(function () { changed = true; });
|
|
277
|
+
try {
|
|
278
|
+
observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true });
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
setTimeout(function () {
|
|
284
|
+
observer.disconnect();
|
|
285
|
+
if (!changed && window.location.href === url) {
|
|
286
|
+
record.is_dead_click = 1;
|
|
287
|
+
heatmap_transactions.update(record);
|
|
288
|
+
}
|
|
289
|
+
}, DEAD_CLICK_MS);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* What a scroll event moved, as a gesture counts it: null for the window (the event's target is
|
|
293
|
+
* the document), the element when it is big enough to be the page's own scrolling, and undefined
|
|
294
|
+
* for anything smaller, which is not recorded.
|
|
295
|
+
*/
|
|
296
|
+
function scrollerOf(target) {
|
|
297
|
+
var doc = document;
|
|
298
|
+
// the window and the document are not elements: either is the page itself scrolling
|
|
299
|
+
if (!target || target.nodeType !== 1 || target === doc.documentElement || target === doc.body) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
var el = target;
|
|
303
|
+
var big = el.clientHeight >= (window.innerHeight || 0) * PAGE_SCROLLER_SHARE
|
|
304
|
+
&& el.clientWidth >= viewportWidth() * PAGE_SCROLLER_SHARE;
|
|
305
|
+
return big ? el : undefined;
|
|
306
|
+
}
|
|
307
|
+
/** Where a scroller is now. */
|
|
308
|
+
function positionOf(scroller) {
|
|
309
|
+
return scroller === null
|
|
310
|
+
? { x: Math.round(window.scrollX || window.pageXOffset || 0), y: Math.round(window.scrollY || window.pageYOffset || 0) }
|
|
311
|
+
: { x: Math.round(scroller.scrollLeft || 0), y: Math.round(scroller.scrollTop || 0) };
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Where a scroller was before this gesture: what the last one left. A content pane seen for the
|
|
315
|
+
* first time is taken to have been at its top, which is where a pane starts.
|
|
316
|
+
*/
|
|
317
|
+
function startOf(scroller) {
|
|
318
|
+
if (scroller === null) {
|
|
319
|
+
return { x: lastX, y: lastY };
|
|
320
|
+
}
|
|
321
|
+
return paneAt.get(scroller) || { x: 0, y: 0 };
|
|
322
|
+
}
|
|
323
|
+
function onScroll(event) {
|
|
324
|
+
if (!canTrackHeatmap('heatmap') || !underCap()) { // no i18n
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
var scroller = scrollerOf(event ? event.target : null);
|
|
328
|
+
if (scroller === undefined) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (scrollTimer !== null && scroller !== scrolling) {
|
|
332
|
+
// another scroller started moving: the gesture on the first one is over
|
|
333
|
+
clearTimeout(scrollTimer);
|
|
334
|
+
endScrollGesture();
|
|
335
|
+
}
|
|
336
|
+
if (scrollTimer === null) {
|
|
337
|
+
scrolling = scroller;
|
|
338
|
+
var start = startOf(scroller);
|
|
339
|
+
scrollStart = nowMs();
|
|
340
|
+
scrollStartX = start.x;
|
|
341
|
+
scrollStartY = start.y;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
clearTimeout(scrollTimer);
|
|
345
|
+
}
|
|
346
|
+
scrollTimer = setTimeout(endScrollGesture, SCROLL_IDLE_MS);
|
|
347
|
+
}
|
|
348
|
+
/** One record for the whole burst, once the page has been still for SCROLL_IDLE_MS. */
|
|
349
|
+
function endScrollGesture() {
|
|
350
|
+
scrollTimer = null;
|
|
351
|
+
var scroller = scrolling;
|
|
352
|
+
scrolling = null;
|
|
353
|
+
try {
|
|
354
|
+
var end = positionOf(scroller);
|
|
355
|
+
var endX = end.x;
|
|
356
|
+
var endY = end.y;
|
|
357
|
+
var dx = endX - scrollStartX;
|
|
358
|
+
var dy = endY - scrollStartY;
|
|
359
|
+
if (scroller === null) {
|
|
360
|
+
lastX = endX;
|
|
361
|
+
lastY = endY;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
paneAt.set(scroller, end);
|
|
365
|
+
}
|
|
366
|
+
if (dx === 0 && dy === 0) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
var duration = Math.max(1, nowMs() - scrollStart);
|
|
370
|
+
var distance = Math.sqrt(dx * dx + dy * dy);
|
|
371
|
+
var direction = Math.abs(dy) >= Math.abs(dx)
|
|
372
|
+
? (dy > 0 ? 'down-scroll' : 'up-scroll') // no i18n
|
|
373
|
+
: (dx > 0 ? 'right-scroll' : 'left-scroll'); // no i18n
|
|
374
|
+
// the depth is of what scrolled: the document, or the content pane and its own content
|
|
375
|
+
var height = (scroller === null ? contentHeight() : scroller.scrollHeight) || 1;
|
|
376
|
+
var visible = scroller === null ? (window.innerHeight || 0) : scroller.clientHeight;
|
|
377
|
+
store(Object.assign(common(), scroller === null ? {} : {
|
|
378
|
+
content_height: scroller.scrollHeight,
|
|
379
|
+
scroll_offset_x: endX,
|
|
380
|
+
scroll_offset_y: endY,
|
|
381
|
+
}, {
|
|
382
|
+
// the server accepts only event|default|buttonclick here; what kind of movement it
|
|
383
|
+
// was belongs in gesture_type. input_type is left off rather than invented: a wheel
|
|
384
|
+
// carries no pointer type, and the intended vocabulary has no value for "unknown"
|
|
385
|
+
triggered_type: 'default', // no i18n
|
|
386
|
+
gesture_type: direction,
|
|
387
|
+
gesture_duration: Math.round(duration),
|
|
388
|
+
element_label: '', // no i18n
|
|
389
|
+
triggered_id: 0,
|
|
390
|
+
coordinate_x: endX,
|
|
391
|
+
coordinate_y: endY,
|
|
392
|
+
normalized_x: 0,
|
|
393
|
+
// how far down the page the gesture left them, which is what a scroll map reads
|
|
394
|
+
normalized_y: fraction(endY + visible, height),
|
|
395
|
+
velocity: Math.round((distance / duration) * 1000),
|
|
396
|
+
is_clickable: 0,
|
|
397
|
+
is_rage_click: 0,
|
|
398
|
+
is_dead_click: 0,
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
logFailure(AP_LOG_MODULE.HEATMAP, 'apptics.heatmap: scroll capture failed: ' + String(err)); // no i18n
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/** Turns capture on or off. Listeners are passive, so they never delay a click or a scroll. */
|
|
406
|
+
function trackHeatmap(status) {
|
|
407
|
+
var wanted = typeof status === 'boolean' ? status : true; // no i18n
|
|
408
|
+
if (wanted === installed) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (wanted) {
|
|
412
|
+
// where the page already is, so the first gesture is measured from here and not from 0
|
|
413
|
+
lastX = Math.round(window.scrollX || 0);
|
|
414
|
+
lastY = Math.round(window.scrollY || 0);
|
|
415
|
+
window.addEventListener('pointerdown', onPointerDown, { capture: true, passive: true }); // no i18n
|
|
416
|
+
window.addEventListener('scroll', onScroll, { capture: true, passive: true }); // no i18n
|
|
417
|
+
installed = true;
|
|
418
|
+
Console.log('apptics.heatmap: capturing clicks and scrolls'); // no i18n
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
window.removeEventListener('pointerdown', onPointerDown, true); // no i18n
|
|
422
|
+
window.removeEventListener('scroll', onScroll, true); // no i18n
|
|
423
|
+
// a gesture still settling when capture stops (a single-page app leaving a heatmap page) is
|
|
424
|
+
// recorded as it stands, not dropped: it was the visitor's last scroll on that page
|
|
425
|
+
if (scrollTimer !== null) {
|
|
426
|
+
clearTimeout(scrollTimer);
|
|
427
|
+
endScrollGesture();
|
|
428
|
+
}
|
|
429
|
+
scrolling = null;
|
|
430
|
+
installed = false;
|
|
431
|
+
Console.log('apptics.heatmap: capture stopped'); // no i18n
|
|
432
|
+
}
|
|
433
|
+
/** Forgets what this visit recorded. reset() calls it; the cap starts again from zero. */
|
|
434
|
+
function resetHeatmapSession() {
|
|
435
|
+
recorded = 0;
|
|
436
|
+
recentClicks = [];
|
|
437
|
+
lastX = 0;
|
|
438
|
+
lastY = 0;
|
|
439
|
+
paneAt = new WeakMap();
|
|
440
|
+
}
|
|
441
|
+
// init() and reset() drive capture through these, so neither has to import this module
|
|
442
|
+
hooks.trackHeatmap = trackHeatmap;
|
|
443
|
+
hooks.resetHeatmap = resetHeatmapSession;
|
|
444
|
+
|
|
445
|
+
export { resetHeatmapSession, trackHeatmap };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import './heatmap/send.js';
|
|
2
|
+
import type { AppticsSdk } from './types.js';
|
|
3
|
+
export type { AppticsSdk, AppticsConfig, ErrorLike, FeedbackType, Properties, TrackingStatus, UserInfo, UserValidationResult, } from './types.js';
|
|
4
|
+
/** Android's Apptics: starting the SDK, and what applies to all of it. */
|
|
5
|
+
export declare const Apptics: AppticsSdk['Apptics'];
|
|
6
|
+
export declare const AppticsEvent: AppticsSdk['AppticsEvent'];
|
|
7
|
+
export type { DefinedEvent } from './engagement/defined-events.js';
|
|
8
|
+
/** Android's AppticsScreenTracker: inScreen(name) returns the view's id, outScreen(id) ends it. */
|
|
9
|
+
export declare const AppticsScreen: AppticsSdk['AppticsScreen'];
|
|
10
|
+
/** Android's AppticsApiTracker: startTrackApi / endTrackApi, and configure for the rules. */
|
|
11
|
+
export declare const AppticsApi: AppticsSdk['AppticsApi'];
|
|
12
|
+
export type { ApiTrackingConfig } from './engagement/api-rules.js';
|
|
13
|
+
/** Errors: the ones the app caught, and the switch for the uncaught ones tracked on their own. */
|
|
14
|
+
export declare const AppticsError: AppticsSdk['AppticsError'];
|
|
15
|
+
/** Android's AppticsUser: the signed-in user, and the builder for their properties. */
|
|
16
|
+
export declare const AppticsUser: AppticsSdk['AppticsUser'];
|
|
17
|
+
/** Heatmap capture: AppticsHeatmap.trackHeatmap() starts or stops recording clicks and scrolls. */
|
|
18
|
+
export declare const AppticsHeatmap: AppticsSdk['AppticsHeatmap'];
|
|
19
|
+
/** Android's AppticsSettings: what the SDK may track for this visitor. */
|
|
20
|
+
export declare const AppticsSettings: AppticsSdk['AppticsSettings'];
|
|
21
|
+
declare const apptics: AppticsSdk;
|
|
22
|
+
export default apptics;
|