@technoapple/ga4 1.0.3 → 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.
- package/.github/workflows/node.js.yml +31 -31
- package/.prettierignore +1 -1
- package/LICENSE +21 -21
- package/README.md +386 -48
- package/REQUIREMENTS.md +548 -0
- package/babel.config.js +5 -5
- package/build/main/dataLayer.d.ts +1 -1
- package/build/main/dataLayer.js +60 -10
- package/build/main/ga4/ga4.d.ts +13 -0
- package/build/main/ga4/ga4.js +24 -1
- package/build/main/helpers/debounce.d.ts +5 -0
- package/build/main/helpers/debounce.js +23 -0
- package/build/main/helpers/delegate.d.ts +8 -0
- package/build/main/helpers/delegate.js +37 -0
- package/build/main/helpers/dom-ready.d.ts +1 -0
- package/build/main/helpers/dom-ready.js +13 -0
- package/build/main/helpers/parse-url.d.ts +11 -0
- package/build/main/helpers/parse-url.js +32 -0
- package/build/main/helpers/session.d.ts +4 -0
- package/build/main/helpers/session.js +50 -0
- package/build/main/index.d.ts +9 -0
- package/build/main/index.js +19 -2
- package/build/main/plugins/clean-url-tracker.d.ts +17 -0
- package/build/main/plugins/clean-url-tracker.js +105 -0
- package/build/main/plugins/event-tracker.d.ts +27 -0
- package/build/main/plugins/event-tracker.js +76 -0
- package/build/main/plugins/impression-tracker.d.ts +32 -0
- package/build/main/plugins/impression-tracker.js +202 -0
- package/build/main/plugins/index.d.ts +8 -0
- package/build/main/plugins/index.js +20 -0
- package/build/main/plugins/media-query-tracker.d.ts +20 -0
- package/build/main/plugins/media-query-tracker.js +96 -0
- package/build/main/plugins/outbound-form-tracker.d.ts +17 -0
- package/build/main/plugins/outbound-form-tracker.js +55 -0
- package/build/main/plugins/outbound-link-tracker.d.ts +19 -0
- package/build/main/plugins/outbound-link-tracker.js +63 -0
- package/build/main/plugins/page-visibility-tracker.d.ts +24 -0
- package/build/main/plugins/page-visibility-tracker.js +93 -0
- package/build/main/plugins/url-change-tracker.d.ts +20 -0
- package/build/main/plugins/url-change-tracker.js +76 -0
- package/build/main/types/plugins.d.ts +78 -0
- package/build/main/types/plugins.js +3 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/docs/examples/react.md +95 -0
- package/docs/examples/vanilla.md +65 -0
- package/docs/examples/vue.md +87 -0
- package/jest.config.ts +195 -195
- package/package.json +56 -52
- package/src/dataLayer.ts +85 -23
- package/src/ga4/ga4.ts +69 -40
- package/src/ga4/ga4option.ts +4 -4
- package/src/ga4/index.ts +4 -4
- package/src/helpers/debounce.ts +28 -0
- package/src/helpers/delegate.ts +51 -0
- package/src/helpers/dom-ready.ts +7 -0
- package/src/helpers/parse-url.ts +37 -0
- package/src/helpers/session.ts +39 -0
- package/src/index.ts +34 -7
- package/src/plugins/clean-url-tracker.ts +112 -0
- package/src/plugins/event-tracker.ts +90 -0
- package/src/plugins/impression-tracker.ts +230 -0
- package/src/plugins/index.ts +8 -0
- package/src/plugins/media-query-tracker.ts +116 -0
- package/src/plugins/outbound-form-tracker.ts +65 -0
- package/src/plugins/outbound-link-tracker.ts +72 -0
- package/src/plugins/page-visibility-tracker.ts +104 -0
- package/src/plugins/url-change-tracker.ts +84 -0
- package/src/types/dataLayer.ts +9 -9
- package/src/types/global.ts +12 -12
- package/src/types/gtag.ts +259 -259
- package/src/types/plugins.ts +98 -0
- package/src/util.ts +18 -18
- package/test/dataLayer.spec.ts +55 -40
- package/test/ga4.spec.ts +36 -36
- package/tsconfig.json +28 -28
- package/tsconfig.module.json +11 -11
package/build/main/ga4/ga4.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ga4 = void 0;
|
|
4
4
|
class ga4 {
|
|
5
5
|
static instance;
|
|
6
|
+
_plugins = [];
|
|
6
7
|
constructor() {
|
|
7
8
|
}
|
|
8
9
|
init(option) {
|
|
@@ -23,9 +24,31 @@ class ga4 {
|
|
|
23
24
|
window.gtag('event', eventName, eventParameters);
|
|
24
25
|
return true;
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Register a plugin with the GA4 instance.
|
|
29
|
+
* @param PluginClass The plugin class constructor
|
|
30
|
+
* @param options Plugin-specific configuration options
|
|
31
|
+
* @returns The plugin instance (call `.remove()` to unregister)
|
|
32
|
+
*/
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
use(PluginClass, options) {
|
|
35
|
+
const send = (eventName, params) => {
|
|
36
|
+
window.gtag('event', eventName, params);
|
|
37
|
+
};
|
|
38
|
+
const plugin = new PluginClass(send, options);
|
|
39
|
+
this._plugins.push(plugin);
|
|
40
|
+
return plugin;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remove all registered plugins and clean up their listeners.
|
|
44
|
+
*/
|
|
45
|
+
removeAll() {
|
|
46
|
+
this._plugins.forEach(p => p.remove());
|
|
47
|
+
this._plugins = [];
|
|
48
|
+
}
|
|
26
49
|
get gtag() {
|
|
27
50
|
return window.gtag;
|
|
28
51
|
}
|
|
29
52
|
}
|
|
30
53
|
exports.ga4 = ga4;
|
|
31
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
54
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2E0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dhNC9nYTQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBTUEsTUFBTSxHQUFHO0lBRUcsTUFBTSxDQUFDLFFBQVEsQ0FBTTtJQUNyQixRQUFRLEdBQWdCLEVBQUUsQ0FBQztJQUVuQztJQUNBLENBQUM7SUFFTSxJQUFJLENBQUMsTUFBZ0I7UUFDeEIsTUFBTSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxLQUFJLEtBQXNCLENBQUEsQ0FBQztRQUM5RCxNQUFNLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLElBQUk7WUFDekIsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDckMsQ0FBQyxDQUFBO1FBQ0QsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzlCLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRU0sTUFBTSxDQUFDLFdBQVc7UUFDckIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUU7WUFDZixHQUFHLENBQUMsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7U0FDNUI7UUFDRCxPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDeEIsQ0FBQztJQUVNLElBQUksQ0FBQyxTQUFnQixFQUFFLGVBQStCO1FBRXpELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUVqRCxPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCw4REFBOEQ7SUFDdkQsR0FBRyxDQUNOLFdBQXlELEVBQ3pELE9BQWE7UUFFYixNQUFNLElBQUksR0FBaUIsQ0FBQyxTQUFpQixFQUFFLE1BQStCLEVBQUUsRUFBRTtZQUM5RSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBd0IsQ0FBQyxDQUFDO1FBQzlELENBQUMsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLElBQUksV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzQixPQUFPLE1BQU0sQ0FBQztJQUNsQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxTQUFTO1FBQ1osSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsUUFBUSxHQUFHLEVBQUUsQ0FBQztJQUN2QixDQUFDO0lBRUQsSUFBSSxJQUFJO1FBQ0osT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ3ZCLENBQUM7Q0FDSjtBQUVPLGtCQUFHIn0=
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.debounce = void 0;
|
|
4
|
+
function debounce(fn, delay) {
|
|
5
|
+
let timer = null;
|
|
6
|
+
const debounced = function (...args) {
|
|
7
|
+
if (timer !== null)
|
|
8
|
+
clearTimeout(timer);
|
|
9
|
+
timer = setTimeout(() => {
|
|
10
|
+
timer = null;
|
|
11
|
+
fn.apply(this, args);
|
|
12
|
+
}, delay);
|
|
13
|
+
};
|
|
14
|
+
debounced.cancel = () => {
|
|
15
|
+
if (timer !== null) {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
timer = null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
return debounced;
|
|
21
|
+
}
|
|
22
|
+
exports.debounce = debounce;
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVib3VuY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaGVscGVycy9kZWJvdW5jZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFLQSxTQUFnQixRQUFRLENBQ3BCLEVBQUssRUFDTCxLQUFhO0lBRWIsSUFBSSxLQUFLLEdBQXlDLElBQUksQ0FBQztJQUV2RCxNQUFNLFNBQVMsR0FBRyxVQUF5QixHQUFHLElBQW1CO1FBQzdELElBQUksS0FBSyxLQUFLLElBQUk7WUFBRSxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDeEMsS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDcEIsS0FBSyxHQUFHLElBQUksQ0FBQztZQUNiLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ3pCLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNkLENBQXlCLENBQUM7SUFFMUIsU0FBUyxDQUFDLE1BQU0sR0FBRyxHQUFHLEVBQUU7UUFDcEIsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFO1lBQ2hCLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNwQixLQUFLLEdBQUcsSUFBSSxDQUFDO1NBQ2hCO0lBQ0wsQ0FBQyxDQUFDO0lBRUYsT0FBTyxTQUFTLENBQUM7QUFDckIsQ0FBQztBQXRCRCw0QkFzQkMifQ==
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface DelegateHandle {
|
|
2
|
+
destroy(): void;
|
|
3
|
+
}
|
|
4
|
+
export interface DelegateOptions {
|
|
5
|
+
composed?: boolean;
|
|
6
|
+
useCapture?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function delegate(target: EventTarget, eventType: string, selector: string, handler: (event: Event, element: Element) => void, options?: DelegateOptions): DelegateHandle;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.delegate = void 0;
|
|
4
|
+
function delegate(target, eventType, selector, handler, options) {
|
|
5
|
+
const useCapture = options?.useCapture ?? false;
|
|
6
|
+
const listener = (event) => {
|
|
7
|
+
let element = event.target;
|
|
8
|
+
// Handle composed events (shadow DOM)
|
|
9
|
+
if (options?.composed && typeof event.composedPath === 'function') {
|
|
10
|
+
const path = event.composedPath();
|
|
11
|
+
for (const node of path) {
|
|
12
|
+
if (node === target)
|
|
13
|
+
break;
|
|
14
|
+
if (node instanceof Element && node.matches(selector)) {
|
|
15
|
+
handler(event, node);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
while (element && element !== target) {
|
|
22
|
+
if (element.matches(selector)) {
|
|
23
|
+
handler(event, element);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
element = element.parentElement;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
target.addEventListener(eventType, listener, useCapture);
|
|
30
|
+
return {
|
|
31
|
+
destroy() {
|
|
32
|
+
target.removeEventListener(eventType, listener, useCapture);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.delegate = delegate;
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVsZWdhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaGVscGVycy9kZWxlZ2F0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFTQSxTQUFnQixRQUFRLENBQ3BCLE1BQW1CLEVBQ25CLFNBQWlCLEVBQ2pCLFFBQWdCLEVBQ2hCLE9BQWlELEVBQ2pELE9BQXlCO0lBRXpCLE1BQU0sVUFBVSxHQUFHLE9BQU8sRUFBRSxVQUFVLElBQUksS0FBSyxDQUFDO0lBRWhELE1BQU0sUUFBUSxHQUFHLENBQUMsS0FBWSxFQUFFLEVBQUU7UUFDOUIsSUFBSSxPQUFPLEdBQW1CLEtBQUssQ0FBQyxNQUF3QixDQUFDO1FBRTdELHNDQUFzQztRQUN0QyxJQUFJLE9BQU8sRUFBRSxRQUFRLElBQUksT0FBTyxLQUFLLENBQUMsWUFBWSxLQUFLLFVBQVUsRUFBRTtZQUMvRCxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDbEMsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLEVBQUU7Z0JBQ3JCLElBQUksSUFBSSxLQUFLLE1BQU07b0JBQUUsTUFBTTtnQkFDM0IsSUFBSSxJQUFJLFlBQVksT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUU7b0JBQ25ELE9BQU8sQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7b0JBQ3JCLE9BQU87aUJBQ1Y7YUFDSjtZQUNELE9BQU87U0FDVjtRQUVELE9BQU8sT0FBTyxJQUFJLE9BQU8sS0FBSyxNQUFNLEVBQUU7WUFDbEMsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFO2dCQUMzQixPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUN4QixPQUFPO2FBQ1Y7WUFDRCxPQUFPLEdBQUcsT0FBTyxDQUFDLGFBQWEsQ0FBQztTQUNuQztJQUNMLENBQUMsQ0FBQztJQUVGLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLEVBQUUsUUFBUSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBRXpELE9BQU87UUFDSCxPQUFPO1lBQ0gsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDaEUsQ0FBQztLQUNKLENBQUM7QUFDTixDQUFDO0FBekNELDRCQXlDQyJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function domReady(callback: () => void): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.domReady = void 0;
|
|
4
|
+
function domReady(callback) {
|
|
5
|
+
if (document.readyState === 'loading') {
|
|
6
|
+
document.addEventListener('DOMContentLoaded', callback, { once: true });
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
callback();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.domReady = domReady;
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9tLXJlYWR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2hlbHBlcnMvZG9tLXJlYWR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLFNBQWdCLFFBQVEsQ0FBQyxRQUFvQjtJQUN6QyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxFQUFFO1FBQ25DLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsRUFBRSxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztLQUMzRTtTQUFNO1FBQ0gsUUFBUSxFQUFFLENBQUM7S0FDZDtBQUNMLENBQUM7QUFORCw0QkFNQyJ9
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseUrl = void 0;
|
|
4
|
+
function parseUrl(url) {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = new URL(url, location.href);
|
|
7
|
+
return {
|
|
8
|
+
href: parsed.href,
|
|
9
|
+
protocol: parsed.protocol,
|
|
10
|
+
hostname: parsed.hostname,
|
|
11
|
+
port: parsed.port,
|
|
12
|
+
pathname: parsed.pathname,
|
|
13
|
+
search: parsed.search,
|
|
14
|
+
hash: parsed.hash,
|
|
15
|
+
origin: parsed.origin,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return {
|
|
20
|
+
href: url,
|
|
21
|
+
protocol: '',
|
|
22
|
+
hostname: '',
|
|
23
|
+
port: '',
|
|
24
|
+
pathname: url,
|
|
25
|
+
search: '',
|
|
26
|
+
hash: '',
|
|
27
|
+
origin: '',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.parseUrl = parseUrl;
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2UtdXJsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2hlbHBlcnMvcGFyc2UtdXJsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVdBLFNBQWdCLFFBQVEsQ0FBQyxHQUFXO0lBQ2hDLElBQUk7UUFDQSxNQUFNLE1BQU0sR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzNDLE9BQU87WUFDSCxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDakIsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO1lBQ3pCLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtZQUN6QixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDakIsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO1lBQ3pCLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtZQUNyQixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDakIsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO1NBQ3hCLENBQUM7S0FDTDtJQUFDLE1BQU07UUFDSixPQUFPO1lBQ0gsSUFBSSxFQUFFLEdBQUc7WUFDVCxRQUFRLEVBQUUsRUFBRTtZQUNaLFFBQVEsRUFBRSxFQUFFO1lBQ1osSUFBSSxFQUFFLEVBQUU7WUFDUixRQUFRLEVBQUUsR0FBRztZQUNiLE1BQU0sRUFBRSxFQUFFO1lBQ1YsSUFBSSxFQUFFLEVBQUU7WUFDUixNQUFNLEVBQUUsRUFBRTtTQUNiLENBQUM7S0FDTDtBQUNMLENBQUM7QUF6QkQsNEJBeUJDIn0=
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isSessionExpired(key: string, timeoutMinutes: number): boolean;
|
|
2
|
+
export declare function updateSessionTimestamp(key: string): void;
|
|
3
|
+
export declare function getSessionValue<T>(key: string): T | null;
|
|
4
|
+
export declare function setSessionValue(key: string, value: unknown): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setSessionValue = exports.getSessionValue = exports.updateSessionTimestamp = exports.isSessionExpired = void 0;
|
|
4
|
+
const SESSION_KEY_PREFIX = 'ga4_session_';
|
|
5
|
+
function isSessionExpired(key, timeoutMinutes) {
|
|
6
|
+
try {
|
|
7
|
+
const stored = sessionStorage.getItem(SESSION_KEY_PREFIX + key);
|
|
8
|
+
if (!stored)
|
|
9
|
+
return true;
|
|
10
|
+
const timestamp = parseInt(stored, 10);
|
|
11
|
+
if (isNaN(timestamp))
|
|
12
|
+
return true;
|
|
13
|
+
return (Date.now() - timestamp) > timeoutMinutes * 60 * 1000;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.isSessionExpired = isSessionExpired;
|
|
20
|
+
function updateSessionTimestamp(key) {
|
|
21
|
+
try {
|
|
22
|
+
sessionStorage.setItem(SESSION_KEY_PREFIX + key, String(Date.now()));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// sessionStorage may be unavailable or full
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.updateSessionTimestamp = updateSessionTimestamp;
|
|
29
|
+
function getSessionValue(key) {
|
|
30
|
+
try {
|
|
31
|
+
const stored = sessionStorage.getItem(SESSION_KEY_PREFIX + key);
|
|
32
|
+
if (stored === null)
|
|
33
|
+
return null;
|
|
34
|
+
return JSON.parse(stored);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.getSessionValue = getSessionValue;
|
|
41
|
+
function setSessionValue(key, value) {
|
|
42
|
+
try {
|
|
43
|
+
sessionStorage.setItem(SESSION_KEY_PREFIX + key, JSON.stringify(value));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// sessionStorage may be unavailable or full
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.setSessionValue = setSessionValue;
|
|
50
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9oZWxwZXJzL3Nlc3Npb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsTUFBTSxrQkFBa0IsR0FBRyxjQUFjLENBQUM7QUFFMUMsU0FBZ0IsZ0JBQWdCLENBQUMsR0FBVyxFQUFFLGNBQXNCO0lBQ2hFLElBQUk7UUFDQSxNQUFNLE1BQU0sR0FBRyxjQUFjLENBQUMsT0FBTyxDQUFDLGtCQUFrQixHQUFHLEdBQUcsQ0FBQyxDQUFDO1FBQ2hFLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDekIsTUFBTSxTQUFTLEdBQUcsUUFBUSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztRQUN2QyxJQUFJLEtBQUssQ0FBQyxTQUFTLENBQUM7WUFBRSxPQUFPLElBQUksQ0FBQztRQUNsQyxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFNBQVMsQ0FBQyxHQUFHLGNBQWMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0tBQ2hFO0lBQUMsTUFBTTtRQUNKLE9BQU8sSUFBSSxDQUFDO0tBQ2Y7QUFDTCxDQUFDO0FBVkQsNENBVUM7QUFFRCxTQUFnQixzQkFBc0IsQ0FBQyxHQUFXO0lBQzlDLElBQUk7UUFDQSxjQUFjLENBQUMsT0FBTyxDQUFDLGtCQUFrQixHQUFHLEdBQUcsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztLQUN4RTtJQUFDLE1BQU07UUFDSiw0Q0FBNEM7S0FDL0M7QUFDTCxDQUFDO0FBTkQsd0RBTUM7QUFFRCxTQUFnQixlQUFlLENBQUksR0FBVztJQUMxQyxJQUFJO1FBQ0EsTUFBTSxNQUFNLEdBQUcsY0FBYyxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsR0FBRyxHQUFHLENBQUMsQ0FBQztRQUNoRSxJQUFJLE1BQU0sS0FBSyxJQUFJO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDakMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBTSxDQUFDO0tBQ2xDO0lBQUMsTUFBTTtRQUNKLE9BQU8sSUFBSSxDQUFDO0tBQ2Y7QUFDTCxDQUFDO0FBUkQsMENBUUM7QUFFRCxTQUFnQixlQUFlLENBQUMsR0FBVyxFQUFFLEtBQWM7SUFDdkQsSUFBSTtRQUNBLGNBQWMsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLEdBQUcsR0FBRyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztLQUMzRTtJQUFDLE1BQU07UUFDSiw0Q0FBNEM7S0FDL0M7QUFDTCxDQUFDO0FBTkQsMENBTUMifQ==
|
package/build/main/index.d.ts
CHANGED
|
@@ -4,3 +4,12 @@ declare const dataLayerHelper: {
|
|
|
4
4
|
};
|
|
5
5
|
declare const ga4: import("./ga4/ga4").ga4;
|
|
6
6
|
export { ga4, dataLayerHelper };
|
|
7
|
+
export { EventTracker } from './plugins/event-tracker';
|
|
8
|
+
export { OutboundLinkTracker } from './plugins/outbound-link-tracker';
|
|
9
|
+
export { OutboundFormTracker } from './plugins/outbound-form-tracker';
|
|
10
|
+
export { PageVisibilityTracker } from './plugins/page-visibility-tracker';
|
|
11
|
+
export { UrlChangeTracker } from './plugins/url-change-tracker';
|
|
12
|
+
export { ImpressionTracker } from './plugins/impression-tracker';
|
|
13
|
+
export { CleanUrlTracker } from './plugins/clean-url-tracker';
|
|
14
|
+
export { MediaQueryTracker } from './plugins/media-query-tracker';
|
|
15
|
+
export type { GA4Plugin, SendFunction, EventTrackerOptions, OutboundLinkTrackerOptions, OutboundFormTrackerOptions, PageVisibilityTrackerOptions, UrlChangeTrackerOptions, ImpressionTrackerOptions, ImpressionElementConfig, CleanUrlTrackerOptions, MediaQueryTrackerOptions, MediaQueryDefinition, MediaQueryDefinitionItem, } from './types/plugins';
|
package/build/main/index.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dataLayerHelper = exports.ga4 = void 0;
|
|
3
|
+
exports.MediaQueryTracker = exports.CleanUrlTracker = exports.ImpressionTracker = exports.UrlChangeTracker = exports.PageVisibilityTracker = exports.OutboundFormTracker = exports.OutboundLinkTracker = exports.EventTracker = exports.dataLayerHelper = exports.ga4 = void 0;
|
|
4
4
|
const index_1 = require("./ga4/index");
|
|
5
5
|
const dataLayer_1 = require("./dataLayer");
|
|
6
6
|
const dataLayerHelper = { get: dataLayer_1.get };
|
|
7
7
|
exports.dataLayerHelper = dataLayerHelper;
|
|
8
8
|
const ga4 = index_1.ga;
|
|
9
9
|
exports.ga4 = ga4;
|
|
10
|
-
|
|
10
|
+
// Plugin exports
|
|
11
|
+
var event_tracker_1 = require("./plugins/event-tracker");
|
|
12
|
+
Object.defineProperty(exports, "EventTracker", { enumerable: true, get: function () { return event_tracker_1.EventTracker; } });
|
|
13
|
+
var outbound_link_tracker_1 = require("./plugins/outbound-link-tracker");
|
|
14
|
+
Object.defineProperty(exports, "OutboundLinkTracker", { enumerable: true, get: function () { return outbound_link_tracker_1.OutboundLinkTracker; } });
|
|
15
|
+
var outbound_form_tracker_1 = require("./plugins/outbound-form-tracker");
|
|
16
|
+
Object.defineProperty(exports, "OutboundFormTracker", { enumerable: true, get: function () { return outbound_form_tracker_1.OutboundFormTracker; } });
|
|
17
|
+
var page_visibility_tracker_1 = require("./plugins/page-visibility-tracker");
|
|
18
|
+
Object.defineProperty(exports, "PageVisibilityTracker", { enumerable: true, get: function () { return page_visibility_tracker_1.PageVisibilityTracker; } });
|
|
19
|
+
var url_change_tracker_1 = require("./plugins/url-change-tracker");
|
|
20
|
+
Object.defineProperty(exports, "UrlChangeTracker", { enumerable: true, get: function () { return url_change_tracker_1.UrlChangeTracker; } });
|
|
21
|
+
var impression_tracker_1 = require("./plugins/impression-tracker");
|
|
22
|
+
Object.defineProperty(exports, "ImpressionTracker", { enumerable: true, get: function () { return impression_tracker_1.ImpressionTracker; } });
|
|
23
|
+
var clean_url_tracker_1 = require("./plugins/clean-url-tracker");
|
|
24
|
+
Object.defineProperty(exports, "CleanUrlTracker", { enumerable: true, get: function () { return clean_url_tracker_1.CleanUrlTracker; } });
|
|
25
|
+
var media_query_tracker_1 = require("./plugins/media-query-tracker");
|
|
26
|
+
Object.defineProperty(exports, "MediaQueryTracker", { enumerable: true, get: function () { return media_query_tracker_1.MediaQueryTracker; } });
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsdUNBQStCO0FBQy9CLDJDQUFnQztBQUVoQyxNQUFNLGVBQWUsR0FBRyxFQUFFLEdBQUcsRUFBSCxlQUFHLEVBQUUsQ0FBQztBQUduQiwwQ0FBZTtBQUY1QixNQUFNLEdBQUcsR0FBRyxVQUFFLENBQUM7QUFFUCxrQkFBRztBQUVYLGlCQUFpQjtBQUNqQix5REFBdUQ7QUFBOUMsNkdBQUEsWUFBWSxPQUFBO0FBQ3JCLHlFQUFzRTtBQUE3RCw0SEFBQSxtQkFBbUIsT0FBQTtBQUM1Qix5RUFBc0U7QUFBN0QsNEhBQUEsbUJBQW1CLE9BQUE7QUFDNUIsNkVBQTBFO0FBQWpFLGdJQUFBLHFCQUFxQixPQUFBO0FBQzlCLG1FQUFnRTtBQUF2RCxzSEFBQSxnQkFBZ0IsT0FBQTtBQUN6QixtRUFBaUU7QUFBeEQsdUhBQUEsaUJBQWlCLE9BQUE7QUFDMUIsaUVBQThEO0FBQXJELG9IQUFBLGVBQWUsT0FBQTtBQUN4QixxRUFBa0U7QUFBekQsd0hBQUEsaUJBQWlCLE9BQUEifQ==
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GA4Plugin, SendFunction, CleanUrlTrackerOptions } from '../types/plugins';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes URLs before they are sent with `page_view` events.
|
|
4
|
+
*
|
|
5
|
+
* Intercepts `gtag()` calls for `config` and `page_view` events
|
|
6
|
+
* and cleans the `page_location` and `page_path` parameters
|
|
7
|
+
* (strip query params, normalize trailing slashes, apply custom filters).
|
|
8
|
+
*/
|
|
9
|
+
export declare class CleanUrlTracker implements GA4Plugin {
|
|
10
|
+
private opts;
|
|
11
|
+
private originalGtag;
|
|
12
|
+
constructor(_send: SendFunction, options?: CleanUrlTrackerOptions);
|
|
13
|
+
private cleanParams;
|
|
14
|
+
cleanUrl(url: string): string;
|
|
15
|
+
cleanPath(path: string): string;
|
|
16
|
+
remove(): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CleanUrlTracker = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Normalizes URLs before they are sent with `page_view` events.
|
|
6
|
+
*
|
|
7
|
+
* Intercepts `gtag()` calls for `config` and `page_view` events
|
|
8
|
+
* and cleans the `page_location` and `page_path` parameters
|
|
9
|
+
* (strip query params, normalize trailing slashes, apply custom filters).
|
|
10
|
+
*/
|
|
11
|
+
class CleanUrlTracker {
|
|
12
|
+
opts;
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
14
|
+
originalGtag = null;
|
|
15
|
+
constructor(_send, options) {
|
|
16
|
+
this.opts = {
|
|
17
|
+
stripQuery: options?.stripQuery ?? false,
|
|
18
|
+
queryParamsAllowlist: options?.queryParamsAllowlist,
|
|
19
|
+
queryParamsDenylist: options?.queryParamsDenylist,
|
|
20
|
+
trailingSlash: options?.trailingSlash,
|
|
21
|
+
urlFilter: options?.urlFilter,
|
|
22
|
+
};
|
|
23
|
+
if (typeof window !== 'undefined' && window.gtag) {
|
|
24
|
+
this.originalGtag = window.gtag;
|
|
25
|
+
const self = this;
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
window.gtag = function () {
|
|
28
|
+
// eslint-disable-next-line prefer-rest-params
|
|
29
|
+
const args = Array.prototype.slice.call(arguments);
|
|
30
|
+
if (args.length >= 3 && typeof args[2] === 'object' && args[2] !== null) {
|
|
31
|
+
const isPageView = args[0] === 'event' && args[1] === 'page_view';
|
|
32
|
+
const isConfig = args[0] === 'config';
|
|
33
|
+
if (isPageView || isConfig) {
|
|
34
|
+
args[2] = self.cleanParams({ ...args[2] });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return self.originalGtag.apply(window, args);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
cleanParams(params) {
|
|
42
|
+
if (typeof params.page_location === 'string') {
|
|
43
|
+
params.page_location = this.cleanUrl(params.page_location);
|
|
44
|
+
}
|
|
45
|
+
if (typeof params.page_path === 'string') {
|
|
46
|
+
params.page_path = this.cleanPath(params.page_path);
|
|
47
|
+
}
|
|
48
|
+
return params;
|
|
49
|
+
}
|
|
50
|
+
cleanUrl(url) {
|
|
51
|
+
try {
|
|
52
|
+
const u = new URL(url);
|
|
53
|
+
u.pathname = this.cleanPath(u.pathname);
|
|
54
|
+
if (this.opts.stripQuery) {
|
|
55
|
+
if (this.opts.queryParamsAllowlist && this.opts.queryParamsAllowlist.length > 0) {
|
|
56
|
+
const allowed = new URLSearchParams();
|
|
57
|
+
this.opts.queryParamsAllowlist.forEach((param) => {
|
|
58
|
+
if (u.searchParams.has(param)) {
|
|
59
|
+
allowed.set(param, u.searchParams.get(param));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
u.search = allowed.toString() ? '?' + allowed.toString() : '';
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
u.search = '';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (this.opts.queryParamsDenylist && this.opts.queryParamsDenylist.length > 0) {
|
|
69
|
+
this.opts.queryParamsDenylist.forEach((param) => {
|
|
70
|
+
u.searchParams.delete(param);
|
|
71
|
+
});
|
|
72
|
+
u.search = u.searchParams.toString() ? '?' + u.searchParams.toString() : '';
|
|
73
|
+
}
|
|
74
|
+
let result = u.toString();
|
|
75
|
+
if (this.opts.urlFilter) {
|
|
76
|
+
result = this.opts.urlFilter(result);
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return url;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
cleanPath(path) {
|
|
85
|
+
let result = path;
|
|
86
|
+
if (this.opts.trailingSlash === 'remove') {
|
|
87
|
+
result = result.length > 1 ? result.replace(/\/+$/, '') : result;
|
|
88
|
+
}
|
|
89
|
+
else if (this.opts.trailingSlash === 'add') {
|
|
90
|
+
if (!result.endsWith('/') && !result.split('/').pop()?.includes('.')) {
|
|
91
|
+
result += '/';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
remove() {
|
|
97
|
+
if (this.originalGtag) {
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
window.gtag = this.originalGtag;
|
|
100
|
+
this.originalGtag = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.CleanUrlTracker = CleanUrlTracker;
|
|
105
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xlYW4tdXJsLXRyYWNrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGx1Z2lucy9jbGVhbi11cmwtdHJhY2tlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQTs7Ozs7O0dBTUc7QUFDSCxNQUFhLGVBQWU7SUFDaEIsSUFBSSxDQUF5QjtJQUNyQyxzRUFBc0U7SUFDOUQsWUFBWSxHQUFvQixJQUFJLENBQUM7SUFFN0MsWUFBWSxLQUFtQixFQUFFLE9BQWdDO1FBQzdELElBQUksQ0FBQyxJQUFJLEdBQUc7WUFDUixVQUFVLEVBQUUsT0FBTyxFQUFFLFVBQVUsSUFBSSxLQUFLO1lBQ3hDLG9CQUFvQixFQUFFLE9BQU8sRUFBRSxvQkFBb0I7WUFDbkQsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLG1CQUFtQjtZQUNqRCxhQUFhLEVBQUUsT0FBTyxFQUFFLGFBQWE7WUFDckMsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTO1NBQ2hDLENBQUM7UUFFRixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVcsSUFBSSxNQUFNLENBQUMsSUFBSSxFQUFFO1lBQzlDLElBQUksQ0FBQyxZQUFZLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQztZQUNoQyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUM7WUFFbEIsOERBQThEO1lBQzdELE1BQWMsQ0FBQyxJQUFJLEdBQUc7Z0JBQ25CLDhDQUE4QztnQkFDOUMsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO2dCQUVuRCxJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVEsSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUFFO29CQUNyRSxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssT0FBTyxJQUFJ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
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GA4Plugin, SendFunction, EventTrackerOptions } from '../types/plugins';
|
|
2
|
+
/**
|
|
3
|
+
* Declarative event tracking via HTML `data-ga4-*` attributes.
|
|
4
|
+
*
|
|
5
|
+
* Listens for DOM events on elements with `data-ga4-on` attributes
|
|
6
|
+
* and sends GA4 events based on attribute values.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```html
|
|
10
|
+
* <button
|
|
11
|
+
* data-ga4-on="click"
|
|
12
|
+
* data-ga4-event-name="video_play"
|
|
13
|
+
* data-ga4-video-title="My Video">
|
|
14
|
+
* Play
|
|
15
|
+
* </button>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare class EventTracker implements GA4Plugin {
|
|
19
|
+
private delegates;
|
|
20
|
+
private send;
|
|
21
|
+
private events;
|
|
22
|
+
private attributePrefix;
|
|
23
|
+
private hitFilter?;
|
|
24
|
+
constructor(send: SendFunction, options?: EventTrackerOptions);
|
|
25
|
+
private handleEvent;
|
|
26
|
+
remove(): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventTracker = void 0;
|
|
4
|
+
const delegate_1 = require("../helpers/delegate");
|
|
5
|
+
function kebabToSnake(str) {
|
|
6
|
+
return str.replace(/-/g, '_');
|
|
7
|
+
}
|
|
8
|
+
function getAttributeParams(element, prefix) {
|
|
9
|
+
const params = {};
|
|
10
|
+
const reservedSuffixes = ['on', 'event-name'];
|
|
11
|
+
for (let i = 0; i < element.attributes.length; i++) {
|
|
12
|
+
const attr = element.attributes[i];
|
|
13
|
+
if (!attr.name.startsWith(prefix))
|
|
14
|
+
continue;
|
|
15
|
+
const suffix = attr.name.slice(prefix.length);
|
|
16
|
+
if (reservedSuffixes.includes(suffix))
|
|
17
|
+
continue;
|
|
18
|
+
params[kebabToSnake(suffix)] = attr.value;
|
|
19
|
+
}
|
|
20
|
+
return params;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Declarative event tracking via HTML `data-ga4-*` attributes.
|
|
24
|
+
*
|
|
25
|
+
* Listens for DOM events on elements with `data-ga4-on` attributes
|
|
26
|
+
* and sends GA4 events based on attribute values.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```html
|
|
30
|
+
* <button
|
|
31
|
+
* data-ga4-on="click"
|
|
32
|
+
* data-ga4-event-name="video_play"
|
|
33
|
+
* data-ga4-video-title="My Video">
|
|
34
|
+
* Play
|
|
35
|
+
* </button>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
class EventTracker {
|
|
39
|
+
delegates = [];
|
|
40
|
+
send;
|
|
41
|
+
events;
|
|
42
|
+
attributePrefix;
|
|
43
|
+
hitFilter;
|
|
44
|
+
constructor(send, options) {
|
|
45
|
+
this.send = send;
|
|
46
|
+
this.events = options?.events ?? ['click'];
|
|
47
|
+
this.attributePrefix = options?.attributePrefix ?? 'data-ga4-';
|
|
48
|
+
this.hitFilter = options?.hitFilter;
|
|
49
|
+
const selector = `[${this.attributePrefix}on]`;
|
|
50
|
+
this.events.forEach((eventType) => {
|
|
51
|
+
const handle = (0, delegate_1.delegate)(document, eventType, selector, (event, element) => this.handleEvent(event, element), { composed: true, useCapture: true });
|
|
52
|
+
this.delegates.push(handle);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
handleEvent(event, element) {
|
|
56
|
+
const prefix = this.attributePrefix;
|
|
57
|
+
const onAttr = element.getAttribute(`${prefix}on`);
|
|
58
|
+
if (onAttr !== event.type)
|
|
59
|
+
return;
|
|
60
|
+
const eventName = element.getAttribute(`${prefix}event-name`) || event.type;
|
|
61
|
+
let params = getAttributeParams(element, prefix);
|
|
62
|
+
if (this.hitFilter) {
|
|
63
|
+
const filtered = this.hitFilter(params, element, event);
|
|
64
|
+
if (filtered === null)
|
|
65
|
+
return;
|
|
66
|
+
params = filtered;
|
|
67
|
+
}
|
|
68
|
+
this.send(eventName, params);
|
|
69
|
+
}
|
|
70
|
+
remove() {
|
|
71
|
+
this.delegates.forEach((d) => d.destroy());
|
|
72
|
+
this.delegates = [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.EventTracker = EventTracker;
|
|
76
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQtdHJhY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wbHVnaW5zL2V2ZW50LXRyYWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0Esa0RBQStEO0FBRS9ELFNBQVMsWUFBWSxDQUFDLEdBQVc7SUFDN0IsT0FBTyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztBQUNsQyxDQUFDO0FBRUQsU0FBUyxrQkFBa0IsQ0FBQyxPQUFnQixFQUFFLE1BQWM7SUFDeEQsTUFBTSxNQUFNLEdBQTRCLEVBQUUsQ0FBQztJQUMzQyxNQUFNLGdCQUFnQixHQUFHLENBQUMsSUFBSSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBRTlDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUNoRCxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25DLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUM7WUFBRSxTQUFTO1FBRTVDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM5QyxJQUFJLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUM7WUFBRSxTQUFTO1FBRWhELE1BQU0sQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO0tBQzdDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDbEIsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUNILE1BQWEsWUFBWTtJQUNiLFNBQVMsR0FBcUIsRUFBRSxDQUFDO0lBQ2pDLElBQUksQ0FBZTtJQUNuQixNQUFNLENBQVc7SUFDakIsZUFBZSxDQUFTO0lBQ3hCLFNBQVMsQ0FBb0M7SUFFckQsWUFBWSxJQUFrQixFQUFFLE9BQTZCO1FBQ3pELElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxlQUFlLEdBQUcsT0FBTyxFQUFFLGVBQWUsSUFBSSxXQUFXLENBQUM7UUFDL0QsSUFBSSxDQUFDLFNBQVMsR0FBRyxPQUFPLEVBQUUsU0FBUyxDQUFDO1FBRXBDLE1BQU0sUUFBUSxHQUFHLElBQUksSUFBSSxDQUFDLGVBQWUsS0FBSyxDQUFDO1FBRS9DLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUU7WUFDOUIsTUFBTSxNQUFNLEdBQUcsSUFBQSxtQkFBUSxFQUNuQixRQUFRLEVBQ1IsU0FBUyxFQUNULFFBQVEsRUFDUixDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxFQUNwRCxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUN2QyxDQUFDO1lBQ0YsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDaEMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRU8sV0FBVyxDQUFDLEtBQVksRUFBRSxPQUFnQjtRQUM5QyxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDO1FBQ3BDLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsR0FBRyxNQUFNLElBQUksQ0FBQyxDQUFDO1FBRW5ELElBQUksTUFBTSxLQUFLLEtBQUssQ0FBQyxJQUFJO1lBQUUsT0FBTztRQUVsQyxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEdBQUcsTUFBTSxZQUFZLENBQUMsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDO1FBQzVFLElBQUksTUFBTSxHQUFHLGtCQUFrQixDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUVqRCxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUU7WUFDaEIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3hELElBQUksUUFBUSxLQUFLLElBQUk7Z0JBQUUsT0FBTztZQUM5QixNQUFNLEdBQUcsUUFBUSxDQUFDO1NBQ3JCO1FBRUQsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVELE1BQU07UUFDRixJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDM0MsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDeEIsQ0FBQztDQUNKO0FBakRELG9DQWlEQyJ9
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GA4Plugin, SendFunction, ImpressionTrackerOptions, ImpressionElementConfig } from '../types/plugins';
|
|
2
|
+
/**
|
|
3
|
+
* Tracks when specific DOM elements become visible in the viewport
|
|
4
|
+
* using `IntersectionObserver`.
|
|
5
|
+
*
|
|
6
|
+
* Useful for tracking ad impressions, CTA visibility, or any
|
|
7
|
+
* element that enters the user's viewport.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ImpressionTracker implements GA4Plugin {
|
|
10
|
+
private send;
|
|
11
|
+
private rootMargin;
|
|
12
|
+
private attributePrefix;
|
|
13
|
+
private eventName;
|
|
14
|
+
private hitFilter?;
|
|
15
|
+
private items;
|
|
16
|
+
private elementMap;
|
|
17
|
+
private thresholdMap;
|
|
18
|
+
private mutationObserver;
|
|
19
|
+
private impressedIds;
|
|
20
|
+
private supported;
|
|
21
|
+
constructor(send: SendFunction, options?: ImpressionTrackerOptions);
|
|
22
|
+
observeElements(elements: Array<string | ImpressionElementConfig>): void;
|
|
23
|
+
unobserveElements(elements: Array<string | ImpressionElementConfig>): void;
|
|
24
|
+
unobserveAllElements(): void;
|
|
25
|
+
private normalizeElements;
|
|
26
|
+
private getObserverForThreshold;
|
|
27
|
+
private handleIntersectionChanges;
|
|
28
|
+
private handleDomMutations;
|
|
29
|
+
private walkNodeTree;
|
|
30
|
+
private disconnectAll;
|
|
31
|
+
remove(): void;
|
|
32
|
+
}
|