@technoapple/ga4 1.0.4 → 1.1.0

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.
Files changed (74) hide show
  1. package/.github/workflows/node.js.yml +31 -31
  2. package/.prettierignore +1 -1
  3. package/LICENSE +21 -21
  4. package/README.md +386 -48
  5. package/REQUIREMENTS.md +548 -0
  6. package/babel.config.js +5 -5
  7. package/build/main/ga4/ga4.d.ts +13 -0
  8. package/build/main/ga4/ga4.js +24 -1
  9. package/build/main/helpers/debounce.d.ts +5 -0
  10. package/build/main/helpers/debounce.js +23 -0
  11. package/build/main/helpers/delegate.d.ts +8 -0
  12. package/build/main/helpers/delegate.js +37 -0
  13. package/build/main/helpers/dom-ready.d.ts +1 -0
  14. package/build/main/helpers/dom-ready.js +13 -0
  15. package/build/main/helpers/parse-url.d.ts +11 -0
  16. package/build/main/helpers/parse-url.js +32 -0
  17. package/build/main/helpers/session.d.ts +4 -0
  18. package/build/main/helpers/session.js +50 -0
  19. package/build/main/index.d.ts +9 -0
  20. package/build/main/index.js +19 -2
  21. package/build/main/plugins/clean-url-tracker.d.ts +17 -0
  22. package/build/main/plugins/clean-url-tracker.js +105 -0
  23. package/build/main/plugins/event-tracker.d.ts +27 -0
  24. package/build/main/plugins/event-tracker.js +76 -0
  25. package/build/main/plugins/impression-tracker.d.ts +32 -0
  26. package/build/main/plugins/impression-tracker.js +202 -0
  27. package/build/main/plugins/index.d.ts +8 -0
  28. package/build/main/plugins/index.js +20 -0
  29. package/build/main/plugins/media-query-tracker.d.ts +20 -0
  30. package/build/main/plugins/media-query-tracker.js +96 -0
  31. package/build/main/plugins/outbound-form-tracker.d.ts +17 -0
  32. package/build/main/plugins/outbound-form-tracker.js +55 -0
  33. package/build/main/plugins/outbound-link-tracker.d.ts +19 -0
  34. package/build/main/plugins/outbound-link-tracker.js +63 -0
  35. package/build/main/plugins/page-visibility-tracker.d.ts +24 -0
  36. package/build/main/plugins/page-visibility-tracker.js +93 -0
  37. package/build/main/plugins/url-change-tracker.d.ts +20 -0
  38. package/build/main/plugins/url-change-tracker.js +76 -0
  39. package/build/main/types/plugins.d.ts +78 -0
  40. package/build/main/types/plugins.js +3 -0
  41. package/build/tsconfig.tsbuildinfo +1 -1
  42. package/docs/examples/react.md +95 -0
  43. package/docs/examples/vanilla.md +65 -0
  44. package/docs/examples/vue.md +87 -0
  45. package/jest.config.ts +195 -195
  46. package/package.json +56 -56
  47. package/src/dataLayer.ts +85 -85
  48. package/src/ga4/ga4.ts +69 -40
  49. package/src/ga4/ga4option.ts +4 -4
  50. package/src/ga4/index.ts +4 -4
  51. package/src/helpers/debounce.ts +28 -0
  52. package/src/helpers/delegate.ts +51 -0
  53. package/src/helpers/dom-ready.ts +7 -0
  54. package/src/helpers/parse-url.ts +37 -0
  55. package/src/helpers/session.ts +39 -0
  56. package/src/index.ts +34 -7
  57. package/src/plugins/clean-url-tracker.ts +112 -0
  58. package/src/plugins/event-tracker.ts +90 -0
  59. package/src/plugins/impression-tracker.ts +230 -0
  60. package/src/plugins/index.ts +8 -0
  61. package/src/plugins/media-query-tracker.ts +116 -0
  62. package/src/plugins/outbound-form-tracker.ts +65 -0
  63. package/src/plugins/outbound-link-tracker.ts +72 -0
  64. package/src/plugins/page-visibility-tracker.ts +104 -0
  65. package/src/plugins/url-change-tracker.ts +84 -0
  66. package/src/types/dataLayer.ts +9 -9
  67. package/src/types/global.ts +12 -12
  68. package/src/types/gtag.ts +259 -259
  69. package/src/types/plugins.ts +98 -0
  70. package/src/util.ts +18 -18
  71. package/test/dataLayer.spec.ts +55 -55
  72. package/test/ga4.spec.ts +36 -36
  73. package/tsconfig.json +28 -28
  74. package/tsconfig.module.json +11 -11
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ImpressionTracker = void 0;
4
+ const dom_ready_1 = require("../helpers/dom-ready");
5
+ /**
6
+ * Tracks when specific DOM elements become visible in the viewport
7
+ * using `IntersectionObserver`.
8
+ *
9
+ * Useful for tracking ad impressions, CTA visibility, or any
10
+ * element that enters the user's viewport.
11
+ */
12
+ class ImpressionTracker {
13
+ send;
14
+ rootMargin;
15
+ attributePrefix;
16
+ eventName;
17
+ hitFilter;
18
+ items = [];
19
+ elementMap = {};
20
+ thresholdMap = {};
21
+ mutationObserver = null;
22
+ impressedIds = new Set();
23
+ supported;
24
+ constructor(send, options) {
25
+ this.supported =
26
+ typeof IntersectionObserver !== 'undefined' &&
27
+ typeof MutationObserver !== 'undefined';
28
+ this.send = send;
29
+ this.rootMargin = options?.rootMargin ?? '0px';
30
+ this.attributePrefix = options?.attributePrefix ?? 'data-ga4-';
31
+ this.eventName = options?.eventName ?? 'element_impression';
32
+ this.hitFilter = options?.hitFilter;
33
+ if (!this.supported)
34
+ return;
35
+ this.handleIntersectionChanges = this.handleIntersectionChanges.bind(this);
36
+ this.handleDomMutations = this.handleDomMutations.bind(this);
37
+ const elements = options?.elements;
38
+ (0, dom_ready_1.domReady)(() => {
39
+ if (elements && elements.length > 0) {
40
+ this.observeElements(elements);
41
+ }
42
+ });
43
+ }
44
+ observeElements(elements) {
45
+ if (!this.supported)
46
+ return;
47
+ const newItems = this.normalizeElements(elements);
48
+ this.items = this.items.concat(newItems);
49
+ newItems.forEach((item) => {
50
+ const observer = this.getObserverForThreshold(item.threshold);
51
+ const element = document.getElementById(item.id);
52
+ this.elementMap[item.id] = element;
53
+ if (element) {
54
+ observer.observe(element);
55
+ }
56
+ });
57
+ if (!this.mutationObserver && document.body) {
58
+ this.mutationObserver = new MutationObserver(this.handleDomMutations);
59
+ this.mutationObserver.observe(document.body, {
60
+ childList: true,
61
+ subtree: true,
62
+ });
63
+ }
64
+ }
65
+ unobserveElements(elements) {
66
+ if (!this.supported)
67
+ return;
68
+ const idsToRemove = new Set(elements.map((el) => (typeof el === 'string' ? el : el.id)));
69
+ this.items = this.items.filter((item) => {
70
+ if (idsToRemove.has(item.id)) {
71
+ const element = this.elementMap[item.id];
72
+ if (element && this.thresholdMap[item.threshold]) {
73
+ this.thresholdMap[item.threshold].unobserve(element);
74
+ }
75
+ delete this.elementMap[item.id];
76
+ return false;
77
+ }
78
+ return true;
79
+ });
80
+ if (this.items.length === 0) {
81
+ this.disconnectAll();
82
+ }
83
+ }
84
+ unobserveAllElements() {
85
+ this.disconnectAll();
86
+ this.items = [];
87
+ this.elementMap = {};
88
+ }
89
+ normalizeElements(elements) {
90
+ return elements.map((el) => {
91
+ if (typeof el === 'string') {
92
+ return { id: el, threshold: 0, trackFirstImpressionOnly: true };
93
+ }
94
+ return {
95
+ id: el.id,
96
+ threshold: el.threshold ?? 0,
97
+ trackFirstImpressionOnly: el.trackFirstImpressionOnly ?? true,
98
+ };
99
+ });
100
+ }
101
+ getObserverForThreshold(threshold) {
102
+ if (!this.thresholdMap[threshold]) {
103
+ this.thresholdMap[threshold] = new IntersectionObserver(this.handleIntersectionChanges, {
104
+ rootMargin: this.rootMargin,
105
+ threshold: [threshold],
106
+ });
107
+ }
108
+ return this.thresholdMap[threshold];
109
+ }
110
+ handleIntersectionChanges(entries) {
111
+ entries.forEach((entry) => {
112
+ if (!entry.isIntersecting)
113
+ return;
114
+ const id = entry.target.id;
115
+ if (!id)
116
+ return;
117
+ const item = this.items.find((i) => i.id === id);
118
+ if (!item)
119
+ return;
120
+ if (item.trackFirstImpressionOnly && this.impressedIds.has(id))
121
+ return;
122
+ this.impressedIds.add(id);
123
+ const prefix = this.attributePrefix;
124
+ const attrParams = {};
125
+ for (let i = 0; i < entry.target.attributes.length; i++) {
126
+ const attr = entry.target.attributes[i];
127
+ if (attr.name.startsWith(prefix)) {
128
+ const key = attr.name.slice(prefix.length).replace(/-/g, '_');
129
+ attrParams[key] = attr.value;
130
+ }
131
+ }
132
+ let params = {
133
+ element_id: id,
134
+ ...attrParams,
135
+ };
136
+ if (this.hitFilter) {
137
+ const filtered = this.hitFilter(params, entry.target);
138
+ if (filtered === null)
139
+ return;
140
+ params = filtered;
141
+ }
142
+ this.send(this.eventName, params);
143
+ if (item.trackFirstImpressionOnly && this.thresholdMap[item.threshold]) {
144
+ this.thresholdMap[item.threshold].unobserve(entry.target);
145
+ }
146
+ });
147
+ }
148
+ handleDomMutations(mutations) {
149
+ mutations.forEach((mutation) => {
150
+ mutation.addedNodes.forEach((node) => {
151
+ if (node.nodeType !== Node.ELEMENT_NODE)
152
+ return;
153
+ this.walkNodeTree(node, (id) => {
154
+ const element = document.getElementById(id);
155
+ if (element) {
156
+ this.elementMap[id] = element;
157
+ const item = this.items.find((i) => i.id === id);
158
+ if (item && this.thresholdMap[item.threshold]) {
159
+ this.thresholdMap[item.threshold].observe(element);
160
+ }
161
+ }
162
+ });
163
+ });
164
+ mutation.removedNodes.forEach((node) => {
165
+ if (node.nodeType !== Node.ELEMENT_NODE)
166
+ return;
167
+ this.walkNodeTree(node, (id) => {
168
+ const element = this.elementMap[id];
169
+ if (element) {
170
+ const item = this.items.find((i) => i.id === id);
171
+ if (item && this.thresholdMap[item.threshold]) {
172
+ this.thresholdMap[item.threshold].unobserve(element);
173
+ }
174
+ }
175
+ this.elementMap[id] = null;
176
+ });
177
+ });
178
+ });
179
+ }
180
+ walkNodeTree(node, callback) {
181
+ if (node.id && node.id in this.elementMap) {
182
+ callback(node.id);
183
+ }
184
+ for (let i = 0; i < node.children.length; i++) {
185
+ this.walkNodeTree(node.children[i], callback);
186
+ }
187
+ }
188
+ disconnectAll() {
189
+ Object.values(this.thresholdMap).forEach((observer) => observer.disconnect());
190
+ this.thresholdMap = {};
191
+ if (this.mutationObserver) {
192
+ this.mutationObserver.disconnect();
193
+ this.mutationObserver = null;
194
+ }
195
+ }
196
+ remove() {
197
+ this.unobserveAllElements();
198
+ this.impressedIds.clear();
199
+ }
200
+ }
201
+ exports.ImpressionTracker = ImpressionTracker;
202
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1wcmVzc2lvbi10cmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BsdWdpbnMvaW1wcmVzc2lvbi10cmFja2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLG9EQUFnRDtBQVFoRDs7Ozs7O0dBTUc7QUFDSCxNQUFhLGlCQUFpQjtJQUNsQixJQUFJLENBQWU7SUFDbkIsVUFBVSxDQUFTO0lBQ25CLGVBQWUsQ0FBUztJQUN4QixTQUFTLENBQVM7SUFDbEIsU0FBUyxDQUF5QztJQUNsRCxLQUFLLEdBQXFCLEVBQUUsQ0FBQztJQUM3QixVQUFVLEdBQW1DLEVBQUUsQ0FBQztJQUNoRCxZQUFZLEdBQXlDLEVBQUUsQ0FBQztJQUN4RCxnQkFBZ0IsR0FBNEIsSUFBSSxDQUFDO0lBQ2pELFlBQVksR0FBZ0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUN0QyxTQUFTLENBQVU7SUFFM0IsWUFBWSxJQUFrQixFQUFFLE9BQWtDO1FBQzlELElBQUksQ0FBQyxTQUFTO1lBQ1YsT0FBTyxvQkFBb0IsS0FBSyxXQUFXO2dCQUMzQyxPQUFPLGdCQUFnQixLQUFLLFdBQVcsQ0FBQztRQUU1QyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sRUFBRSxVQUFVLElBQUksS0FBSyxDQUFDO1FBQy9DLElBQUksQ0FBQyxlQUFlLEdBQUcsT0FBTyxFQUFFLGVBQWUsSUFBSSxXQUFXLENBQUM7UUFDL0QsSUFBSSxDQUFDLFNBQVMsR0FBRyxPQUFPLEVBQUUsU0FBUyxJQUFJLG9CQUFvQixDQUFDO1FBQzVELElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxFQUFFLFNBQVMsQ0FBQztRQUVwQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVM7WUFBRSxPQUFPO1FBRTVCLElBQUksQ0FBQyx5QkFBeUIsR0FBRyxJQUFJLENBQUMseUJBQXlCLENBQUMsSUFBSSxDQUFDLElBQUks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
@@ -0,0 +1,8 @@
1
+ export { EventTracker } from './event-tracker';
2
+ export { OutboundLinkTracker } from './outbound-link-tracker';
3
+ export { OutboundFormTracker } from './outbound-form-tracker';
4
+ export { PageVisibilityTracker } from './page-visibility-tracker';
5
+ export { UrlChangeTracker } from './url-change-tracker';
6
+ export { ImpressionTracker } from './impression-tracker';
7
+ export { CleanUrlTracker } from './clean-url-tracker';
8
+ export { MediaQueryTracker } from './media-query-tracker';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MediaQueryTracker = exports.CleanUrlTracker = exports.ImpressionTracker = exports.UrlChangeTracker = exports.PageVisibilityTracker = exports.OutboundFormTracker = exports.OutboundLinkTracker = exports.EventTracker = void 0;
4
+ var event_tracker_1 = require("./event-tracker");
5
+ Object.defineProperty(exports, "EventTracker", { enumerable: true, get: function () { return event_tracker_1.EventTracker; } });
6
+ var outbound_link_tracker_1 = require("./outbound-link-tracker");
7
+ Object.defineProperty(exports, "OutboundLinkTracker", { enumerable: true, get: function () { return outbound_link_tracker_1.OutboundLinkTracker; } });
8
+ var outbound_form_tracker_1 = require("./outbound-form-tracker");
9
+ Object.defineProperty(exports, "OutboundFormTracker", { enumerable: true, get: function () { return outbound_form_tracker_1.OutboundFormTracker; } });
10
+ var page_visibility_tracker_1 = require("./page-visibility-tracker");
11
+ Object.defineProperty(exports, "PageVisibilityTracker", { enumerable: true, get: function () { return page_visibility_tracker_1.PageVisibilityTracker; } });
12
+ var url_change_tracker_1 = require("./url-change-tracker");
13
+ Object.defineProperty(exports, "UrlChangeTracker", { enumerable: true, get: function () { return url_change_tracker_1.UrlChangeTracker; } });
14
+ var impression_tracker_1 = require("./impression-tracker");
15
+ Object.defineProperty(exports, "ImpressionTracker", { enumerable: true, get: function () { return impression_tracker_1.ImpressionTracker; } });
16
+ var clean_url_tracker_1 = require("./clean-url-tracker");
17
+ Object.defineProperty(exports, "CleanUrlTracker", { enumerable: true, get: function () { return clean_url_tracker_1.CleanUrlTracker; } });
18
+ var media_query_tracker_1 = require("./media-query-tracker");
19
+ Object.defineProperty(exports, "MediaQueryTracker", { enumerable: true, get: function () { return media_query_tracker_1.MediaQueryTracker; } });
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGx1Z2lucy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpREFBK0M7QUFBdEMsNkdBQUEsWUFBWSxPQUFBO0FBQ3JCLGlFQUE4RDtBQUFyRCw0SEFBQSxtQkFBbUIsT0FBQTtBQUM1QixpRUFBOEQ7QUFBckQsNEhBQUEsbUJBQW1CLE9BQUE7QUFDNUIscUVBQWtFO0FBQXpELGdJQUFBLHFCQUFxQixPQUFBO0FBQzlCLDJEQUF3RDtBQUEvQyxzSEFBQSxnQkFBZ0IsT0FBQTtBQUN6QiwyREFBeUQ7QUFBaEQsdUhBQUEsaUJBQWlCLE9BQUE7QUFDMUIseURBQXNEO0FBQTdDLG9IQUFBLGVBQWUsT0FBQTtBQUN4Qiw2REFBMEQ7QUFBakQsd0hBQUEsaUJBQWlCLE9BQUEifQ==
@@ -0,0 +1,20 @@
1
+ import { GA4Plugin, SendFunction, MediaQueryTrackerOptions } from '../types/plugins';
2
+ /**
3
+ * Tracks CSS media query breakpoint matching and changes.
4
+ *
5
+ * Fires an event whenever the active breakpoint changes
6
+ * (e.g. from "mobile" to "desktop"), debounced to avoid
7
+ * rapid-fire events during window resizing.
8
+ */
9
+ export declare class MediaQueryTracker implements GA4Plugin {
10
+ private send;
11
+ private changeTimeout;
12
+ private eventName;
13
+ private changeTemplate?;
14
+ private hitFilter?;
15
+ private trackedQueries;
16
+ private debouncedHandlers;
17
+ constructor(send: SendFunction, options?: MediaQueryTrackerOptions);
18
+ private trackDefinition;
19
+ remove(): void;
20
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MediaQueryTracker = void 0;
4
+ const debounce_1 = require("../helpers/debounce");
5
+ function getMatchingValue(definition) {
6
+ for (const item of definition.items) {
7
+ if (window.matchMedia(item.media).matches) {
8
+ return item.name;
9
+ }
10
+ }
11
+ return '(not set)';
12
+ }
13
+ /**
14
+ * Tracks CSS media query breakpoint matching and changes.
15
+ *
16
+ * Fires an event whenever the active breakpoint changes
17
+ * (e.g. from "mobile" to "desktop"), debounced to avoid
18
+ * rapid-fire events during window resizing.
19
+ */
20
+ class MediaQueryTracker {
21
+ send;
22
+ changeTimeout;
23
+ eventName;
24
+ changeTemplate;
25
+ hitFilter;
26
+ trackedQueries = [];
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ debouncedHandlers = [];
29
+ constructor(send, options) {
30
+ this.send = send;
31
+ this.changeTimeout = options?.changeTimeout ?? 1000;
32
+ this.eventName = options?.eventName ?? 'media_query_change';
33
+ this.changeTemplate = options?.changeTemplate;
34
+ this.hitFilter = options?.hitFilter;
35
+ if (typeof window.matchMedia !== 'function')
36
+ return;
37
+ const definitions = options?.definitions ?? [];
38
+ definitions.forEach((definition) => {
39
+ this.trackDefinition(definition);
40
+ });
41
+ }
42
+ trackDefinition(definition) {
43
+ const initialValue = getMatchingValue(definition);
44
+ definition.items.forEach((item) => {
45
+ const mql = window.matchMedia(item.media);
46
+ const debouncedHandler = (0, debounce_1.debounce)(() => {
47
+ const newValue = getMatchingValue(definition);
48
+ const tracked = this.trackedQueries.find((tq) => tq.definition === definition);
49
+ if (!tracked || newValue === tracked.currentValue)
50
+ return;
51
+ const oldValue = tracked.currentValue;
52
+ tracked.currentValue = newValue;
53
+ const changeLabel = this.changeTemplate
54
+ ? this.changeTemplate(oldValue, newValue)
55
+ : `${oldValue} => ${newValue}`;
56
+ let params = {
57
+ media_query_name: definition.name,
58
+ media_query_value: newValue,
59
+ media_query_change: changeLabel,
60
+ };
61
+ if (this.hitFilter) {
62
+ const filtered = this.hitFilter(params);
63
+ if (filtered === null)
64
+ return;
65
+ params = filtered;
66
+ }
67
+ this.send(this.eventName, params);
68
+ }, this.changeTimeout);
69
+ this.debouncedHandlers.push(debouncedHandler);
70
+ const listener = () => {
71
+ debouncedHandler();
72
+ };
73
+ if (typeof mql.addEventListener === 'function') {
74
+ mql.addEventListener('change', listener);
75
+ }
76
+ this.trackedQueries.push({
77
+ definition,
78
+ mql,
79
+ currentValue: initialValue,
80
+ listener,
81
+ });
82
+ });
83
+ }
84
+ remove() {
85
+ this.trackedQueries.forEach(({ mql, listener }) => {
86
+ if (typeof mql.removeEventListener === 'function') {
87
+ mql.removeEventListener('change', listener);
88
+ }
89
+ });
90
+ this.debouncedHandlers.forEach((d) => d.cancel());
91
+ this.trackedQueries = [];
92
+ this.debouncedHandlers = [];
93
+ }
94
+ }
95
+ exports.MediaQueryTracker = MediaQueryTracker;
96
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVkaWEtcXVlcnktdHJhY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wbHVnaW5zL21lZGlhLXF1ZXJ5LXRyYWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0Esa0RBQWtFO0FBU2xFLFNBQVMsZ0JBQWdCLENBQUMsVUFBZ0M7SUFDdEQsS0FBSyxNQUFNLElBQUksSUFBSSxVQUFVLENBQUMsS0FBSyxFQUFFO1FBQ2pDLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxFQUFFO1lBQ3ZDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQztTQUNwQjtLQUNKO0lBQ0QsT0FBTyxXQUFXLENBQUM7QUFDdkIsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQWEsaUJBQWlCO0lBQ2xCLElBQUksQ0FBZTtJQUNuQixhQUFhLENBQVM7SUFDdEIsU0FBUyxDQUFTO0lBQ2xCLGNBQWMsQ0FBOEM7SUFDNUQsU0FBUyxDQUF5QztJQUNsRCxjQUFjLEdBQW1CLEVBQUUsQ0FBQztJQUM1Qyw4REFBOEQ7SUFDdEQsaUJBQWlCLEdBQTZCLEVBQUUsQ0FBQztJQUV6RCxZQUFZLElBQWtCLEVBQUUsT0FBa0M7UUFDOUQsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLGFBQWEsR0FBRyxPQUFPLEVBQUUsYUFBYSxJQUFJLElBQUksQ0FBQztRQUNwRCxJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLElBQUksb0JBQW9CLENBQUM7UUFDNUQsSUFBSSxDQUFDLGNBQWMsR0FBRyxPQUFPLEVBQUUsY0FBYyxDQUFDO1FBQzlDLElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxFQUFFLFNBQVMsQ0FBQztRQUVwQyxJQUFJLE9BQU8sTUFBTSxDQUFDLFVBQVUsS0FBSyxVQUFVO1lBQUUsT0FBTztRQUVwRCxNQUFNLFdBQVcsR0FBRyxPQUFP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
@@ -0,0 +1,17 @@
1
+ import { GA4Plugin, SendFunction, OutboundFormTrackerOptions } from '../types/plugins';
2
+ /**
3
+ * Automatically tracks form submissions to external domains.
4
+ *
5
+ * Sends an event when a form's `action` attribute points to a
6
+ * different hostname than the current page.
7
+ */
8
+ export declare class OutboundFormTracker implements GA4Plugin {
9
+ private delegateHandle;
10
+ private send;
11
+ private eventName;
12
+ private shouldTrackOutboundForm;
13
+ private hitFilter?;
14
+ constructor(send: SendFunction, options?: OutboundFormTrackerOptions);
15
+ private handleFormSubmit;
16
+ remove(): void;
17
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OutboundFormTracker = void 0;
4
+ const delegate_1 = require("../helpers/delegate");
5
+ const parse_url_1 = require("../helpers/parse-url");
6
+ function defaultShouldTrack(form, parseUrlFn) {
7
+ const action = form.action;
8
+ if (!action)
9
+ return false;
10
+ const url = parseUrlFn(action);
11
+ return url.hostname !== location.hostname && url.protocol.startsWith('http');
12
+ }
13
+ /**
14
+ * Automatically tracks form submissions to external domains.
15
+ *
16
+ * Sends an event when a form's `action` attribute points to a
17
+ * different hostname than the current page.
18
+ */
19
+ class OutboundFormTracker {
20
+ delegateHandle;
21
+ send;
22
+ eventName;
23
+ shouldTrackOutboundForm;
24
+ hitFilter;
25
+ constructor(send, options) {
26
+ this.send = send;
27
+ this.eventName = options?.eventName ?? 'outbound_form_submit';
28
+ this.shouldTrackOutboundForm = options?.shouldTrackOutboundForm ?? defaultShouldTrack;
29
+ this.hitFilter = options?.hitFilter;
30
+ const formSelector = options?.formSelector ?? 'form';
31
+ this.delegateHandle = (0, delegate_1.delegate)(document, 'submit', formSelector, (event, element) => this.handleFormSubmit(event, element), { composed: true, useCapture: true });
32
+ }
33
+ handleFormSubmit(event, form) {
34
+ if (!this.shouldTrackOutboundForm(form, parse_url_1.parseUrl))
35
+ return;
36
+ const url = (0, parse_url_1.parseUrl)(form.action);
37
+ let params = {
38
+ form_action: url.href,
39
+ form_domain: url.hostname,
40
+ outbound: true,
41
+ };
42
+ if (this.hitFilter) {
43
+ const filtered = this.hitFilter(params, form, event);
44
+ if (filtered === null)
45
+ return;
46
+ params = filtered;
47
+ }
48
+ this.send(this.eventName, params);
49
+ }
50
+ remove() {
51
+ this.delegateHandle.destroy();
52
+ }
53
+ }
54
+ exports.OutboundFormTracker = OutboundFormTracker;
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0Ym91bmQtZm9ybS10cmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BsdWdpbnMvb3V0Ym91bmQtZm9ybS10cmFja2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLGtEQUErRDtBQUMvRCxvREFBZ0Q7QUFFaEQsU0FBUyxrQkFBa0IsQ0FBQyxJQUFxQixFQUFFLFVBQWlGO0lBQ2hJLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUM7SUFDM0IsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLEtBQUssQ0FBQztJQUMxQixNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0IsT0FBTyxHQUFHLENBQUMsUUFBUSxLQUFLLFFBQVEsQ0FBQyxRQUFRLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDakYsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBYSxtQkFBbUI7SUFDcEIsY0FBYyxDQUFpQjtJQUMvQixJQUFJLENBQWU7SUFDbkIsU0FBUyxDQUFTO0lBQ2xCLHVCQUF1QixDQUFxRTtJQUM1RixTQUFTLENBQTJDO0lBRTVELFlBQVksSUFBa0IsRUFBRSxPQUFvQztRQUNoRSxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLElBQUksc0JBQXNCLENBQUM7UUFDOUQsSUFBSSxDQUFDLHVCQUF1QixHQUFHLE9BQU8sRUFBRSx1QkFBdUIsSUFBSSxrQkFBa0IsQ0FBQztRQUN0RixJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLENBQUM7UUFFcEMsTUFBTSxZQUFZLEdBQUcsT0FBTyxFQUFFLFlBQVksSUFBSSxNQUFNLENBQUM7UUFFckQsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFBLG1CQUFRLEVBQzFCLFFBQVEsRUFDUixRQUFRLEVBQ1IsWUFBWSxFQUNaLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEtBQUssRUFBRSxPQUEwQixDQUFDLEVBQzVFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLENBQ3ZDLENBQUM7SUFDTixDQUFDO0lBRU8sZ0JBQWdCLENBQUMsS0FBWSxFQUFFLElBQXFCO1FBQ3hELElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxFQUFFLG9CQUFRLENBQUM7WUFBRSxPQUFPO1FBRTFELE1BQU0sR0FBRyxHQUFHLElBQUEsb0JBQVEsRUFBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFbEMsSUFBSSxNQUFNLEdBQTRCO1lBQ2xDLFdBQVcsRUFBRSxHQUFHLENBQUMsSUFBSTtZQUNyQixXQUFXLEVBQUUsR0FBRyxDQUFDLFFBQVE7WUFDekIsUUFBUSxFQUFFLElBQUk7U0FDakIsQ0FBQztRQUVGLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUNoQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDckQsSUFBSSxRQUFRLEtBQUssSUFBSTtnQkFBRSxPQUFPO1lBQzlCLE1BQU0sR0FBRyxRQUFRLENBQUM7U0FDckI7UUFFRCxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVELE1BQU07UUFDRixJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2xDLENBQUM7Q0FDSjtBQS9DRCxrREErQ0MifQ==
@@ -0,0 +1,19 @@
1
+ import { GA4Plugin, SendFunction, OutboundLinkTrackerOptions } from '../types/plugins';
2
+ /**
3
+ * Automatically tracks clicks on outbound links (links to external domains).
4
+ *
5
+ * Sends an event when a user clicks a link whose hostname differs
6
+ * from the current page's hostname.
7
+ */
8
+ export declare class OutboundLinkTracker implements GA4Plugin {
9
+ private delegates;
10
+ private send;
11
+ private events;
12
+ private linkSelector;
13
+ private eventName;
14
+ private shouldTrackOutboundLink;
15
+ private hitFilter?;
16
+ constructor(send: SendFunction, options?: OutboundLinkTrackerOptions);
17
+ private handleLinkInteraction;
18
+ remove(): void;
19
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OutboundLinkTracker = void 0;
4
+ const delegate_1 = require("../helpers/delegate");
5
+ const parse_url_1 = require("../helpers/parse-url");
6
+ function defaultShouldTrack(link, parseUrlFn) {
7
+ const href = link.getAttribute('href') || link.getAttribute('xlink:href');
8
+ if (!href)
9
+ return false;
10
+ const url = parseUrlFn(href);
11
+ return url.hostname !== location.hostname && url.protocol.startsWith('http');
12
+ }
13
+ /**
14
+ * Automatically tracks clicks on outbound links (links to external domains).
15
+ *
16
+ * Sends an event when a user clicks a link whose hostname differs
17
+ * from the current page's hostname.
18
+ */
19
+ class OutboundLinkTracker {
20
+ delegates = [];
21
+ send;
22
+ events;
23
+ linkSelector;
24
+ eventName;
25
+ shouldTrackOutboundLink;
26
+ hitFilter;
27
+ constructor(send, options) {
28
+ this.send = send;
29
+ this.events = options?.events ?? ['click'];
30
+ this.linkSelector = options?.linkSelector ?? 'a, area';
31
+ this.eventName = options?.eventName ?? 'outbound_link_click';
32
+ this.shouldTrackOutboundLink = options?.shouldTrackOutboundLink ?? defaultShouldTrack;
33
+ this.hitFilter = options?.hitFilter;
34
+ this.events.forEach((eventType) => {
35
+ const handle = (0, delegate_1.delegate)(document, eventType, this.linkSelector, (event, element) => this.handleLinkInteraction(event, element), { composed: true, useCapture: true });
36
+ this.delegates.push(handle);
37
+ });
38
+ }
39
+ handleLinkInteraction(event, element) {
40
+ if (!this.shouldTrackOutboundLink(element, parse_url_1.parseUrl))
41
+ return;
42
+ const href = element.getAttribute('href') || element.getAttribute('xlink:href') || '';
43
+ const url = (0, parse_url_1.parseUrl)(href);
44
+ let params = {
45
+ link_url: url.href,
46
+ link_domain: url.hostname,
47
+ outbound: true,
48
+ };
49
+ if (this.hitFilter) {
50
+ const filtered = this.hitFilter(params, element, event);
51
+ if (filtered === null)
52
+ return;
53
+ params = filtered;
54
+ }
55
+ this.send(this.eventName, params);
56
+ }
57
+ remove() {
58
+ this.delegates.forEach((d) => d.destroy());
59
+ this.delegates = [];
60
+ }
61
+ }
62
+ exports.OutboundLinkTracker = OutboundLinkTracker;
63
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0Ym91bmQtbGluay10cmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BsdWdpbnMvb3V0Ym91bmQtbGluay10cmFja2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLGtEQUErRDtBQUMvRCxvREFBZ0Q7QUFFaEQsU0FBUyxrQkFBa0IsQ0FBQyxJQUFhLEVBQUUsVUFBaUY7SUFDeEgsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzFFLElBQUksQ0FBQyxJQUFJO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDeEIsTUFBTSxHQUFHLEdBQUcsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzdCLE9BQU8sR0FBRyxDQUFDLFFBQVEsS0FBSyxRQUFRLENBQUMsUUFBUSxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBQ2pGLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQWEsbUJBQW1CO0lBQ3BCLFNBQVMsR0FBcUIsRUFBRSxDQUFDO0lBQ2pDLElBQUksQ0FBZTtJQUNuQixNQUFNLENBQVc7SUFDakIsWUFBWSxDQUFTO0lBQ3JCLFNBQVMsQ0FBUztJQUNsQix1QkFBdUIsQ0FBcUU7SUFDNUYsU0FBUyxDQUEyQztJQUU1RCxZQUFZLElBQWtCLEVBQUUsT0FBb0M7UUFDaEUsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDM0MsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLEVBQUUsWUFBWSxJQUFJLFNBQVMsQ0FBQztRQUN2RCxJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLElBQUkscUJBQXFCLENBQUM7UUFDN0QsSUFBSSxDQUFDLHVCQUF1QixHQUFHLE9BQU8sRUFBRSx1QkFBdUIsSUFBSSxrQkFBa0IsQ0FBQztRQUN0RixJQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLENBQUM7UUFFcEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxTQUFTLEVBQUUsRUFBRTtZQUM5QixNQUFNLE1BQU0sR0FBRyxJQUFBLG1CQUFRLEVBQ25CLFFBQVEsRUFDUixTQUFTLEVBQ1QsSUFBSSxDQUFDLFlBQVksRUFDakIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxFQUM5RCxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUN2QyxDQUFDO1lBQ0YsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDaEMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRU8scUJBQXFCLENBQUMsS0FBWSxFQUFFLE9BQWdCO1FBQ3hELElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsT0FBTyxFQUFFLG9CQUFRLENBQUM7WUFBRSxPQUFPO1FBRTdELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdEYsTUFBTSxHQUFHLEdBQUcsSUFBQSxvQkFBUSxFQUFDLElBQUksQ0FBQyxDQUFDO1FBRTNCLElBQUksTUFBTSxHQUE0QjtZQUNsQyxRQUFRLEVBQUUsR0FBRyxDQUFDLElBQUk7WUFDbEIsV0FBVyxFQUFFLEdBQUcsQ0FBQyxRQUFRO1lBQ3pCLFFBQVEsRUFBRSxJQUFJO1NBQ2pCLENBQUM7UUFFRixJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUU7WUFDaEIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3hELElBQUksUUFBUSxLQUFLLElBQUk7Z0JBQUUsT0FBTztZQUM5QixNQUFNLEdBQUcsUUFBUSxDQUFDO1NBQ3JCO1FBRUQsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRCxNQUFNO1FBQ0YsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDO0lBQ3hCLENBQUM7Q0FDSjtBQXRERCxrREFzREMifQ==
@@ -0,0 +1,24 @@
1
+ import { GA4Plugin, SendFunction, PageVisibilityTrackerOptions } from '../types/plugins';
2
+ /**
3
+ * Tracks how long a page is in the visible state vs. hidden (background tab).
4
+ *
5
+ * Sends a `page_visibility` event each time the visibility state changes,
6
+ * reporting how long the page was in the previous state.
7
+ * Optionally sends a new `page_view` when the page becomes visible
8
+ * again after the session timeout has elapsed.
9
+ */
10
+ export declare class PageVisibilityTracker implements GA4Plugin {
11
+ private send;
12
+ private sendInitialPageview;
13
+ private sessionTimeout;
14
+ private eventName;
15
+ private hitFilter?;
16
+ private lastChangeTime;
17
+ private isVisible;
18
+ private boundVisibilityChange;
19
+ private boundBeforeUnload;
20
+ constructor(send: SendFunction, options?: PageVisibilityTrackerOptions);
21
+ private onVisibilityChange;
22
+ private onBeforeUnload;
23
+ remove(): void;
24
+ }