@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.
Files changed (76) 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/dataLayer.d.ts +1 -1
  8. package/build/main/dataLayer.js +60 -10
  9. package/build/main/ga4/ga4.d.ts +13 -0
  10. package/build/main/ga4/ga4.js +24 -1
  11. package/build/main/helpers/debounce.d.ts +5 -0
  12. package/build/main/helpers/debounce.js +23 -0
  13. package/build/main/helpers/delegate.d.ts +8 -0
  14. package/build/main/helpers/delegate.js +37 -0
  15. package/build/main/helpers/dom-ready.d.ts +1 -0
  16. package/build/main/helpers/dom-ready.js +13 -0
  17. package/build/main/helpers/parse-url.d.ts +11 -0
  18. package/build/main/helpers/parse-url.js +32 -0
  19. package/build/main/helpers/session.d.ts +4 -0
  20. package/build/main/helpers/session.js +50 -0
  21. package/build/main/index.d.ts +9 -0
  22. package/build/main/index.js +19 -2
  23. package/build/main/plugins/clean-url-tracker.d.ts +17 -0
  24. package/build/main/plugins/clean-url-tracker.js +105 -0
  25. package/build/main/plugins/event-tracker.d.ts +27 -0
  26. package/build/main/plugins/event-tracker.js +76 -0
  27. package/build/main/plugins/impression-tracker.d.ts +32 -0
  28. package/build/main/plugins/impression-tracker.js +202 -0
  29. package/build/main/plugins/index.d.ts +8 -0
  30. package/build/main/plugins/index.js +20 -0
  31. package/build/main/plugins/media-query-tracker.d.ts +20 -0
  32. package/build/main/plugins/media-query-tracker.js +96 -0
  33. package/build/main/plugins/outbound-form-tracker.d.ts +17 -0
  34. package/build/main/plugins/outbound-form-tracker.js +55 -0
  35. package/build/main/plugins/outbound-link-tracker.d.ts +19 -0
  36. package/build/main/plugins/outbound-link-tracker.js +63 -0
  37. package/build/main/plugins/page-visibility-tracker.d.ts +24 -0
  38. package/build/main/plugins/page-visibility-tracker.js +93 -0
  39. package/build/main/plugins/url-change-tracker.d.ts +20 -0
  40. package/build/main/plugins/url-change-tracker.js +76 -0
  41. package/build/main/types/plugins.d.ts +78 -0
  42. package/build/main/types/plugins.js +3 -0
  43. package/build/tsconfig.tsbuildinfo +1 -1
  44. package/docs/examples/react.md +95 -0
  45. package/docs/examples/vanilla.md +65 -0
  46. package/docs/examples/vue.md +87 -0
  47. package/jest.config.ts +195 -195
  48. package/package.json +56 -52
  49. package/src/dataLayer.ts +85 -23
  50. package/src/ga4/ga4.ts +69 -40
  51. package/src/ga4/ga4option.ts +4 -4
  52. package/src/ga4/index.ts +4 -4
  53. package/src/helpers/debounce.ts +28 -0
  54. package/src/helpers/delegate.ts +51 -0
  55. package/src/helpers/dom-ready.ts +7 -0
  56. package/src/helpers/parse-url.ts +37 -0
  57. package/src/helpers/session.ts +39 -0
  58. package/src/index.ts +34 -7
  59. package/src/plugins/clean-url-tracker.ts +112 -0
  60. package/src/plugins/event-tracker.ts +90 -0
  61. package/src/plugins/impression-tracker.ts +230 -0
  62. package/src/plugins/index.ts +8 -0
  63. package/src/plugins/media-query-tracker.ts +116 -0
  64. package/src/plugins/outbound-form-tracker.ts +65 -0
  65. package/src/plugins/outbound-link-tracker.ts +72 -0
  66. package/src/plugins/page-visibility-tracker.ts +104 -0
  67. package/src/plugins/url-change-tracker.ts +84 -0
  68. package/src/types/dataLayer.ts +9 -9
  69. package/src/types/global.ts +12 -12
  70. package/src/types/gtag.ts +259 -259
  71. package/src/types/plugins.ts +98 -0
  72. package/src/util.ts +18 -18
  73. package/test/dataLayer.spec.ts +55 -40
  74. package/test/ga4.spec.ts +36 -36
  75. package/tsconfig.json +28 -28
  76. package/tsconfig.module.json +11 -11
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2E0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dhNC9nYTQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBS0EsTUFBTSxHQUFHO0lBRUcsTUFBTSxDQUFDLFFBQVEsQ0FBTTtJQUU3QjtJQUNBLENBQUM7SUFFTSxJQUFJLENBQUMsTUFBZ0I7UUFDeEIsTUFBTSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxLQUFJLEtBQXNCLENBQUEsQ0FBQztRQUM5RCxNQUFNLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLElBQUk7WUFDekIsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDckMsQ0FBQyxDQUFBO1FBQ0QsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzlCLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRU0sTUFBTSxDQUFDLFdBQVc7UUFDckIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUU7WUFDZixHQUFHLENBQUMsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7U0FDNUI7UUFDRCxPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDeEIsQ0FBQztJQUVNLElBQUksQ0FBQyxTQUFnQixFQUFFLGVBQStCO1FBRXpELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUVqRCxPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQsSUFBSSxJQUFJO1FBQ0osT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ3ZCLENBQUM7Q0FDSjtBQUVPLGtCQUFHIn0=
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2E0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dhNC9nYTQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBTUEsTUFBTSxHQUFHO0lBRUcsTUFBTSxDQUFDLFFBQVEsQ0FBTTtJQUNyQixRQUFRLEdBQWdCLEVBQUUsQ0FBQztJQUVuQztJQUNBLENBQUM7SUFFTSxJQUFJLENBQUMsTUFBZ0I7UUFDeEIsTUFBTSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxLQUFJLEtBQXNCLENBQUEsQ0FBQztRQUM5RCxNQUFNLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLElBQUk7WUFDekIsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDckMsQ0FBQyxDQUFBO1FBQ0QsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzlCLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRU0sTUFBTSxDQUFDLFdBQVc7UUFDckIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUU7WUFDZixHQUFHLENBQUMsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7U0FDNUI7UUFDRCxPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDeEIsQ0FBQztJQUVNLElBQUksQ0FBQyxTQUFnQixFQUFFLGVBQStCO1FBRXpELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUVqRCxPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCw4REFBOEQ7SUFDdkQsR0FBRyxDQUNOLFdBQXlELEVBQ3pELE9BQWE7UUFFYixNQUFNLElBQUksR0FBaUIsQ0FBQyxTQUFpQixFQUFFLE1BQStCLEVBQUUsRUFBRTtZQUM5RSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBd0IsQ0FBQyxDQUFDO1FBQzlELENBQUMsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLElBQUksV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzQixPQUFPLE1BQU0sQ0FBQztJQUNsQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxTQUFTO1FBQ1osSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsUUFBUSxHQUFHLEVBQUUsQ0FBQztJQUN2QixDQUFDO0lBRUQsSUFBSSxJQUFJO1FBQ0osT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ3ZCLENBQUM7Q0FDSjtBQUVPLGtCQUFHIn0=
@@ -0,0 +1,5 @@
1
+ export interface DebouncedFunction<T extends (...args: never[]) => void> {
2
+ (...args: Parameters<T>): void;
3
+ cancel(): void;
4
+ }
5
+ export declare function debounce<T extends (...args: never[]) => void>(fn: T, delay: number): DebouncedFunction<T>;
@@ -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,11 @@
1
+ export interface ParsedUrl {
2
+ href: string;
3
+ protocol: string;
4
+ hostname: string;
5
+ port: string;
6
+ pathname: string;
7
+ search: string;
8
+ hash: string;
9
+ origin: string;
10
+ }
11
+ export declare function parseUrl(url: string): ParsedUrl;
@@ -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==
@@ -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';
@@ -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
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsdUNBQStCO0FBQy9CLDJDQUFnQztBQUVoQyxNQUFNLGVBQWUsR0FBRyxFQUFFLEdBQUcsRUFBSCxlQUFHLEVBQUUsQ0FBQztBQUduQiwwQ0FBZTtBQUY1QixNQUFNLEdBQUcsR0FBRyxVQUFFLENBQUM7QUFFUCxrQkFBRyJ9
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
+ }