PageSourceSearch

https://www.mobileaction.co/wp-content/plugins/custom-gtm-tracker/assets/tracking.js?ver=1755183108

js mobileaction.co collected 2026-10-02 04:25:33 UTC 17,262 bytes, 398 lines download raw bytes

1(function() {
2    'use strict';
3    
4    // Ensure dataLayer exists
5    window.dataLayer = window.dataLayer || [];
6    
7    // Get configuration from WordPress
8    const config = window.gtmTrackerData || {
9        context: {},
10        trackLinks: [],
11        trackForms: [],
12        debugMode: false
13    };
14    
15    // Debug logging helper
16    function debugLog(message, data) {
17        if (config.debugMode) {
18            if (data) {
19                console.log('MobileAction GTM Tracker: ' + message, data);
20            } else {
21                console.log('MobileAction GTM Tracker: ' + message);
22            }
23        }
24    }
25    
26    // Generate unique form submission ID
27    function generateFormSubmissionId() {
28        const date = new Date();
29        const dateStr = date.getFullYear().toString() + 
30                       (date.getMonth() + 1).toString().padStart(2, '0') + 
31                       date.getDate().toString().padStart(2, '0');
32        const randomStr = Math.random().toString(36).substring(2, 8).toUpperCase();
33        return 'FS-' + dateStr + '-' + randomStr;
34    }
35    
36    // Get or create persistent user ID
37    function getPersistentUserId() {
38        // Check if user already has a persistent ID in sessionStorage
39        let persistentId = sessionStorage.getItem('ma_persistent_user_id');
40        
41        if (!persistentId) {
42            // Check if logged in user has ID from JWT
43            if (config.context && config.context.userId) {
44                persistentId = config.context.userId;
45            } else {
46                // Generate new ID for form submission
47                persistentId = generateFormSubmissionId();
48            }
49            sessionStorage.setItem('ma_persistent_user_id', persistentId);
50        }
51        
52        return persistentId;
53    }
54    
55    // Helper functions
56    function buildBase() {
57        // Validate gtmContext exists
58        if (!config.context) {
59            if (config.debugMode) {
60                console.warn('MobileAction GTM Tracker: gtmContext not found');
61            }
62            return {
63                user: {
64                    status: 'guest',
65                    userId: null,
66                    organizationId: null
67                },
68                page: {
69                    type: 'other',
70                    url: window.location.href,
71                    path: window.location.pathname,
72                    title: document.title
73                }
74            };
75        }
76        
77        return {
78            user: {
79                status: config.context.userStatus || 'guest',
80                userId: config.context.userId || null,
81                organizationId: config.context.organizationId || null
82            },
83            page: {
84                type: config.context.pageType || 'other',
85                url: window.location.href,
86                path: window.location.pathname,
87                title: document.title
88            }
89        };
90    }
91    
92    function pushEvent(payload) {
93        try {
94            if (window.dataLayer && typeof window.dataLayer.push === 'function') {
95                window.dataLayer.push(payload);
96                debugLog('Event pushed', payload);
97            } else {
98                console.error('MobileAction GTM Tracker: dataLayer not available');
99            }
100        } catch (error) {
101            console.error('MobileAction GTM Tracker: Error pushing event', error);
102        }
103    }
104    
105    // Initialize tracking - check if DOM is already loaded
106    function initializeTracking() {
107        debugLog('Initializing with config', config);
108        
109        // Check if cookie exists and warn if not
110        if (!document.cookie.includes('MA_userAuth')) {
111            if (config.debugMode) {
112                console.warn('MobileAction GTM Tracker: MA_userAuth cookie not found - user will be tracked as guest');
113            }
114        }
115        
116        // Validate and log all configured selectors on initialization
117        if (config.trackLinks) {
118            debugLog('Configured click tracking selectors:');
119            config.trackLinks.forEach(function(cfg, index) {
120                try {
121                    // Test if selector is valid
122                    const elements = document.querySelectorAll(cfg.selector);
123                    debugLog(`[${index}] Selector: "${cfg.selector}" - Found ${elements.length} elements`, {
124                        selector: cfg.selector,
125                        interactionName: cfg.interactionName,
126                        elementsFound: elements.length
127                    });
128                    
129                    // In debug mode, log the first few matching elements
130                    if (config.debugMode && elements.length > 0) {
131                        elements.forEach(function(el, i) {
132                            if (i < 3) { // Log first 3 matches
133                                console.log(`  Match ${i + 1}:`, {
134                                    tag: el.tagName,
135                                    id: el.id || 'no-id',
136                                    classes: el.className || 'no-classes',
137                                    attributes: Array.from(el.attributes).map(attr => `${attr.name}="${attr.value}"`)
138                                });
139                            }
140                        });
141                    }
142                } catch (error) {
143                    console.error(`MobileAction GTM Tracker: Invalid selector at index ${index}`, {
144                        selector: cfg.selector,
145                        error: error.message,
146                        hint: 'Check for escaped quotes or invalid syntax',
147                        rawSelector: cfg.selector,
148                        charCodes: cfg.selector.split('').map(c => c.charCodeAt(0))
149                    });
150                }
151            });
152        }
153        
154        // 1. Initial page load event (not page_view)
155        const baseData = buildBase();
156        
157        // Check if user has been identified from a previous form submission
158        const persistentUserId = sessionStorage.getItem('ma_persistent_user_id');
159        let userStatus = baseData.user.status;
160        let userId = baseData.user.userId;
161        
162        if (persistentUserId && !userId) {
163            // User was identified in a previous session
164            userStatus = 'identified';
165            userId = persistentUserId;
166        }
167        
168        pushEvent({
169            event: 'page_load',
170            page: {
171                url: baseData.page.url,
172                path: baseData.page.path,
173                title: baseData.page.title,
174                type: baseData.page.type
175            },
176            user: {
177                status: userStatus,
178                userId: userId,
179                organizationId: baseData.user.organizationId
180            }
181        });
182        
183        // 2. Delegated click tracking for all elements
184        if (config.trackLinks && config.trackLinks.length > 0) {
185            document.addEventListener('click', function(e) {
186                const target = e.target;
187                
188                // Check all configured selectors
189                config.trackLinks.forEach(function(cfg) {
190                    try {
191                        // Check if clicked element or any of its parents match the selector
192                        let matchedElement = null;
193                        let currentElement = target;
194                        
195                        // Walk up the DOM tree to find a matching element
196                        while (currentElement && currentElement !== document.body) {
197                            if (currentElement.matches && currentElement.matches(cfg.selector)) {
198                                matchedElement = currentElement;
199                                break;
200                            }
201                            currentElement = currentElement.parentElement;
202                        }
203                        
204                        if (matchedElement) {
205                            // Prevent tracking the same click multiple times
206                            if (e.gtmTracked) return;
207                            e.gtmTracked = true;
208                            
209                            // Extract element identifier
210                            let elementId = '';
211                            
212                            // Try to get ID from the element
213                            if (matchedElement.id) {
214                                elementId = matchedElement.id;
215                            } else if (cfg.selector.includes('#')) {
216                                // Extract ID from selector if present
217                                const match = cfg.selector.match(/#([^\s\[\.]+)/);
218                                elementId = match ? match[1] : '';
219                            } else {
220                                // Use a sanitized version of selector as identifier
221                                elementId = cfg.selector.replace(/[^\w-]/g, '_');
222                            }
223                            
224                            // Get element text content
225                            let elementText = matchedElement.textContent.trim();
226                            if (!elementText && matchedElement.getAttribute('aria-label')) {
227                                elementText = matchedElement.getAttribute('aria-label');
228                            }
229                            if (!elementText && matchedElement.getAttribute('title')) {
230                                elementText = matchedElement.getAttribute('title');
231                            }
232                            
233                            // Get data attributes for debugging
234                            const dataAttrs = {};
235                            Array.from(matchedElement.attributes).forEac
235h(attr => {
236                                if (attr.name.startsWith('data-')) {
237                                    dataAttrs[attr.name] = attr.value;
238                                }
239                            });
240                            
241                            const baseData = buildBase();
242                            const persistentUserId = sessionStorage.getItem('ma_persistent_user_id');
243                            let userStatus = baseData.user.status;
244                            let userId = baseData.user.userId;
245                            
246                            if (persistentUserId && !userId) {
247                                userStatus = 'identified';
248                                userId = persistentUserId;
249                            }
250                            
251                            pushEvent({
252                                event: 'interaction',
253                                interaction: {
254                                    type: 'click',
255                                    name: cfg.interactionName,
256                                    element: elementId,
257                                    elementText: elementText,
258                                    location: cfg.location,
259                                },
260                                user: {
261                                    status: userStatus,
262                                    userId: userId,
263                                    organizationId: baseData.user.organizationId
264                                },
265                                page: {
266                                    type: baseData.page.type,
267                                    url: baseData.page.url,
268                                    path: baseData.page.path
269                                }
270                            });
271                            
272                            debugLog('Element clicked:', {
273                                selector: cfg.selector,
274                                matchedElement: {
275                                    tag: matchedElement.tagName,
276                                    id: matchedElement.id,
277                                    classes: matchedElement.className,
278                                    dataAttributes: dataAttrs
279                                },
280                                clickTarget: {
281                                    tag: target.tagName,
282                                    id: target.id,
283                                    classes: target.className
284                                }
285                            });
286                        }
287                    } catch (error) {
288                        if (config.debugMode) {
289                            console.error('MobileAction GTM Tracker: Invalid selector', {
290                                selector: cfg.selector,
291                                error: error.message
292                            });
293                        }
294                    }
295                });
296            });
297        }
298        
299        // 3. HubSpot form submission tracking via postMessage
300        if (config.trackForms && config.trackForms.length > 0) {
301            window.addEventListener('message', function(event) {
302                // Validate message origin if needed
303                // if (event.origin !== 'https://forms.hubspot.com') return;
304                
305                if (event.data && event.data.type === 'hsFormCallback' && event.data.eventName === 'onFormSubmit') {
306                    debugLog('HubSpot form submitted', event.data);
307                    
308                    const hubspotFormId = event.data.id || '';
309                    
310                    // Find matching form configuration by HubSpot form ID
311                    let matchedForm = null;
312                    config.trackForms.forEach(function(cfg) {
313                        if (cfg.hubspotFormId && cfg.hubspotFormId === hubspotFormId) {
314                            matchedForm = cfg;
315                        }
316                    });
317                    
318                    // If no match by ID, use the first form config as fallback
319                    if (!matchedForm && config.trackForms.length > 0) {
320                        matchedForm = config.trackForms[0];
321                        debugLog('No form ID match found, using default form config');
322                    }
323                    
324                    if (matchedForm) {
325                        const baseData = buildBase();
326                        const persistentUserId = getPersistentUserId();
327                        const interactionName = matchedForm.formName + '_submitted';
328                        
329                        pushEvent({
330                            event: 'interaction',
331                            interaction: {
332                                type: 'form_submission',
333                                name: interactionName
334                            },
335                            user: {
336                                status: 'identified',
337                                userId: persistentUserId
338                            },
339                            page: {
340                                type: baseData.page.type,
341                                url: baseData.page.url,
342                                path: baseData.page.path
343                            },
344                            form: {
345                                formName: matchedForm.formName + '_form',
346                                intent: matchedForm.intent
347                            }
348                        });
349                        
350                        debugLog('Form submission tracked with config:', {
351                            formId: hubspotFormId,
352                            formName: matchedForm.formName,
353                            userId: persistentUserId
354                        });
355                    } else {
356                        debugLog('No form configuration found for submission');
357                    }
358                }
359            });
360        }
361        
362        debugLog('Initialization complete');
363    }
364    
365    // Check if DOM is already loaded
366    if (document.readyState === 'loading') {
367        // DOM not yet loaded, wait for it
368        document.addEventListener('DOMContentLoaded', initializeTracking);
369    } else {
370        // DOM is already loaded, initialize immediately
371        initializeTracking();
372    }
373    
374    // Expose test function for debugging
375    window.testGTMSelector = function(selector) {
376        try {
377            const elements = document.querySelectorAll(selector);
378            console.log(`Testing selector: "${selector}"`);
379            console.log(`Found ${elements.length} elements`);
380            if (elements.length > 0) {
381                elements.forEach((el, i) => {
382                    console.log(`Element ${i + 1}:`, {
383                        tag: el.tagName,
384                        id: el.id,
385                        classes: el.className,
386                        innerHTML: el.innerHTML.substring(0, 100) + '...',
387                        attributes: Array.from(el.attributes).map(attr => `${attr.name}="${attr.value}"`)
388                    });
389                });
390            }
391            return elements;
392        } catch (error) {
393            console.error('Invalid selector:', error.message);
394            return null;
395        }
396    };
397    
398})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.