PageSourceSearch

https://www.cauleyferrari.com/wp-content/themes/aanWordpress/scripts/sd_events.js?=1761246400

js cauleyferrari.com collected 2026-10-01 15:38:54 UTC 7,576 bytes, 190 lines download raw bytes

1/**
2 * sd_events.js - Comprehensive Client-Side Event Tracker for Shift Digital Analytics
3 *
4 * This script handles all client-side event tracking requirements outlined in the SD Analytics guide.
5 * It uses a single, efficient event listener and relies on data-attributes in the HTML for configuration.
6 *
7 * @version 2.3
8 * @author AAN
9 */
10document.addEventListener('DOMContentLoaded', () => {
11
12    /**
13     * Centralized helper function to send an SD Analytics event.
14     * @param {object} data - The data layer object for the event.
15     */
16    function sendSDEvent(data) {
17        if (typeof sd !== 'function') {
18            console.error('Shift Digital Analytics function "sd" not found.');
19            return;
20        }
21
22        // If vehicle data exists on the page, intelligently attach it to the event.
23        if (window.sdVehicleData) {
24            const eventName = data.events;
25            // Attach to any event that is not a generic page-level event like formShown
26            if (eventName && !eventName.endsWith('Shown')) {
27                 data.vehicleDetails = window.sdVehicleData;
28                if (eventName.startsWith('form')) {
29                    data.formVehicle = window.sdVehicleData;
30                }
31            }
32        }
33
34        sd('dataLayer', data);
35        sd('send');
36        console.log('SD Event Sent:', data);
37    }
38
39    // Expose for inline handlers (e.g., onclick="sendSDEvent(...)")
40    window.sendSDEvent = sendSDEvent;
41
42    /**
43     * Retrieves vehicle data if it's embedded in the page.
44     */
45    function loadVehicleData() {
46        const vehicleDataElement = document.getElementById('sd-vehicle-data');
47        if (vehicleDataElement) {
48            try {
49                window.sdVehicleData = JSON.parse(vehicleDataElement.textContent);
50            } catch (e) {
51                console.error('Error parsing SD vehicle data JSON.', e);
52            }
53        }
54    }
55
56    loadVehicleData();
57
58    /**
59     * ===================================================================
60     * Consolidated Click Event Listener
61     * ===================================================================
62     */
63    document.body.addEventListener('click', (event) => {
64        const trackableElement = event.target.closest('[data-sd-event]');
65        if (!trackableElement) return;
66
67        const eventType = trackableElement.dataset.sdEvent;
68
69        // FIX: Ignore events that are handled by other listeners (e.g., form submit)
70        if (eventType === 'typedSearch') {
71            return;
72        }
73        
74        // Prevent default link navigation to see the console log
75        event.preventDefault();
76        
77        let eventData = { events: eventType };
78
79        switch (eventType) {
80            case 'clickToCall':
81            case 'clickToText':
82                eventData.clickToCallDepartment = trackableElement.dataset.sdDepartment || 'Other';
83                break;
84            case 'getDirections':
85            case 'scheduleServiceClick':
86                // No extra data needed
87                break;
88            case 'brochureDownload':
89                eventData.brochureName = trackableElement.href ? trackableElement.href.split('/').pop() : 'unknown';
90                break;
91            case 'linkClick':
92                eventData.linkType = trackableElement.dataset.sdLinkType || 'Other';
93                break;
94            case 'carouselClick':
95                eventData.carouselClick = {
96                    assetName: trackableElement.dataset.sdAssetName || 'Unnamed',
97                    assetPosition: parseInt(trackableElement.dataset.sdAssetPosition, 10) || 0
98                };
99                break;
100            case 'vehicleQuickView':
101                try {
102                    eventData.vehicleDetails = JSON.parse(trackableElement.dataset.sdVehicleJson || '{}');
103                } catch (e) { console.error('Invalid JSON for vehicleQuickView', e); }
104                break;
105            case 'offerClick':
106            case 'serviceOfferClick':
107                 try {
108                    const offerData = JSON.parse(trackableElement.dataset.sdOfferJson || '{}');
109                    eventData[eventType] = offerData; // Use dynamic key for correct object name
110                } catch (e) { console.error('Invalid JSON for offerClick', e); }
111                break;
112            default:
113                console.warn(`Unknown data-sd-event type: ${eventType}`);
114                return;
115        }
116        sendSDEvent(eventData);
117    });
118
119    /**
120     * ===================================================================
121     * Form Interaction & Submission Tracking
122     * ===================================================================
123     */
124    // FIX: Broaden the selector to include the typed search form.
125    const forms = document.querySelectorAll('form[data-sd-form-type], form[data-sd-event="typedSearch"]');
126    forms.forEach(form => {
127        // Fallback for typedSearch forms that don't have a form-type
128        const formType = form.dataset.sdFormType || 'Typed Search'; 
129        const displayType = form.dataset.sdDisplayType || 'in-page';
130        let hasBeenInitiated = false;
131
132        const observer = new IntersectionObserver((entries) => {
133            entries.forEach(entry => {
134                if (entry.isIntersecting) {
135                    sendSDEvent({ events: 'formShown', formType, displayType });
136                    observer.unobserve(form);
137                }
138            });
139        }, { threshold: 0.5 });
140        observer.observe(form);
141
142        form.addEventListener('focusin', () => {
143            if (!hasBeenInitiated) {
144                hasBeenInitiated = true;
145                sendSDEvent({ events: 'formInitiation', formType, displayType });
146            }
147        }, { once: true, capture: true });
148
149        form.addEventListener('input', (event) => {
150            if (event.target.matches('input, textarea, select')) {
151                const fieldName = event.target.name || 'unknown';
152                clearTimeout(event.target.sdDebounce);
153                event.target.sdDebounce = setTimeout(() => {
154                    sendSDEvent({ events: 'formFieldInteraction', formType, formFieldName: fieldName, displayType });
155                }, 500);
156            }
157        });
158
159        form.addEventListener('submit', (event) => {
160            event.preventDefault(); // Stop actual form submission for testing
161            if (form.dataset.sdEvent === 'typedSearch') {
162                const input = form.querySelector('input[type="search"], input[type="text"]');
163                if (input) {
164                    sendSDEvent({ events: 'typedSearch', typedSearchContent: input.value });
165                }
166            } else {
167                const leadId = form.dataset.sdLeadId || `client-${Date.now()}`;
168                sendSDEvent({
169                    events: 'formSubmission',
170                    formType,
171                    displayType,
172                    leadId,
173                    leadType: form.dataset.sdLeadType || 'lead'
174                });
175            }
176        });
177    });
178
179    /**
180     * Global function to trigger a 'formSubmissionError' event.
181     */
182    window.triggerSDFormError = function(formElement, errorMessage) {
183        if (formElement && formElement.dataset.sdFormType) {
184            const formType = formElement.dataset.sdFormType;
185            const displayType = formElement.dataset.sdDisplayType || 'in-page';
186            sendSDEvent({ events: 'formSubmissionError', formType, displayType, formError: errorMessage });
187        }
188    }
189});
190

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.