PageSourceSearch

https://www.cloudbeds.com/wp-content/themes/cloudbeds-rebrand/asse…/form-progressive-view.js?ver=1.0.0

js cloudbeds.com collected 2026-10-01 08:31:48 UTC 55,751 bytes, 1,284 lines download raw bytes

1/* form-progressive-view.js (no <script> tag!) */
2(function () {
3  'use strict';
4
5  // ---- Config injected by render.php via wp_add_inline_script() ----
6  const CFG = window.CB_FORM || {};
7  const STR = CFG.strings || {};
8  const IMAGES  = CFG.imagesProps || {};
9  const CLASSES = CFG.classes || {};
10  const CLASS_ADD    = CLASSES.businessTypeButtonAdd    || '';
11  const CLASS_REMOVE = CLASSES.businessTypeButtonRemove || '';
12
13
14  // Close the Google Places dropdown without destroying it
15  const closePacDropdown = () => {
16    console.log("Close PAC");
17    const input = document.getElementById('hotel-search');
18    if (input) {
19      input.blur();
20    }
21    document.querySelectorAll('.pac-container').forEach(container => {
22      container.classList.add('pac-force-hidden');
23    });
24  };
25
26// ---- UTIL: safe dataLayer ----
27  const pushDL = (obj) => {
28    if (Array.isArray(window.dataLayer)) window.dataLayer.push(obj);
29  };
30
31  // ---- Google Places UI fixes  ----
32
33    let pacShouldBeHidden = false;
34
35    const pacObserver = new MutationObserver(() => {
36      document.querySelectorAll('.pac-container').forEach((container) => {
37        container.style.zIndex = '9999999';
38        container.style.position = 'absolute';
39        if (container.children.length > 0) container.style.display = 'block';
40        container.style.visibility = 'visible';
41        container.style.pointerEvents = 'auto';
42      });
43    });
44    pacObserver.observe(document.body, { childList: true, subtree: true });
45
46  const style = document.createElement('style');
47  style.textContent = `
48    .pac-container .pac-logo,
49    .pac-container .pac-logo:after { display: none !important; content: none !important; }
50  `;
51  document.head.appendChild(style);
52
53  // ---- Google Maps loader (avoid hardcoding the key in JS) ----
54  // Prefer: pass CFG.googleMapsKey from PHP; if missing, we still support your current URL.
55  function loadGoogleMaps(callback) {
56    if (window.google && google.maps && google.maps.places) { callback(); return; }
57    const key = CFG.googleMapsKey || null;
58    const url = key
59      ? `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(key)}&libraries=places`
60      : "https://maps.googleapis.com/maps/api/js?key=AIzaSyBBSiFcuELNG00NSxG5s_5eCChXpNzbVjk&libraries=places";
61    const s = document.createElement('script');
62    s.src = url; s.async = true; s.defer = true; s.onload = callback;
63    document.head.appendChild(s);
64  }
65
66// ---- Google API Autocomplete ----
67let autocompleteInstance = null;
68let lastGoogleSelection = ''; // Track last Google Places selection
69let partialCompanyName = ''; // Company name from a prior PARTIAL form-fill; submitted as property_name (Pardot dedup key) while places_hotel_name carries the Google Places selection
70
71function initAutocomplete() {
72  const input = document.getElementById("hotel-search");
73  if (!input) { 
74    console.error("⚠ hotel-search input not found"); 
75    return; 
76  }
77
78  // If already initialized, do nothing
79  if (autocompleteInstance) {
80    console.log("✓ Autocomplete already initialized - keeping it active");
81    return;
82  }
83
84  console.log("✓ Creating Google Places autocomplete - will stay active forever");
85
86  autocompleteInstance = new google.maps.places.Autocomplete(input, {
87    types: ["lodging"],
88    // No restrictions, worldwide componentRestrictions: {}
89  });
90
91  autocompleteInstance.addListener("place_changed", () => {
92    const place = autocompleteInstance.getPlace();
93
94    console.log("Place selected:", place.name);
95
96    let hotelName = place.name || '';
97    
98    // Store this as the last valid Google selection
99    lastGoogleSelection = hotelName;
100    
101    let address = place.formatted_address || '';
102    let placeWebsite = place.website || '';
103    let placePhone = place.formatted_phone_number || place.international_phone_number || '';
104    let city = '', state = '', country = '', zip = '', streetNumber = '', route = '';
105
106    if (placeWebsite) {
107      try {
108        const u = new URL(placeWebsite);
109        u.search = ""; u.hash = "";
110        placeWebsite = u.toString();
111      } catch (_) {
112        placeWebsite = placeWebsite.split('?')[0].split('#')[0];
113      }
114    }
115    
116    if (!place.geometry) {
117      alert("No location data available for selection."); 
118      return;
119    }
120
121    $("#demo-form .places-formatted-address").val(address);
122    address = address.split(',')[0];
123
124    (place.address_components || []).forEach((comp) => {
125      const t = comp.types || [];
126      if (t.includes('locality')) city = comp.long_name;
127      if (t.includes('postal_code')) zip = comp.long_name;
128      if (t.includes('country')) country = comp.long_name;
129      if (t.includes('street_number')) streetNumber = comp.long_name;
130      if (t.includes('route')) route = comp.long_name;
131      if (t.includes('administrative_area_level_1')) state = comp.long_name;
132    });
133
134    $("#demo-form .places-state").val(state);
135    $("#demo-form .places-website").val(placeWebsite);
136    $("#demo-form .places-phone").val(placePhone);
137    $("#demo-form .places-street-prospect").val([streetNumber, route].filter(Boolean).join(" "));
138
139    document.getElementById('hotel_name').value = hotelName;
140    document.getElementById('address').value = address;
141    document.getElementById('city').value = city;
142    document.getElementById('state').value = state;
143    document.getElementById('country').value = country;
144    document.getElementById('zip').value = zip;
145
146    // DON'T clear anything - let autocomplete stay active!
147  });
148
149  // Clear hidden fields when user manually types (not from Google)
150  input.addEventListener('input', function () {
151    // If the current value doesn't match the last Google selection, clear hidden fields
152    if (this.value.trim() !== lastGoogleSelection) {
153      document.getElementById('hotel_name').value = '';
154      document.getElementById('address').value = '';
155      document.getElementById('city').value = '';
156      document.getElementById('state').value = '';
157      document.getElementById('country').value = '';
158      document.getElementById('zip').value = '';
159      $("#demo-form .places-formatted-address").val('');
160      $("#demo-form .places-state").val('');
161      $("#demo-form .places-website").val('');
162      $("#demo-form .places-phone").val('');
163      $("#demo-form .places-street-prospect").val('');
164    }
165  });
166}
167
168  // ================== jQuery section ==================
169
170  /* * Mobile Fix for intl-tel-input: (Mutation observer for flags repositioning on mobile)
171  * Moves the dropdown (.iti--container) from <body> to .iti__flag-container on click.
172  * Uses a resettable 10s interval to ensure correct placement on every interaction.
173  */
174  //----------------------------------------------------
175
176/*  $(document).ready(function() {
177      // Shared variables
178      let cbflagsInterval;
179      let cbflagsMoveCount = 0;
180      const cbflagsMaxMoves = 20; // 20 checks * 500ms = 10 seconds of monitoring
181
182      // Helper: Check mobile state dynamically
183      function cbflagsIsMobileDevice() {
184          return window.innerWidth <= 768 || 
185                /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test((navigator.userAgent || navigator.vendor || window.opera).toLowerCase());
186      }
187
188      // The Worker Function
189      function cbflagsCheckAndMove() {
190          // 1. Safety Limit: Stop if we've checked too many times
191          if (cbflagsMoveCount >= cbflagsMaxMoves) {
192              clearInterval(cbflagsInterval);
193              return;
194          }
195
196          const $itiContainer = $('.iti.iti--container');
197          const $flagContainer = $('.iti__flag-container');
198
199          // 2. The Move Logic
200          // We check if the container is currently sitting directly in 'body' (the wrong place)
201          if ($itiContainer.length && $itiContainer.parent().is('body')) {
202              $itiContainer.appendTo($flagContainer);
203              // We do NOT clear interval here, just in case the library tries to fight back 
204              // and move it out again within the next few seconds.
205          }
206          
207          cbflagsMoveCount++;
208      }
209
210      // The Trigger
211      $(document).on('click', '.iti__flag-container', function() {
212          // Optimization: Don't run logic if we are currently on a desktop screen
213          if (!cbflagsIsMobileDevice()) return;
214
215          // Reset: Wake up the logic
216          cbflagsMoveCount = 0;
217          clearInterval(cbflagsInterval); // Kill any old timers
218          
219          // Start: Run immediately, then every 500ms
220          cbflagsCheckAndMove(); 
221          cbflagsInterval = setInterval(cbflagsCheckAndMove, 500);
222      });
223  });
224  */
225  // End of Mutation observer for flags repositioning on mobile
226  //----------------------------------------------------
227
228  jQuery(function ($) {
229
230    // ---- Prefill from cookie data (returning visitor who hasn't submitted yet) ----
231    (function prefillFromCookie() {
232      var cbd = window.__cbUserInfoData;
233      if (!cbd || cbd.form_filled === true) return;
234
235      // Match an element whose literal value attribute equals `val`. Avoids interpolating
236      // unescaped cookie data into a jQuery attribute selector, where a stray quote would
237      // throw a Sizzle parse error and abort the rest of this function.
238      function byValueAttr($set, val) {
239        return $set.filter(function () { return this.getAttribute('value') === val; });
240      }
241
242      // Step 1: Company / property name (PARTIAL form-fill only)
243      // Handled FIRST — before the contact-info guard below — so the Pardot dedup override
244      // is still set for a partial visitor who has a company name but no stored
245      // name/email/phone. We deliberately do NOT prefill the Google Places search field
246      // (#hotel-search): we want the visitor to search and pick a real place so we still
247      // capture accurate address/geo data. But the company NAME they already gave on the
248      // earlier partial form is authoritative — Google Places' official place.name rarely
249      // matches what they typed, and submitting a different string makes Pardot's
250      // email+company dedup create a duplicate/aliased prospect. So we stash the partial
251      // company name and force it as the submitted company name at the final step (see
252      // FINAL STEP below), overriding whatever Google Places returns.
253      if (cbd.form_filled === 'partial' && cbd.property_name) {
254        partialCompanyName = String(cbd.property_name).trim();
255        if (partialCompanyName) {
256          // Non-lodging path uses a plain typed field (not Google Places) — safe to prefill.
257          $('.flow-items .company-name').val(partialCompanyName);
258        }
259      }
260
261      // Check there's actually something useful to prefill (beyond the company name above)
262      if (!cbd.first_name && !cbd.email && !cbd.phone) return;
263
264      // Step 3: Name, email, phone
265      if (cbd.first_name || cbd.last_name) {
266        var fullName = ((cbd.first_name || '') + ' ' + (cbd.last_name || '')).trim();
267        if (fullName) $('.flow-items .full-name').val(fullName);
268      }
269      if (cbd.email) $('.flow-items .email').val(cbd.email);
270      if (cbd.phone) $('.flow-items .company-phone').val(cbd.phone);
271
272      // Step 2: Rooms, beds, VR fields
273      if (cbd.room_count) $('.flow-items .room-count').val(cbd.room_count);
274      if (cbd.bed_count) $('.flow-items .bed-count').val(cbd.bed_count);
275      if (cbd.vr_listings_number) $('.flow-items .vr-listings').val(cbd.vr_listings_number);
276      if (cbd.vr_address_number) $('.flow-items .vr-address').val(cbd.vr_address_number);
277
278      // Step 2: Property group type select
279      if (cbd.property_group_type) {
280        var $groupSelect = $('select[name="property-group-type"]');
281        if (byValueAttr($groupSelect.find('option'), cbd.property_group_type).length) {
282          $groupSelect.val(cbd.property_group_type);
283        }
284      }
285
286      // Step 3: Job role select
287      if (cbd.job_role) {
288        var $jobSelect = $('select[name="job-role"]');
289        if (byValueAttr($jobSelect.find('option'), cbd.job_role).length) {
290          $jobSelect.val(cbd.job_role);
291        }
292      }
293
294      // Step 1: Auto-select business type button (if property_type is set)
295      // Deferred — click handler is registered later in this same jQuery ready block
296      if (cbd.property_type) {
297        setTimeout(function() {
298          var $btn = byValueAttr($('.business-type-button'), cbd.property_type);
299          if ($btn.length) {
300            $btn.trigger('click');
301          }
302        }, 0);
303      }
304    })();
305
306    // ---- Phone country code init (relies on intlTelInput already enqueued) ----
307    /* OLD BLOCK - Targeting wrong path
308     let iti;
309    function phoneCcode() {
310      let geo_obj = {};
311      try { geo_obj = JSON.parse(localStorage.getItem("geo_data") || "{}"); } catch (_) { }
312      const ccode_now = geo_obj.geo_country;
313      const input = document.querySelector(".company-phone");
314      if (!input || typeof window.intlTelInput !== 'function') return null;
315      return window.intlTelInput(input, {
316        separateDialCode: true,
317        initialCountry: ccode_now || 'auto',
318        utilsScript: "/wp-content/themes/cloudbeds-rebrand/assets/vendor/ccode/js/utils.js"
319      });
320    }
321    if (!localStorage.getItem("geo_data")) {
322      window.addEventListener("message", (event) => {
323        if (event.data === "geo_populated") iti = phoneCcode();
324      });
325    } else {
326      iti = phoneCcode();
327    } 
328    */
329
330    // intl-tel-input: country code prefix/flags on phone field
331    let iti;
332    function phoneCcode() {
333      let geo_obj = {};
334      try { geo_obj = JSON.parse(localStorage.getItem("geo_data") || "{}"); } catch (_) { }
335      const ccode_now = geo_obj.geo_country;
336      const input = document.querySelector(".company-phone");
337      if (!input || typeof window.intlTelInput !== 'function') return null;
338      return window.intlTelInput(input, {
339        separateDialCode: true,
340        initialCountry: ccode_now || 'us',
341        loadUtils: () => import("/wp-content/themes/cloudbeds-rebrand/assets/vendor/ccode/js/utils.js")
342      });
343    }
344    // Always initialize immediately (fallback to 'us' if no geo_data)
345    iti = phoneCcode();
346    // Update country if geo data arrives later
347    window.addEventListener("message", (event) => {
348      if (event.data === "geo_populated") {
349        let geo_obj = {};
350        try { geo_obj = JSON.parse(localStorage.getItem("geo_data") || "{}"); } catch (_) { }
351        if (geo_obj.geo_country && iti) {
352          iti.setCountry(geo_obj.geo_country);
353        }
354      }
355    });
356
357    // Phone field: only allow digits and leading plus sign (no letters)
358    $(document).on('input', '.company-phone, .form-phone', function () {
359      let val = $(this).val();
360      // Strip everything except digits and +
361      val = val.replace(/[^0-9+]/g, '');
362      // Allow + only as the first character
363      const first = val.charAt(0);
364      val = first + val.slice(1).replace(/\+/g, '');
365      if (val !== $(this).val()) $(this).val(val);
366      // Clear error style as user corrects input
367      $(this).removeClass('input-error-style');
368    });
369
370    // Close Google Places dropdown on any page scroll (desktop + touch devices)
371    $(window).on('scroll wheel', function() {
372      const pacContainer = document.querySelector('.pac-container');
373      if (pacContainer && !pacContainer.classList.contains('pac-force-hidden')) {
374        closePacDropdown();
375      }
376    });
377
378    // Touch devices - listen on document for better iOS/Android support
379    document.addEventListener('touchmove', function() {
380      const pacContainer = document.querySelector('.pac-container');
381      if (pacContainer && !pacContainer.classList.contains('pac-force-hidden')) {
382        closePacDropdown();
383      }
384    }, { passive: true });
385
386    // Initialize autocomplete on initial load if visible
387    setTimeout(function() {
388      const input = document.getElementById("hotel-search");
389      const $field = $('.hotel-search-field');
390      const isVisible = $field.hasClass('flex') && !$field.hasClass('dis-none');
391      
392      if (input && isVisible) {
393        console.log("✓ Initial load - loading Google Maps");
394        loadGoogleMaps(initAutocomplete);
395      }
396    }, 500);
397
398    // ---- Helpers  ----
399
400    function updateStepSubtitle(itemName) {
401    
402      const subtitles = {
403        'get-property-type': STR.step1_subtitle || 'Which best describes your business?',
404        'get-rooms': STR.step2_subtitle || "Let's get some details about your property",
405        'get-name-email': STR.step3_subtitle || 'Last step, a little about you'
406      };
407      
408      const subtitle = subtitles[itemName];
409      if (subtitle) {
410        $('.top-banner-title .font-w-400').text(subtitle);
411      }
412    }  
413
414    function extractEmailDomian2(email) {
415      const parts = String(email || '').toLowerCase().split("@");
416      return parts.length === 2 ? parts[1] : null;
417    }
418    function filterEmail2(email) {
419      const bad = new Set([
420        'blick.net','business.com','duscore.com','fastmail.com','internetkeno.com','mailinator.com',
421        'oneraman.com','rhyta.com','teleworm.us','ipnuc.com','byom.de','bikedid.com'
422      ]);
423      return bad.has(extractEmailDomian2(email)) ? 1 : null;
424    }
425
426    let demo_lead_created = false;
427    let items = [];
428    let item_count = 0;
429    let quiz_results = {};
430    let business_priorities = {};
431    // let rooms_label = STR.rooms_title_hotel || 'How many rooms do you have?';
432
433    //function apply_variables_ui(){ $('.get-rooms-label').text(rooms_label); }
434    //apply_variables_ui();
435
436    function fix_name(fullName=''){
437      const index = fullName.indexOf(" ");
438      let fname = index > -1 ? fullName.substr(0, index) : fullName;
439      let lname = index > -1 ? fullName.substr(index + 1) : '';
440      if (!fname && lname) { fname = lname; lname = lname + "_lastname"; }
441      if (!lname) lname = fname + "_lastname";
442      quiz_results.first_name = fname; quiz_results.last_name = lname;
443    }
444
445    function setPaginate(pagenumber=0){
446      items = [];
447      let i=0;
448      $('.item-paginator').html('');
449      $('.flow-items .flow-item').each(function(){
450        if (!$(this).hasClass('disable')) {
451          items[i] = $(this).attr('item');
452          $('.item-paginator').append('<div class="pag_item pag_item_' + i + ' flex col justify-center"><i class="fa fa-solid fa-square mgl-5 mgr-5"></i></div>');
453          (pagenumber>0 ? $('.pag_item_'+pagenumber) : $('.pag_item_0')).addClass('site-color-14');
454          i++;
455        }
456      });
457    }
458    setPaginate();
459
460    // ---- Restore step after Qualified-triggered page reload (skip animation) ----
461    (function() {
462      try {
463        if (sessionStorage.getItem('cb_pf_qualified') !== '1') return;
464        var savedStep = parseInt(sessionStorage.getItem('cb_pf_step'), 10);
465        sessionStorage.removeItem('cb_pf_qualified');
466        sessionStorage.removeItem('cb_pf_step');
467        if (isNaN(savedStep) || savedStep < 0 || savedStep >= items.length) return;
468
469        // Skip the intro screen
470        $('.intro-item').addClass('dis-none');
471        $('.flow-container').removeClass('dis-none');
472
473        // Hide all steps, show saved step instantly (no animation)
474        $('.flow-items .flow-item').addClass('dis-none').removeClass('flex');
475        var $target = $('.flow-item[item|=' + items[savedStep] + ']');
476        $target.removeClass('dis-none fadeInUp fadeOutUp fadeInDown fadeOutDown').addClass('flex').css('opacity', '1');
477
478        // Sync pagination and state
479        item_count = savedStep;
480        setPaginate(savedStep);
481        if (savedStep > 0) { $('.back-button').removeClass('opacity-0'); } else { $('.back-button').addClass('opacity-0'); }
482        if (savedStep === 3) {
483          $('.top-banner-title').addClass('dis-none');
484          $('.top-banner-title2').removeClass('dis-none');
485        }
486      } catch(e) {}
487    })();
488
489    $('.get-started').on('click', function(){
490      $('.intro-item').addClass('dis-none');
491      $('.flow-container').removeClass('dis-none');
492    });
493
494    // Auto-hide validation warnings on interaction
495    $(document).on('input change', '.cb-data-field, .business-type-button, .compact-field, .room-count, .vr-listings, .vr-address, .bed-count, .full-name, .email, .company-phone', function () {
496      $(this).closest('.flow-item').find('.selection-warning-wrapper').addClass('dis-none');
497    });
498
499    // Clear Terms & Conditions error on check
500    $(document).on('change', '.form-terms', function () {
501      if ($(this).is(':checked')) {
502        $(this).removeClass('input-error-style');
503      }
504    });
505
506    let item_ready = 0;
507
508    // small helper to get selected business type value
509    const getSelectedBusinessType = () =>
510      $('.flow-item[item|=get-property-type]').find('.business-type-button.business-selected').val()
511      || $('.flow-item[item|=get-property-type]').find('.business-type-button.business-selected').attr('value');
512
513    /**
514     * START THE FORM VALIDATION
515     *  
516     */  
517    function form_validate(){
518      let valid = 0;
519
520      // VALIDATE-  get-property-type
521      if (items[item_count] === 'get-property-type') {
522        const $it = $('.flow-item[item|=get-property-type]');
523        if (!$it.find('.business-type-button').hasClass('business-selected')) valid = 1;
524        
525        if(valid === 1) {
526          $('.selection-warning-wrapper.instance-01').removeClass('dis-none');
527        } else {
528          $('.selection-warning-wrapper.instance-01').addClass('dis-none');
529        }
530
531        if (valid === 1) $('.next-button').addClass('opacity-05');
532
533        if (valid === 0) {
534          pushDL({ 
535            event:"demo_form_wizard",
536            category:"Request a demo",
537            action:"Step 1", 
538            element_type:"Wizard", 
539            element_position:"In page",
540            form_property_type: quiz_results.property_type 
541          });
542        }
543
544        const selectedVal = getSelectedBusinessType();
545        $('#redirect-partner').attr('style', 'display: none !important;');
546        $('#redirect-consultant').attr('style', 'display: none !important;');
547        
548        // Show only the relevant one
549        if (selectedVal === 'Technology Partner') {
550          $('#redirect-partner').attr('style', 'display: flex !important;');
551          return;
552        }
553        if (selectedVal === 'Consultant') {
554          $('#redirect-consultant').attr('style', 'display: flex !important;');
555          return;
556        }
557        console.log(items[item_count]);
558      }
559
560      // redirect-pages
561      if (items[item_count] === 'redirect-page') {
562        const selectedVal = getSelectedBusinessType();
563        if (selectedVal === 'Technology Partner') {
564          $(".close-modal").trigger('click');
565          window.location.href = CFG.redirectLinkPartner;
566          return;
567        }
568        if (selectedVal === 'Consultant') {
569          $(".close-modal").trigger('click');
570          window.location.href = CFG.redirectLinkConsultant;
571          return;
572        }
573      }
574
575      
576      // VALIDATE ---- get-rooms ---- DIV - GET HOTEL NAME AND INFO ROOMS VIA GOOGLE PLACES API
577      if (items[item_count] === 'get-rooms') {
578        console.log(items[item_count]);
579        
580        const $it = $('.flow-item[item|=get-rooms]');
581        $it.find('.valid-rooms,.valid-listings,.valid-addresses,.valid-country').text('').addClass('dis-none');
582        $it.find('.valid-beds').text('');
583        $('.vr-radio').removeClass('site-color-13');
584
585        const $groupType = $it.find('select[name="property-group-type"]');
586        if ($groupType.val() === "no-option") {
587          valid = 1;
588          $it.find('.valid-property-group-type').text(`(${STR.please_select_an_option || 'Please select an option'})`);
589          $groupType.addClass('input-error-style');
590        } else {
591          $groupType.removeClass('input-error-style');
592        }
593
594        // Check if property-info is visible (manual entry mode)
595        const propertyInfoVisible = $('.property-info').hasClass('flex') && !$('.property-info').hasClass('dis-none');
596        console.log('🏨 Property info visible?', propertyInfoVisible); // DEBUG
597        
598//        if (propertyInfoVisible) {
599          // Validate property-info fields instead of other fields
600          console.log('🔍 Validating property-info fields'); // DEBUG
601
602          // Store the SVG in a variable
603          const editIcon = '<svg width="10" height="13" viewBox="0 0 10 13" fill="none" xmlns="https://www.w3.org/2000/svg"><g clip-path="url(#clip0_1_2)"><path d="M9.87821 9.04902C9.84336 8.97157 9.79569 8.90049 9.73718 8.83873L6.76923 5.86275C6.6466 5.75835 6.48886 5.70379 6.32753 5.70999C6.1662 5.71618 6.01316 5.78267 5.899 5.89616C5.78483 6.00965 5.71795 6.16179 5.71172 6.32217C5.70549 6.48255 5.76037 6.63937 5.86538 6.76127L7.73718 8.62206H3.25C2.73997 8.62206 2.25083 8.42064 1.89018 8.06212C1.52953 7.70359 1.32692 7.21733 1.32692 6.71029V0.764706C1.32692 0.595695 1.25939 0.433607 1.13917 0.314098C1.01896 0.19459 0.855908 0.127451 0.685897 0.127451C0.515887 0.127451 0.35284 0.19459 0.232624 0.314098C0.112408 0.433607 0.0448718 0.595695 0.0448718 0.764706V6.73578C0.0448718 7.58084 0.382554 8.39128 0.983632 8.98882C1.58471 9.58636 2.39995 9.92206 3.25 9.92206H7.73718L5.86538 11.7828C5.8053 11.8421 5.75761 11.9126 5.72507 11.9902C5.69253 12.0679 5.67577 12.1512 5.67577 12.2353C5.67577 12.3194 5.69253 12.4027 5.72507 12.4804C5.75761 12.558 5.8053 12.6285 5.86538 12.6877C5.92528 12.7468 5.99632 12.7935 6.07442 12.8252C6.15252 12.857 6.23615 12.873 6.32051 12.8725C6.48868 12.8718 6.64983 12.8055 6.76923 12.6877L9.73718 9.73726C9.79784 9.67722 9.84579 9.60572 9.87821 9.52696C9.94058 9.37368 9.94058 9.2023 9.87821 9.04902Z" fill="black"></path></g><defs><clipPath id="clip0_1_2"><rect width="10" height="13" fill="white"></rect></clipPath></defs></svg>';
604                 
605          //  Validate Google API hotel-search field*
606          const hotelSearchValue = $('#hotel-search').val();
607          if (!hotelSearchValue || !hotelSearchValue.trim()) {
608            valid = 1;
609            $('.valid-hotel-search')
610              .text(`(${STR.hotel_search_required || 'Please enter your hotel name'})`)
611              .removeClass('dis-none');
612            $('#hotel-search').addClass('input-error-style');
613            styleErrorInputs();
614          } else {
615            // If there's a value but hotel_name is empty, sync it now
616            if (!$('#hotel_name').val()) {
617              $('#hotel_name').val(hotelSearchValue.trim());
618            }
619          }
620
621          // Normal validation for room count fields
622          if (quiz_results.property_type !== 'Vacation Homes') {
623            const $roomCount = $it.find('.room-count');
624            if (!/^([1-9]|[1-9][0-9]{1,3})$/.test($roomCount.val())) {
625              valid = 1;
626              $it.find('.valid-rooms').text(`(${STR.rooms_valid_name || 'Enter a number equal to 1 or higher'})`).removeClass('dis-none');
627              $roomCount.addClass('input-error-style');
628            } else {
629              $roomCount.removeClass('input-error-style');
630            }
631          } else {
632            const $vrListings = $it.find('.vr-listings');
633            if (!/^([1-9]|[1-9][0-9]{1,2}|1[0-9]{3}|2000)$/.test($vrListings.val())) {
634              $it.find('.valid-listings').text(`(${STR.rooms_vr_valid_listings || 'Enter the number of listings you own/manage'})`).removeClass('dis-none');
635              $vrListings.addClass('input-error-style');
636              valid = 1;
637            } else {
638              $vrListings.removeClass('input-error-style');
639            }
640            const $vrAddress = $it.find('.vr-address');
641            if (!/^([1-9]|[1-9][0-9]{1,2}|1[0-9]{3}|2000)$/.test($vrAddress.val())) {
642              $it.find('.valid-addresses').text(`(${STR.rooms_vr_valid_addresses || 'Please enter the number of addresses'})`).removeClass('dis-none');
643              $vrAddress.addClass('input-error-style');
644              valid = 1;
645            } else {
646              $vrAddress.removeClass('input-error-style');
647            }
648          }
649
650          // MA-654: country no longer arrives pre-filled from GeoIP, so it must be answered.
651          const $country = $it.find('select.chili-countries');
652          const $countryLabel = $country.closest('.cb-field').find('.cb-label');
653          if (!$country.val()) {
654            valid = 1;
655            $it.find('.valid-country')
656              .text(`(${STR.country_required || 'Country is required'})`)
657              .removeClass('dis-none');
658            $country.addClass('input-error-style');
659            $countryLabel.addClass('label-error-style');
660          } else {
661            $country.removeClass('input-error-style');
662            $countryLabel.removeClass('label-error-style');
663          }
664
665          if (quiz_results.property_type === 'Hostel') {
666            const $bedCount = $it.find('.bed-count');
667            if (!$bedCount.val()) {
668              $it.find('.valid-beds').text(`(${STR.rooms_valid_name || 'Enter a number equal to 1 or higher'})`);
669              $bedCount.addClass('input-error-style');
670              valid = 1;
671            } else {
672              $bedCount.removeClass('input-error-style');
673            }
674          }
675
676          console.log('Step 2 Validation Status:', { valid, item: items[item_count] });
677          if(valid === 1) {
678            $('.selection-warning-wrapper.instance-02').removeClass('dis-none');
679          } else {
680            $('.selection-warning-wrapper.instance-02').addClass('dis-none');
681          }
682  //      }
683
684        if (valid === 0) {
685          pushDL({
686            event: "demo_form_wizard",
687            category: "Request a demo",
688            action: "Step 2",
689            element_type: "Wizard",
690            element_position: "In page",
691            form_property_type: quiz_results.property_type,
692            form_rooms_number: quiz_results.room_count,
693            form_group_independent: $it.find('select[name="property-group-type"]').val(),
694          });
695        }
696      }
697
698      // VALIDATE get-name-email DIV - GET PERSONAL INFO NAME, EMAIL, PHONE 
699      if (items[item_count] === 'get-name-email') {
700        const $wrap = $('.flow-items');
701        $wrap.find('.valid-full-name,.valid-email,.valid-job-role,.valid-phone').text('');
702
703        const $jobRole = $wrap.find('select[name="job-role"]');
704        if ($jobRole.val() === "no-role") {
705          valid = 1;
706          $wrap.find('.valid-job-role').text(`(${STR.please_select_a_role || 'Please select a role'})`);
707          $jobRole.addClass('input-error-style');
708        } else {
709          $jobRole.removeClass('input-error-style');
710        }
711
712        const $fullName = $wrap.find('.full-name');
713        if (!$fullName.val()) {
714          valid = 1;
715          $wrap.find('.valid-full-name').text(`(${STR.get_email_valid_name || 'Please enter full name'})`);
716          $fullName.addClass('input-error-style');
717        } else {
718          $fullName.removeClass('input-error-style');
719        }
720
721        const $emailInput = $wrap.find('.email');
722        const emailVal = $emailInput.val();
723        if (/^([a-zA-Z0-9_.+\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,})$/.test(emailVal)) {
724          if (filterEmail2(emailVal) === 1) {
725            valid = 1;
726            $wrap.find('.valid-email').text(`(${STR.get_email_valid_email || 'Please enter a valid email'})`);
727            $emailInput.addClass('input-error-style');
728          } else {
729            $emailInput.removeClass('input-error-style');
730          }
731          $('#email_hidden').val(emailVal);
732        } else {
733          valid = 1;
734          $wrap.find('.valid-email').text(`(${STR.get_email_valid_email || 'Please enter a valid email'})`);
735          $emailInput.addClass('input-error-style');
736        }
737
738        const $phoneInput = $wrap.find('.company-phone, .form-phone');
739        const phoneRaw = $phoneInput.val();
740        const phoneVal = phoneRaw.replace(/[\s\-\(\)]/g, ''); // strip spaces, dashes, parens for validation
741        if (/^[\+]?[0-9]{7,15}$/.test(phoneVal)) {
742            // Valid phone - at least 7 digits, max 15, optional leading +
743            $('#phone_hidden').val(phoneRaw);
744            $wrap.find('.valid-phone').addClass('hidden');
745            $phoneInput.removeClass('input-error-style');
746        } else {
747            valid = 1;
748            $wrap.find('.valid-phone').text(`(${STR.get_phone_valid_phone || 'Please enter a valid phone number'})`).removeClass('hidden');
749            $phoneInput.addClass('input-error-style');
750        }
751
752        // Validate Terms & Conditions checkbox
753        // Commented out — SMS opt-in is not required
754        // if (!$wrap.find('.form-terms').is(':checked')) {
755        //   valid = 1;
756        //   $wrap.find('.form-terms').addClass('input-error-style');
757        // } else {
758        //   $wrap.find('.form-terms').removeClass('input-error-style');
759        // }
760
761        console.log('Step 3 Validation Status:', { valid, item: items[item_count], emailVal, phoneRaw, phoneVal, jobRole: $wrap.find('select[name="job-role"]').val(), fullName: $wrap.find('.full-name').val() });
762        if(valid === 1) {
763          $('.selection-warning-wrapper.instance-03').removeClass('dis-none');
764        } else {
765          $('.selection-warning-wrapper.instance-03').addClass('dis-none');
766        }
767
768        if (valid === 0) {
769          pushDL({
770            event: "demo_form_wizard",
771            category: "Request a demo",
772            action: "Step 3",
773            element_type: "Wizard",
774            element_position: "In page",
775            form_property_type: quiz_results.property_type,
776            form_job_role: $wrap.find('select[name="job-role"]').val(),
777            form_country: $wrap.find('select[name="country"]').val()
778          });
779        }
780      }
781
782      // VALIDATE Get-company-name DIV
783      if (items[item_count] === 'get-company-name') {
784        const $it = $('.flow-item[item|=get-company-name]');
785        $it.find('.valid-company-name,.valid-company-phone').text('');
786        const $companyName = $it.find('.company-name');
787        if (!$companyName.val()) {
788          valid = 1;
789          $it.find('.valid-company-name').text(`(${STR.company_name_val
789id || 'Please enter your company name'})`);
790          $companyName.addClass('input-error-style');
791        } else {
792          $companyName.removeClass('input-error-style');
793        }
794        const $companyPhone = $it.find('.company-phone');
795        if (!$companyPhone.val()) {
796          valid = 1;
797          $it.find('.valid-company-phone').text(`(${STR.company_phone_valid || 'Please enter your phone Number'})`);
798          $companyPhone.addClass('input-error-style');
799        } else {
800          $companyPhone.removeClass('input-error-style');
801        }
802      }
803
804      // FINAL STEP → populate hidden #demo-form and submit FORM
805      if (valid === 0 && items[item_count] === items[items.length - 1]) {
806
807        /*
808          console.log('Final step - collecting values');
809          console.log('Full name value:', $('.flow-items .full-name').val());
810          console.log('Email value:', $('.flow-items .email').val());
811          console.log('Company name:', $('.flow-items .company-name').val());
812          console.log('Hotel name:', $('#hotel_name').val());
813          console.log('All items:', items);
814          console.log('Item Length',items[items.length - 1]);          
815          console.log('Current item:', items[item_count]); 
816        */
817
818        fix_name($('.flow-items .full-name').val());
819
820        // Sync company_name and hotel_name - use whichever has a value
821        let company_name_value = $('.flow-items .company-name').val() || $('#hotel_name').val();
822
823        // Name the visitor actually selected from Google Places (place.name). Read from
824        // #hotel_name — NOT lastGoogleSelection: the input handler clears #hotel_name (and
825        // the address/geo fields) whenever the user edits the search box after selecting, so
826        // it is the authoritative "is a Google Place currently selected?" signal and won't
827        // submit a stale name. Captured before the property_name override below.
828        let googlePlacesName = $('#hotel_name').val() || '';
829
830        // Partial-form handling: property_name keeps the company name the prospect
831        // originally TYPED on the partial form — the Pardot email+company dedup key, so
832        // we don't spawn a duplicate prospect. The Google Places selection is submitted
833        // separately as places_hotel_name (see below).
834        if (partialCompanyName) {
835          company_name_value = partialCompanyName;
836        }
837
838        $('.flow-items .company-name').val(company_name_value);
839        $('#hotel_name').val(company_name_value);
840
841        // property_name — the visitor's own company name (the typed value for a partial visitor).
842        quiz_results.company_name = company_name_value;
843
844        // places_hotel_name — for a PARTIAL visitor who picked a Google Places result,
845        // submit that selected place name here while property_name stays the typed value.
846        // Otherwise (non-partial, or no Google selection) mirror the company name.
847        quiz_results.hotel_name = (partialCompanyName && googlePlacesName)
848          ? googlePlacesName
849          : company_name_value;
850
851        quiz_results.email = $('.flow-items .email').val();
852        quiz_results.job_role = $('.flow-items .job-role').val();
853        quiz_results.property_group_type = $('.flow-items').find('select[name="property-group-type"]').val();
854        quiz_results.user_language = $('.user-language').val();
855        
856        // Get full international phone number (dial code + local number)
857        // iti.getNumber() returns "" if utils.js hasn't loaded yet, so fallback
858        // to manually constructing the number from dial code + input value
859        quiz_results.phone = '';
860        if (iti) {
861          const itiNumber = (typeof iti.getNumber === 'function') ? iti.getNumber() : '';
862          if (itiNumber) {
863            quiz_results.phone = itiNumber;
864          } else {
865            // utils.js not loaded yet — build number manually
866            const dialCode = iti.getSelectedCountryData()?.dialCode || '';
867            const localNum = $('.company-phone').val().replace(/[^\d]/g, '');
868            quiz_results.phone = dialCode && localNum ? '+' + dialCode + localNum : localNum;
869          }
870        } else {
871          quiz_results.phone = $('.company-phone').val();
872        }
873
874        quiz_results.country = $('.flow-items select.chili-countries').val();
875        quiz_results.language = $('.flow-items .user-language').val();
876        quiz_results.langid = $('.flow-items .user-language').find(':selected').attr('langid');
877
878        let room_mod = quiz_results.room_count;
879
880        const $f = $("#demo-form");
881        $f.find(".form-property-name").val(quiz_results.company_name);
882        $f.find(".form-property-type").val(quiz_results.property_type);
883        $f.find(".form-multiple-properties").val(quiz_results.vr_locations);
884        $f.find(".form-room-count").val(room_mod);
885        $f.find(".form-bed-count").val(quiz_results.bed_count);
886        $f.find(".form-full-name").val(quiz_results.first_name + ' ' + quiz_results.last_name);
887        $f.find(".form-first-name").val(quiz_results.first_name);
888        $f.find(".form-last-name").val(quiz_results.last_name);
889        $f.find(".form-email").val(quiz_results.email);
890        $f.find(".form-phone").val(quiz_results.phone);
891        $f.find(".places-city").val($('#city').val());
892        $f.find(".places-country").val($('#country').val());
893        $f.find("input.chili-countries").val(quiz_results.country);
894        $f.find(".places-postal-code").val($('#zip').val());
895        $f.find(".places-hotel-name").val(quiz_results.hotel_name);
896        $f.find(".user-language").val(quiz_results.user_language);
897        $f.find(".chili-languages").val(quiz_results.language);
898        $f.find(".form-vr-listings-number").val(quiz_results.vr_listings_number);
899        $f.find(".form-vr-address-number").val(quiz_results.vr_address_number);
900        $f.find(".form-property-group-type").val(quiz_results.property_group_type);
901        $f.find(".form-job-role").val(quiz_results.job_role);
902        $f.find(".form-terms").val($('.form-terms:checkbox').is(':checked') ? '1' : '0');
903
904        // sfcid_qs → hidden input from server store (do not overwrite if already set)
905        (function () {
906          try {
907            var data = (window.__cbUserInfoData && Object.keys(window.__cbUserInfoData).length)
908              ? window.__cbUserInfoData
909              : null;
910            if (!data) return;
911            const v = (data.sfcid_qs || '').trim();
912            if (!v) return;
913            const $input = $f.find('.sfcid-qs, input[name="sfcid_qs"]');
914            if ($input.length && !$input.val()) {
915              $input.val(v).trigger('change');
916            }
917          } catch (e) { /* ignore */ }
918        })();
919
920        user_validate($f, 1);
921      }
922
923      return valid;
924    } // /form_validate completed
925
926    // ###################  BACK BUTTON ####################3
927    $('.back-button').on('click', function(){
928
929        closePacDropdown();
930
931        $('.next-button').removeClass('dis-none');
932        $('.next-button-pending').addClass('dis-none');
933
934        if (Number(item_count) > 0 && Number(item_ready) === 0) {
935          $('.next-button').removeClass('opacity-05');
936          $('.selection-warning-wrapper').addClass('dis-none');
937          $('.pag_item').removeClass('site-color-14');
938          $('.pag_item_' + (item_count-1)).addClass('site-color-14');
939
940          item_ready = 1;
941          const $wrap = $(this).closest('.flow-container');
942          const old_item = $wrap.find('.flow-item[item|=' + items[item_count] + ']');
943          const new_item = $wrap.find('.flow-item[item|=' + items[item_count-1] + ']');
944
945          $(new_item).removeClass('fadeInUp fadeOutUp fadeInDown fadeOutDown').removeClass('dis-none').addClass('flex');
946          animateme($(old_item),"fadeOutDown","","0","1","0");
947          animateme($(new_item),"fadeInDown","","0","1","0");
948
949          item_count--;
950
951          updateStepSubtitle(items[item_count]);
952
953          if (item_count <= 0) { $('.back-button').addClass('opacity-0'); } else { $('.back-button').removeClass('opacity-0'); }
954
955          $(new_item).one('animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function(){ item_ready = 0;
956
957
958          // Ensure autocomplete exists, never destroy it
959          setTimeout(function() {
960                const $field = $('.hotel-search-field');
961                const isVisible = $field.hasClass('flex') && !$field.hasClass('dis-none');
962                
963                if (isVisible && !autocompleteInstance) {
964                  console.log("✓ Initializing autocomplete on back");
965                  loadGoogleMaps(initAutocomplete);
966                }
967              }, 100);
968            });          
969
970
971          $('.top-banner-title').removeClass('dis-none');
972          $('.top-banner-title2').addClass('dis-none');
973          
974         
975        }
976      });
977
978    //  ################  Next button ############
979    $('.next-button').on('click', function(){
980
981      // Close Google Places dropdown
982      closePacDropdown();
983
984      // CHANGED: Reinitialize or ensure autocomplete works when navigating to hotel search
985      setTimeout(function () {
986        const $field = $('.hotel-search-field');
987        const isVisible = $field.hasClass('flex') && !$field.hasClass('dis-none');
988        
989        if (isVisible && !autocompleteInstance) {
990          console.log("✓ Initializing autocomplete");
991          loadGoogleMaps(initAutocomplete);
992        }
993      }, 350);
994
995      // apply_variables_ui();
996
997      if (item_count < items.length && item_ready === 0 && !form_validate()) {
998        if (item_count !== items.length - 1) {
999          $('.pag_item').removeClass('site-color-14');
1000          $('.pag_item_' + (item_count+1)).addClass('site-color-14');
1001
1002          item_ready = 1;
1003          const $wrap = $(this).closest('.flow-container');
1004          const old_item = $wrap.find('.flow-item[item|=' + items[item_count] + ']');
1005          const new_item = $wrap.find('.flow-item[item|=' + items[item_count+1] + ']');
1006
1007          $(new_item).removeClass('fadeInUp fadeOutUp fadeInDown fadeOutDown').removeClass('dis-none').addClass('flex');
1008          animateme($(old_item),"fadeOutUp","","0","1","0");
1009          animateme($(new_item),"fadeInUp","","0","1","0");
1010
1011          item_count++;
1012
1013          updateStepSubtitle(items[item_count]);
1014
1015          if (item_count > 0) { $('.back-button').removeClass('opacity-0'); } else { $('.back-button').addClass('opacity-0'); }
1016
1017          $(new_item).one('animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function(){ item_ready = 0; });
1018
1019          if (item_count === 3) {
1020            $('.top-banner-title').addClass('dis-none');
1021            $('.top-banner-title2').removeClass('dis-none');
1022          }
1023
1024          const nowrooms = parseInt($('.flow-items .room-count').val()) || 0;
1025          const nowbeds  = parseInt($('.flow-items .bed-count').val()) || 0;
1026
1027          if (item_count === 3) {
1028            const w_roomcount = nowrooms + Math.floor(nowbeds/4);
1029            if (w_roomcount <= 5 &&
1030                quiz_results.property_type !== 'Vacation Homes' &&
1031                $('.flow-item[item|=get-rooms]').find('select[name="property-group-type"]').val() !== 'Part of a Group') {
1032              $('.flow-item[item|=show-price]').removeClass('disable');
1033            } else {
1034              $('.flow-item[item|=show-price]').addClass('disable');
1035            }
1036            setPaginate(3);
1037          }
1038        }
1039      }
1040    });
1041
1042    // Business type buttons 
1043    $('.business-type-button').on('click', function(){
1044
1045      $('.selection-warning-wrapper').addClass('dis-none');
1046
1047      $('.flow-item[item|=redirect-page]').addClass('disable');
1048      item_count = 0;
1049
1050      // Reset the redirect messages
1051      $('#redirect-partner').attr('style', 'display: none !important;');
1052      $('#redirect-consultant').attr('style', 'display: none !important;');
1053
1054      // These rely on external vars you had: imagesProps, businessTypeButtonAdd/Remove
1055      // Make sure they exist globally or wire them similarly through CFG.
1056      $('.business-hotel').attr('src', IMAGES.prophotel);
1057      $('.business-bnb').attr('src', IMAGES.propbnb);
1058      $('.business-hostel').attr('src', IMAGES.prophostel);
1059      $('.business-vr').attr('src', IMAGES.propvr);
1060      $('.business-apt').attr('src', IMAGES.propapt);
1061      $('.business-alt').attr('src', IMAGES.propalt);
1062      $('.business-tp').attr('src', IMAGES.proptp);
1063      $('.business-consultant').attr('src', IMAGES.propconsultant);
1064
1065      $('.business-type-button').addClass(CLASS_ADD).removeClass(CLASS_REMOVE);
1066      $(this).find('img').attr('src', IMAGES.propcheck);
1067      $(this).removeClass(CLASS_ADD).addClass(CLASS_REMOVE);
1068
1069      $('.next-button').removeClass('opacity-05');
1070
1071      quiz_results.property_type = $(this).attr('value');
1072
1073      $('.hostel-beds, .hostel-rooms-label, .vr-rental-single-multi').addClass('dis-none');
1074      $('.main-rooms-container').removeClass('dis-none');
1075
1076      switch ($(this).attr('value')) {
1077        case 'Hotel':
1078          $('.flow-item').removeClass('disable');
1079          $('.flow-item[item|=redirect-page], .flow-item[item|=property-info], .flow-item[item|=get-company-name]').addClass('disable');
1080          break;
1081        case 'Hostel':
1082          $('.flow-item').removeClass('disable');
1083      //    rooms_label = STR.rooms_title_hostel || rooms_label;
1084          $('.hostel-beds').removeClass('dis-none');
1085          $('.flow-item[item|=redirect-page], .flow-item[item|=property-info], .flow-item[item|=get-company-name]').addClass('disable');
1086          break;
1087        case 'Vacation Homes':
1088          $('.flow-item').removeClass('disable');
1089        //  rooms_label = STR.rooms_title_other || rooms_label;
1090          $('.vr-rental-single-multi').removeClass('dis-none');
1091          $('.main-rooms-container').addClass('dis-none');
1092          $('.flow-item[item|=redirect-page], .flow-item[item|=property-info], .flow-item[item|=get-company-name]').addClass('disable');
1093          $('.almost-done-message').addClass('dis-none');
1094          break;
1095        case 'Groups':
1096          $('.flow-item').removeClass('disable');
1097          $('.flow-item[item|=get-rooms], .flow-item[item|=redirect-page], .flow-item[item|=property-info]').addClass('disable');
1098          break;
1099        case 'Alternative Accommodations':
1100          $('.flow-item').removeClass('disable');
1101         // rooms_label = STR.rooms_title_other || rooms_label;
1102          $('.flow-item[item|=redirect-page], .flow-item[item|=property-info], .flow-item[item|=get-company-name]').addClass('disable');
1103          break;
1104        case 'Technology Partner':
1105        case 'Consultant':
1106          $('.flow-item').removeClass('disable');
1107          $('.flow-item[item|=get-rooms], .flow-item[item|=property-info], .flow-item[item|=get-name-email]').addClass('disable');
1108          $('.flow-item[item|=redirect-page]').removeClass('disable');
1109          break;
1110        default:
1111       //   rooms_label = STR.rooms_title_hotel || rooms_label;
1112      }
1113
1114      $('.flow-item[item|=show-price]').addClass('disable');
1115      setPaginate();
1116      $('.item-paginator').removeClass('opacity-0');
1117      $('.numeric-paginator').removeClass('opacity-0');
1118      console.log("show paginator");
1119      // apply_variables_ui();
1120    });
1121
1122    // Room number buttons
1123    $('.room-number-button').on('click', function(){
1124      const $it = $('.flow-item[item|=get-rooms]');
1125      $('.room-number-button').addClass('site-white').removeClass('.room-num-button-selected site-color-13-bk site-color-27-bk');
1126      $(this).removeClass('site-white').addClass('site-white site-color-27-bk room-num-button-selected');
1127
1128      if (quiz_results.property_type === 'Vacation Homes') {
1129        if ($it.find('.vr-rental-single-multi input:checked').val() !== undefined) {
1130          $('.next-button').removeClass('opacity-05');
1131        }
1132      } else if (quiz_results.property_type === 'Hostel') {
1133        if ($it.find('.bed-count').val()) $('.next-button').removeClass('opacity-05');
1134      } else {
1135        $('.next-button').removeClass('opacity-05');
1136      }
1137
1138      quiz_results.room_count = $(this).attr('value');
1139      form_validate();
1140    });
1141
1142    // other change handlers
1143    $('.flow-item[item|=get-rooms] input[type=radio][name=multiple_properties]').on('change', function(){
1144      const $it = $('.flow-item[item|=get-rooms]');
1145 
1145     if ($it.find('.room-number-button').hasClass('room-num-button-selected') && this.value !== undefined) {
1146        $('.next-button').removeClass('opacity-05');
1147      }
1148      quiz_results.vr_locations = this.value;
1149    });
1150
1151    $('.flow-item[item|=get-rooms] .room-count').on('change', function(){
1152      const $it = $('.flow-item[item|=get-rooms]');
1153      quiz_results.room_count = this.value;
1154      if ((quiz_results.room_count > 5) ||
1155          ($('.flow-item[item|=get-rooms]').find('select[name="property-group-type"]').val() === 'Part of a Group')) {
1156        $('.almost-done-message').removeClass('dis-none');
1157      } else {
1158        $('.almost-done-message').addClass('dis-none');
1159      }
1160    });
1161
1162    $('.flow-item[item|=get-rooms] .bed-count').on('change', function(){
1163      const $it = $('.flow-item[item|=get-rooms]');
1164      if ($it.find('.room-number-button').hasClass('room-num-button-selected') && this.value) {
1165        $('.next-button').removeClass('opacity-05');
1166      }
1167      quiz_results.bed_count = this.value;
1168    });
1169
1170    $('.flow-item[item|=get-rooms] .vr-listings').on('change', function(){
1171      quiz_results.vr_listings_number = this.value;
1172      quiz_results.room_count = this.value;
1173      form_validate();
1174    });
1175
1176    $('.flow-item[item|=get-rooms] .vr-address').on('change', function(){
1177      quiz_results.vr_address_number = this.value;
1178      form_validate();
1179    });
1180
1181    $('.next-button').on('click', function(){ /* DL marker */ });
1182
1183    // Clear validation when typing in hotel-search AND re-enable PAC dropdown
1184    $('#hotel-search').on('focus input', function(){
1185      $(this).removeClass('input-error-style');
1186      $('.valid-hotel-search').text('').addClass('dis-none');
1187      
1188      // Re-enable Google Places dropdown
1189      document.querySelectorAll('.pac-container').forEach(container => {
1190        container.classList.remove('pac-force-hidden');
1191      });
1192    });
1193
1194  }); // jQuery ready
1195
1196  // ---- Shared validator styles ----
1197  //  -- Handles added error styling
1198window.styleErrorInputs = function(clearAll = false){
1199  const $ = jQuery;
1200  
1201  // If clearAll is true, clear all errors immediately
1202  if (clearAll) {
1203    $('.cb-validation').text('').addClass('dis-none');
1204    $('.cb-data-field').removeClass('input-error-style');
1205    $('.cb-label').removeClass('label-error-style');
1206    $('.valid-hotel-search').text('').addClass('dis-none');
1207    $('#hotel-search').removeClass('input-error-style');
1208    return;
1209  }
1210  
1211  // Otherwise, do the normal validation styling
1212  $(".cb-validation").each(function(){
1213    if ($(this).is(":visible")) {
1214      const inputDataElement = $(this).closest(".cb-field").find(".cb-data-field");
1215      const labelDataElement = $(this).closest(".cb-field").find(".cb-label");
1216      if (inputDataElement.is("input, textarea, select")) {
1217        const isInvalid =
1218          (inputDataElement.is("input, textarea") && String(inputDataElement.val() || '').trim() === "") ||
1219          (inputDataElement.is("select") && ["", "no-role", "no-option"].includes(String(inputDataElement.find("option:selected").val() || "")));
1220        if (isInvalid) {
1221          inputDataElement.addClass("input-error-style");
1222          labelDataElement.addClass("label-error-style");
1223        } else {
1224          inputDataElement.removeClass("input-error-style");
1225          labelDataElement.removeClass("label-error-style");
1226        }
1227      }
1228    } else {
1229      $(this).next().removeClass("input-error-style");
1230    }
1231  });
1232  
1233  $(".input-error-style").on('click', function(){
1234    $(this).closest(".cb-field").find(".input-error-style").removeClass("input-error-style");
1235    $(this).closest(".cb-field").find(".label-error-style").removeClass("label-error-style");
1236    });
1237  };
1238
1239})();
1240/// start the country drop down
1241/* $(document).ready(function() {
1242    initCountryDropdown();
1243}); */
1244
1245// Simple Numeric Pagination: Updates "X of Y" based on the progress dots
1246jQuery(function ($) {
1247  const dotsContainer = document.querySelector('.item-paginator');
1248  const numPagContainer = document.querySelector('.numeric-paginator');
1249  if (!dotsContainer || !numPagContainer) return;
1250
1251  const sync = () => {
1252    const $numPag = $(numPagContainer);
1253    const $dots = $('.item-paginator .pag_item');
1254
1255    // If it's still hidden by opacity-0, keep it empty and skip calculation
1256    if ($numPag.hasClass('opacity-0') || !$dots.length) {
1257      $numPag.text('');
1258      return;
1259    }
1260
1261    const total = $dots.length;
1262    const activeIndex = $dots.filter('.site-color-14').index();
1263    const current = activeIndex !== -1 ? activeIndex + 1 : 1;
1264
1265    const ofText = (window.CB_FORM && window.CB_FORM.strings && window.CB_FORM.strings.of) || 'of';
1266    $numPag.text(current + ' ' + ofText + ' ' + total);
1267  };
1268
1269  // 1. Watch dots for progress changes
1270  new MutationObserver(sync).observe(dotsContainer, {
1271    childList: true,
1272    subtree: true,
1273    attributes: true,
1274    attributeFilter: ['class']
1275  });
1276
1277  // 2. Watch the paginator itself for when it becomes visible (opacity-0 removed)
1278  new MutationObserver(sync).observe(numPagContainer, {
1279    attributes: true,
1280    attributeFilter: ['class']
1281  });
1282
1283  sync(); // Initial run (will stay empty due to opacity-0)
1284});

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.