PageSourceSearch

https://www.cauleyferrari.com/wp-content/themes/aanWordpress/scripts/main.js?=1781703667

js cauleyferrari.com collected 2026-10-01 15:38:45 UTC 38,556 bytes, 1,059 lines download raw bytes

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.