@dotcms/analytics 1.2.0-next.9 → 1.2.1-next.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/README.md +261 -15
- package/lib/core/dot-analytics.content.js +84 -0
- package/lib/core/plugin/click/dot-analytics.click-tracker.d.ts +108 -0
- package/lib/core/plugin/click/dot-analytics.click-tracker.js +144 -0
- package/lib/core/plugin/click/dot-analytics.click.plugin.d.ts +36 -0
- package/lib/core/plugin/click/dot-analytics.click.plugin.js +27 -0
- package/lib/core/plugin/click/dot-analytics.click.utils.d.ts +12 -0
- package/lib/core/plugin/click/dot-analytics.click.utils.js +55 -0
- package/lib/core/plugin/enricher/dot-analytics.enricher.plugin.d.ts +1 -1
- package/lib/core/plugin/enricher/dot-analytics.enricher.plugin.js +14 -14
- package/lib/core/{shared/dot-content-analytics.activity-tracker.d.ts → plugin/identity/dot-analytics.identity.activity-tracker.d.ts} +2 -17
- package/lib/core/{shared/dot-content-analytics.activity-tracker.js → plugin/identity/dot-analytics.identity.activity-tracker.js} +17 -37
- package/lib/core/plugin/identity/dot-analytics.identity.plugin.js +7 -7
- package/lib/core/plugin/identity/dot-analytics.identity.utils.d.ts +0 -16
- package/lib/core/{shared/dot-content-analytics.impression-tracker.d.ts → plugin/impression/dot-analytics.impression-tracker.d.ts} +2 -2
- package/lib/core/{shared/dot-content-analytics.impression-tracker.js → plugin/impression/dot-analytics.impression-tracker.js} +41 -57
- package/lib/core/plugin/impression/dot-analytics.impression.plugin.d.ts +4 -2
- package/lib/core/plugin/impression/dot-analytics.impression.plugin.js +13 -21
- package/lib/core/plugin/impression/dot-analytics.impression.utils.d.ts +0 -25
- package/lib/core/plugin/impression/dot-analytics.impression.utils.js +15 -38
- package/lib/core/plugin/{dot-analytics.plugin.d.ts → main/dot-analytics.plugin.d.ts} +2 -1
- package/lib/core/plugin/main/dot-analytics.plugin.js +129 -0
- package/lib/core/shared/constants/{dot-content-analytics.constants.d.ts → dot-analytics.constants.d.ts} +16 -0
- package/lib/core/shared/constants/dot-analytics.constants.js +53 -0
- package/lib/core/shared/constants/index.d.ts +1 -1
- package/lib/core/shared/dot-analytics.logger.d.ts +85 -0
- package/lib/core/shared/dot-analytics.logger.js +90 -0
- package/lib/core/shared/{dot-content-analytics.http.d.ts → http/dot-analytics.http.d.ts} +1 -1
- package/lib/core/shared/http/dot-analytics.http.js +34 -0
- package/lib/core/shared/models/event.model.d.ts +69 -2
- package/lib/core/shared/models/library.model.d.ts +31 -5
- package/lib/core/shared/queue/dot-analytics.queue.utils.js +44 -37
- package/lib/core/shared/{dot-content-analytics.utils.d.ts → utils/dot-analytics.utils.d.ts} +72 -4
- package/lib/core/shared/utils/dot-analytics.utils.js +202 -0
- package/lib/react/hook/useContentAnalytics.js +17 -11
- package/lib/react/hook/useRouterTracker.js +4 -4
- package/lib/react/internal/utils.js +1 -1
- package/package.json +7 -6
- package/lib/core/dot-content-analytics.js +0 -56
- package/lib/core/plugin/dot-analytics.plugin.js +0 -97
- package/lib/core/shared/constants/dot-content-analytics.constants.js +0 -48
- package/lib/core/shared/dot-content-analytics.http.js +0 -36
- package/lib/core/shared/dot-content-analytics.utils.js +0 -147
- /package/lib/core/{dot-content-analytics.d.ts → dot-analytics.content.d.ts} +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AnalyticsInstance } from 'analytics';
|
|
2
|
+
import { DotCMSAnalyticsConfig } from '../../shared/models';
|
|
3
|
+
/**
|
|
4
|
+
* Click Plugin for DotAnalytics
|
|
5
|
+
* Handles automatic tracking of clicks on content elements.
|
|
6
|
+
*
|
|
7
|
+
* This plugin initializes the click tracker which:
|
|
8
|
+
* - Uses MutationObserver to detect contentlet containers
|
|
9
|
+
* - Attaches click listeners to each .dotcms-analytics-contentlet element
|
|
10
|
+
* - Filters for clicks on <a> or <button> elements inside tracked contentlets
|
|
11
|
+
* - Extracts contentlet data and element metadata
|
|
12
|
+
* - Throttles clicks to prevent duplicates
|
|
13
|
+
* - Fires 'content_click' events via subscription callback
|
|
14
|
+
*
|
|
15
|
+
* Note: This plugin is only registered if config.clicks is enabled.
|
|
16
|
+
* See getEnhancedTrackingPlugins() for conditional loading logic.
|
|
17
|
+
*
|
|
18
|
+
* @param {DotCMSAnalyticsConfig} config - Configuration with clicks settings
|
|
19
|
+
* @returns {Object} Plugin object with lifecycle methods
|
|
20
|
+
*/
|
|
21
|
+
export declare const dotAnalyticsClickPlugin: (config: DotCMSAnalyticsConfig) => {
|
|
22
|
+
name: string;
|
|
23
|
+
/**
|
|
24
|
+
* Initialize click tracking
|
|
25
|
+
* Called when Analytics.js initializes the plugin with instance context
|
|
26
|
+
* @param instance - Analytics.js instance with track method
|
|
27
|
+
*/
|
|
28
|
+
initialize: ({ instance }: {
|
|
29
|
+
instance: AnalyticsInstance;
|
|
30
|
+
}) => Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Setup cleanup handlers when plugin is loaded
|
|
33
|
+
* Called after Analytics.js completes plugin loading
|
|
34
|
+
*/
|
|
35
|
+
loaded: () => boolean;
|
|
36
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DotCMSClickTracker as o } from "./dot-analytics.click-tracker.js";
|
|
2
|
+
import { createPluginLogger as u, isBrowser as a, setupPluginCleanup as s } from "../../shared/utils/dot-analytics.utils.js";
|
|
3
|
+
const g = (n) => {
|
|
4
|
+
let i = null, l = null;
|
|
5
|
+
const e = u("Click", n);
|
|
6
|
+
return {
|
|
7
|
+
name: "dot-analytics-click",
|
|
8
|
+
/**
|
|
9
|
+
* Initialize click tracking
|
|
10
|
+
* Called when Analytics.js initializes the plugin with instance context
|
|
11
|
+
* @param instance - Analytics.js instance with track method
|
|
12
|
+
*/
|
|
13
|
+
initialize: ({ instance: r }) => (i = new o(n), l = i.onClick((c, t) => {
|
|
14
|
+
r.track(c, t);
|
|
15
|
+
}), i.initialize(), e.info("Click tracking plugin initialized"), Promise.resolve()),
|
|
16
|
+
/**
|
|
17
|
+
* Setup cleanup handlers when plugin is loaded
|
|
18
|
+
* Called after Analytics.js completes plugin loading
|
|
19
|
+
*/
|
|
20
|
+
loaded: () => (a() && i && s(() => {
|
|
21
|
+
l && (l.unsubscribe(), l = null), i && (i.cleanup(), i = null, e.info("Click tracking cleaned up on page unload"));
|
|
22
|
+
}), !0)
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
g as dotAnalyticsClickPlugin
|
|
27
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DotCMSContentClickPayload } from '../../shared/models';
|
|
2
|
+
import { createPluginLogger } from '../../shared/utils/dot-analytics.utils';
|
|
3
|
+
/**
|
|
4
|
+
* Handles click events on elements within a contentlet.
|
|
5
|
+
* The contentlet element is already known since we attach listeners to contentlets.
|
|
6
|
+
*
|
|
7
|
+
* @param event - The mouse event
|
|
8
|
+
* @param contentletElement - The contentlet container element
|
|
9
|
+
* @param trackCallback - Callback to execute if the click is valid
|
|
10
|
+
* @param logger - Logger instance for debug messages
|
|
11
|
+
*/
|
|
12
|
+
export declare const handleContentletClick: (event: MouseEvent, contentletElement: HTMLElement, trackCallback: (eventName: string, payload: DotCMSContentClickPayload) => void, logger: ReturnType<typeof createPluginLogger>) => void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CLICKABLE_ELEMENTS_SELECTOR as l, CLICK_EVENT_TYPE as m } from "../../shared/constants/dot-analytics.constants.js";
|
|
2
|
+
import { extractContentletData as p } from "../../shared/utils/dot-analytics.utils.js";
|
|
3
|
+
import { getViewportMetrics as b } from "../impression/dot-analytics.impression.utils.js";
|
|
4
|
+
const _ = (r, o, c, e) => {
|
|
5
|
+
const a = r.target;
|
|
6
|
+
e.debug("Click detected on:", a);
|
|
7
|
+
const t = a.closest(l);
|
|
8
|
+
if (!t) {
|
|
9
|
+
e.debug("No <a> or <button> found in click path");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (!o.contains(t)) {
|
|
13
|
+
e.debug("Click was outside contentlet boundary");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
e.debug("Found clickable element:", t);
|
|
17
|
+
const n = p(o);
|
|
18
|
+
if (!n.identifier) {
|
|
19
|
+
e.debug("Contentlet has no identifier");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
e.debug("Contentlet data:", n);
|
|
23
|
+
const d = b(o), s = [];
|
|
24
|
+
for (const i of t.attributes)
|
|
25
|
+
!i.name.startsWith("data-dot-analytics") && i.name !== "class" && i.name !== "id" && i.name !== "href" && s.push(`${i.name}:${i.value}`);
|
|
26
|
+
const f = parseInt(o.dataset.dotAnalyticsDomIndex || "-1", 10), u = {
|
|
27
|
+
content: {
|
|
28
|
+
identifier: n.identifier,
|
|
29
|
+
inode: n.inode,
|
|
30
|
+
title: n.title,
|
|
31
|
+
content_type: n.contentType
|
|
32
|
+
},
|
|
33
|
+
position: {
|
|
34
|
+
viewport_offset_pct: d.offsetPercentage,
|
|
35
|
+
dom_index: f
|
|
36
|
+
},
|
|
37
|
+
element: {
|
|
38
|
+
text: (t.innerText || t.textContent || "").trim().substring(0, 100),
|
|
39
|
+
// Limit length
|
|
40
|
+
type: t.tagName.toLowerCase(),
|
|
41
|
+
id: t.id || "",
|
|
42
|
+
// Required by backend, empty string if not present
|
|
43
|
+
class: t.className || "",
|
|
44
|
+
// Required by backend, empty string if not present
|
|
45
|
+
href: t.getAttribute("href") || "",
|
|
46
|
+
// Path as written in HTML (relative), empty string for buttons
|
|
47
|
+
attributes: s
|
|
48
|
+
// Additional attributes (data-*, aria-*, target, etc.)
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
c(m, u);
|
|
52
|
+
};
|
|
53
|
+
export {
|
|
54
|
+
_ as handleContentletClick
|
|
55
|
+
};
|
|
@@ -21,7 +21,7 @@ export declare const dotAnalyticsEnricherPlugin: () => {
|
|
|
21
21
|
}) => EnrichedAnalyticsPayload;
|
|
22
22
|
/**
|
|
23
23
|
* TRACK EVENT ENRICHMENT - Runs after identity context injection
|
|
24
|
-
* Adds page data and timestamp for predefined events.
|
|
24
|
+
* Adds page data and timestamp for predefined content events.
|
|
25
25
|
* For custom events, only adds timestamp.
|
|
26
26
|
*
|
|
27
27
|
* @returns {EnrichedTrackPayload} Enriched payload ready for event structuring
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DotCMSPredefinedEventType as
|
|
2
|
-
import { getLocalTime as i, enrichPagePayloadOptimized as o } from "../../shared/dot-
|
|
1
|
+
import { DotCMSPredefinedEventType as r } from "../../shared/constants/dot-analytics.constants.js";
|
|
2
|
+
import { getLocalTime as i, enrichPagePayloadOptimized as o } from "../../shared/utils/dot-analytics.utils.js";
|
|
3
3
|
const l = () => ({
|
|
4
4
|
name: "enrich-dot-analytics",
|
|
5
5
|
/**
|
|
@@ -8,34 +8,34 @@ const l = () => ({
|
|
|
8
8
|
* @returns {EnrichedAnalyticsPayload} Enriched payload ready for event creation
|
|
9
9
|
*/
|
|
10
10
|
"page:dot-analytics": ({
|
|
11
|
-
payload:
|
|
11
|
+
payload: e
|
|
12
12
|
}) => {
|
|
13
|
-
const
|
|
14
|
-
if (!
|
|
13
|
+
const t = o(e);
|
|
14
|
+
if (!t.page)
|
|
15
15
|
throw new Error("DotCMS Analytics: Missing required page data");
|
|
16
|
-
return
|
|
16
|
+
return t;
|
|
17
17
|
},
|
|
18
18
|
/**
|
|
19
19
|
* TRACK EVENT ENRICHMENT - Runs after identity context injection
|
|
20
|
-
* Adds page data and timestamp for predefined events.
|
|
20
|
+
* Adds page data and timestamp for predefined content events.
|
|
21
21
|
* For custom events, only adds timestamp.
|
|
22
22
|
*
|
|
23
23
|
* @returns {EnrichedTrackPayload} Enriched payload ready for event structuring
|
|
24
24
|
*/
|
|
25
25
|
"track:dot-analytics": ({
|
|
26
|
-
payload:
|
|
26
|
+
payload: e
|
|
27
27
|
}) => {
|
|
28
|
-
const { event:
|
|
29
|
-
return
|
|
30
|
-
...
|
|
28
|
+
const { event: t } = e, n = i();
|
|
29
|
+
return t === r.CONTENT_IMPRESSION || t === r.CONTENT_CLICK || t === r.CONVERSION ? {
|
|
30
|
+
...e,
|
|
31
31
|
page: {
|
|
32
32
|
title: document.title,
|
|
33
33
|
url: window.location.href
|
|
34
34
|
},
|
|
35
|
-
local_time:
|
|
35
|
+
local_time: n
|
|
36
36
|
} : {
|
|
37
|
-
...
|
|
38
|
-
local_time:
|
|
37
|
+
...e,
|
|
38
|
+
local_time: n
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ANALYTICS_WINDOWS_ACTIVE_KEY, ANALYTICS_WINDOWS_CLEANUP_KEY } from '
|
|
2
|
-
import { DotCMSAnalyticsConfig } from '
|
|
1
|
+
import { ANALYTICS_WINDOWS_ACTIVE_KEY, ANALYTICS_WINDOWS_CLEANUP_KEY } from '../../../../../../uve/src/internal.ts';
|
|
2
|
+
import { DotCMSAnalyticsConfig } from '../../shared/models';
|
|
3
3
|
declare global {
|
|
4
4
|
interface Window {
|
|
5
5
|
[ANALYTICS_WINDOWS_CLEANUP_KEY]?: () => void;
|
|
@@ -10,13 +10,6 @@ declare global {
|
|
|
10
10
|
* Updates session activity with throttling
|
|
11
11
|
*/
|
|
12
12
|
export declare const updateSessionActivity: () => void;
|
|
13
|
-
/**
|
|
14
|
-
* Gets session information for debugging
|
|
15
|
-
*/
|
|
16
|
-
export declare const getSessionInfo: () => {
|
|
17
|
-
lastActivity: number;
|
|
18
|
-
isActive: boolean;
|
|
19
|
-
};
|
|
20
13
|
/**
|
|
21
14
|
* Initializes activity tracking
|
|
22
15
|
*/
|
|
@@ -25,11 +18,3 @@ export declare const initializeActivityTracking: (config: DotCMSAnalyticsConfig)
|
|
|
25
18
|
* Cleans up activity tracking listeners and resets analytics state
|
|
26
19
|
*/
|
|
27
20
|
export declare const cleanupActivityTracking: () => void;
|
|
28
|
-
/**
|
|
29
|
-
* Checks if user has been inactive
|
|
30
|
-
*/
|
|
31
|
-
export declare const isUserInactive: () => boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Gets last activity time
|
|
34
|
-
*/
|
|
35
|
-
export declare const getLastActivity: () => number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ANALYTICS_WINDOWS_ACTIVE_KEY as
|
|
2
|
-
import { DEFAULT_SESSION_TIMEOUT_MINUTES as
|
|
3
|
-
class
|
|
1
|
+
import { ANALYTICS_WINDOWS_ACTIVE_KEY as a, ANALYTICS_WINDOWS_CLEANUP_KEY as o } from "../../../../uve/src/internal/constants.js";
|
|
2
|
+
import { DEFAULT_SESSION_TIMEOUT_MINUTES as r, ACTIVITY_EVENTS as l } from "../../shared/constants/dot-analytics.constants.js";
|
|
3
|
+
class v {
|
|
4
4
|
constructor() {
|
|
5
5
|
this.activityListeners = [], this.lastActivityTime = Date.now(), this.inactivityTimer = null, this.isThrottled = !1, this.config = null, this.ACTIVITY_THROTTLE_MS = 1e3;
|
|
6
6
|
}
|
|
@@ -12,24 +12,11 @@ class l {
|
|
|
12
12
|
this.lastActivityTime = Date.now(), this.inactivityTimer && clearTimeout(this.inactivityTimer), this.inactivityTimer = setTimeout(
|
|
13
13
|
() => {
|
|
14
14
|
var i;
|
|
15
|
-
(i = this.config) != null && i.debug && console.warn("DotCMS Analytics: User became inactive after timeout"), this.inactivityTimer = null;
|
|
15
|
+
(i = this.config) != null && i.debug && console.warn("DotCMS Analytics [Activity]: User became inactive after timeout"), this.inactivityTimer = null;
|
|
16
16
|
},
|
|
17
|
-
|
|
17
|
+
r * 60 * 1e3
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Checks if user has been inactive
|
|
22
|
-
*/
|
|
23
|
-
isUserInactive() {
|
|
24
|
-
const i = c * 60 * 1e3;
|
|
25
|
-
return Date.now() - this.lastActivityTime > i;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Gets last activity time
|
|
29
|
-
*/
|
|
30
|
-
getLastActivity() {
|
|
31
|
-
return this.lastActivityTime;
|
|
32
|
-
}
|
|
33
20
|
/**
|
|
34
21
|
* Updates session activity with throttling
|
|
35
22
|
*/
|
|
@@ -45,17 +32,19 @@ class l {
|
|
|
45
32
|
if (this.cleanup(), this.config = i, typeof window > "u")
|
|
46
33
|
return;
|
|
47
34
|
const s = () => this.updateSessionActivity();
|
|
48
|
-
|
|
49
|
-
window.addEventListener(
|
|
50
|
-
() => window.removeEventListener(
|
|
35
|
+
l.forEach((c) => {
|
|
36
|
+
window.addEventListener(c, s, { passive: !0 }), this.activityListeners.push(
|
|
37
|
+
() => window.removeEventListener(c, s)
|
|
51
38
|
);
|
|
52
39
|
});
|
|
53
40
|
const n = () => {
|
|
54
|
-
document.visibilityState === "visible" && (this.updateSessionActivity(), i.debug && console.warn(
|
|
41
|
+
document.visibilityState === "visible" && (this.updateSessionActivity(), i.debug && console.warn(
|
|
42
|
+
"DotCMS Analytics [Activity]: User returned to tab, session reactivated"
|
|
43
|
+
));
|
|
55
44
|
};
|
|
56
45
|
document.addEventListener("visibilitychange", n), this.activityListeners.push(
|
|
57
46
|
() => document.removeEventListener("visibilitychange", n)
|
|
58
|
-
), this.updateActivityTime(), i.debug && console.warn("DotCMS Analytics: Activity tracking initialized");
|
|
47
|
+
), this.updateActivityTime(), i.debug && console.warn("DotCMS Analytics [Activity]: Activity tracking initialized");
|
|
59
48
|
}
|
|
60
49
|
/**
|
|
61
50
|
* Cleans up all activity tracking listeners
|
|
@@ -63,25 +52,16 @@ class l {
|
|
|
63
52
|
cleanup() {
|
|
64
53
|
this.activityListeners.forEach((i) => i()), this.activityListeners = [], this.inactivityTimer && (clearTimeout(this.inactivityTimer), this.inactivityTimer = null), this.config = null;
|
|
65
54
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Gets session information for debugging
|
|
68
|
-
*/
|
|
69
|
-
getSessionInfo() {
|
|
70
|
-
return {
|
|
71
|
-
lastActivity: this.getLastActivity(),
|
|
72
|
-
isActive: !this.isUserInactive()
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
55
|
}
|
|
76
|
-
const t = new
|
|
56
|
+
const t = new v(), d = () => {
|
|
77
57
|
t.updateSessionActivity();
|
|
78
|
-
},
|
|
58
|
+
}, u = (e) => {
|
|
79
59
|
t.initialize(e);
|
|
80
60
|
}, y = () => {
|
|
81
|
-
t.cleanup(), typeof window < "u" && (window[
|
|
61
|
+
t.cleanup(), typeof window < "u" && (window[a] = !1, window[o] = void 0, window.dispatchEvent(new CustomEvent("dotcms:analytics:cleanup")));
|
|
82
62
|
};
|
|
83
63
|
export {
|
|
84
64
|
y as cleanupActivityTracking,
|
|
85
|
-
|
|
86
|
-
|
|
65
|
+
u as initializeActivityTracking,
|
|
66
|
+
d as updateSessionActivity
|
|
87
67
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { cleanupActivityTracking as n, updateSessionActivity as r, initializeActivityTracking as a } from "./dot-analytics.identity.activity-tracker.js";
|
|
2
|
+
import { getAnalyticsContext as o } from "../../shared/utils/dot-analytics.utils.js";
|
|
3
3
|
const s = (t) => ({
|
|
4
4
|
name: "dot-analytics-identity",
|
|
5
5
|
/**
|
|
@@ -12,8 +12,8 @@ const s = (t) => ({
|
|
|
12
12
|
* This runs BEFORE the enricher plugin
|
|
13
13
|
*/
|
|
14
14
|
pageStart: ({ payload: e }) => {
|
|
15
|
-
|
|
16
|
-
const i =
|
|
15
|
+
r();
|
|
16
|
+
const i = o(t);
|
|
17
17
|
return {
|
|
18
18
|
...e,
|
|
19
19
|
context: i
|
|
@@ -24,8 +24,8 @@ const s = (t) => ({
|
|
|
24
24
|
* This runs BEFORE the enricher plugin
|
|
25
25
|
*/
|
|
26
26
|
trackStart: ({ payload: e }) => {
|
|
27
|
-
|
|
28
|
-
const i =
|
|
27
|
+
r();
|
|
28
|
+
const i = o(t);
|
|
29
29
|
return {
|
|
30
30
|
...e,
|
|
31
31
|
context: i
|
|
@@ -35,7 +35,7 @@ const s = (t) => ({
|
|
|
35
35
|
* Clean up on plugin unload
|
|
36
36
|
* Sets up cleanup handlers for activity tracking
|
|
37
37
|
*/
|
|
38
|
-
loaded: () => (typeof window < "u" && (window.addEventListener("beforeunload",
|
|
38
|
+
loaded: () => (typeof window < "u" && (window.addEventListener("beforeunload", n), window.addEventListener("pagehide", n)), !0)
|
|
39
39
|
});
|
|
40
40
|
export {
|
|
41
41
|
s as dotAnalyticsIdentityPlugin
|
|
@@ -1,20 +1,4 @@
|
|
|
1
1
|
import { DotCMSEventUtmData } from '../../shared/models';
|
|
2
|
-
/**
|
|
3
|
-
* Updates activity timestamp
|
|
4
|
-
*/
|
|
5
|
-
export declare const updateActivityTime: () => void;
|
|
6
|
-
/**
|
|
7
|
-
* Checks if user has been inactive
|
|
8
|
-
*/
|
|
9
|
-
export declare const isUserInactive: () => boolean;
|
|
10
|
-
/**
|
|
11
|
-
* Checks if a new day has started since session creation
|
|
12
|
-
*/
|
|
13
|
-
export declare const hasPassedMidnight: (sessionStartTime: number) => boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Gets the last activity time
|
|
16
|
-
*/
|
|
17
|
-
export declare const getLastActivityTime: () => number;
|
|
18
2
|
/**
|
|
19
3
|
* Compares UTM parameters to detect campaign changes.
|
|
20
4
|
* Only checks significant parameters: source, medium, and campaign.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DotCMSAnalyticsConfig, DotCMSContentImpressionPayload } from '
|
|
1
|
+
import { DotCMSAnalyticsConfig, DotCMSContentImpressionPayload } from '../../shared/models';
|
|
2
2
|
/** Callback function for impression events */
|
|
3
3
|
export type ImpressionCallback = (eventName: string, payload: DotCMSContentImpressionPayload) => void;
|
|
4
4
|
/** Subscription object with unsubscribe method */
|
|
@@ -17,7 +17,7 @@ export declare class DotCMSImpressionTracker {
|
|
|
17
17
|
private impressionConfig;
|
|
18
18
|
private currentPagePath;
|
|
19
19
|
private subscribers;
|
|
20
|
-
private
|
|
20
|
+
private logger;
|
|
21
21
|
constructor(config: DotCMSAnalyticsConfig);
|
|
22
22
|
/**
|
|
23
23
|
* Subscribe to impression events
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { getUVEState as
|
|
2
|
-
import "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import { getUVEState as m } from "../../../../uve/src/lib/core/core.utils.js";
|
|
2
|
+
import "../../../../uve/src/internal/constants.js";
|
|
3
|
+
import { getViewportMetrics as d, isElementMeetingVisibilityThreshold as u } from "./dot-analytics.impression.utils.js";
|
|
4
|
+
import { DEFAULT_IMPRESSION_CONFIG as l, IMPRESSION_EVENT_TYPE as g } from "../../shared/constants/dot-analytics.constants.js";
|
|
5
|
+
import { createPluginLogger as p, isBrowser as a, INITIAL_SCAN_DELAY_MS as f, findContentlets as b, extractContentletIdentifier as c, createContentletObserver as v, extractContentletData as I } from "../../shared/utils/dot-analytics.utils.js";
|
|
6
|
+
class E {
|
|
6
7
|
constructor(e) {
|
|
7
|
-
this.observer = null, this.mutationObserver = null, this.elementImpressionStates = /* @__PURE__ */ new Map(), this.sessionTrackedImpressions = /* @__PURE__ */ new Set(), this.currentPagePath = "", this.subscribers = /* @__PURE__ */ new Set(), this.
|
|
8
|
+
this.observer = null, this.mutationObserver = null, this.elementImpressionStates = /* @__PURE__ */ new Map(), this.sessionTrackedImpressions = /* @__PURE__ */ new Set(), this.currentPagePath = "", this.subscribers = /* @__PURE__ */ new Set(), this.logger = p("Impression", e), this.impressionConfig = this.resolveImpressionConfig(e.impressions);
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Subscribe to impression events
|
|
@@ -24,7 +25,7 @@ class I {
|
|
|
24
25
|
try {
|
|
25
26
|
t(e, i);
|
|
26
27
|
} catch (s) {
|
|
27
|
-
this.
|
|
28
|
+
this.logger.error("Error in impression subscriber:", s);
|
|
28
29
|
}
|
|
29
30
|
});
|
|
30
31
|
}
|
|
@@ -37,15 +38,14 @@ class I {
|
|
|
37
38
|
}
|
|
38
39
|
/** Initializes tracking: sets up observers, finds contentlets, handles visibility/navigation */
|
|
39
40
|
initialize() {
|
|
40
|
-
if (
|
|
41
|
-
if (
|
|
42
|
-
this.
|
|
41
|
+
if (a()) {
|
|
42
|
+
if (m()) {
|
|
43
|
+
this.logger.warn("Impression tracking disabled in editor mode");
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
this.initializeIntersectionObserver(),
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
);
|
|
46
|
+
this.initializeIntersectionObserver(), typeof window < "u" && setTimeout(() => {
|
|
47
|
+
this.logger.debug("Running initial scan after timeout..."), this.findAndObserveContentletElements();
|
|
48
|
+
}, f), this.initializeDynamicContentDetector(), this.initializePageVisibilityHandler(), this.initializePageNavigationHandler(), this.logger.info("Impression tracking initialized with config:", this.impressionConfig);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
/** Sets up IntersectionObserver with configured visibility threshold */
|
|
@@ -63,11 +63,9 @@ class I {
|
|
|
63
63
|
/** Finds contentlets in DOM, validates them, and starts observing (respects maxNodes limit) */
|
|
64
64
|
findAndObserveContentletElements() {
|
|
65
65
|
if (!this.observer) return;
|
|
66
|
-
const e =
|
|
67
|
-
`.${a}`
|
|
68
|
-
);
|
|
66
|
+
const e = b();
|
|
69
67
|
if (e.length === 0) {
|
|
70
|
-
this.
|
|
68
|
+
this.logger.warn("No contentlets found to track");
|
|
71
69
|
return;
|
|
72
70
|
}
|
|
73
71
|
const i = Math.min(e.length, this.impressionConfig.maxNodes);
|
|
@@ -77,45 +75,33 @@ class I {
|
|
|
77
75
|
if (r) {
|
|
78
76
|
const o = this.shouldSkipElement(n);
|
|
79
77
|
if (o) {
|
|
80
|
-
this.debug
|
|
81
|
-
`DotCMS Analytics: Skipping element ${r} (${o})`
|
|
82
|
-
);
|
|
78
|
+
this.logger.debug(`Skipping element ${r} (${o})`);
|
|
83
79
|
continue;
|
|
84
80
|
}
|
|
85
|
-
this.elementImpressionStates.has(r) || (this.observer.observe(n), this.elementImpressionStates.set(r, {
|
|
81
|
+
this.elementImpressionStates.has(r) || (n.dataset.dotAnalyticsDomIndex || (n.dataset.dotAnalyticsDomIndex = String(s)), this.observer.observe(n), this.elementImpressionStates.set(r, {
|
|
86
82
|
timer: null,
|
|
87
83
|
visibleSince: null,
|
|
88
84
|
tracked: this.hasBeenTrackedInSession(r),
|
|
89
85
|
element: n
|
|
90
|
-
}), t
|
|
86
|
+
}), t++);
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
)
|
|
89
|
+
this.logger.info(`Observing ${t} contentlets`), e.length > i && this.logger.warn(
|
|
90
|
+
`${e.length - i} contentlets not tracked (maxNodes limit: ${this.impressionConfig.maxNodes})`
|
|
91
|
+
);
|
|
96
92
|
}
|
|
97
93
|
/** Watches for new contentlets added to DOM (debounced for performance) */
|
|
98
94
|
initializeDynamicContentDetector() {
|
|
99
|
-
|
|
100
|
-
return;
|
|
101
|
-
const e = m(() => {
|
|
95
|
+
a() && (this.mutationObserver = v(() => {
|
|
102
96
|
this.findAndObserveContentletElements();
|
|
103
|
-
},
|
|
104
|
-
this.mutationObserver = new MutationObserver(() => {
|
|
105
|
-
e();
|
|
106
|
-
}), this.mutationObserver.observe(document.body, {
|
|
107
|
-
childList: !0,
|
|
108
|
-
subtree: !0
|
|
109
|
-
}), this.debug && console.warn(
|
|
110
|
-
"DotCMS Analytics: MutationObserver enabled for dynamic content detection"
|
|
111
|
-
);
|
|
97
|
+
}), this.logger.info("MutationObserver enabled for dynamic content detection"));
|
|
112
98
|
}
|
|
113
99
|
/** Cancels all timers when page is hidden (prevents false impressions) */
|
|
114
100
|
initializePageVisibilityHandler() {
|
|
115
101
|
document.addEventListener("visibilitychange", () => {
|
|
116
102
|
document.visibilityState === "hidden" && (this.elementImpressionStates.forEach((e) => {
|
|
117
103
|
e.timer !== null && (window.clearTimeout(e.timer), e.timer = null, e.visibleSince = null);
|
|
118
|
-
}), this.
|
|
104
|
+
}), this.logger.warn("Page hidden, all impression timers cancelled"));
|
|
119
105
|
});
|
|
120
106
|
}
|
|
121
107
|
/** Resets tracking on SPA navigation (listens to pushState, replaceState, popstate) */
|
|
@@ -123,8 +109,8 @@ class I {
|
|
|
123
109
|
this.currentPagePath = window.location.pathname;
|
|
124
110
|
const e = () => {
|
|
125
111
|
const s = window.location.pathname;
|
|
126
|
-
s !== this.currentPagePath && (this.
|
|
127
|
-
`
|
|
112
|
+
s !== this.currentPagePath && (this.logger.warn(
|
|
113
|
+
`Navigation detected (${this.currentPagePath} → ${s}), resetting impression tracking`
|
|
128
114
|
), this.currentPagePath = s, this.sessionTrackedImpressions.clear(), this.elementImpressionStates.forEach((n) => {
|
|
129
115
|
n.timer !== null && (window.clearTimeout(n.timer), n.timer = null, n.visibleSince = null);
|
|
130
116
|
}), this.elementImpressionStates.clear());
|
|
@@ -135,7 +121,7 @@ class I {
|
|
|
135
121
|
i.apply(this, s), e();
|
|
136
122
|
}, history.replaceState = function(...s) {
|
|
137
123
|
t.apply(this, s), e();
|
|
138
|
-
}
|
|
124
|
+
};
|
|
139
125
|
}
|
|
140
126
|
/** Handles visibility changes: starts timer on enter, cancels on exit */
|
|
141
127
|
processIntersectionChanges(e) {
|
|
@@ -148,23 +134,23 @@ class I {
|
|
|
148
134
|
startImpressionDwellTimer(e, i) {
|
|
149
135
|
const t = this.elementImpressionStates.get(e);
|
|
150
136
|
t && (t.tracked || this.hasBeenTrackedInSession(e) || t.timer === null && document.visibilityState === "visible" && (t.visibleSince = Date.now(), t.element = i, t.timer = window.setTimeout(() => {
|
|
151
|
-
this.isElementStillVisible(i) ? this.trackAndSendImpression(e, i) : (this.
|
|
152
|
-
`
|
|
137
|
+
this.isElementStillVisible(i) ? this.trackAndSendImpression(e, i) : (this.logger.warn(
|
|
138
|
+
`Dwell timer expired for ${e} but element no longer visible, skipping impression`
|
|
153
139
|
), t.timer = null, t.visibleSince = null);
|
|
154
|
-
}, this.impressionConfig.dwellMs), this.debug
|
|
155
|
-
`
|
|
140
|
+
}, this.impressionConfig.dwellMs), this.logger.debug(
|
|
141
|
+
`Started dwell timer for ${e} (${this.impressionConfig.dwellMs}ms)`
|
|
156
142
|
)));
|
|
157
143
|
}
|
|
158
144
|
/** Cancels active dwell timer (element left viewport before dwell time) */
|
|
159
145
|
cancelImpressionDwellTimer(e) {
|
|
160
146
|
const i = this.elementImpressionStates.get(e);
|
|
161
|
-
!i || i.timer === null || (window.clearTimeout(i.timer), i.timer = null, i.visibleSince = null, this.debug
|
|
147
|
+
!i || i.timer === null || (window.clearTimeout(i.timer), i.timer = null, i.visibleSince = null, this.logger.debug(`Cancelled dwell timer for ${e}`));
|
|
162
148
|
}
|
|
163
149
|
/** Fires impression event with content & position data (page data added by enricher plugin) */
|
|
164
150
|
trackAndSendImpression(e, i) {
|
|
165
151
|
const t = this.elementImpressionStates.get(e);
|
|
166
152
|
if (!t) return;
|
|
167
|
-
const s = t.visibleSince ? Date.now() - t.visibleSince : 0, n =
|
|
153
|
+
const s = t.visibleSince ? Date.now() - t.visibleSince : 0, n = I(i), r = d(i), o = parseInt(i.dataset.dotAnalyticsDomIndex || "-1", 10), h = {
|
|
168
154
|
content: {
|
|
169
155
|
identifier: n.identifier,
|
|
170
156
|
inode: n.inode,
|
|
@@ -173,15 +159,13 @@ class I {
|
|
|
173
159
|
},
|
|
174
160
|
position: {
|
|
175
161
|
viewport_offset_pct: r.offsetPercentage,
|
|
176
|
-
dom_index:
|
|
177
|
-
document.querySelectorAll(`.${a}`)
|
|
178
|
-
).indexOf(i)
|
|
162
|
+
dom_index: o
|
|
179
163
|
}
|
|
180
164
|
};
|
|
181
|
-
this.notifySubscribers(
|
|
182
|
-
`
|
|
165
|
+
this.notifySubscribers(g, h), this.markImpressionAsTracked(e), t.timer = null, t.visibleSince = null, t.tracked = !0, this.observer && this.observer.unobserve(i), this.logger.info(
|
|
166
|
+
`Fired impression for ${e} (dwell: ${s}ms) - element unobserved`,
|
|
183
167
|
n
|
|
184
|
-
)
|
|
168
|
+
);
|
|
185
169
|
}
|
|
186
170
|
/** Returns skip reason if element is hidden/too small, null if trackable */
|
|
187
171
|
shouldSkipElement(e) {
|
|
@@ -193,7 +177,7 @@ class I {
|
|
|
193
177
|
}
|
|
194
178
|
/** Post-dwell check: verifies element still meets visibility threshold */
|
|
195
179
|
isElementStillVisible(e) {
|
|
196
|
-
return document.visibilityState !== "visible" ? !1 :
|
|
180
|
+
return document.visibilityState !== "visible" ? !1 : u(
|
|
197
181
|
e,
|
|
198
182
|
this.impressionConfig.visibilityThreshold
|
|
199
183
|
);
|
|
@@ -210,9 +194,9 @@ class I {
|
|
|
210
194
|
cleanup() {
|
|
211
195
|
this.observer && (this.observer.disconnect(), this.observer = null), this.mutationObserver && (this.mutationObserver.disconnect(), this.mutationObserver = null), this.elementImpressionStates.forEach((e) => {
|
|
212
196
|
e.timer !== null && window.clearTimeout(e.timer);
|
|
213
|
-
}), this.elementImpressionStates.clear(), this.subscribers.clear(), this.
|
|
197
|
+
}), this.elementImpressionStates.clear(), this.subscribers.clear(), this.logger.info("Impression tracking cleaned up");
|
|
214
198
|
}
|
|
215
199
|
}
|
|
216
200
|
export {
|
|
217
|
-
|
|
201
|
+
E as DotCMSImpressionTracker
|
|
218
202
|
};
|
|
@@ -3,7 +3,6 @@ import { DotCMSAnalyticsConfig } from '../../shared/models';
|
|
|
3
3
|
/**
|
|
4
4
|
* Impression Plugin for DotAnalytics
|
|
5
5
|
* Handles automatic tracking of content visibility and impressions.
|
|
6
|
-
* Only activates when config.impressions is enabled (true or config object).
|
|
7
6
|
*
|
|
8
7
|
* This plugin initializes the impression tracker which:
|
|
9
8
|
* - Uses IntersectionObserver to detect when contentlets are visible
|
|
@@ -17,13 +16,16 @@ import { DotCMSAnalyticsConfig } from '../../shared/models';
|
|
|
17
16
|
* 3. Main Plugin - Sends to queue/server
|
|
18
17
|
* 4. Impression Plugin - Runs independently, fires events via instance.track()
|
|
19
18
|
*
|
|
19
|
+
* Note: This plugin is only registered if config.impressions is enabled.
|
|
20
|
+
* See getEnhancedTrackingPlugins() for conditional loading logic.
|
|
21
|
+
*
|
|
20
22
|
* @param {DotCMSAnalyticsConfig} config - Configuration with impressions settings
|
|
21
23
|
* @returns {Object} Plugin object with lifecycle methods
|
|
22
24
|
*/
|
|
23
25
|
export declare const dotAnalyticsImpressionPlugin: (config: DotCMSAnalyticsConfig) => {
|
|
24
26
|
name: string;
|
|
25
27
|
/**
|
|
26
|
-
* Initialize impression tracking
|
|
28
|
+
* Initialize impression tracking
|
|
27
29
|
* Called when Analytics.js initializes the plugin with instance context
|
|
28
30
|
* @param instance - Analytics.js instance with track method
|
|
29
31
|
*/
|