PageSourceSearch

https://www.jdblockpaving.co.uk/wp-content/plugins/dh-utilities/assets/js/event.js?ver=2.1.1

js jdblockpaving.co.uk collected 2026-10-07 00:50:14 UTC 8,251 bytes, 243 lines download raw bytes

1/**
2 * D5 Event Tracking
3 *
4 * Tracks user interactions (phone calls, emails, WhatsApp, form submissions)
5 * Pure vanilla JavaScript - no jQuery dependency
6 *
7 * @package D5_Utilities
8 * @since 2.0.0
9 */
10
11document.addEventListener('DOMContentLoaded', function() {
12
13	// Capture ad tracking params on first page of session (cache-safe: reads live from browser URL)
14	if (!sessionStorage.getItem('d5_entry_page')) {
15		var params = new URLSearchParams(window.location.search);
16		sessionStorage.setItem('d5_entry_page',    window.location.href.split('?')[0]);
17		sessionStorage.setItem('d5_gclid',         params.get('gclid')        || '');
18		sessionStorage.setItem('d5_msclkid',       params.get('msclkid')      || '');
19		sessionStorage.setItem('d5_fbclid',        params.get('fbclid')       || '');
20		sessionStorage.setItem('d5_ttclid',        params.get('ttclid')       || '');
21		sessionStorage.setItem('d5_utm_source',    params.get('utm_source')   || '');
22		sessionStorage.setItem('d5_utm_medium',    params.get('utm_medium')   || '');
23		sessionStorage.setItem('d5_utm_campaign',  params.get('utm_campaign') || '');
24		sessionStorage.setItem('d5_utm_term',      params.get('utm_term')     || '');
25		sessionStorage.setItem('d5_utm_content',   params.get('utm_content')  || '');
26	}
27
28	// Track recent events to prevent duplicates (2 second window)
29	const recentEvents = new Map();
30	const DEBOUNCE_DURATION = 2000; // 2 seconds
31
32	/**
33	 * Check if event is duplicate within debounce window
34	 *
35	 * @param {string} type Event type (Phone Call, Email, WhatsApp, Form Submission)
36	 * @param {string} subject Event subject/identifier
37	 * @return {boolean} True if duplicate, false if new event
38	 */
39	function isDuplicateEvent(type, subject) {
40		const eventKey = `${type}:${subject}`;
41		const now = Date.now();
42		const lastEventTime = recentEvents.get(eventKey);
43
44		if (lastEventTime && (now - lastEventTime) < DEBOUNCE_DURATION) {
45			return true; // Duplicate within debounce window
46		}
47
48		// Record this event
49		recentEvents.set(eventKey, now);
50
51		// Clean up old entries (older than debounce duration)
52		for (const [key, timestamp] of recentEvents.entries()) {
53			if (now - timestamp > DEBOUNCE_DURATION) {
54				recentEvents.delete(key);
55			}
56		}
57
58		return false;
59	}
60
61	/**
62	 * Send event data directly to logEvent.php via sendBeacon (URLSearchParams).
63	 * Sends cross-domain directly — no WordPress AJAX server-side hop needed.
64	 * Falls back to fetch() for browsers that don't support sendBeacon.
65	 *
66	 * @param {Object} eventData Event data to send
67	 */
68	function d5SendEventData(eventData) {
69		var params = new URLSearchParams();
70
71		for (var key in eventData) {
72			params.append(key, eventData[key]);
73		}
74
75		if (navigator.sendBeacon) {
76			navigator.sendBeacon(d5_events_script_vars.logurl, params);
77		} else {
78			fetch(d5_events_script_vars.logurl, {
79				method: 'POST',
80				body: params
81			}).catch(function(error) {
82				console.error('D5 Event tracking error:', error);
83			});
84		}
85	}
86
87	/**
88	 * Get base event data from localized script variables
89	 *
90	 * @return {Object} Base event data
91	 */
92	function d5GetBaseEventData() {
93		return {
94			domain: window.location.host,
95			url: window.location.pathname,
96			entrypage: sessionStorage.getItem('d5_entry_page') || d5_events_script_vars.entrypage,
97			email: d5_events_script_vars.email,
98			user_agent: d5_events_script_vars.userAgent,
99			county: d5_events_script_vars.county,
100			city: d5_events_script_vars.city,
101			referer: d5_events_script_vars.referer,
102			gclid:        sessionStorage.getItem('d5_gclid')        || '',
103			msclkid:      sessionStorage.getItem('d5_msclkid')      || '',
104			fbclid:       sessionStorage.getItem('d5_fbclid')       || '',
105			ttclid:       sessionStorage.getItem('d5_ttclid')       || '',
106			utm_source:   sessionStorage.getItem('d5_utm_source')   || '',
107			utm_medium:   sessionStorage.getItem('d5_utm_medium')   || '',
108			utm_campaign: sessionStorage.getItem('d5_utm_campaign') || '',
109			utm_term:     sessionStorage.getItem('d5_utm_term')     || '',
110			utm_content:  sessionStorage.getItem('d5_utm_content')  || ''
111		};
112	}
113
114	// Track phone call clicks
115	document.querySelectorAll('a[href^="tel:"]').forEach(function(telLink) {
116		telLink.addEventListener('click', function() {
117			const phoneNumber = this.getAttribute('href').replace('tel:', '');
118
119			// Check for duplicate
120			if (isDuplicateEvent('Phone Call', phoneNumber)) {
121				return;
122			}
123
124			const eventData = d5GetBaseEventData();
125			eventData.subject = phoneNumber;
126			eventData.type = 'Phone Call';
127
128			d5SendEventData(eventData);
129		});
130	});
131
132	// Track email clicks
133	document.querySelectorAll('a[href^="mailto:"]').forEach(function(emailLink) {
134		emailLink.addEventListener('click', function() {
135			const emailAddress = this.getAttribute('href').replace('mailto:', '');
136
137			// Check for duplicate
138			if (isDuplicateEvent('Email', emailAddress)) {
139				return;
140			}
141
142			const eventData = d5GetBaseEventData();
143			eventData.subject = 'Email link clicked';
144			eventData.type = 'Email';
145
146			d5SendEventData(eventData);
147		});
148	});
149
150	// Track WhatsApp clicks
151	document.querySelectorAll('a[href^="https://wa.me"]').forEach(function(whatsappLink) {
152		whatsappLink.addEventListener('click', function(e) {
153			const waUrl = this.getAttribute('href');
154
155			// Check for duplicate
156			if (isDuplicateEvent('WhatsApp', waUrl)) {
157				e.preventDefault();
158				window.location.href = waUrl; // Still navigate, just don't track
159				return;
160			}
161
162			e.preventDefault();
163
164			const eventData = d5GetBaseEventData();
165			eventData.subject = 'Whats App clicked';
166			eventData.type = 'WhatsApp';
167
168			d5SendEventData(eventData);
169
170			window.location.href = waUrl;
171		});
172	});
173
174	// Track form submissions
175	document.querySelectorAll('form:not(.notracking):not([id^="gform"])').forEach(function(form) {
176		form.addEventListener('submit', function() {
177			let formSubject = '';
178			let submissionType = 'Form Submission';
179
180			// WooCommerce order detection - Skip standard checkout (tracked server-side)
181			const isWooCheckout = this.classList.contains('woocommerce-checkout') ||
182			                      this.classList.contains('wc-block-checkout__form') ||
183			                      this.closest('.woocommerce-checkout') ||
184			                      this.closest('.wp-block-woocommerce-checkout') ||
185			                      (this.getAttribute('name') && this.getAttribute('name') === 'checkout');
186
187			if (isWooCheckout) {
188				return; // Skip - server-side tracking handles WooCommerce checkout
189			}
190			// Custom checkout forms (d5-simple-checkout-form, productForm) - track client-side
191			else if (this.getAttribute('id') === 'd5-simple-checkout-form' || this.getAttribute('id') === 'productForm') {
192				formSubject = 'Order Placed';
193				submissionType = 'Order';
194			}
195			// Ignore WooCommerce non-checkout forms
196			else if (
197				this.classList.contains('cart') ||
198				this.classList.contains('woocommerce-cart-form') ||
199				this.classList.contains('woocommerce-form-coupon') ||
200				this.classList.contains('woocommerce-shipping-calculator') ||
201				this.classList.contains('woocommerce-ordering') ||
202				this.classList.contains('pa_size') ||
203				this.classList.contains('orderby') ||
204				this.getAttribute('action') === window.location.pathname
205			) {
206				return;
207			}
208			// Extract form subject
209			else {
210				const subjectField = this.querySelector('.subject-field');
211
212				if (subjectField) {
213					const subjectInput = subjectField.querySelector('input');
214					const subjectSelect = subjectField.querySelector('select');
215
216					if (subjectInput) {
217						formSubject = subjectInput.value;
218					} else if (subjectSelect) {
219						formSubject = subjectSelect.value;
220					}
221				} else {
222					const firstSelect = this.querySelector('select');
223					if (firstSelect) {
224						formSubject = firstSelect.value;
225					}
226				}
227			}
228
229			// Check for duplicate submission
230			const formIdentifier = formSubject || this.getAttribute('id') || this.getAttribute('action');
231			if (isDuplicateEvent(submissionType, formIdentifier)) {
232				// Don't prevent form submission, just don't track it again
233				return;
234			}
235
236			const eventData = d5GetBaseEventData();
237			eventData.subject = formSubject;
238			eventData.type = submissionType;
239
240			d5SendEventData(eventData);
241		});
242	});
243});

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.