PageSourceSearch

https://www.brambleski.com/wp-content/themes/bramble/js/events.js

js brambleski.com collected 2026-10-06 17:02:41 UTC 25,603 bytes, 629 lines download raw bytes

1// events.js - Site event handlers
2(function() {
3    window.dataLayer = window.dataLayer || [];
4
5    // SHA256 hash for privacy-safe email tracking (META, GA4, Google Ads)
6    function hashEmail(email) {
7        if (!email) return Promise.resolve('');
8        var normalized = email.trim().toLowerCase();
9        if (!normalized) return Promise.resolve('');
10        if (window.crypto && window.crypto.subtle) {
11            var encoder = new TextEncoder();
12            var data = encoder.encode(normalized);
13            return crypto.subtle.digest('SHA-256', data).then(function(buffer) {
14                return Array.from(new Uint8Array(buffer)).map(function(b) {
15                    return ('0' + b.toString(16)).slice(-2);
16                }).join('');
17            });
18        }
19        return Promise.resolve('');
20    }
21
22    // Push to dataLayer with SHA256 hashed email alongside plaintext
23    function pushWithHashedEmail(eventData) {
24        var email = eventData.email || '';
25        delete eventData.email;
26        hashEmail(email).then(function(hashed) {
27            eventData.email_sha256 = hashed;
28            dataLayer.push(eventData);
29        });
30    }
31
32    var searchStarted = false;
33    var formSubmitted = false;
34    var enquiryStarted = false;
35
36    // Chalet Search - capture Enter key before form submits
37    document.addEventListener('keydown', function(e) {
38        if (e.key === 'Enter' && e.target && e.target.id === 'search-input') {
39            e.preventDefault();
40            e.stopPropagation();
41
42            if (formSubmitted) return;
43            formSubmitted = true;
44
45            var searchQuery = e.target.value || '';
46            var form = document.getElementById('header_search');
47
48            dataLayer.push({
49                'event': 'chalet_search',
50                'search_query': searchQuery,
51                'eventCallback': function() {
52                    if (form) form.submit();
53                },
54                'eventTimeout': 500
55            });
56
57            // Fallback if eventCallback doesn't fire
58            setTimeout(function() {
59                if (form) form.submit();
60            }, 500);
61        }
62    }, true);
63
64    // Chalet Search Started - fires once when typing begins
65    document.addEventListener('DOMContentLoaded', function() {
66        var searchInput = document.getElementById('search-input');
67
68        if (searchInput) {
69            searchInput.addEventListener('input', function() {
70                if (!searchStarted && this.value.length >= 2) {
71                    searchStarted = true;
72                    dataLayer.push({
73                        'event': 'chalet_search_started',
74                        'search_query': this.value
75                    });
76                }
77            });
78        }
79    });
80
81    // Chalet Enquiry Tracking (chalet detail page)
82    document.addEventListener('DOMContentLoaded', function() {
83        var chaletKeyInfo = document.querySelector('.chalet_key_info');
84        if (!chaletKeyInfo) return;
85
86        // Detect currency from price display on page
87        function detectCurrency() {
88            var priceRow = document.querySelector('.priceRange_row');
89            if (priceRow) {
90                var text = priceRow.textContent || '';
91                if (text.indexOf('EUR') !== -1 || text.indexOf('€') !== -1) return 'EUR';
92                if (text.indexOf('GBP') !== -1 || text.indexOf('£') !== -1) return 'GBP';
93            }
94            return 'CHF'; // Default
95        }
96
97        // Store values before form submission (form clears fields on submit)
98        var storedRentalValue = 0;
99        var storedChaletEmail = '';
100
101        // Helper to get enquiry data
102        function getEnquiryData() {
103            var chaletId = document.getElementById('chaletID');
104            var chaletName = document.getElementById('chaletName');
105            var chaletDestination = document.getElementById('chaletDestination');
106            var chaletCountry = document.getElementById('chaletCountry');
107            var dateRange = document.getElementById('searchDateRange');
108            var serviceLevel = document.getElementById('searchServiceLevel');
109            var emailField = document.getElementById('field_tjr8l');
110            var priceField = document.getElementById('field_ic0cf');
111            var priceDisplay = document.getElementById('chaletPrice');
112
113            // Get price value - try form field first, then parse from display
114            var rentalValue = 0;
115
116            // Try form field
117            if (priceField && priceField.value) {
118                var parsed = parseFloat(priceField.value.replace(/[^0-9]/g, ''));
119                if (!isNaN(parsed) && parsed > 0) {
120                    rentalValue = parsed;
121                }
122            }
123
124            // Fallback: parse from price display element
125            if (rentalValue === 0 && priceDisplay) {
126                var priceText = priceDisplay.textContent || '';
127                var match = priceText.match(/[\d.,]+/);
128                if (match) {
129                    var parsed = parseFloat(match[0].replace(/[^0-9]/g, ''));
130                    if (!isNaN(parsed) && parsed > 0) {
131                        rentalValue = parsed;
132                    }
133                }
134            }
135
136            // Use stored value if current is 0
137            if (rentalValue === 0 && storedRentalValue > 0) {
138                rentalValue = storedRentalValue;
139            }
140
141            // Store for later (before form clears)
142            if (rentalValue > 0) {
143                storedRentalValue = rentalValue;
144            }
145
146            // Store email before form clears
147            var emailVal = emailField ? emailField.value : '';
148            if (emailVal) {
149                storedChaletEmail = emailVal;
150            }
151
152            var serviceLevelText = '';
153            if (serviceLevel && serviceLevel.selectedIndex > 0) {
154                serviceLevelText = serviceLevel.options[serviceLevel.selectedIndex].text;
155            }
156
157            return {
158                'chalet_id': chaletId ? chaletId.value : '',
159                'chalet_name': chaletName ? chaletName.value : '',
160                'search_destination': chaletDestination ? chaletDestination.value : '',
161                'chalet_country': chaletCountry ? chaletCountry.value : '',
162                'search_daterange': dateRange ? dateRange.value : '',
163                'service_level': serviceLevelText,
164                'email': emailVal || storedChaletEmail || '',
165                'rental_value': rentalValue,
166                'currency': detectCurrency()
167            };
168        }
169
170        // Chalet Inquiry Started - fires once when user interacts with form
171        function fireInquiryStarted() {
172            if (enquiryStarted) return;
173            enquiryStarted = true;
174
175            var data = getEnquiryData();
176            dataLayer.push({
177                'event': 'chalet_inquiry_started',
178                'chalet_id': data.chalet_id,
179                'chalet_name': data.chalet_name,
180                'search_destination': data.search_destination,
181                'chalet_country': data.chalet_country
182            });
183        }
184
185        // Track interactions with datepicker and service level
186        var dateRangeInput = document.getElementById('searchDateRange');
187        var serviceLevelSelect = document.getElementById('searchServiceLevel');
188        var enquireBtn = document.getElementById('enqFormBtn');
189
190        if (dateRangeInput) {
191            dateRangeInput.addEventListener('click', fireInquiryStarted);
192            dateRangeInput.addEventListener('focus', fireInquiryStarted);
193        }
194        if (serviceLevelSelect) {
195            serviceLevelSelect.addEventListener('click', fireInquiryStarted);
196            serviceLevelSelect.addEventListener('focus', fireInquiryStarted);
197        }
198        if (enquireBtn) {
199            enquireBtn.addEventListener('click', function() {
200                fireInquiryStarted();
201                // Capture price when clicking Enquire (price is loaded at this point)
202                getEnquiryData();
203            });
204        }
205
206        // Capture email as user types (2-step form - field only visible in step 2, cleared on submit)
207        jQuery(document).on('input change', '#field_tjr8l', function() {
208            if (this.value) {
209                storedChaletEmail = this.value;
210            }
211        });
212
213        // Chalet Inquiry Sent - fires on Formidable form submission
214        if (typeof jQuery !== 'undefined') {
215            jQuery(document).on('frmFormComplete', function(event, form, response) {
216                // Check if this is the chalet inquiry form (form ID 14)
217                var formId = jQuery(form).find('input[name="form_id"]').val();
218                if (formId == '14') {
219                    var data = getEnquiryData();
220                    var eventData = {
221                        'event': 'chalet_inquiry_sent',
222                        'chalet_id': data.chalet_id,
223                        'chalet_name': data.chalet_name,
224                        'search_destination': data.search_destination,
225                        'chalet_country': data.chalet_country,
226                        'search_daterange': data.search_daterange,
227                        'service_level': data.service_level,
228                        'email': data.email,
229                        'rental_value': data.rental_value,
230                        'currency': data.currency
231                    };
232
233                    // Only add value if it's a valid number (for GA4 conversion tracking)
234                    if (data.rental_value > 0) {
235                        eventData.value = data.rental_value;
236                    }
237
238                    pushWithHashedEmail(eventData);
239                }
240            });
241        }
242    });
243
244    // Wishlist Tracking (favorites page)
245    document.addEventListener('DOMContentLoaded', function() {
246        var favshareModal = document.getElementById('favshareModal');
247        var favenquireModal = document.getElementById('favenquireModal');
248
249        // Only run on favorites page
250        if (!favshareModal && !favenquireModal) return;
251
252        var wishlistInquiryStarted = false;
253
254        // Helper to get wishlist data from form fields
255        function getWishlistData() {
256            var chaletNamesField = document.getElementById('field_tmky42');
257            var chaletIdsField = document.getElementById('field_outp2');
258
259            return {
260                'chalet_names': chaletNamesField ? chaletNamesField.value : '',
261                'chalet_ids': chaletIdsField ? chaletIdsField.value : ''
262            };
263        }
264
265        // Wishlist Shared - track share button clicks
266        if (typeof jQuery !== 'undefined') {
267            // Copy Link button
268            jQuery('#copyButton').on('click', function() {
269                dataLayer.push({
270                    'event': 'wishlist_shared',
271                    'share_method': 'copy_link'
272                });
273            });
274
275            // WhatsApp link
276            jQuery('#favshareModal a[href*="wa.me"]').on('click', function() {
277                dataLayer.push({
278                    'event': 'wishlist_shared',
279                    'share_method': 'whatsapp'
280                });
281            });
282
283            // Email - track when email is sent successfully
284            jQuery('#sendMessageBtn').on('click', function() {
285                dataLayer.push({
286                    'event': 'wishlist_shared',
287                    'share_method': 'email'
288                });
289            });
290        }
291
292        // Wishlist Inquiry Started - track form interactions
293        function fireWishlistInquiryStarted() {
294            if (wishlistInquiryStarted) return;
295            wishlistInquiryStarted = true;
296
297            var data = getWishlistData();
298            dataLayer.push({
299                'event': 'wishlist_inquiry_started',
300                'chalet_names': data.chalet_names,
301                'chalet_ids': data.chalet_ids
302            });
303        }
304
305        if (typeof jQuery !== 'undefined') {
306            // Track when enquire modal is opened
307            jQuery('#favenquireModal').on('shown.bs.modal', function() {
308                // Slight delay to ensure form fields are populated
309                setTimeout(function() {
310                    // Track interaction with any form field
311                    jQuery('#favenquireModal input, #favenquireModal select, #favenquireModal textarea').on('focus click', function() {
312                        fireWishlistInquiryStarted();
313                    });
314                }, 100);
315            });
316
317            // Wishlist Inquiry Sent - fires on Formidable form 16 submission
318            jQuery(document).on('frmFormComplete', function(event, form, response) {
319                var formId = jQuery(form).find('input[name="form_id"]').val();
320                if (formId == '16') {
321                    var data = getWishlistData();
322                    dataLayer.push({
323                        'event': 'wishlist_inquiry_sent',
324                        'chalet_names': data.chalet_names,
325                        'chalet_ids': data.chalet_ids
326                    });
327                }
328            });
329        }
330    });
331
332    // Shared: Map budget range to midpoint value for GA4 conversion tracking
333    function getBudgetValue(budget) {
334        var map = {
335            'Less than 10000': 5000,
336            '10000-25000': 17500,
337            '25000-50000': 37500,
338            '50000-75000': 62500,
339            '75000-100K': 87500,
340            '100K-125K': 112500,
341            '125K-150K': 137500,
342            'More than 150K': 175000
343        };
344        return map[budget] || 0;
345    }
346
347    // Booking Inquiry Form Tracking (form ID 21 - enquiryform2024)
348    document.addEventListener('DOMContentLoaded', function() {
349        var bookingForm = document.getElementById('form_enquiryform2024');
350        if (!bookingForm) return;
351
352        var bookingInquiryStarted = false;
353        var storedBookingData = {};
354
355        // Store form data before Formidable clears fields on submit
356        bookingForm.addEventListener('submit', function() {
357            storedBookingData = getBookingInquiryData();
358        });
359
360        // Helper to get booking inquiry form data
361        function getBookingInquiryData() {
362            var emailField = document.getElementById('field_pok6m3');
363            var arrivalField = document.getElementById('field_jbjcw3');
364            var departureField = document.getElementById('field_w4uql3');
365            var budgetField = document.getElementById('field_v5fn02');
366
367            // Combine dates into search_daterange format
368            var daterange = '';
369            if (arrivalField && departureField && arrivalField.value && departureField.value) {
370                daterange = arrivalField.value + ' - ' + departureField.value;
371            }
372
373            var budgetText = budgetField ? budgetField.value : '';
374
375            return {
376                'email': emailField ? emailField.value : '',
377                'search_daterange': daterange,
378                'inquiry_budget': budgetText,
379                'value': getBudgetValue(budgetText)
380            };
381        }
382
383        // Booking Inquiry Started - fires once when user interacts with form
384        function fireBookingInquiryStarted() {
385            if (bookingInquiryStarted) return;
386            bookingInquiryStarted = true;
387
388            dataLayer.push({
389                'event': 'booking_inquiry_started'
390            });
391        }
392
393        // Track interactions with form fields
394        var formFields = bookingForm.querySelectorAll('input:not([type="hidden"]), select, textarea');
395        formFields.forEach(function(field) {
396            field.addEventListener('focus', fireBookingInquiryStarted);
397            field.addEventListener('click', fireBookingInquiryStarted);
398        });
399
400        // Booking Inquiry Sent - fires on Formidable form 21 submission
401        if (typeof jQuery !== 'undefined') {
402            jQuery(document).on('frmFormComplete', function(event, form, response) {
403                var formId = jQuery(form).find('input[name="form_id"]').val();
404                if (formId == '21') {
405                    var data = storedBookingData.email ? storedBookingData : getBookingInquiryData();
406                    var eventData = {
407                        'event': 'booking_inquiry_sent',
408                        'email': data.email,
409                        'search_daterange': data.search_daterange,
410                        'inquiry_budget': data.inquiry_budget
411                    };
412
413                    if (data.value > 0) {
414                        eventData.value = data.value;
415                        eventData.currency = 'CHF';
416                    }
417
418                    pushWithHashedEmail(eventData);
419                }
420            });
421        }
422    });
423
424    // General Inquiry Form Tracking (form ID 18 - contactform2)
425    document.addEventListener('DOMContentLoaded', function() {
426        var generalForm = document.getElementById('form_contactform2');
427        if (!generalForm) return;
428
429        var generalInquiryStarted = false;
430        var storedGeneralEmail = '';
431
432        // Store email before Formidable clears fields on submit
433        generalForm.addEventListener('submit', function() {
434            var emailField = document.getElementById('field_zzk0i2');
435            storedGeneralEmail = emailField ? emailField.value : '';
436        });
437
438        // General Inquiry Started - fires once when user interacts with form
439        function fireGeneralInquiryStarted() {
440            if (generalInquiryStarted) return;
441            generalInquiryStarted = true;
442
443            dataLayer.push({
444                'event': 'general_inquiry_started'
445            });
446        }
447
448        // Track interactions with form fields
449        var formFields = generalForm.querySelectorAll('input:not([type="hidden"]), select, textarea');
450        formFields.forEach(function(field) {
451            field.addEventListener('focus', fireGeneralInquiryStarted);
452            field.addEventListener('click', fireGeneralInquiryStarted);
453        });
454
455        // General Inquiry Sent - fires on Formidable form 18 submission
456        if (typeof jQuery !== 'undefined') {
457            jQuery(document).on('frmFormComplete', function(event, form, response) {
458                var formId = jQuery(form).find('input[name="form_id"]').val();
459                if (formId == '18') {
460                    pushWithHashedEmail({
461                        'event': 'general_inquiry_sent',
462                        'email': storedGeneralEmail || ''
463                    });
464                }
465            });
466        }
467    });
468
469    // Callback Request Tracking (request a call back modal)
470    document.addEventListener('DOMContentLoaded', function() {
471        var checkAvailabilityBtn = document.getElementById('show_availability');
472        if (!checkAvailabilityBtn) return;
473
474        // Callback Started - fires when user clicks "Check Availability"
475        checkAvailabilityBtn.addEventListener('click', function() {
476            dataLayer.push({
477                'event': 'callback_started'
478            });
479        });
480
481        // Callback Sent - fires on Formidable form 13 submission
482        if (typeof jQuery !== 'undefined') {
483            jQuery(document).on('frmFormComplete', function(event, form, response) {
484                var formId = jQuery(form).find('input[name="form_id"]').val();
485                if (formId == '13') {
486                    var emailField = document.getElementById('field_sm0iv');
487                    pushWithHashedEmail({
488                        'event': 'callback_sent',
489                        'email': emailField ? emailField.value : ''
490                    });
491                }
492            });
493        }
494    });
495
496    // French Inquiry Form Tracking (form ID 6)
497    document.addEventListener('DOMContentLoaded', function() {
498        var frenchContainer = document.getElementById('frm_form_6_container');
499        if (!frenchContainer) return;
500
501        var frenchForm = frenchContainer.querySelector('form');
502        if (!frenchForm) return;
503
504        var frenchInquiryStarted = false;
505        var storedFrenchData = {};
506
507        // Store form data before Formidable clears fields on submit
508        frenchForm.addEventListener('submit', function() {
509            storedFrenchData = getFrenchInquiryData();
510        });
511
512        function getFrenchInquiryData() {
513            var emailField = document.getElementById('field_d9tly2');
514            var budgetField = document.getElementById('field_1tr8d2');
515            var budgetText = budgetField ? budgetField.value : '';
516
517            return {
518                'email': emailField ? emailField.value : '',
519                'inquiry_budget': budgetText,
520                'value': getBudgetValue(budgetText)
521            };
522        }
523
524        // French Inquiry Started - fires once when user interacts with form
525        function fireFrenchInquiryStarted() {
526            if (frenchInquiryStarted) return;
527            frenchInquiryStarted = true;
528
529            dataLayer.push({
530                'event': 'french_inquiry_started'
531            });
532        }
533
534        var formFields = frenchForm.querySelectorAll('input:not([type="hidden"]), select, textarea');
535        formFields.forEach(function(field) {
536            field.addEventListener('focus', fireFrenchInquiryStarted);
537            field.addEventListener('click', fireFrenchInquiryStarted);
538        });
539
540        // French Inquiry Sent - fires on Formidable form 6 submission
541        if (typeof jQuery !== 'undefined') {
542            jQuery(document).on('frmFormComplete', function(event, form, response) {
543                var formId = jQuery(form).find('input[name="form_id"]').val();
544                if (formId == '6') {
545                    var data = storedFrenchData.email ? storedFrenchData : getFrenchInquiryData();
546                    var eventData = {
547                        'event': 'french_inquiry_sent',
548                        'email': data.email,
549                        'inquiry_budget': data.inquiry_budget
550                    };
551
552                    if (data.value > 0) {
553                        eventData.value = data.value;
554                        eventData.currency = 'CHF';
555                    }
556
557                    pushWithHashedEmail(eventData);
558                }
559            });
560        }
561    });
562
563    // German Inquiry Form Tracking (form ID 4)
564    document.addEventListener('DOMContentLoaded', function() {
565        var germanContainer = document.getElementById('frm_form_4_container');
566        if (!germanContainer) return;
567
568        var germanForm = germanContainer.querySelector('form');
569        if (!germanForm) return;
570
571        var germanInquiryStarted = false;
572        var storedGermanData = {};
573
574        // Store form data before Formidable clears fields on submit
575        germanForm.addEventListener('submit', function() {
576            storedGermanData = getGermanInquiryData();
577        });
578
579        function getGermanInquiryData() {
580            var emailField = document.getElementById('field_d9tly');
581            var budgetField = document.getElementById('field_1tr8d');
582            var budgetText = budgetField ? budgetField.value : '';
583
584            return {
585                'email': emailField ? emailField.value : '',
586                'inquiry_budget': budgetText,
587                'value': getBudgetValue(budgetText)
588            };
589        }
590
591        // German Inquiry Started - fires once when user interacts with form
592        function fireGermanInquiryStarted() {
593            if (germanInquiryStarted) return;
594            germanInquiryStarted = true;
595
596            dataLayer.push({
597                'event': 'german_inquiry_started'
598            });
599        }
600
601        var formFields = germanForm.querySelectorAll('input:not([type="hidden"]), select, textarea');
602        formFields.forEach(function(field) {
603            field.addEventListener('focus', fireGermanInquiryStarted);
604            field.addEventListener('click', fireGermanInquiryStarted);
605        });
606
607        // German Inquiry Sent - fires on Formidable form 4 submission
608        if (typeof jQuery !== 'undefined') {
609            jQuery(document).on('frmFormComplete', function(event, form, response) {
610                var formId = jQuery(form).find('input[name="form_id"]').val();
611                if (formId == '4') {
612                    var data = storedGermanData.email ? storedGermanData : getGermanInquiryData();
613                    var eventData = {
614                        'event': 'german_inquiry_sent',
615                        'email': data.email,
616                        'inquiry_budget': data.inquiry_budget
617                    };
618
619                    if (data.value > 0) {
620                        eventData.value = data.value;
621                        eventData.currency = 'CHF';
622                    }
623
624                    pushWithHashedEmail(eventData);
625                }
626            });
627        }
628    });
629})();

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.