1var $ = jQuery.noConflict(); 2let megaMenu = false, 3current_listing = '', 4spin_gif = '', 5load_timeout, 6nl_subscribe = ( email, name ) => { 7 if (email.length < 3 || name.length < 3) { 8 $('#nl_message_box').html('Please, Fill Out All Fields.'); 9 return; 10 } 11 name = name.split(' '); 12 13 $.get( "/api/?module=form_popup&formname=newsletter_subscribe&email="+ email +"&fname="+ name[0] +"&lname="+ name[1] +"&sf" ).done( function( text ) { 14 if( text == 'OK' ){ 15 document.cookie = "nl_subscribe=true"; 16 $('#nl_message_box').html('You are Now subscribed. Thank You.'); 17 $('#nl_controls_box').hide(); 18 }else{ 19 if( text.indexOf('already exists') > -1 ) 20 document.cookie = "nl_subscribe=true"; 21 $('#nl_message_box').html(text); 22 } 23 }); 24}, 25loadLibrary = (src_url, callback) => { 26 let js = document.createElement("script"); 27 js.type = "text/javascript"; 28 js.src = src_url; 29 js.onload = () => { 30 callback(); 31 } 32 document.body.appendChild(js); 33}, 34toggleSMSwrap = (o) => { 35 if (typeof toggleSMS !== 'function') { 36 loadLibrary(template_directory_uri +'/scripts/sms.js', () => { 37 toggleSMS(o); 38 }) 39 } else 40 toggleSMS(o); 41}, 42doForm = (nest_id, formname, vid, callback) => { 43 insertFormLoadable(nest_id, formname, vid, function() { 44 if (!$('#closeContacts').length) 45 $('#quickContent').prepend( 46 `<div id="closeContacts" onclick="closeContacts()"> 47 <span class="glyphicon glyphicon-remove"></span> 48 </div>`); 49 $('#quickContacts').show(); 50 if (typeof callback == 'function') 51 callback(); 52 }); 53}, 54insertForm = (nest_id, formname, vid, formCallback) => { 55 const afterInsert = () => { 56 const form = document.querySelector( 57 `#${nest_id} form[name="${formname}"]` 58 ); 59 60 initValidationOutlineObserver(form); 61 62 if (typeof formCallback === 'function') { 63 formCallback(); 64 } 65 66 if (formname == 'schedule_service') wrapFormSections(document.querySelector('.form_body')); 67 if (formname == 'parts') parts_addition_func_init(); 68 }; 69 70 if ($('script[src*="'+ forms_src_url +'"]').length < 1) { 71 loadLibrary(forms_src_url, () => { 72 insertFormLoadable(nest_id, formname, vid, afterInsert); 73 }); 74 } else { 75 load_timeout = setTimeout(() => { 76 insertFormLoadable(nest_id, formname, vid, afterInsert); 77 }, 1000); 78 } 79}, 80createPopup = (nest_id, formname, vid, callback) => { 81 $('#quickContacts').remove(); 82 $('body').prepend( 83 '<div id="quickContacts" style="display: block; z-index: 9999;">\ 84 <div id="quickContent" ></div>\ 85 </div>'); 86 87 if (typeof insertFormLoadable !== 'function') { 88 loadLibrary(forms_src_url, () => { 89 doForm(nest_id, formname, vid, callback); 90 }) 91 } else 92 doForm(nest_id, formname, vid, callback); 93 94}, 95createFrPopup = (nest_id, formname, veh_obj, title='', callback) => { 96 const veh = JSON.parse(veh_obj) 97 const popup = createPopupInstance(veh, title); 98 popup.open(); 99 setQueryParam('form', formname); 100 setQueryParam('form_state', 'opened'); 101 let vehicleId = veh?.vid || ''; 102 vehicleId = vehicleId.toString(); 103 const afterInsert = () => { 104 const form = document.querySelector( 105 `#${nest_id} form` 106 ); 107 108 initValidationOutlineObserver(form); 109 110 if (typeof callback === 'function') { 111 callback(); 112 } 113 }; 114 $('.popup__backdrop, .popup__close').on('click', () => { 115 popup.close(); 116 removeQueryParam('form'); 117 removeQueryParam('form_state'); 118 }); 119 120 if (typeof insertFormLoadable !== 'function') { 121 loadLibrary(forms_src_url, () => { 122 insertFormLoadable(nest_id, formname, vehicleId, afterInsert); 123 }); 124 } else { 125 insertFormLoadable(nest_id, formname, vehicleId, afterInsert); 126 } 127}, 128numberFormat = (number, decimals, decPoint, thousandsSep) => { 129 number = (number + '').replace(/[^0-9+\-Ee.]/g, '') 130 var n = !isFinite(+number) ? 0 : +number 131 var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals) 132 var sep = (typeof thousandsSep === 'undefined') ? ',' : thousandsSep 133 var dec = (typeof decPoint === 'undefined') ? '.' : decPoint 134 var s = '' 135 var toFixedFix = function(n, prec) { 136 var k = Math.pow(10, prec) 137 return '' + (Math.round(n * k) / k).toFixed(prec) 138 } 139 s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.') 140 if (s[0].length > 3) { 141 s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) 142 } 143 if ((s[1] || '').length < prec) { 144 s[1] = s[1] || '' 145 s[1] += new Array(prec - s[1].length + 1).join('0') 146 } 147 return s.join(dec) 148}, 149waitFor = (variable, callback) => { 150 var interval = setInterval(() => { 151 if (window[variable]) { 152 clearInterval(interval); 153 callback(); 154 } 155 }, 200); 156}, 157activeSmsPopup = null, 158sentSmsPopup = (veh_obj) => { 159 if (activeSmsPopup) return; 160 if (typeof toggleSMS !== 'function') loadLibrary(template_directory_uri +'/scripts/sms.js', () => {}); 161 162 const popup = createSmsPopupInstance(veh_obj); 163 activeSmsPopup = popup; 164 popup.open(); 165 166 $('.sms-popup__backdrop, .sms-modal__close').on('click', () => { 167 popup.close(); 168 activeSmsPopup = null; 169 });
170}, 171$bannerSlider; 172 173/* 174 * hide title tooltips on mouse over 175*/ 176$( document ).ready(function(){ 177 var cachedTitle = ''; 178 $( 'a[title]' ).on( 'mouseenter', function(){ 179 var $this = $( this ); 180 cachedTitle = $this.attr( 'title' ); 181 $this.removeAttr( 'title' ); 182 }).on( 'mouseleave', function(){ 183 var $this = $( this ); 184 if( cachedTitle ) { 185 $this.attr( 'title', cachedTitle ); 186 } 187 }); 188/* 189 $("#main_banner .slider").slick( { 190 fade: true, 191 autoplay: true, 192 autoplaySpeed: 5000, 193 speed: 1500, 194 arrows: false, 195 dots: true, 196 pauseOnHover: false, 197 pauseOnDotsHover: true, 198 }); 199 let slider = $("#main_banner .slider"), 200 $video2 = $( '.home-vid' ); 201 202 if($video2.length > 0){ 203 $video2[ 0 ].play(); 204 $(slider).slick('slickPause'); 205 $video2.bind( 'ended', function( ) { 206 $(slider).slick('slickPlay'); 207 $(slider).slick('slickNext'); 208 }); 209 }*/ 210}); 211 212/* 213 * stop propagation 214 */ 215$( 'body' ).on( 'click', '.stopprop', function( e ){ 216 e.stopPropagation(); 217}); 218 219//Header Specific Code 220$(window).load(function(e){ 221 e.preventDefault(); 222 223 if(megaMenu){ 224 $.get( "/api/cars/?sold=Available").done( function( text ) { 225 var text1 = text.split("\n"); 226 next_search = []; 227 next_search = JSON.parse(text1[1]); 228 next_search_totals = JSON.parse(text1[2]); 229 $('#'+ 'make_menue').append($( 230 '<div class="elm">\ 231 <div class="title">\ 232 <a href="/inventory/">\ 233 View All / '+ text1[0] +'\ 234 </a>\ 235 </div>\ 236 </div>')); 237 var split = Math.ceil(next_search['make'].length/2); 238 for(var fld in next_search['make']){ 239 let value = '', 240 totlas = typeof(next_search_totals['make'][next_search['make'][fld]]) != 'undefined' ? ' / '+ next_search_totals['make'][next_search['make'][fld]] : ''; 241 value = next_search['make'][fld].toUpperCase().replace('_', ' '); 242 if (fld <= split) { 243 $('#' + 'make_menue').append($( 244 '<div class="elm">\ 245 <div class="title">\ 246 <a href="javascript:;" onclick="location.href=\'inventory/?' + 'make' + '=' + next_search['make'][fld] + '\'">\ 247 ' + value + totlas + '\ 248 </a>\ 249 </div>\ 250 </div>')); 251 }else{ 252 $('#'+ 'make_menue2').append($( 253 '<div class="elm">\ 254 <div class="title">\ 255 <a href="javascript:;" onclick="location.href=\'inventory/?'+ 'make' +'='+ next_search['make'][fld] +'\'">\ 256 '+ value + totlas +'\ 257 </a>\ 258 </div>\ 259 </div>')); 260 } 261 } 262 }); 263 264 $(".invmenu").click(function(e){ 265 e.preventDefault(); 266 $("#headInv").slideToggle(); 267 if($(window).width()<992){ 268 $(".menuicBox").trigger('click'); 269 } 270 271 }); 272 } 273 274}); 275//sms 276 277function createSmsPopupInstance(veh = {}) { 278 const popup = document.createElement('div'); 279 if (!veh.model) return; 280 popup.className = 'sms-popup-fr'; 281 popup.innerHTML = ` 282 <div class="sms-popup__backdrop"></div> 283 <div id="smsPopup" class="sms-modal"> 284 <div class="sms-modal__content"> 285 <img class="sms-modal-logo" src="/wp-content/themes/aanWordpress/assets/images/prancing-horse-black.svg"> 286 <button class="sms-modal__close fr-rollover-btn fr-btn--sm fr-btn--red-circle"> 287 <div class="rollover-wrapper"> 288 <svg class="fr-btn__progress-ring" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 289 <circle class="progress-ring__circle" cx="48" cy="48" r="46"></circle> 290 </svg> 291 292 <svg class="fr-btn__roll" width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 293 <circle cx="20" cy="20" r="19" fill="#fff"></circle> 294 <mask id="path-1-inside-1_874_3134" fill="white"> 295 <path d="M0 20C0 8.95431 8.95431 0 20 0C31.0457 0 40 8.95431 40 20C40 31.0457 31.0457 40 20 40C8.95431 40 0 31.0457 0 20Z"></path> 296 </mask> 297 <path d="M20 40V38C10.0589 38 2 29.9411 2 20H0H-2C-2 32.1503 7.84974 42 20 42V40ZM40 20H38C38 29.9411 29.9411 38 20 38V40V42C32.1503 42 42 32.1503 42 20H40ZM20 0V2C29.9411 2 38 10.0589 38 20H40H42C42 7.84974 32.1503 -2 20 -2V0ZM20 0V-2C7.84974 -2 -2 7.84974 -2 20H0H2C2 10.0589 10.
2970589 2 20 2V0Z" fill="#CDCBCB" mask="url(#path-1-inside-1_874_3134)"></path> 298 <path d="M20 40V38C10.0589 38 2 29.9411 2 20H0H-2C-2 32.1503 7.84974 42 20 42V40ZM40 20H38C38 29.9411 29.9411 38 20 38V40V42C32.1503 42 42 32.1503 42 20H40ZM20 0V2C29.9411 2 38 10.0589 38 20H40H42C42 7.84974 32.1503 -2 20 -2V0ZM20 0V-2C7.84974 -2 -2 7.84974 -2 20H0H2C2 10.0589 10.0589 2 20 2V0Z" fill="url(#paint0_radial_874_3408)" fill-opacity="0.6" mask="url(#path-1-inside-1_874_3134)"></path> 299 <defs> 300 <radialGradient id="paint0_radial_874_3408" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(20) rotate(90) scale(15.2 23.2)"> 301 <stop stop-color="#303030"></stop> 302 <stop offset="1" stop-color="#303030" stop-opacity="0.01"></stop> 303 </radialGradient> 304 </defs> 305 </svg> 306 307 <span class="fr-btn__icon" aria-hidden="true"> 308 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none"> 309 <path fill-rule="evenodd" clip-rule="evenodd" d="M8 9.32703L3.05025 13.6581C2.85509 13.8532 2.53831 13.8532 2.34315 13.6581C2.14798 13.4629 2.14798 13.1461 2.34315 12.951L6.67417 8.00121L2.34315 3.05146C2.14798 2.8563 2.14798 2.53952 2.34315 2.34435C2.53831 2.14919 2.85509 2.14919 3.05025 2.34435L8 6.67538L12.9497 2.34435C13.1449 2.14919 13.4617 2.14919 13.6569 2.34435C13.852 2.53952 13.852 2.8563 13.6569 3.05146L9.32583 8.00121L13.6569 12.951C13.852 13.1461 13.852 13.4629 13.6569 13.6581C13.4617 13.8532 13.1449 13.8532 12.9497 13.6581L8 9.32703Z" fill="#303030"></path> 310 </svg> 311 </span> 312 </div> 313 </button> 314 <h2>Continue on Your Phone</h2> 315 <p>Receive this Ferrari instantly on your phone â including full details, photos, and availability.<br/> Continue the experience anytime, anywhere.</p> 316 <div class="smsMSG opened sms-popup"> 317 <div class="inboxContainer"> 318 <div class="inputBox"> 319 <div class="input-outer form-group"> 320 <input class="form-control" 321 type="tel" 322 inputmode="numeric" 323 maxlength="10" 324 autocomplete="off" 325 placeholder=" "> 326 <div class="phone-mask" aria-hidden="true"> 327 <span class="country">+1</span> 328 <span class="number"> (___) ___-____</span> 329 </div> 330 <div class="error"></div> 331 </div> 332 <div class="buttonSMS fr-primary-btn fr-btn--md" tabindex="0" role="button" 333 <span class="fr-btn__text">Send Details</span> 334 <span class="fr-btn__icon" aria-hidden="true"> 335 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none"> 336 <path d="M11.2686 9.5459C11.278 9.53814 11.2866 9.52941 11.2959 9.52148L11.373 9.45312H11.3711C11.7574 9.08846 12 8.57313 12 8C12 7.42711 11.7571 6.91249 11.3711 6.54785L11.373 6.54688L11.2959 6.47949C11.2866 6.47156 11.278 6.46284 11.2686 6.45508L4 0L8 8L4 16L11.2686 9.5459Z" fill="currentColor"></path> 337 </svg> 338 </span> 339 </div> 340 </div> 341 </div> 342 </div> 343 </div> 344 </div> 345 `; 346 347 document.body.appendChild(popup); 348 349 const backdrop = popup.querySelector('.sms-popup__backdrop'); 350 const dialog = popup.querySelector('#smsPopup'); 351 const button = popup.querySelector('.buttonSMS'); 352 const input = popup.querySelector('input'); 353 const maskNumber = popup.querySelector('.phone-mask .number'); 354 355 input.addEventListener('input', () => { 356 let digits = input.value.replace(/\D/g, '').slice(0, 10); 357 input.value = digits; 358 renderMask(digits); 359 });
360 361 input.addEventListener('focus', () => { 362 renderMask(input.value); 363 }); 364 365 function renderMask(d) { 366 const caret = '<span class="caret"></span>'; 367 const len = d.length; 368 369 let out = ''; 370 371 if (len === 0) { 372 out = ` (${caret}___) ___-____`; 373 } 374 else if (len <= 3) { 375 out = ` (${d}${caret}${'_'.repeat(3 - len)}) ___-____`; 376 } 377 else if (len <= 6) { 378 out = ` (${d.slice(0, 3)}) ${d.slice(3)}${caret}${'_'.repeat(6 - len)}-____`; 379 } 380 else { 381 out = ` (${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}${caret}${'_'.repeat(10 - len)}`; 382 } 383 384 maskNumber.innerHTML = out; 385 } 386 387 /* click */ 388 button.addEventListener('click', (e) => { 389 e.stopPropagation(); 390 sendSMS(button); 391 }); 392 393 /* keydown (Enter) */ 394 button.addEventListener('keydown', (e) => { 395 if (e.key !== 'Enter') return; 396 397 e.preventDefault(); 398 e.stopPropagation(); 399 400 sendSMS({ 401 el: button, 402 veh, 403 value: input.value 404 }); 405 }); 406 407 gsap.set(backdrop, { opacity: 0 }); 408 gsap.set(dialog, { opacity: 0 }); 409 410 tl = gsap.timeline({ paused: true }) 411 .to(backdrop, { 412 opacity: 1, 413 duration: 0.25, 414 ease: 'power2.out' 415 }) 416 .to(dialog, { 417 opacity: 1, 418 duration: 0.45, 419 ease: 'power3.out' 420 }, '-=0.15'); 421 422 popup.addEventListener('click', (e) => { 423 if (e.target.dataset.smsClose !== undefined) { 424 close(); 425 } 426 }); 427 428 429 430 return { 431 el: popup, 432 tl, 433 open() { 434 ScrollSmoother.get()?.paused(true); 435 popup.classList.add('is-active'); 436 Object.keys(veh).forEach(key => { 437 button.dataset[key] = veh[key]; 438 }); 439 440 popup.querySelector('input').value = ''; 441 popup.querySelector('.error').innerHTML = ''; 442 tl.play(0); 443 }, 444 close() { 445 ScrollSmoother.get()?.paused(false); 446 447 tl.reverse().eventCallback('onReverseComplete', () => { 448 popup.classList.remove('is-active'); 449 popup.remove(); 450 }); 451 } 452 }; 453} 454 455//Popup animation functions 456function createPopupInstance(veh = {}, title) { 457 const hasVehicle = Boolean(veh?.img); 458 const vehicleMarkup = hasVehicle 459 ? ` 460 <div class="popup__vehicle"> 461 <div class="popup__vehicle-image-outer"> 462 <img src="${veh.img ?? veh.image_link[0]}" alt="${veh.year + ' ' + veh.make + ' ' + veh.model }"> 463 </div> 464 <div class="popup__vehicle-title-outer"> 465 <div class="popup__vehicle-title"> 466 <h2>${ veh.make + ' ' + veh.model }</h2> 467 <div class="popup__vehicle-spec"> 468 ${ veh.year } ${ veh.mileage ? numberFormat(veh.mileage)+' MI' : '' } 469 </div> 470 </div> 471 <div class="popup__vehicle-price"> 472 ${veh.price ?? veh.lower_price } 473 </div> 474 </div> 475 </div> 476 ` 477 : ''; 478 const popup = document.createElement('div'); 479 480 popup.className = 'popup-fr'; 481 popup.innerHTML = ` 482 <div class="popup__backdrop"></div> 483 <div class="popup__dialog"> 484 <button class="popup__close fr-rollover-btn fr-btn--sm fr-btn--red-circle"> 485 <div class="rollover-wrapper"> 486 <svg class="fr-btn__progress-ring" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 487 <circle class="progress-ring__circle" cx="48" cy="48" r="46"></circle> 488 </svg> 489 490 <svg class="fr-btn__roll" width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 491 <circle cx="20" cy="20" r="19" fill="#fff" /> 492 <mask id="path-1-inside-1_874_3408" fill="white"> 493 <path d="M0 20C0 8.95431 8.95431 0 20 0C31.0457 0 40 8.95431 40 20C40 31.0457 31.0457 40 20 40C8.95431 40 0 31.0457 0 20Z"></path> 494 </mask> 495 <path d="M20 40V38C10.0589 38 2 29.9411 2 20H0H-2C-2 32.1503 7.84974 42 20 42V40ZM40 20H38C38 29.9411 29.9411 38 20 38V40V42C32.1503 42 42 32.1503 42 20H40ZM20 0V2C29.9411 2 38 10.0589 38 20H40H42C42 7.84974 32.1503 -2 20 -2V0ZM20 0V-2C7.84974 -2 -2 7.84974 -2 20H0H2C2 10.0589 10.
4950589 2 20 2V0Z" fill="#CDCBCB" mask="url(#path-1-inside-1_874_3408)"></path> 496 <path d="M20 40V38C10.0589 38 2 29.9411 2 20H0H-2C-2 32.1503 7.84974 42 20 42V40ZM40 20H38C38 29.9411 29.9411 38 20 38V40V42C32.1503 42 42 32.1503 42 20H40ZM20 0V2C29.9411 2 38 10.0589 38 20H40H42C42 7.84974 32.1503 -2 20 -2V0ZM20 0V-2C7.84974 -2 -2 7.84974 -2 20H0H2C2 10.0589 10.0589 2 20 2V0Z" fill="url(#paint0_radial_874_3408)" fill-opacity="0.6" mask="url(#path-1-inside-1_874_3408)"></path> 497 <defs> 498 <radialGradient id="paint0_radial_874_3408" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(20) rotate(90) scale(15.2 23.2)"> 499 <stop stop-color="#303030"></stop> 500 <stop offset="1" stop-color="#303030" stop-opacity="0.01"></stop> 501 </radialGradient> 502 </defs> 503 </svg> 504 505 <span class="fr-btn__icon" aria-hidden="true"> 506 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none"> 507 <path fill-rule="evenodd" clip-rule="evenodd" d="M8 9.32703L3.05025 13.6581C2.85509 13.8532 2.53831 13.8532 2.34315 13.6581C2.14798 13.4629 2.14798 13.1461 2.34315 12.951L6.67417 8.00121L2.34315 3.05146C2.14798 2.8563 2.14798 2.53952 2.34315 2.34435C2.53831 2.14919 2.85509 2.14919 3.05025 2.34435L8 6.67538L12.9497 2.34435C13.1449 2.14919 13.4617 2.14919 13.6569 2.34435C13.852 2.53952 13.852 2.8563 13.6569 3.05146L9.32583 8.00121L13.6569 12.951C13.852 13.1461 13.852 13.4629 13.6569 13.6581C13.4617 13.8532 13.1449 13.8532 12.9497 13.6581L8 9.32703Z" fill="#303030"/> 508 </svg> 509 </span> 510 </div> 511 </button> 512 ${vehicleMarkup} 513 <div class="popup__content ${hasVehicle ? '' : 'popup__content-f-height'}"> 514 ${title ? '<h2>'+title+'</h2>' : ''} 515 <div class="popup__required-title">*Required fields</div> 516 <div id="quickContent"></div> 517 </div> 518 </div> 519 `; 520 521 document.body.appendChild(popup); 522 523 const backdrop = popup.querySelector('.popup__backdrop'); 524 const dialog = popup.querySelector('.popup__dialog'); 525 526 gsap.set(backdrop, { opacity: 0 }); 527 gsap.set(dialog, { x: '100%' }); 528 529 const tl = gsap.timeline({ paused: true }) 530 .to(backdrop, { 531 opacity: 1, 532 duration: 0.25, 533 ease: 'power2.out' 534 }) 535 .to(dialog, { 536 x: '0%', 537 duration: 0.45, 538 ease: 'power3.out' 539 }, '-=0.15'); 540 541 return { 542 el: popup, 543 tl, 544 open() { 545 ScrollSmoother.get()?.paused(true); 546 popup.classList.add('is-active'); 547 tl.play(0); 548 }, 549 close() { 550 ScrollSmoother.get()?.paused(false); 551 const form = popup.querySelector('form'); 552 destroyValidationOutlineObserver(form); 553 554 tl.reverse().eventCallback('onReverseComplete', () => { 555 popup.classList.remove('is-active'); 556 popup.remove(); 557 }); 558 } 559 }; 560} 561 562(function () { 563 564 const init = () => { 565 566 /* ========================================================= 567 * 1. Detect keyboard navigation 568 * ======================================================= */ 569 // document.addEventListener('keydown', (e) => { 570 // if (e.key === 'Tab') { 571 // document.body.classList.add('using-keyboard'); 572 // } 573 // }); 574 575 // document.addEventListener('mousedown', () => { 576 // document.body.classList.remove('using-keyboard'); 577 // }); 578 579 /* ========================================================= 580 * 2. Init labels from aria-label 581 * ======================================================= */ 582 const initFormLabels = (root = document) => { 583 const groups = root.querySelectorAll('.form-group'); 584 585 groups.forEach(group => { 586 if (group.dataset.labelInit === '1') return; 587 if (group.classList.contains('boxed')) return; 588 589 const control = group.querySelector('input.form-control, textarea.form-control'); 590 if (!control) return; 591 592 if (group.querySelector('label')) return; 593 594 const ariaLabel = control.getAttribute('aria-label'); 595 if (!ariaLabel) return; 596 597 const placeholder = control.getAttribute('placeholder'); 598 if (!placeholder) return; 599 600 if (!control.id) { 601 control.id = `fc-${Math.random().toString(36).slice(2, 9)}`; 602 } 603 604 // helper span (only once) 605 if (!group.querySelector('span')) { 606 const span = document.createElement('span'); 607 control.insertAdjacentElement('afterend', span); 608 } 609 610 const label = document.createElement('label'); 611 label.setAttribute('for', control.id); 612 label.textContent = placeholder; 613 614 control.insertAdjacentElement('afterend', label); 615 control.classList.add('placeholder-hidden'); 616 617 group.dataset.labelInit = '1'; 618 }); 619 620 }; 621 622 /* ========================================================= 623 * 3. MutationObserver (DOM exists here) 624 * ======================================================= */ 625 const observer = new MutationObserver(mutations => { 626 mutations.forEach(mutation => {
627 mutation.addedNodes.forEach(node => { 628 if (!(node instanceof HTMLElement)) return; 629 630 if (node.classList?.contains('form-group')) { 631 initFormLabels(node.parentNode); 632 } else if (node.querySelectorAll) { 633 initFormLabels(node); 634 } 635 }); 636 }); 637 }); 638 639 observer.observe(document.body, { 640 childList: true, 641 subtree: true 642 }); 643 644 /* ========================================================= 645 * 4. Initial run 646 * ======================================================= */ 647 initFormLabels(); 648 }; 649 650 if (document.readyState === 'loading') { 651 document.addEventListener('DOMContentLoaded', init); 652 } else { 653 init(); 654 } 655 656})(); 657 658const MOBILE_BP = window.matchMedia('(max-width: 959px)'); 659 660//accordion 661document.addEventListener('DOMContentLoaded', () => { 662 handleMenuAccordionByViewport() 663 initAccordions(); 664 665 MOBILE_BP.addEventListener('change', () => { 666 handleMenuAccordionByViewport(); 667 }); 668}); 669function handleMenuAccordionByViewport() { 670 if (MOBILE_BP.matches) { 671 prepareWpMenuAccordions(document.querySelector(".super-footer__navigation"), true); 672 } else { 673 destroyMenuAccordions(document.querySelector(".super-footer__navigation")); 674 } 675} 676function destroyMenuAccordions(scope) { 677 scope.querySelectorAll('.menu-item-has-children').forEach(item => { 678 const link = item.querySelector(':scope > a'); 679 const submenu = item.querySelector(':scope > .sub-menu'); 680 if (!link || !submenu) return; 681 682 683 gsap.killTweensOf(submenu); 684 gsap.set(submenu, { clearProps: 'all' }); 685 686 delete item.dataset.accordion; 687 delete link.dataset.accordionTrigger; 688 delete submenu.dataset.accordionContent; 689 690 link.removeAttribute('role'); 691 link.removeAttribute('aria-expanded'); 692 693 item.classList.remove('opened'); 694 }); 695} 696 697function prepareWpMenuAccordions(scope) { 698 scope.querySelectorAll('.menu-item-has-children').forEach(item => { 699 if (item.dataset.accordion) return; 700 701 const link = item.querySelector(':scope > a'); 702 const submenu = item.querySelector(':scope > .sub-menu'); 703 if (!link || !submenu) return; 704 705 item.dataset.accordion = ''; 706 link.dataset.accordionTrigger = ''; 707 submenu.dataset.accordionContent = ''; 708 709 link.setAttribute('role', 'button'); 710 link.setAttribute('aria-expanded', 'false'); 711 }); 712} 713 714function initAccordions(scope = document) { 715 scope.querySelectorAll('[data-accordion]').forEach(accordion => { 716 if (accordion.dataset.inited) return; 717 accordion.dataset.inited = '1'; 718 const trigger = accordion.querySelector('[data-accordion-trigger]'); 719 const content = accordion.querySelector('[data-accordion-content]'); 720 if (!trigger || !content) return; 721 722 const isOpenByDefault = accordion.hasAttribute('data-accordion-open'); 723 724 gsap.set(content, { 725 height: isOpenByDefault ? 'auto' : 0 726 }); 727 728 trigger.setAttribute('aria-expanded', isOpenByDefault); 729 accordion.classList.toggle('opened', isOpenByDefault); 730 731 trigger.addEventListener('click', (e) => { 732 e.preventDefault(); 733 const isOpen = accordion.hasAttribute('data-accordion-open'); 734 const willOpen = !isOpen; 735 736 accordion.toggleAttribute('data-accordion-open', willOpen); 737 accordion.classList.toggle('opened', willOpen); 738 trigger.setAttribute('aria-expanded', willOpen); 739 740 gsap.to(content, { 741 height: willOpen ? 'auto' : 0, 742 duration: 0.55, 743 ease: 'power3.inOut', 744 overwrite: 'auto' 745 }); 746 }); 747 }); 748} 749let ObserverStart = false; 750 751function initValidationOutlineObserver(formEl) { 752 753 if (!formEl || formEl.__outlineObserver) return; 754 755 const isValidOutline = (outline) => 756 /green|#0f0|#00ff00|rgb\(0,\s*\d+,\s*0\)/i.test(outline); 757 758 const isInvalidOutline = (outline) => 759 /red|#f00|#ff0000|rgb\(255,\s*0,\s*0\)/i.test(outline); 760 761 const observer = new MutationObserver(mutations => { 762 mutations.forEach(mutation => { 763 if (mutation.type !== 'attributes') return; 764 if (mutation.attributeName !== 'style') return; 765 766 const el = mutation.target; 767 if (!el || !el.style || !el.style.outline) return; 768 769 const outline = el.style.outline; 770 771 if (isValidOutline(outline)) { 772 el.style.outline = 'none'; 773 el.classList.add('fr-is-valid'); 774 el.classList.remove('fr-is-invalid'); 775 } 776 else if (isInvalidOutline(outline)) { 777 el.style.outline = 'none'; 778 el.classList.add('fr-is-invalid'); 779 el.classList.remove('fr-is-valid'); 780 } 781 }); 782 });
783 784 formEl.querySelectorAll('input, textarea, select').forEach(el => { 785 observer.observe(el, { 786 attributes: true, 787 attributeFilter: ['style'] 788 }); 789 }); 790 791 formEl.__outlineObserver = observer; 792 ObserverStart = true; 793} 794 795/** From MutationObserver destroyer*/ 796function destroyValidationOutlineObserver(formEl) { 797 if (!formEl || !formEl.__outlineObserver) return; 798 799 formEl.__outlineObserver.disconnect(); 800 delete formEl.__outlineObserver; 801} 802//schedule_service form add groups 803function wrapFormSections(formBody) { 804 if (!formBody || formBody.dataset.sectionsInitialized) return; 805 806 formBody.classList.add("form-has-groups"); 807 808 const children = Array.from(formBody.children); 809 let currentSection = null; 810 811 children.forEach(node => { 812 if (node.querySelector && node.querySelector('h2')) { 813 currentSection = document.createElement('div'); 814 currentSection.className = 'form-section'; 815 currentSection.appendChild(node); 816 formBody.insertBefore(currentSection, node.nextSibling); 817 return; 818 } 819 820 if (currentSection) { 821 currentSection.appendChild(node); 822 } 823 }); 824 825 formBody.dataset.sectionsInitialized = '1'; 826 827 const checkboxes = formBody.querySelectorAll('.checkbox-fixer'); 828 checkboxes.forEach((wrapper, index) => { 829 const input = wrapper.querySelector('input[type="checkbox"]'); 830 const label = wrapper.querySelector('label'); 831 832 if (!input || !label) return; 833 834 if (!input.id) { 835 const name = input.getAttribute('name') || 'checkbox'; 836 input.id = `${name}_${index}`; 837 } 838 839 if (!label.hasAttribute('for')) { 840 label.setAttribute('for', input.id); 841 } 842 }); 843} 844 845//scroll to element 846document.addEventListener('click', (e) => { 847 const trigger = e.target.closest('.fr-scroll-to'); 848 if (!trigger) return; 849 850 e.preventDefault(); 851 852 const target = 853 trigger.dataset.scrollTarget || 854 trigger.getAttribute('href'); 855 const checkTarget = trigger.dataset.check; 856 857 if (!target) return; 858 859 if (checkTarget) { 860 checkInput(checkTarget); 861 } 862 863 scrollToTarget(target, { 864 offset: trigger.dataset.scrollOffset 865 ? parseInt(trigger.dataset.scrollOffset, 10) 866 : 0 867 }); 868}); 869 870function scrollToTarget(target, options = {}) { 871 const { 872 offset = 0, 873 smooth = true 874 } = options; 875 876 const smoother = window.ScrollSmoother?.get(); 877 const isTouch = window.matchMedia('(pointer: coarse)').matches; 878 879 if (target === 'top') { 880 if (smoother && !isTouch) { 881 smoother.scrollTo(0, smooth); 882 } else { 883 window.scrollTo({ top: 0, behavior: smooth ? 'smooth' : 'auto' }); 884 } 885 return; 886 } 887 888 const el = 889 typeof target === 'string' 890 ? document.querySelector(target) 891 : target; 892 893 if (!el) return; 894 895 if (smoother && !isTouch) { 896 smoother.scrollTo(el, smooth); 897 } else { 898 const y = 899 el.getBoundingClientRect().top + 900 window.pageYOffset - 901 offset; 902 903 window.scrollTo({ 904 top: y, 905 behavior: smooth ? 'smooth' : 'auto' 906 }); 907 } 908} 909//input checker 910function checkInput(target) { 911 const el = 912 typeof target === 'string' 913 ? document.querySelector(target) 914 : target; 915 916 if (!el) return; 917 918 if (el.type === 'checkbox' || el.type === 'radio') { 919 el.checked = true; 920 el.dispatchEvent(new Event('change', { bubbles: true })); 921 } 922} 923 924function parts_addition_func_init() { 925 document.querySelectorAll('.form-section').forEach(section => { 926 const parts = section.querySelectorAll('.additional_part'); 927 if (parts.length === 0) return; 928 929 const lastPart = parts[parts.length - 1]; 930 931 const wrapper = document.createElement('div'); 932 wrapper.className = 'form-group col-sm-12'; 933 934 const btn = document.createElement('button'); 935 btn.type = 'button'; 936 btn.className = 'fr-tiertiary-btn fr-btn--md'; 937 btn.innerHTML = '<span class="fr-btn__text">Add Additional Part</span>'; 938 939 wrapper.appendChild(btn); 940 lastPart.after(wrapper); 941 942 btn.addEventListener('click', () => { 943 const hidden = section.querySelectorAll('.additional_part'); 944 945 if (hidden.length < 2) { 946 wrapper.remove(); 947 return; 948 } 949 950 hidden[0].classList.remove('additional_part'); 951 hidden[1].classList.remove('additional_part'); 952 953 if (section.querySelectorAll('.additional_part').length === 0) { 954 wrapper.remove(); 955 } 956 }); 957 });
958} 959 960function setQueryParam(key, value) { 961 const url = new URL(window.location); 962 url.searchParams.set(key, value); 963 window.history.replaceState({}, '', url); 964} 965 966function removeQueryParam(key) { 967 const url = new URL(window.location); 968 url.searchParams.delete(key); 969 window.history.replaceState({}, '', url); 970} 971 972document.addEventListener('DOMContentLoaded', () => { 973 974 const params = new URLSearchParams(window.location.search); 975 976 const form = params.get('form'); 977 const state = params.get('form_state'); 978 979 const isFerrariModelPage = 980 window.location.pathname.includes('/ferrari-model-range/'); 981 982 if (!isFerrariModelPage) return; 983 if (state !== 'opened') return; 984 if (form !== 'contacts') return; 985 986 setTimeout(() => { 987 createFrPopup( 988 'quickContent', 989 'contacts', 990 JSON.stringify({}), // â не null 991 'Contact us' 992 ); 993 }, 300); 994}); 995 996(function ($) { 997 function initReadMore($scope = $(document)) {
998 $scope.find('.fr-read-more').each(function () { 999 const block = this; 1000 1001 if (block.dataset.readMoreInit) { 1002 return; 1003 } 1004 1005 block.dataset.readMoreInit = 'true'; 1006 const button = block.querySelector('.read-more__toggle'); 1007 1008 if (!button) { 1009 return; 1010 } 1011 1012 button.addEventListener('click', () => { 1013 block.classList.toggle('is-open'); 1014 }); 1015 }); 1016 } 1017 function initTabs($scope = $(document)) { 1018 $scope.find('.fr-tabs').each(function () { 1019 const tabs = this; 1020 1021 if (tabs.dataset.tabsInit) { 1022 return; 1023 } 1024 1025 tabs.dataset.tabsInit = 'true'; 1026 1027 const buttons = tabs.querySelectorAll('.fr-tabs__button'); 1028 const panels = tabs.querySelectorAll('.fr-tabs__panel'); 1029 1030 buttons.forEach((button, index) => { 1031 button.addEventListener('click', () => { 1032 buttons.forEach(btn => btn.classList.remove('is-active')); 1033 panels.forEach(panel => panel.classList.remove('is-active')); 1034 1035 button.classList.add('is-active'); 1036 1037 if (panels[index]) { 1038 panels[index].classList.add('is-active'); 1039 } 1040 }); 1041 }); 1042 }); 1043 } 1044 1045 $(window).on('elementor/frontend/init', function () { 1046 elementorFrontend.hooks.addAction( 1047 'frontend/element_ready/global', 1048 function ($scope) { 1049 initReadMore($scope); 1050 initTabs($scope); 1051 } 1052 ); 1053 }); 1054 1055 $(document).ready(function () { 1056 initReadMore(); 1057 initTabs(); 1058 }); 1059})(jQuery);
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.