@kylelogue/eventfoundry-tracker 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  Embedded JavaScript tracking script for EventFoundry - enables custom GA4 event tracking with a visual editor.
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [How It Works](#how-it-works)
10
+ - [Features](#features)
11
+ - [Configuration](#configuration)
12
+ - [Development Mode](#development-mode)
13
+ - [Editor Integration](#editor-integration)
14
+ - [Architecture](#architecture)
15
+ - [Browser Compatibility](#browser-compatibility)
16
+ - [Development](#development)
17
+ - [PostMessage API Reference](#postmessage-api-reference)
18
+ - [License](#license)
19
+
5
20
  ## Installation
6
21
 
7
22
  ### Via unpkg CDN (Recommended)
@@ -19,7 +34,7 @@ Or pin to a specific version for production stability:
19
34
 
20
35
  ```html
21
36
  <!-- Pinned version -->
22
- <script src="https://unpkg.com/@kylelogue/eventfoundry-tracker@0.1.0/dist/tracker.min.js"
37
+ <script src="https://unpkg.com/@kylelogue/eventfoundry-tracker@0.3.0/dist/tracker.min.js"
23
38
  data-site-key="YOUR_SITE_KEY_HERE"
24
39
  async></script>
25
40
  ```
@@ -65,40 +80,298 @@ If you already have GA4 installed, EventFoundry will detect and use it:
65
80
 
66
81
  ## How It Works
67
82
 
68
- EventFoundry Tracker runs in two modes:
83
+ EventFoundry Tracker operates in two distinct modes:
84
+
85
+ ### 1. Tracking Mode (Production)
86
+
87
+ This is the default mode when the script is embedded on your website:
69
88
 
70
- 1. **Tracking Mode** (default): Fetches event definitions from your EventFoundry account, attaches click listeners to elements, and sends events directly to GA4.
89
+ 1. **Initialization**: On page load, the tracker reads configuration from script attributes
90
+ 2. **GA4 Setup**: Either detects existing GA4 installation or auto-injects it based on `data-measurement-id`
91
+ 3. **Event Fetching**: Retrieves event definitions from EventFoundry API using your site key
92
+ 4. **Caching**: Stores event definitions in localStorage with 1-hour cache duration
93
+ 5. **Click Tracking**: Attaches click listeners to elements matching CSS selectors from your event definitions
94
+ 6. **Event Sending**: When tracked elements are clicked, sends events to GA4 using the Beacon API for reliable delivery (even during page navigation)
71
95
 
72
- 2. **Editor Mode**: When loaded in the EventFoundry app, enables a visual overlay for defining custom events by clicking elements on your site.
96
+ ### 2. Editor Mode (EventFoundry App)
97
+
98
+ When loaded inside the EventFoundry application's iframe:
99
+
100
+ 1. **Detection**: Automatically detects iframe environment and initializes editor mode
101
+ 2. **Visual Overlay**: Displays an interactive overlay for selecting elements on your page
102
+ 3. **Element Selection**: Click any element to select it and generate an optimized CSS selector
103
+ 4. **Selector Generation**: Uses a priority-based algorithm to create the most reliable selector
104
+ 5. **Communication**: Sends element data back to the parent app via secure PostMessage
105
+ 6. **Preview Testing**: Allows testing events in preview mode with real-time logging
73
106
 
74
107
  ## Features
75
108
 
76
- - **Lightweight**: < 1KB gzipped
109
+ - **Lightweight**: ~3.2 KB gzipped (8.2 KB minified)
77
110
  - **Zero dependencies**: Pure vanilla JavaScript
78
111
  - **Fast**: 1-hour localStorage caching of event definitions
79
112
  - **Non-blocking**: Async loading won't slow down your site
80
113
  - **Browser support**: Chrome, Firefox, Safari, Edge (ES2015+)
114
+ - **Reliable delivery**: Uses Beacon API to ensure events complete even during navigation
115
+ - **Development mode**: Auto-detects localhost and logs events without sending to GA4
116
+ - **Visual editor**: Interactive element selection overlay for defining events
117
+ - **Smart selectors**: Priority-based CSS selector generation algorithm
118
+ - **Secure communication**: Origin validation for iframe PostMessage API
81
119
 
82
120
  ## Configuration
83
121
 
84
- The script accepts the following attributes:
122
+ The script accepts the following data attributes:
123
+
124
+ ### Required
125
+
126
+ - **`data-site-key`**: Your EventFoundry site key (obtained from your EventFoundry account)
127
+
128
+ ### Optional
129
+
130
+ - **`data-measurement-id`**: Your GA4 measurement ID (e.g., `G-XXXXXXXXXX`)
131
+ - Only required if you want EventFoundry to auto-inject GA4
132
+ - Omit this if you already have GA4 installed on your site
133
+
134
+ - **`data-dev-mode`**: Control development mode behavior
135
+ - `"true"`: Force development mode on (logs events, doesn't send to GA4)
136
+ - `"false"`: Force production mode on
137
+ - Not set: Auto-detects (dev mode on localhost, production mode elsewhere)
138
+
139
+ ## Development Mode
140
+
141
+ Development mode helps you test EventFoundry tracking without sending events to GA4.
142
+
143
+ ### Auto-Detection
144
+
145
+ By default, the tracker automatically enables development mode when running on `localhost`:
146
+
147
+ ```javascript
148
+ // On localhost - dev mode is automatically enabled
149
+ // Events are logged to console but NOT sent to GA4
150
+ ```
151
+
152
+ ### Manual Control
153
+
154
+ You can override the auto-detection:
155
+
156
+ ```html
157
+ <!-- Force dev mode on production domain (for testing) -->
158
+ <script src="https://unpkg.com/@kylelogue/eventfoundry-tracker@latest/dist/tracker.min.js"
159
+ data-site-key="YOUR_SITE_KEY_HERE"
160
+ data-measurement-id="G-XXXXXXXXXX"
161
+ data-dev-mode="true"
162
+ async></script>
163
+
164
+ <!-- Force production mode on localhost (to test real GA4 events) -->
165
+ <script src="https://unpkg.com/@kylelogue/eventfoundry-tracker@latest/dist/tracker.min.js"
166
+ data-site-key="YOUR_SITE_KEY_HERE"
167
+ data-measurement-id="G-XXXXXXXXXX"
168
+ data-dev-mode="false"
169
+ async></script>
170
+ ```
171
+
172
+ ### Behavior in Dev Mode
173
+
174
+ When development mode is active:
175
+ - Events are logged to the browser console with full details
176
+ - No events are sent to Google Analytics
177
+ - A console message indicates dev mode is active
178
+ - All tracking logic still runs (useful for testing selectors and event configuration)
179
+
180
+ ## Editor Integration
181
+
182
+ EventFoundry Tracker includes a sophisticated visual editor for defining events directly on your website.
183
+
184
+ ### How Editor Mode Works
185
+
186
+ When your site is loaded inside the EventFoundry application:
187
+
188
+ 1. The tracker detects the iframe environment
189
+ 2. Sends an `IFRAME_READY` message to the parent app
190
+ 3. Activates the visual overlay system
191
+ 4. Waits for element selection requests from the parent app
192
+
193
+ ### Visual Element Selection
194
+
195
+ The editor overlay provides:
196
+ - **Click-to-select**: Click any element to select it
197
+ - **Visual highlighting**: Selected elements are highlighted in the overlay
198
+ - **Parent traversal**: Navigate up the DOM tree to select parent elements
199
+ - **Selector preview**: See the generated CSS selector in real-time
200
+
201
+ ### CSS Selector Generation
202
+
203
+ The tracker uses a priority-based algorithm to generate reliable CSS selectors:
204
+
205
+ 1. **ID selectors** (highest priority): `#unique-element-id`
206
+ 2. **Unique attributes**: `[data-track="value"]`, `[name="value"]`
207
+ 3. **Class-based selectors**: `.class-name`, `.multiple.classes`
208
+ 4. **Tag + nth-child**: `div:nth-child(3)`, with parent context for specificity
209
+ 5. **Fallback combinations**: Combines multiple strategies for robustness
210
+
211
+ The algorithm ensures selectors are:
212
+ - Specific enough to target the correct element
213
+ - Resilient to minor page changes
214
+ - Human-readable when possible
215
+
216
+ ### Preview Mode
85
217
 
86
- - `data-site-key` (required): Your EventFoundry site key
87
- - `data-measurement-id` (optional): Your GA4 measurement ID (if not already installed)
218
+ Test your events before deploying:
219
+ - Preview mode logs events to the parent app instead of GA4
220
+ - See real-time feedback when clicking tracked elements
221
+ - Verify event parameters and tracking accuracy
222
+ - No impact on your actual GA4 data
223
+
224
+ ## Architecture
225
+
226
+ EventFoundry Tracker is built with a modular architecture:
227
+
228
+ ### Source Structure
229
+
230
+ ```
231
+ src/
232
+ ├── index.js - Main entry point, config parsing, GA4 setup, caching
233
+ ├── tracker.js - Event tracking logic, click listeners, Beacon API
234
+ ├── editor-mode.js - Editor mode detection and PostMessage communication
235
+ └── editor-overlay.js - Visual overlay, element selection, selector generation
236
+ ```
237
+
238
+ ### Module Responsibilities
239
+
240
+ - **index.js**: Initialization, configuration parsing, GA4 detection/injection, event caching
241
+ - **tracker.js**: Click event handling, event data collection, GA4 event sending
242
+ - **editor-mode.js**: Iframe detection, parent communication, mode switching
243
+ - **editor-overlay.js**: DOM overlay rendering, element selection UI, CSS selector algorithm
244
+
245
+ ### Build Output
246
+
247
+ - **Format**: IIFE (Immediately Invoked Function Expression)
248
+ - **Target**: ES2015 for broad browser compatibility
249
+ - **Global**: `window.EventFoundry` (for programmatic access if needed)
250
+ - **Minification**: Template minification and console statement stripping in production
251
+
252
+ ## Browser Compatibility
253
+
254
+ EventFoundry Tracker is built with ES2015+ JavaScript and supports all modern browsers:
255
+
256
+ - **Chrome**: 51+
257
+ - **Firefox**: 54+
258
+ - **Safari**: 10+
259
+ - **Edge**: 15+
260
+
261
+ ### Required Browser Features
262
+
263
+ - ES2015 syntax (arrow functions, const/let, template literals)
264
+ - `localStorage` API
265
+ - `fetch` API
266
+ - `navigator.sendBeacon` API (for reliable event delivery)
267
+ - `postMessage` API (for editor mode communication)
268
+
269
+ All these features are widely supported in browsers from 2016 onwards.
88
270
 
89
271
  ## Development
90
272
 
273
+ ### Setup
274
+
91
275
  ```bash
92
276
  # Install dependencies
93
277
  npm install
278
+ ```
279
+
280
+ ### Building
94
281
 
95
- # Build for production
282
+ ```bash
283
+ # Production build (outputs to dist/tracker.min.js)
96
284
  npm run build
97
285
 
98
- # Development watch mode
286
+ # Development watch mode (outputs to ../eventfoundry-app/frontend/public/tracker.min.js)
99
287
  npm run dev
100
288
  ```
101
289
 
290
+ ### Build System
291
+
292
+ The tracker uses **esbuild** for fast, efficient bundling:
293
+
294
+ - **Template minification**: CSS and HTML strings are minified via `esbuild-minify-templates`
295
+ - **Console stripping**: All `console.*` statements are removed from production builds
296
+ - **Sourcemaps**: Generated in development mode for debugging
297
+ - **Watch mode**: Automatically rebuilds on file changes and outputs to sibling project
298
+
299
+ ### Build Configuration
300
+
301
+ - Production builds optimize for size and performance
302
+ - Development builds include sourcemaps and preserve console logs
303
+ - Both modes produce IIFE bundles targeting ES2015
304
+
305
+ ## PostMessage API Reference
306
+
307
+ When running in editor mode (inside EventFoundry app iframe), the tracker communicates with the parent window using the following PostMessage events:
308
+
309
+ ### Messages Sent by Tracker (to Parent)
310
+
311
+ #### `IFRAME_READY`
312
+ Sent when the tracker initializes in editor mode.
313
+
314
+ ```javascript
315
+ {
316
+ type: 'IFRAME_READY'
317
+ }
318
+ ```
319
+
320
+ #### `ELEMENT_SELECTED`
321
+ Sent when a user selects an element in the visual editor.
322
+
323
+ ```javascript
324
+ {
325
+ type: 'ELEMENT_SELECTED',
326
+ selector: 'button.primary', // Generated CSS selector
327
+ tagName: 'BUTTON', // Element tag name
328
+ text: 'Click me', // Element text content
329
+ id: 'submit-btn', // Element ID (if present)
330
+ classes: ['primary', 'btn'], // Array of classes
331
+ attributes: { // All element attributes
332
+ type: 'submit',
333
+ 'data-track': 'signup'
334
+ }
335
+ }
336
+ ```
337
+
338
+ #### `EVENT_FIRED`
339
+ Sent in preview mode when a tracked event is triggered.
340
+
341
+ ```javascript
342
+ {
343
+ type: 'EVENT_FIRED',
344
+ eventName: 'button_click',
345
+ eventParams: {
346
+ // Event parameters that would be sent to GA4
347
+ }
348
+ }
349
+ ```
350
+
351
+ ### Messages Received by Tracker (from Parent)
352
+
353
+ #### `ENABLE_PREVIEW_MODE`
354
+ Activates preview mode for testing events.
355
+
356
+ ```javascript
357
+ {
358
+ type: 'ENABLE_PREVIEW_MODE'
359
+ }
360
+ ```
361
+
362
+ #### `DISABLE_PREVIEW_MODE`
363
+ Deactivates preview mode and returns to normal editor mode.
364
+
365
+ ```javascript
366
+ {
367
+ type: 'DISABLE_PREVIEW_MODE'
368
+ }
369
+ ```
370
+
371
+ ### Security
372
+
373
+ All PostMessage communication includes origin validation to ensure messages are only accepted from authorized EventFoundry domains. This prevents malicious sites from interfering with the tracker's operation.
374
+
102
375
  ## License
103
376
 
104
377
  MIT © Kyle Logue
@@ -1 +1 @@
1
- var EventFoundry=(()=>{var y=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames,b=Object.getOwnPropertySymbols;var A=Object.prototype.hasOwnProperty,$=Object.prototype.propertyIsEnumerable;var F=(e,n,t)=>n in e?y(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t,I=(e,n)=>{for(var t in n||(n={}))A.call(n,t)&&F(e,t,n[t]);if(b)for(var t of b(n))$.call(n,t)&&F(e,t,n[t]);return e};var j=(e,n)=>{for(var t in n)y(e,t,{get:n[t],enumerable:!0})},H=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of G(n))!A.call(e,r)&&r!==t&&y(e,r,{get:()=>n[r],enumerable:!(o=R(n,r))||o.enumerable});return e};var V=e=>H(y({},"__esModule",{value:!0}),e);var f=(e,n,t)=>new Promise((o,r)=>{var s=c=>{try{i(t.next(c))}catch(l){r(l)}},a=c=>{try{i(t.throw(c))}catch(l){r(l)}},i=c=>c.done?o(c.value):Promise.resolve(c.value).then(s,a);i((t=t.apply(e,n)).next())});var re={};j(re,{reloadEventDefinitions:()=>L});var d=null,g=!1,v=null,u=!1;function C(){return g}function h(){return u}function W(){return["http://localhost","https://eventfoundry.app"]}function K(e){return W().includes(e)}function M(){if(g)return;g=!0;let e=document.createElement("style");e.textContent=` .eventfoundry-highlight { outline: 2px solid #3498db !important; outline-offset: 2px; cursor: pointer !important; } .eventfoundry-test-highlight { outline: 3px solid #e74c3c !important; outline-offset: 3px; background-color: rgba(231, 76, 60, 0.1) !important; } .eventfoundry-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 999998; pointer-events: none; cursor: crosshair; } body.eventfoundry-edit-mode, body.eventfoundry-edit-mode * { cursor: crosshair !important; } `,document.head.appendChild(e);let n=document.createElement("div");n.className="eventfoundry-overlay",document.body.appendChild(n),v=n,document.body.classList.add("eventfoundry-edit-mode"),document.body.addEventListener("mouseover",t=>{u||t.target.classList.contains("eventfoundry-overlay")||(d&&d.classList.remove("eventfoundry-highlight"),t.target.classList.add("eventfoundry-highlight"),d=t.target)},!0),document.body.addEventListener("mouseout",t=>{u||t.target.classList.contains("eventfoundry-overlay")||t.target.classList.remove("eventfoundry-highlight")},!0),document.body.addEventListener("click",t=>{if(t.target.classList.contains("eventfoundry-overlay")||u)return;t.preventDefault(),t.stopPropagation();let o=t.target,r="";o.className&&typeof o.className=="string"&&(r=o.className.trim().split(/\s+/).filter(i=>!i.startsWith("eventfoundry-")).join(" "));let s=window.location.pathname+window.location.search+window.location.hash,a={tagName:o.tagName.toLowerCase(),text:o.textContent.trim().substring(0,50),id:o.id,className:r,selector:B(o),page_path:s};window.parent.postMessage({type:"ELEMENT_SELECTED",element:a},"*")},!0),window.addEventListener("message",t=>{if(K(t.origin)&&(t.data.type==="HIGHLIGHT_SELECTOR"&&U(t.data.selector),t.data.type==="COUNT_MATCHES")){let o=t.data.selector,r=0;try{r=document.querySelectorAll(o).length}catch(s){r=0}window.parent.postMessage({type:"MATCH_COUNT_RESULT",selector:o,count:r},"*")}})}function m(e){return e?e.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g,"\\$1"):""}function q(e){return e?!!(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i.test(e)||/\d{13}/.test(e)||/^[a-f0-9]{16,}$/i.test(e)||/-\d{4,}$/.test(e)):!1}function p(e,n){try{let t=document.querySelectorAll(n);return t.length===1&&t[0]===e}catch(t){return!1}}function B(e){let n=[];if(e.id&&!q(e.id)){let a=`#${m(e.id)}`;p(e,a)&&n.push({selector:a,priority:100})}let t=Array.from(e.attributes).filter(a=>a.name.startsWith("data-")&&a.value);for(let a of t){let i=`[${a.name}="${m(a.value)}"]`;if(p(e,i)){n.push({selector:i,priority:90});break}}if(e.className&&typeof e.className=="string"){let a=e.className.trim().split(/\s+/).filter(i=>!i.startsWith("eventfoundry-"));for(let i of a){let c=`.${m(i)}`;if(p(e,c)){n.push({selector:c,priority:80});break}}}let o=e.tagName.toLowerCase();e instanceof SVGElement&&(o=e.tagName.toLowerCase());let r=o,s=e.parentElement;if(s){let a;if(s.className&&typeof s.className=="string"){let l=s.className.trim().split(/\s+/).filter(x=>!x.startsWith("eventfoundry-"));a=l.length>0?`.${m(l[0])}`:s.tagName.toLowerCase()}else a=s.tagName.toLowerCase();let c=Array.from(s.children).indexOf(e)+1;r=`${a} ${o}:nth-child(${c})`}return p(e,r)&&n.push({selector:r,priority:50}),n.length>0?(n.sort((a,i)=>i.priority-a.priority),n[0].selector):o}function U(e){document.querySelectorAll(".eventfoundry-test-highlight").forEach(n=>{n.classList.remove("eventfoundry-test-highlight")});try{let n=document.querySelectorAll(e);n.forEach(t=>{t.classList.add("eventfoundry-test-highlight")}),setTimeout(()=>{n.forEach(t=>{t.classList.remove("eventfoundry-test-highlight")})},3e3)}catch(n){}}function P(){g&&(u=!0,v&&(v.style.display="none"),document.body.classList.remove("eventfoundry-edit-mode"),d&&(d.classList.remove("eventfoundry-highlight"),d=null))}function D(){g&&(u=!1,v&&(v.style.display=""),document.body.classList.add("eventfoundry-edit-mode"))}function E(e,n=!1){!e||e.length===0||document.addEventListener("click",t=>{if(C()&&!h())return;let o=e.filter(a=>{try{return t.target.closest(a.selector)!==null}catch(i){return!1}});if(o.length===0)return;let r=o[0],s=I({event_category:"EventFoundry",event_label:r.name,transport_type:"beacon"},r.gaEventParams);if(n||window.gtag("event",r.gaEventName,s),h())try{window.parent.postMessage({type:"EVENT_FIRED",timestamp:Date.now(),eventName:r.name,gaEventName:r.gaEventName,selector:r.selector,params:s,pagePath:window.location.pathname},"*")}catch(a){}o.length>1},!0)}function z(){return["http://localhost","https://eventfoundry.app"]}function J(e){return z().some(t=>t==="http://localhost"?e.startsWith("http://localhost"):e===t)}function w(){try{return window.self!==window.top}catch(e){return!0}}function T(){w()&&(window.addEventListener("message",e=>{if(J(e.origin))switch(e.data.type){case"ENTER_EDIT_MODE":Y();break;case"ENABLE_PREVIEW":Q();break;case"DISABLE_PREVIEW":X();break;case"RELOAD_CONFIG":L();break}}),setTimeout(()=>{window.parent&&window.parent.postMessage({type:"IFRAME_READY"},"*")},100))}function Y(){M()}function Q(){P()}function X(){D()}var _=window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1",Z=_?"http://localhost/api/tracking/events":"https://eventfoundry.app/api/tracking/events",ee=3600*1e3;function N(e){return`eventfoundry_definitions_${e}`}function k(){let e=document.currentScript||document.querySelector("script[data-site-key]");if(!e)return null;let n=e.getAttribute("data-dev-mode"),t=n==="false"?!1:_||n==="true";return{siteKey:e.getAttribute("data-site-key"),measurementId:e.getAttribute("data-measurement-id"),devMode:t}}function te(e){return f(this,null,function*(){if(window.gtag&&typeof window.gtag=="function")return{hasGtag:!0,wasInjected:!1};if(!e)return{hasGtag:!1,wasInjected:!1};let n=document.createElement("script");n.async=!0,n.src=`https://www.googletagmanager.com/gtag/js?id=${e}`,document.head.appendChild(n),window.dataLayer=window.dataLayer||[];function t(){dataLayer.push(arguments)}return window.gtag=t,t("js",new Date),t("config",e),yield new Promise(o=>{n.onload=o,setTimeout(o,5e3)}),{hasGtag:!0,wasInjected:!0}})}function S(){return f(this,null,function*(){let e=k();if(!e||!e.siteKey)return;let{siteKey:n,measurementId:t,devMode:o}=e;w()&&T();let{hasGtag:r,wasInjected:s}=yield te(t);if(r)try{let i=(yield O(n)).events;E(i,o)}catch(a){}})}function O(e){return f(this,null,function*(){let n=ne(e);if(n)return n;let t=yield fetch(`${Z}?siteKey=${e}`);if(!t.ok)throw new Error(`API returned ${t.status}`);let o=yield t.json();return oe(e,o),o})}function ne(e){try{let n=N(e),t=localStorage.getItem(n);if(!t)return null;let{data:o,timestamp:r}=JSON.parse(t);return Date.now()-r>ee?(localStorage.removeItem(n),null):o}catch(n){return null}}function oe(e,n){try{let t=N(e),o={data:n,timestamp:Date.now()};localStorage.setItem(t,JSON.stringify(o))}catch(t){}}function L(){return f(this,null,function*(){let e=k();if(!e||!e.siteKey)return;let{siteKey:n,devMode:t}=e;try{let o=N(n);localStorage.removeItem(o)}catch(o){}try{let r=(yield O(n)).events;E(r,t)}catch(o){}})}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",S):S();return V(re);})();
1
+ var EventFoundry=(()=>{var p=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames,N=Object.getOwnPropertySymbols;var F=Object.prototype.hasOwnProperty,V=Object.prototype.propertyIsEnumerable;var A=(e,t,n)=>t in e?p(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,I=(e,t)=>{for(var n in t||(t={}))F.call(t,n)&&A(e,n,t[n]);if(N)for(var n of N(t))V.call(t,n)&&A(e,n,t[n]);return e};var K=(e,t)=>{for(var n in t)p(e,n,{get:t[n],enumerable:!0})},W=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of H(t))!F.call(e,r)&&r!==n&&p(e,r,{get:()=>t[r],enumerable:!(o=j(t,r))||o.enumerable});return e};var q=e=>W(p({},"__esModule",{value:!0}),e);var g=(e,t,n)=>new Promise((o,r)=>{var a=d=>{try{l(n.next(d))}catch(m){r(m)}},s=d=>{try{l(n.throw(d))}catch(m){r(m)}},l=d=>d.done?o(d.value):Promise.resolve(d.value).then(a,s);l((n=n.apply(e,t)).next())});var ve={};K(ve,{reloadEventDefinitions:()=>b});var u=null,v=!1,y=null,f=!1;function P(){return v}function E(){return f}function B(){return["http://localhost","https://eventfoundry.app"]}function U(e){return B().includes(e)}function D(){if(v)return;v=!0;let e=document.createElement("style");e.textContent=` .eventfoundry-highlight { outline: 2px solid #3498db !important; outline-offset: 2px; cursor: pointer !important; } .eventfoundry-test-highlight { outline: 3px solid #e74c3c !important; outline-offset: 3px; background-color: rgba(231, 76, 60, 0.1) !important; } .eventfoundry-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 999998; pointer-events: none; cursor: crosshair; } body.eventfoundry-edit-mode, body.eventfoundry-edit-mode * { cursor: crosshair !important; } `,document.head.appendChild(e);let t=document.createElement("div");t.className="eventfoundry-overlay",document.body.appendChild(t),y=t,document.body.classList.add("eventfoundry-edit-mode"),document.body.addEventListener("mouseover",n=>{f||n.target.classList.contains("eventfoundry-overlay")||(u&&u.classList.remove("eventfoundry-highlight"),n.target.classList.add("eventfoundry-highlight"),u=n.target)},!0),document.body.addEventListener("mouseout",n=>{f||n.target.classList.contains("eventfoundry-overlay")||n.target.classList.remove("eventfoundry-highlight")},!0),document.body.addEventListener("click",n=>{if(n.target.classList.contains("eventfoundry-overlay")||f)return;n.preventDefault(),n.stopPropagation();let o=n.target,r="";o.className&&typeof o.className=="string"&&(r=o.className.trim().split(/\s+/).filter(l=>!l.startsWith("eventfoundry-")).join(" "));let a=window.location.pathname+window.location.search+window.location.hash,s={tagName:o.tagName.toLowerCase(),text:o.textContent.trim().substring(0,50),id:o.id,className:r,selector:ne(o),page_path:a};window.parent.postMessage({type:"ELEMENT_SELECTED",element:s},"*")},!0),window.addEventListener("message",n=>{if(U(n.origin)&&(n.data.type==="HIGHLIGHT_SELECTOR"&&oe(n.data.selector),n.data.type==="COUNT_MATCHES")){let o=n.data.selector,r=0;try{r=document.querySelectorAll(o).length}catch(a){r=0}window.parent.postMessage({type:"MATCH_COUNT_RESULT",selector:o,count:r},"*")}})}function i(e){return e?e.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g,"\\$1"):""}function z(e){return e?!!(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i.test(e)||/\d{13}/.test(e)||/^[a-f0-9]{16,}$/i.test(e)||/-\d{4,}$/.test(e)):!1}function J(e,t){try{let n=document.querySelectorAll(t);return n.length===1&&n[0]===e}catch(n){return!1}}function w(e){return!e.className||typeof e.className!="string"?[]:e.className.trim().split(/\s+/).filter(t=>t&&!t.startsWith("eventfoundry-"))}function $(e,t=!0){let n=e.parentElement;if(!n)return null;let o=w(n);return t&&o.length>0?`.${i(o[0])}`:n.tagName.toLowerCase()}function c(e,t,n,o){return J(o,t)?(e.push({selector:t,priority:n}),!0):!1}function Y(e){let t=[];if(e.id&&!z(e.id)){let n=`#${i(e.id)}`;c(t,n,100,e)}return t}function Q(e){let t=[],n=Array.from(e.attributes).filter(o=>o.name.startsWith("data-")&&o.value);for(let o of n){let r=`[${o.name}="${i(o.value)}"]`;if(c(t,r,90,e))break}return t}function X(e){let t=[],n=w(e);if(n.length===0)return t;let o=e.tagName.toLowerCase();for(let r of n){let a=`${o}.${i(r)}`;if(c(t,a,85,e))break}if(n.length>=2){let r=`.${i(n[0])}.${i(n[1])}`;if(c(t,r,82,e),n.length>=3){let a=`.${i(n[0])}.${i(n[1])}.${i(n[2])}`;c(t,a,82,e)}}for(let r of n){let a=`.${i(r)}`;if(c(t,a,80,e))break}return t}function Z(e){let t=[],n=e.tagName.toLowerCase(),o=["type","name","href","role","aria-label"];for(let r of o){let a=e.getAttribute(r);if(a){let s=`${n}[${r}="${i(a)}"]`;if(c(t,s,70,e))break}}return t}function ee(e){let t=[],n=$(e,!0);if(!n)return t;let o=e.tagName.toLowerCase(),r=w(e);if(r.length>0){let s=`${n} .${i(r[0])}`;c(t,s,75,e)}if(r.length>0){let s=`${n} ${o}.${i(r[0])}`;c(t,s,72,e)}let a=`${n} ${o}`;return c(t,a,65,e),t}function te(e){let t=[],n=e.tagName.toLowerCase(),o=e.parentElement;if(!o)return[{selector:n,priority:10}];let r=$(e,!0),s=Array.from(o.children).indexOf(e)+1,l=`${r} ${n}:nth-child(${s})`;return c(t,l,50,e),t}function ne(e){let t=[];return t.push(...Y(e)),t.push(...Q(e)),t.push(...X(e)),t.push(...Z(e)),t.push(...ee(e)),t.push(...te(e)),t.length>0?(t.sort((n,o)=>o.priority-n.priority),t[0].selector):e.tagName.toLowerCase()}function oe(e){document.querySelectorAll(".eventfoundry-test-highlight").forEach(t=>{t.classList.remove("eventfoundry-test-highlight")});try{let t=document.querySelectorAll(e);t.forEach(n=>{n.classList.add("eventfoundry-test-highlight")}),setTimeout(()=>{t.forEach(n=>{n.classList.remove("eventfoundry-test-highlight")})},3e3)}catch(t){}}function M(){v&&(f=!0,y&&(y.style.display="none"),document.body.classList.remove("eventfoundry-edit-mode"),u&&(u.classList.remove("eventfoundry-highlight"),u=null))}function T(){v&&(f=!1,y&&(y.style.display=""),document.body.classList.add("eventfoundry-edit-mode"))}var _=console.log;function h(e,...t){e&&_&&_(...t)}function L(e,t=!1){!e||e.length===0||document.addEventListener("click",n=>{if(P()&&!E())return;let o=e.filter(s=>{try{return n.target.closest(s.selector)!==null}catch(l){return!1}});if(o.length===0)return;let r=o[0],a=I({event_category:"EventFoundry",event_label:r.name,transport_type:"beacon"},r.gaEventParams);if(h(t,"EventFoundry [DEV]: Tracked event (not sent to GA4)",r.gaEventName,a),t||window.gtag("event",r.gaEventName,a),E())try{window.parent.postMessage({type:"EVENT_FIRED",timestamp:Date.now(),eventName:r.name,gaEventName:r.gaEventName,selector:r.selector,params:a,pagePath:window.location.pathname},"*")}catch(s){}o.length>1},!0)}function re(){return["http://localhost","https://eventfoundry.app"]}function ae(e){return re().some(n=>n==="http://localhost"?e.startsWith("http://localhost"):e===n)}function S(){try{return window.self!==window.top}catch(e){return!0}}function k(){S()&&(window.addEventListener("message",e=>{if(ae(e.origin))switch(e.data.type){case"ENTER_EDIT_MODE":se();break;case"ENABLE_PREVIEW":ie();break;case"DISABLE_PREVIEW":ce();break;case"RELOAD_CONFIG":b();break}}),setTimeout(()=>{window.parent&&window.parent.postMessage({type:"IFRAME_READY"},"*")},100))}function se(){D()}function ie(){M()}function ce(){T()}var O=window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1",le=O?"http://localhost/api/tracking/events":"https://eventfoundry.app/api/tracking/events",de=3600*1e3;function C(e){return`eventfoundry_definitions_${e}`}function R(){let e=document.currentScript||document.querySelector("script[data-site-key]");if(!e)return null;let t=e.getAttribute("data-dev-mode"),n=t==="false"?!1:O||t==="true";return{siteKey:e.getAttribute("data-site-key"),measurementId:e.getAttribute("data-measurement-id"),devMode:n}}function ue(e){return g(this,null,function*(){if(window.gtag&&typeof window.gtag=="function")return{hasGtag:!0,wasInjected:!1};if(!e)return{hasGtag:!1,wasInjected:!1};let t=document.createElement("script");t.async=!0,t.src=`https://www.googletagmanager.com/gtag/js?id=${e}`,document.head.appendChild(t),window.dataLayer=window.dataLayer||[];function n(){dataLayer.push(arguments)}return window.gtag=n,n("js",new Date),n("config",e),yield new Promise(o=>{t.onload=o,setTimeout(o,5e3)}),{hasGtag:!0,wasInjected:!0}})}function x(){return g(this,null,function*(){let e=R();if(!e||!e.siteKey)return;let{siteKey:t,measurementId:n,devMode:o}=e;S()&&k();let{hasGtag:r,wasInjected:a}=yield ue(n);if(r){h(o,"\u26A0 Dev mode: Events disabled"),h(!o,"\u2713 Production mode: Events enabled");try{let l=(yield G(t)).events;L(l,o)}catch(s){}}})}function G(e){return g(this,null,function*(){let t=fe(e);if(t)return t;let n=yield fetch(`${le}?siteKey=${e}`);if(!n.ok)throw new Error(`API returned ${n.status}`);let o=yield n.json();return ge(e,o),o})}function fe(e){try{let t=C(e),n=localStorage.getItem(t);if(!n)return null;let{data:o,timestamp:r}=JSON.parse(n);return Date.now()-r>de?(localStorage.removeItem(t),null):o}catch(t){return null}}function ge(e,t){try{let n=C(e),o={data:t,timestamp:Date.now()};localStorage.setItem(n,JSON.stringify(o))}catch(n){}}function b(){return g(this,null,function*(){let e=R();if(!e||!e.siteKey)return;let{siteKey:t,devMode:n}=e;try{let o=C(t);localStorage.removeItem(o)}catch(o){}try{let r=(yield G(t)).events;L(r,n)}catch(o){}})}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",x):x();return q(ve);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylelogue/eventfoundry-tracker",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "EventFoundry tracking script for custom GA4 event tracking",
5
5
  "main": "dist/tracker.min.js",
6
6
  "files": [