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.