1function fillPACode(form) { 2 var hiddenInput = form.querySelector('input[name="pa_code"]'); 3 var textInput = form.querySelector('.pacode-input'); 4 if ( hiddenInput && textInput ) { 5 hiddenInput.value = 'PA' + textInput.value.replace(/\D/g, ''); 6 } 7} 8 9function checkForResults(form) { 10 var hiddenInput = form.querySelector('input[name="pa_code"]'); 11 if ( ! hiddenInput ) { 12 return; 13 } 14 fillPACode(form); 15 var pa_code = hiddenInput.value; 16 17 jQuery.ajax({ 18 method: "GET", 19 url: `${window.DP_REST_API.v1}/dpapi/proxy/dentist/v1/dentalsearch/dentist/${pa_code}`, 20 success: async function (data) { 21 if ( data.dentists.length === 0 ) { 22 doFormError(form); 23 } else { 24 if ( data.dentists && data.dentists[0].dentistAddress && data.dentists[0].dentistAddress.zip ) { 25 var pa_code_zip = data.dentists[0].dentistAddress.zip; 26 if ( typeof window.overrideLocation === 'function' ) { 27 await window.overrideLocation(pa_code_zip); 28 } 29 } 30 form.submit(); 31 } 32 }, 33 error: function (data) { 34 console.error("pa code search Error: " + pa_code, data); 35 }, 36 }); 37} 38 39function doFormError(form) { 40 var textInput = form.querySelector('.pacode-input'); 41 if ( textInput ) { 42 textInput.classList.add('error'); 43 } 44 form.querySelectorAll('.error-message').forEach(function (el) { 45 el.classList.remove('hidden'); 46 }); 47} 48 49function clearFormError(form) { 50 var textInput = form.querySelector('.pacode-input'); 51 if ( textInput ) { 52 textInput.classList.remove('error'); 53 } 54 form.querySelectorAll('.error-message').forEach(function (el) { 55 el.classList.add('hidden'); 56 }); 57} 58 59function disableFormSubmit(form, disable) { 60 var submitBtn = form.querySelector('[type="submit"]'); 61 if ( submitBtn ) { 62 submitBtn.disabled = disable; 63 } 64} 65 66function isFormSubmitDisabled(form) { 67 var submitBtn = form.querySelector('[type="submit"]'); 68 return submitBtn ? submitBtn.disabled : true; 69} 70 71document.addEventListener("DOMContentLoaded", function () { 72 var forms = document.querySelectorAll('.dentist-pa-code-form'); 73 74 forms.forEach(function (form) { 75 var input = form.querySelector('.pacode-input'); 76 if ( ! input ) { 77 return; 78 } 79 80 input.addEventListener('input', function (event) { 81 input.value = event.target.value.replace(/\D/g, ''); 82 clearFormError(form); 83 disableFormSubmit(form, input.value.length < 1); 84 }); 85 86 input.addEventListener('paste', function (event) { 87 event.preventDefault(); 88 input.value = (event.clipboardData || window.clipboardData).getData("text").replace(/\D/g, ''); 89 clearFormError(form); 90 disableFormSubmit(form, input.value.length < 1); 91 }); 92 }); 93 94 document.body.addEventListener("submit", function (event) { 95 var form = event.target; 96 if ( ! form || ! form.classList || ! form.classList.contains('dentist-pa-code-form') ) { 97 return; 98 } 99 100 event.preventDefault(); 101 if ( ! isFormSubmitDisabled(form) ) { 102 fillPACode(form); 103 checkForResults(form); 104 } 105 }); 106}); 107; 108 109document.addEventListener('DOMContentLoaded', function () { 110 111 ///////////////////////////////////////////////////////////////////////////////////////////// 112 // Define elements. 113 114 let searchFormBlock = null; 115 116 let overlay = null; 117 118 let closeButton = null; 119 120 let searchForm = null; 121 122 let filterByWrapper = null; 123 let filterByToggleButton = null; 124 let filterByOptionsWrapper = null; 125 let filterByOptionsInputs = null; 126 let filterByOptionsInputsArray = null; 127 128 let searchInput = null; 129 130 let autosuggestWrapper = null; 131 let autosuggestContent = null; 132 133 let bootstrapFocusTrapModals = []; 134 135 ///////////////////////////////////////////////////////////////////////////////////////////// 136 // Define state variables. 137 138 let autosuggestAbortController = null; 139 let filterByOptionPreviousValue = null; 140 141 ///////////////////////////////////////////////////////////////////////////////////////////// 142 // Define helpers and handlers. 143 144 function renderMobileHeaderBrand( container ) { 145 const brand = window.dp_search_form && window.dp_search_form.mobile_header_brand; 146 if ( ! container || ! brand || ! brand.home_url ) { 147 return; 148 } 149 const link = document.createElement( 'a' ); 150 link.href = brand.home_url; 151 if ( brand.logo && brand.logo.url ) { 152 link.className = 'site-home-link'; 153 const img = document.createElement( 'img' ); 154 img.src = brand.logo.url; 155 if ( brand.logo.srcset ) { 156 img.setAttribute( 'srcset', brand.logo.srcset ); 157 } 158 if ( brand.logo.sizes ) { 159 img.setAttribute( 'sizes', brand.logo.sizes ); 160 } 161 img.alt = typeof brand.logo.alt === 'string' ? brand.logo.alt : ''; 162 img.decoding = 'async'; 163 link.appendChild( img ); 164 container.appendChild( link ); 165 } else if ( brand.site_title ) { 166 link.textContent = brand.site_title; 167 container.appendChild( link ); 168 } 169 } 170
171 function init() { 172 173 if ( ! searchFormBlock ) { 174 searchFormBlock = document.createElement('div'); 175 searchFormBlock.id = 'dp-site-search-form'; 176 searchFormBlock.classList.add('dp-block-search-form'); 177 178 searchFormBlock.innerHTML = ` 179 <div class="dp-block-search-form__overlay"></div> 180 <div class="dp-block-search-form__inner"> 181 <div class="dp-block-search-form__mobile-header"> 182 <button type="button" class="dp-block-search-form__close" aria-label="Close search"> 183 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 184 <path d="M4 4L20 20" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> 185 <path d="M4 20L20 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> 186 </svg> 187 </button> 188 <div class="dp-block-search-form__mobile-logo"></div> 189 </div> 190 <form name="dp-search-form" method="get" action="${window.dp_search_form.form_action}"> 191 <div class="dp-search-form-main"> 192 <div class="dp-search-form__filter-by-dropdown"> 193 <button type="button" class="filter-by-dropdown__toggle" aria-controls="dp-site-search-form__category-options"> 194 <span class="filter-by-dropdown__toggle-label">Category:</span> 195 <span class="filter-by-dropdown__toggle-value">All</span> 196 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="9" viewBox="0 0 14 9" fill="none"> 197 <path d="M12.3135 1.65674L6.65662 7.31359L0.999769 1.65674" stroke="#2445EA" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> 198 </svg> 199 </button> 200 <div id="dp-site-search-form__category-options" class="filter-by-dropdown__options visually-hidden"> 201 <label> 202 <input type="radio" name="facet" value="all" class="visually-hidden" checked /> 203 All 204 </label> 205 ${window.dp_search_form.search_filter_options.map(option => { 206 return ` 207 <label> 208 <input type="radio" name="facet" value="${option.value}" class="visually-hidden" /> 209 ${option.label} 210 </label> 211 `; 212 }).join('')} 213 </div> 214 </div> 215 <input name="s" type="search" placeholder="What are you looking for..." autocomplete="off" required /> 216 <input type="reset" value="Clear" class="clear-search-input" /> 217 <button type="submit" class="submit-search-input"> 218 <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 25 25" fill="none"> 219 <title>Search</title> 220 <circle cx="9.6" cy="9.6" r="8.6" stroke="currentColor" stroke-width="2"/> 221 <path d="M16.8 16.7998L24 23.9998" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> 222 </svg> 223 </button> 224 </div> 225 <div class="dp-search-form-autosuggest"> 226 <div class="dp-search-form-autosuggest__content"></div> 227 <p class="default-message">Need to find a dentist? Look <a href="${window.dp_search_form.dentist_href}">here →</a></p> 228 </div> 229 </form>
230 </div> 231 `; 232 233 document.body.appendChild(searchFormBlock); 234 235 const mobileLogoEl = searchFormBlock.querySelector( '.dp-block-search-form__mobile-logo' ); 236 renderMobileHeaderBrand( mobileLogoEl ); 237 } 238 239 overlay = searchFormBlock.querySelector('.dp-block-search-form__overlay'); 240 241 closeButton = searchFormBlock.querySelector('.dp-block-search-form__close'); 242 243 searchForm = searchFormBlock.querySelector('form[name="dp-search-form"]'); 244 245 filterByWrapper = searchFormBlock.querySelector('.dp-search-form__filter-by-dropdown'); 246 filterByToggleButton = filterByWrapper.querySelector('.filter-by-dropdown__toggle'); 247 filterByOptionsWrapper = filterByWrapper.querySelector('.filter-by-dropdown__options'); 248 filterByOptionsInputs = filterByOptionsWrapper.querySelectorAll('input[name="facet"]'); 249 filterByOptionsInputsArray = Array.from(filterByOptionsInputs); 250 251 searchInput = searchFormBlock.querySelector('input[name="s"]'); 252 253 autosuggestWrapper = searchFormBlock.querySelector('.dp-search-form-autosuggest'); 254 autosuggestContent = autosuggestWrapper.querySelector('.dp-search-form-autosuggest__content'); 255 256 bootstrapFocusTrapModals = []; 257 258 // Define state variables. 259 260 autosuggestAbortController = null; 261 filterByOptionPreviousValue = getFilterByOptionCurrentValue(); 262 263 // Attach event listeners. 264 265 overlay.addEventListener('click', event => { 266 if ( event.target === event.currentTarget ) { 267 // Only toggle visibility if no child element was targeted. 268 toggleSearchFormVisibility(); 269 } 270 }); 271 272 if ( closeButton ) { 273 closeButton.addEventListener('click', () => { 274 if ( document.body.classList.contains('dp-block-search-form--open') ) { 275 toggleSearchFormVisibility(); 276 } 277 }); 278 } 279 280 filterByToggleButton.addEventListener('click', toggleFilterByOptionsVisibility); 281 282 searchForm.addEventListener('reset', event => { 283 setTimeout( 284 () => { 285 // This changes the selected category, so remember the value after reset. 286 // It's now the new "previous" option. 287 filterByOptionPreviousValue = getFilterByOptionCurrentValue(); 288 // Now handle search input change. 289 handleSearchInputChange(event); 290 }, 291 0 // Fire right after this event to ensure the form values have already been reset. 292 ); 293 }); 294 295 searchForm.addEventListener('submit', event => { 296 const query = searchInput.value.trim(); 297 const [ , filterByLabel ] = getFilterByOptionCurrentValue(); 298 dataLayerPush({ 299 event: ( event?.submitter?.classList?.contains('submit-more-results') ? "more_search_success" : "term_search_success" ), 300 search_term: query, 301 search_category: filterByLabel, 302 }); 303 }); 304 305 filterByOptionsInputs.forEach(input => { 306 input.addEventListener('change', handleSearchInputChange); 307 }); 308 309 searchInput.addEventListener('input', handleSearchInputChange); 310 } 311 312 function dataLayerPush(data) { 313 window.dataLayer = window.dataLayer || []; 314 window.dataLayer.push(data); 315 window.console.log('pushed to dataLayer ->', JSON.stringify(data, null, 4)); 316 } 317 318 function toggleSearchFormVisibility() { 319 if ( !! document.body.classList.toggle('dp-block-search-form--open') ) { 320 // Prevent Bootstrap's modal focus trapping from preventing focus. 321 bootstrapFocusTrapModals.length = 0; 322 bsModals = document.querySelectorAll('.modal.show[aria-modal="true"][role="dialog"]'); 323 if ( bsModals ) { 324 bsModals.forEach(modal => { 325 const bsModalInstance = bootstrap.Modal.getInstance(modal); 326 if ( bsModalInstance?._focustrap?._isActive ) { 327 // Only capture modals with focus trapping enabled. 328 bootstrapFocusTrapModals.push(bsModalInstance); 329 bsModalInstance._focustrap.deactivate(); // See https://github.com/twbs/bootstrap/blob/v5.2.3/js/src/modal.js#L70 330 } 331 }); 332 } 333 // Auto-focus the search input field. 334 searchInput.focus(); 335 // Send analytics event. 336 dataLayerPush({ 337 event: "search_overlay_opened", 338 }); 339 } else { 340 // Re-activate Bootstrap modal focus trapping. 341 if ( bootstrapFocusTrapModals ) { 342 bootstrapFocusTrapModals.forEach(modalInstance => { 343 modalInstance._focustrap.activate(); 344 }) 345 } 346 } 347 } 348 349 function toggleFilterByOptionsVisibility() { 350 filterByOptionsWrapper.classList.toggle('visually-hidden'); 351 } 352 353 async function handleSearchInputChange(event) { 354 355 // Ensure current filter option is set. 356 setStateFilterBy(getFilterByOptionCurrent()); 357 358 if (filterByOptionsInputsArray.includes(event.target)) { 359 // Change event from filter category input selection.
360 const [ , prevCategoryLabel ] = filterByOptionPreviousValue; 361 filterByOptionPreviousValue = getFilterByOptionCurrentValue(); 362 const [ , newCategoryLabel ] = filterByOptionPreviousValue; 363 dataLayerPush({ 364 event: "search_category_interest", 365 previous_category: prevCategoryLabel, 366 new_category: newCategoryLabel, 367 }); 368 } 369 370 // Ensure the filter options are hidden while user searches. 371 filterByOptionsWrapper.classList.add('visually-hidden'); 372 373 // Act based on input value. 374 if ( searchInput?.value ) { 375 376 searchFormBlock.classList.add('has-query-input'); 377 378 const query = searchInput.value.trim(); 379 const [ filterByValue, filterByLabel ] = getFilterByOptionCurrentValue(); 380 const limit = 4; 381 const autosuggestResults = await fetchAutosuggestResults(query, filterByValue, limit); 382 if ( 383 'success' === autosuggestResults?.status && 384 autosuggestResults?.data?.topSearchResults?.length 385 ) { 386 // Found search results. 387 388 const topSearchResultsListItems = autosuggestResults.data.topSearchResults.map( 389 post => { 390 if ( ! post?.search_result_item_html ) { 391 return ''; 392 } 393 return `<li>${post.search_result_item_html}</li>`; 394 } 395 ); 396 397 let maybeMoreButton = ``; 398 if ( autosuggestResults?.data?.totalSearchResultsCount > limit ) { 399 maybeMoreButton = `<button type="submit" class="submit-more-results">More...</button>`; 400 } 401 402 autosuggestContent.innerHTML = ` 403 <div class="top-search-results"> 404 <p class="heading">Top Results For <strong>${query}</strong> in <strong>${filterByLabel}</strong></p> 405 <ul> 406 ${topSearchResultsListItems.join("\n")} 407 </ul> 408 ${maybeMoreButton} 409 </div> 410 `; 411 412 autosuggestContent.querySelectorAll('.top-search-results li a').forEach(el => { 413 el.addEventListener('click', () => { 414 const itemTitle = el.querySelector('.search-result-item__title'); 415 dataLayerPush({ 416 event: "suggested_search_success", 417 search_term: query, 418 search_category: filterByLabel, 419 suggested_name: ( itemTitle?.innerText ) ? itemTitle.innerText.trim() : el.innerText.trim(), 420 suggested_url: el.href, 421 }); 422 }); 423 }); 424 } else { 425 // No search results retrieved. 426 427 const viewHoursTooltipHTML = ` 428 <strong>Mon–Fri:</strong> 8:30am – 9pm<br /> 429 <strong>Sat:</strong> 9am – 6pm<br /> 430 <strong>Sun:</strong> Closed<br /> 431 <small>All times are in EST</small> 432 `; 433 434 autosuggestContent.innerHTML = ` 435 <div class="top-search-results no-results"> 436 <p class="heading">We didn't find anything that matches “${query}”.</p> 437 <div class="no-results__cta"> 438 <div class="cta__see-faq"> 439 <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none"> 440 <path d="M2.55996 35.9501C2.99996 33.8101 3.54996 31.6801 4.50996 29.3401C2.92996 26.3301 1.51996 23.1101 1.39996 19.7101C1.17996 13.2801 5.74996 7.46005 11.33 4.25005C13.06 3.26005 14.9 2.45005 16.84 1.98005C23.55 0.340052 31.1 3.16005 35.17 8.74005C39.24 14.3201 39.64 22.3101 36.21 28.3101C32.79 34.3101 25.77 38.0301 18.88 37.5801C15.87 37.3801 12.84 36.4101 10.76 34.2701C8.02996 35.
4400101 5.29996 35.7501 2.54996 35.9501H2.55996Z" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 441 <path d="M15.3096 15.6403C15.4596 13.8203 16.5596 12.0803 18.1896 11.2503C19.8196 10.4203 21.9296 10.5903 23.3596 11.7303C24.7896 12.8703 25.3896 14.9703 24.7096 16.6703C24.2796 17.7403 23.4196 18.5803 22.5096 19.3003C21.7396 19.9103 20.8996 20.4803 20.3496 21.3003C19.7996 22.1203 19.6496 23.3003 20.2796 24.0503" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 442 <path d="M20.3203 28.6702L20.3703 29.1902C20.5403 29.2902 20.8003 29.2202 20.8903 29.0402C20.9803 28.8602 20.8903 28.6102 20.3203 28.6702Z" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 443 <path d="M2.55996 35.9501C2.99996 33.8101 3.54996 31.6801 4.50996 29.3401C2.92996 26.3301 1.51996 23.1101 1.39996 19.7101C1.17996 13.2801 5.74996 7.46005 11.33 4.25005C13.06 3.26005 14.9 2.45005 16.84 1.98005C23.55 0.340052 31.1 3.16005 35.17 8.74005C39.24 14.3201 39.64 22.3101 36.21 28.3101C32.79 34.3101 25.77 38.0301 18.88 37.5801C15.87 37.3801 12.84 36.4101 10.76 34.2701C8.02996 35.
4430101 5.29996 35.7501 2.54996 35.9501H2.55996Z" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 444 <path d="M15.3096 15.6403C15.4596 13.8203 16.5596 12.0803 18.1896 11.2503C19.8196 10.4203 21.9296 10.5903 23.3596 11.7303C24.7896 12.8703 25.3896 14.9703 24.7096 16.6703C24.2796 17.7403 23.4196 18.5803 22.5096 19.3003C21.7396 19.9103 20.8996 20.4803 20.3496 21.3003C19.7996 22.1203 19.6496 23.3003 20.2796 24.0503" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 445 <path d="M20.3203 28.6702L20.3703 29.1902C20.5403 29.2902 20.8003 29.2202 20.8903 29.0402C20.9803 28.8602 20.8903 28.6102 20.3203 28.6702Z" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 446 </svg> 447 <p>Take a look at our frequently asked questions</p> 448 <a href="${dp_search_form.faq_href}" class="dp-button dp-button--tertiary">Go to FAQ</a> 449 </div> 450 <div class="cta__call-us"> 451 <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none"> 452 <g clip-path="url(#clip0_1_828)"> 453 <path d="M20.0001 38.8801C30.4273 38.8801 38.8801 30.4273 38.8801 20.0001C38.8801 9.57298 30.4273 1.12012 20.0001 1.12012C9.57298 1.12012 1.12012 9.57298 1.12012 20.0001C1.12012 30.4273 9.57298 38.8801 20.0001 38.8801Z" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 454 <path d="M9.29004 29.2897C10.09 25.6597 12.76 22.4897 16.21 21.0897C17.93 20.3897 19.8 20.1097 21.57 19.5297C23.34 18.9497 25.1 17.9897 26.01 16.3597C27.04 14.4997 26.68 12.0197 25.3 10.3897C23.92 8.7597 21.67 7.9897 19.55 8.2097C18.48 8.3197 17.42 8.6797 16.57 9.3397C14.9 10.6497 14.31 13.0597 14.93 15.0897C15.55 17.1197 17.22 18.7197 19.18 19.5397C20.93 20.2697 22.89 20.4197 24.64 21.1497C27.97 22.5297 30.25 26.1397 30.06 29.7397" stroke="#2445EA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 455 </g> 456 <defs> 457 <clipPath id="clip0_1_828"> 458 <rect width="40" height="40" fill="white"/> 459 </clipPath> 460 </defs> 461 </svg> 462 <p>Give us a call, we are here to help</p> 463 <button type="button" class="call-us__view-hours" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="top" data-bs-fallbackPlacements="['top', 'bottom', 'left', 'right']" data-bs-title="${viewHoursTooltipHTML}">View Hours</button> 464 <a href="${dp_search_form.dp_phone_href}" class="dp-button dp-phone dp-phone-without-country-code">${dp_search_form.dp_phone_label}</a> 465 </div> 466 </div> 467 </div> 468 `; 469 470 // Enable the Bootstrap tooltip. 471 autosuggestContent.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => { 472 return new bootstrap.Tooltip(el); 473 }); 474 } 475 476 // Retain query params on dynamically added hyperlinks. 477 dpSetPhoneNumber(autosuggestContent); 478 } else { 479 // No search input value. 480 searchFormBlock.classList.remove('has-query-input'); 481 autosuggestContent.innerHTML = ''; 482 } 483 } 484 485 function setStateFilterBy(inputEl) { 486 let buttonLabel = 'All'; 487 if ( inputEl?.labels?.length ) { 488 buttonLabel = inputEl.labels[0].innerText; 489 } 490 filterByToggleButton.querySelector('span.filter-by-dropdown__toggle-value').innerText = buttonLabel; 491 filterByOptionsWrapper.classList.add('visually-hidden'); 492 } 493 494 function getFilterByOptionCurrent() { 495 for ( const input of filterByOptionsInputs ) { 496 if ( input.checked ) { 497 return input; 498 } 499 } 500 return null; 501 } 502 503 function getFilterByOptionCurrentValue() { 504 const inputEl = getFilterByOptionCurrent(); 505 506 let value = 'all'; 507 if ( inputEl?.value ) { 508 value = inputEl.value; 509 } 510 511 let label = 'All'; 512 if ( inputEl?.labels?.[0]?.innerText ) { 513 label = inputEl.labels[0].innerText; 514 } 515 516 return [ value, label ]; 517 } 518 519 async function fetchAutosuggestResults(query, filterBy, limit) { 520 521 if ( ! query ) { 522 return null; // Don't fetch for blank queries. 523 } 524 525 // Abort any existing request. 526 if ( autosuggestAbortController ) { 527 autosuggestAbortController.abort(); 528 } 529 530 // Create a new AbortController for the current request. 531 autosuggestAbortController = new AbortController(); 532 533 try { 534 535 const response = await fetch( 536 `${window.DP_REST_API.v1}/search/autosuggest/?s=${query}&facet=${filterBy}&limit=${limit}`, 537 { 538 "method": 'GET', 539 "credentials": 'same-origin', 540 "signal": autosuggestAbortController.signal 541 } 542 ); 543 544 if ( ! response.ok ) { 545 throw new Error('Network response was not ok'); 546 } 547 548 const data = await response.json(); 549 return data; 550 } catch ( error ) { 551 if ( 'AbortError' !== error.name ) { 552 window.console.error('Failed to fetch search autosuggestion results:', error); 553 } 554 } 555 556 return null; // The data couldn't be fetched. 557 } 558 559 ///////////////////////////////////////////////////////////////////////////////////////////// 560 // Attach entry point event listeners. 561 562 document.querySelectorAll('.search-link').forEach(link => { 563 link.addEventListener('click', event => { 564 event.preventDefault();
564 // Do not navigate href. 565 566 // Initialize the search form if it's not already initialized. 567 if ( ! searchFormBlock ) { 568 init(); 569 } 570 571 // Toggle the search form visibility. 572 toggleSearchFormVisibility(); 573 }); 574 }); 575}, { once: true } ); 576; 577(function () { 578 579 let prevAccordionWindowLocationHash = ( window.location.hash.startsWith('#faq-') ) ? window.location.hash : ''; 580 581 function openAccordionForWindowLocationHash() { 582 if ( window.location.hash.startsWith('#faq-') ) { 583 584 document.querySelectorAll(`.»active[href*="${prevAccordionWindowLocationHash}"]`).forEach(el => { el.classList.remove('»active'); }); 585 586 const targetAccordion = document.querySelector(`.wp-block-details[id="${window.location.hash.substring(1)}"]`); 587 targetAccordion.parentElement.querySelectorAll('.wp-block-details[open]').forEach(el => { el.open = false; }); 588 targetAccordion.open = true; 589 590 if ( 'function' === typeof targetAccordion?.scrollIntoViewIfNeeded ) { 591 targetAccordion.scrollIntoViewIfNeeded({ "behavior": "smooth" }); 592 } else if ( 'function' === typeof targetAccordion?.scrollIntoView ) { 593 targetAccordion.scrollIntoView({ "behavior": "smooth" }); 594 } 595 596 document.querySelectorAll(`[href*="${window.location.hash}"]`).forEach(el => { el.classList.add('»active'); }); 597 598 prevAccordionWindowLocationHash = window.location.hash; 599 } 600 } 601 602 document.addEventListener('DOMContentLoaded', openAccordionForWindowLocationHash); 603 window.addEventListener('hashchange', openAccordionForWindowLocationHash); 604})(); 605; 606maxProcCount = 10; 607minAdultCount = 1; 608maxAdultCount = 2; 609maxChildCount = 3; 610 611function dpSynchSavingsCalcDrawers() { 612 if (jQuery('input[name="plan"]:checked').val() === "family") { 613 jQuery("#savings-calculator-form").removeClass("»list-one--hidden"); 614 } else { 615 jQuery("#savings-calculator-form").addClass("»list-one--hidden"); 616 jQuery('input[name="adultCount"]').val(1); 617 jQuery('input[name="childrenCount"]').val(0); 618 } 619 620 $procInputs = jQuery('input[name^="prc"]'); 621 if (jQuery('input[name="procedures"]:checked').val() === "yes") { 622 $procInputs.prop("disabled", false); 623 jQuery("#savings-calculator-form").removeClass("»list-two--hidden"); 624 } else { 625 $procInputs.prop("disabled", true); 626 jQuery("#savings-calculator-form").addClass("»list-two--hidden"); 627 } 628} 629 630jQuery(document).ready(function () { 631 dpSynchSavingsCalcDrawers(); 632 633 jQuery('input[name="procedures"], input[name="plan"]').change(function (e) { 634 dpSynchSavingsCalcDrawers(); 635 }); 636 637 //assert email is not disabled in the case of back button 638 jQuery('input[name="email"]', "#savings-calculator-form").prop('disabled', false); 639 640 jQuery('input[name="email"]', "#savings-calculator-form").change(function (e) { 641 let email = jQuery(this).val(); 642 email = email.trim(); 643 jQuery('input[name="eid"]', "#savings-calculator-form").val(btoa(email)); 644 645 }); 646 647 //deal with +/- clicks 648 jQuery(".savings-calculator-procedures--list").on( 649 "click", 650 ".savings-calculator-item--minus, .savings-calculator-item--plus", 651 function (e) { 652 e.preventDefault(); 653 let $button = jQuery(this); 654 let $input = $button.parents(".savings-calculator-item").find("input"); 655 //mas o menos 656 let val = parseInt($input.val()); 657 658 //case procs 659 if ($button.hasClass("savings-calculator-item--plus")) { 660 let $form = jQuery($button).parents("form"); 661 let procTotal = 0; 662 $form 663 .find( 664 ".savings-calculator-procedures--list .savings-calculator-item--input" 665 ) 666 .each(function (idx, el) { 667 procTotal += parseInt(jQuery(this).val()); 668 }); 669 if (procTotal >= maxProcCount) { 670 alert( 671 "Please limit selection to " + maxProcCount + " or less procedures" 672 ); 673 } else { 674 $input.val(Math.max(val + 1, 0)); 675 } 676 } else { 677 $input.val(Math.max(val - 1, 0)); 678 } 679 } 680 ); 681 682 //deal with +/- clicks 683 jQuery(".savings-calculator-members").on( 684 "click", 685 " .savings-calculator-item--minus, .savings-calculator-item--plus", 686 function (e) { 687 e.preventDefault(); 688 let $button = jQuery(this); 689 let $input = $button.parents(".savings-calculator-item").find("input"); 690 //mas o menos 691 let val = parseInt($input.val()); 692 693 //case procs 694 if ($button.hasClass("savings-calculator-item--plus")) { 695 if ($input.attr("name") == "adultCount" && val >= maxAdultCount) { 696 alert( 697 "Please limit adults selection to " + maxAdultCount + " or less " 698 ); 699 } else if ( 700 $input.attr("name") == "childrenCount" && 701 val >= maxChildCount 702 ) { 703 alert( 704 "Please limit children selection to " + maxChildCount + " or less " 705 ); 706 } else {
707 $input.val(Math.max(val + 1, 0)); 708 } 709 } else { 710 if ($input.attr("name") == "adultCount" && val <= minAdultCount) { 711 alert( 712 "Please limit adults selection to " + minAdultCount + " or more " 713 ); 714 } else { 715 $input.val(Math.max(val - 1, 0)); 716 } 717 } 718 } 719 ); 720 721 jQuery("input", "#savings-calculator-form").on("input", function () { 722 var $this = jQuery(this); 723 var value = $this.val(); 724 725 if (value) { 726 $this.addClass("»dirty"); 727 $this.removeClass("»error"); 728 } else { 729 $this.removeClass("»dirty"); 730 } 731 }); 732 733 jQuery(".savings-calculator .dp-input--input").each(function (i, e) { 734 var $this = jQuery(e); 735 $this.addClass("»dirty"); 736 $this.removeClass("»error"); 737 }); 738 setTimeout(() => { 739 jQuery(".savings-calculator .dp-input--input").each(function (i, e) { 740 var $this = jQuery(e); 741 var value = $this.val(); 742 if (value) { 743 $this.addClass("»dirty"); 744 $this.removeClass("»error"); 745 } else { 746 $this.removeClass("»dirty"); 747 } 748 }); 749 }, 100); 750 751 // Multistep form: Next/Back navigation (only when multistep-form DOM is present). 752 if (jQuery("#sc-question2").length) { 753 jQuery("#btn-next-one").on("click", function () { 754 jQuery("#sc-question2").show(); 755 jQuery(".step2").show(); 756 jQuery("#sc-question1").hide(); 757 jQuery(".step1").hide(); 758 }); 759 jQuery("#btn-next-two").on("click", function () { 760 jQuery("#sc-question3").show(); 761 jQuery(".step3").show(); 762 jQuery("#sc-question2").hide(); 763 jQuery(".step2").hide(); 764 }); 765 jQuery("#btn-back-two").on("click", function () { 766 jQuery("#sc-question1").show(); 767 jQuery(".step1").show(); 768 jQuery("#sc-question2").hide(); 769 jQuery(".step2").hide(); 770 }); 771 jQuery("#btn-back-three").on("click", function () { 772 jQuery("#sc-question2").show(); 773 jQuery(".step2").show(); 774 jQuery("#sc-question3").hide(); 775 jQuery(".step3").hide(); 776 }); 777 } 778}); 779 780// Add event listener to the body element's submit event 781document.body.addEventListener("submit", async function (event) { 782 var form = event.target; 783 if (!form.matches(".savings-calculator--form")) { 784 return; // ignore submits from other forms 785 } 786 787 // Prevent the default action of the form 788 event.preventDefault(); 789 790 if (DP_VISITORID) { 791 form.querySelectorAll("[name=visitorId]").forEach(function (input) { 792 input.value = DP_VISITORID; 793 }); 794 } 795 796 if (DP_AFFID) { 797 form.querySelectorAll("[name=affid]").forEach(function (input) { 798 input.value = DP_AFFID; 799 }); 800 } 801 802 if (dpIsPPC()) { 803 form.querySelectorAll("[name=includeTopPlan]").forEach(function (input) { 804 input.value = 'true'; 805 }); 806 } 807 808 //disable input so we don't see in Query Params 809 const emailInput = form.querySelector("[name=email]"); 810 if ( emailInput ) { 811 emailInput.disabled = true; 812 } 813 814 //disable inputs to keep URL clean 815 const procedureInputs = form.querySelectorAll(".savings-calculator-procedures--list input"); 816 procedureInputs.forEach(function (input) { 817 if (parseInt(input.value) === 0) { 818 input.disabled = true; 819 } 820 }); 821 822 window.dataLayer = window.dataLayer || []; 823 // prevent multiple Calculate GTM events 824 if (!dataLayer.find(function (obj) { return obj.event === "Calculate" })) { 825 window.dataLayer.push({ 826 'event': 'Calculate', 827 'category': 'Button' 828 }); 829 } 830 831 //detect input changes 832 var changedInputs = form.querySelectorAll("input.changed"); 833 if (changedInputs.length) { 834 //build query for radar 835 var radarQuery = form.querySelector("input.radar-location").value; 836 // they changed the input, update the location 837 await overrideLocation(radarQuery, form); 838 } 839 840 // submit the form if input validation passed. 841 if (form.checkValidity()) { 842 tidyAndSubmit(form); 843 } else { 844 // re-enable inputs for user to edit their submission. 845 emailInput.disabled = false; 846 procedureInputs.forEach(function (input) { 847 if (parseInt(input.value) === 0) { 848 input.disabled = false; 849 } 850 }); 851 } 852}); 853 854// reset savings calculator form on back button cache 855window.addEventListener('pageshow', (event) => { 856 if (event.persisted || performance.getEntriesByType("navigation")[0].type === 'back_forward') { 857 jQuery('input[name="plan"][value="individual"]').click(); 858 jQuery('input[name="procedures"][value="no"]').click(); 859 jQuery('#savings-calculator-form .savings-calculator-procedures--list input[type="checkbox"]').prop('checked', false); 860 } 861}); 862;
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.