PageSourceSearch

https://wineclub.mormoraia.it/site/vinora.1.0/theme/js/theme.js?v=639256786042143076

js mormoraia.it collected 2026-10-05 22:01:32 UTC 42,769 bytes, 1,041 lines download raw bytes

1/*  ---------------------------------------------------------------------------
2 *  Utilita' di accessibilita' condivise (audit Ruffino righe 8, 118, 119)
3 *  ---------------------------------------------------------------------------
4 *  Focus trap riutilizzabile per modali e pannelli sovrapposti. Bootstrap 3 ha un
5 *  proprio enforceFocus, ma riporta il focus sul contenitore del dialog invece di
6 *  ciclare tra i controlli: Shift+Tab si blocca e l'ordine di tabulazione si
7 *  interrompe (WCAG 2.4.3). Qui il ciclo e' esplicito e bidirezionale.
8 *
9 *  a11yFocusTrap($contenitore, $trigger) attiva il ciclo; a11yFocusTrapOff lo
10 *  rimuove e riporta il focus dove era prima dell'apertura.
11 *  ------------------------------------------------------------------------- */
12
13var a11yFocusabili = 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
14
15function a11yVisibili($elementi) {
16    return $elementi.filter(function () {
17        return this.offsetWidth > 0 || this.offsetHeight > 0 || this === document.activeElement;
18    });
19}
20
21function a11yFocusTrap($contenitore, $trigger) {
22    if (!$contenitore || !$contenitore.length) {
23        return;
24    }
25    $contenitore.data('a11yTrigger', $trigger && $trigger.length ? $trigger : null);
26
27    $contenitore.off('keydown.a11yTrap').on('keydown.a11yTrap', function (e) {
28        if (e.key !== 'Tab' && e.keyCode !== 9) {
29            return;
30        }
31        var $f = a11yVisibili($contenitore.find(a11yFocusabili));
32        if (!$f.length) {
33            return;
34        }
35        var primo = $f.first()[0];
36        var ultimo = $f.last()[0];
37
38        // Il contenitore stesso ha tabindex="-1" e non fa parte del ciclo: se il
39        // focus ci finisce (o esce del tutto) si rientra dall'estremo giusto.
40        if (e.shiftKey) {
41            if (document.activeElement === primo || !$.contains($contenitore[0], document.activeElement)) {
42                e.preventDefault();
43                ultimo.focus();
44            }
45        } else {
46            if (document.activeElement === ultimo || !$.contains($contenitore[0], document.activeElement)) {
47                e.preventDefault();
48                primo.focus();
49            }
50        }
51    });
52}
53
54function a11yFocusTrapOff($contenitore) {
55    if (!$contenitore || !$contenitore.length) {
56        return;
57    }
58    $contenitore.off('keydown.a11yTrap');
59    var $trigger = $contenitore.data('a11yTrigger');
60    if ($trigger && $trigger.length && $trigger.is(':visible')) {
61        $trigger.trigger('focus');
62    }
63    $contenitore.removeData('a11yTrigger');
64}
65
66
67/*  ---------------------------------------------------------------------------
68 *  ACCESSIBILITA' (audit Ruffino righe 46, 47, 48, 87) - carousel lightSlider
69 *  ---------------------------------------------------------------------------
70 *  Gli slider erano solo visivi: nessun ruolo, nessun nome, slide fuori vista lette
71 *  dagli screen reader come testo continuo, frecce <a> senza href (non raggiungibili
72 *  da tastiera) e pallini annunciati come "link 1, link 2" senza stato.
73 *
74 *  Invece di modificare il plugin (in modalita' seo si carica lightslider.min.js) si
75 *  avvolge $.fn.lightSlider: ogni slider del sito, del tema e di /custom/, riceve il
76 *  pattern WAI-ARIA APG carousel tramite i callback onSliderLoad/onAfterSlide, senza
77 *  perdere quelli passati da chi lo inizializza.
78 *   - contenitore: role="region", aria-roledescription, aria-label (opzione a11yLabel,
79 *     altrimenti il primo titolo della sezione che contiene lo slider);
80 *   - slide: role="group", aria-roledescription, aria-label "n di N"; quelle fuori
81 *     vista e i cloni del loop hanno aria-hidden e i loro controlli escono dal tab order;
82 *   - frecce del plugin: role="button", tabindex, nome, Invio/Spazio;
83 *   - opzione a11yControls: pulsanti Precedente/Successivo per gli slider senza frecce
84 *     (altrimenti navigabili solo trascinando, WCAG 2.5.7);
85 *   - pallini: dentro un <nav> con nome, <button type="button"> al posto dell'<a href="#">
86 *     del plugin, "Vai alla slide n", aria-current.
87 *  ------------------------------------------------------------------------- */
88
89function a11yCarouselTesto(chiave) {
90    var predefiniti = { roledescription: 'carousel', slide: 'slide', slideOf: '{0} / {1}', prev: 'Previous slide', next: 'Next slide', goTo: 'Go to slide {0}', pager: 'Slide navigation', image: 'Image {0}' };
91    return (typeof a11y_carousel !== 'undefined' && a11y_carousel[chiave]) ? a11y_carousel[chiave] : predefiniti[chiave];
92}
93
94function a11yFormat(testo, a, b) {
95    return testo.replace('{0}', a).replace('{1}', b);
96}
97
98function a11yAttivaConTastiera(e) {
99    if (e.key === 'Enter' || e.key === ' ' || e.keyCode === 13 || e.keyCode === 32) {
100        e.preventDefault();
101        $(this).trigger('click');
102    }
103}
104
105// Toglie o rimette nel tab order i controlli di una slide, ricordando il tabindex originale.
106function a11yTabOrder($contenitore, attivo) {
107    $contenitore.find(a11yFocusabili).add($contenitore.find('[data-a11y-tabindex]')).each(function () {
108        var $f = $(this);
109        if (attivo) {
110            if ($f.is('[data-a11y-tabindex]')) {
111                var t = $f.attr('data-a11y-tabindex');
112                if (t === '') {
113                    $f.removeAttr('tabindex');
114                } else {
115                    $f.attr('tabindex', t);
116                }
117                $f.removeAttr('data-a11y-tabindex');
118            }
119        } else if (!$f.is('[data-a11y-tabindex]')) {
120            $f.attr('data-a11y-tabindex', $f.attr('tabindex') || '').attr('tabindex', '-1');
121        }
122    });
123}
124
125function a11yCarouselInit($el, opzioni) {
126    var $outer = $el.closest('.lSSlideOuter');
127    if (!$outer.length || $outer.data('a11yCarousel')) {
128        return;
129    }
130    $outer.data('a11yCarousel', { $el: $el, opzioni: opzioni }).attr('data-a11y-carousel', '1');
131
132    // Senza a11yLabel si usa il titolo piu' vicino: si risale di pochi livelli e ci si ferma
133    // al primo antenato che ne contiene uno fuori dallo slider. Conta anche il titolo solo
134    // visivo (<p class="h3">, es. "Potrebbe interessarti" nel carrello), altrimenti si
135    // finirebbe sul titolo della pagina.
136    var nome = opzioni.a11yLabel;
137    if (!nome) {
138        $outer.parents().slice(0, 4).each(function () {
139            var $titolo = $(this).find(':header, .h1, .h2, .h3, .h4, .h5').not($outer.find('*')).first();
140            if ($titolo.length) {
141                nome = $.trim($titolo.text());
142                return false;
143            }
144        });
145    }
146    $outer.attr({ 'role': 'region', 'aria-roledescription': a11yCarouselTesto('roledescription') });
147    if (nome) {
148        $outer.attr('aria-label', nome);
149    }
150
151    // Frecce del plugin: <a> senza href. Non si aggiungono classi: il plugin distingue
152    // le due frecce confrontando l'attributo class per intero.
153    $outer.find('.lSAction > a').each(function () {
154        var $a = $(this);
155        $a.attr({ 'role': 'button', 'tabindex': '0', 'aria-label': a11yCarouselTesto($a.hasClass('lSPrev') ? 'prev' : 'next') })
156            .on('keydown', a11yAttivaConTastiera)
157            .find('img').attr('alt', '');
158    });
159
160    if (opzioni.a11yControls) {
161        var $controlli = $('<div class="a11y-carousel-controls">' +
162            '<button type="button" class="a11y-carousel-prev"><span aria-hidden="true">&larr;</span></button>' +
163            '<button type="button" class="a11y-carousel-next"><span aria-hidden="true">&rarr;</span></button>' +
164            '</div>');
165        $controlli.find('.a11y-carousel-prev').attr('aria-label', a11yCarouselTesto('prev')).on('click', function () {
166            if ($(this).attr('aria-disabled') !== 'true') {
167                $el.goToPrevSlide();
168            }
169        });
170        $controlli.find('.a11y-carousel-next').attr('aria-label', a11yCarouselTesto('next')).on('click', function () {
171            if ($(this).attr('aria-disabled') !== 'true') {
172                $el.goToNextSlide();
173            }
174        });
175        $outer.append($controlli);
176    }
177
178    // I pallini vengono rigenerati dal plugin al resize: la conversione in <button> e gli
179    // attributi si rimettono in a11yCarouselUpdate, qui solo il contenitore di navigazione.
180    // Nessun handler di tastiera: Invio e Spazio arrivano dal <button> nativo.
181    var $pager = $outer.find('.lSPager');
182    if ($pager.length && !$pager.parent().is('nav.a11y-carousel-pager')) {
183        $pager.wrap('<nav class="a11y-carousel-pager"></nav>');
184        $pager.parent().attr('aria-label', a11yCarouselTesto('pager'));
185    }
186}
187
188/*  I pallini (e le miniature della galleria) sono <a href="#"> generati dal plugin: si
189 *  sostituiscono con <button type="button">, cosi' ruolo, Invio e Spazio arrivano
190 *  dall'elemento invece che da role="button" piu' un handler di tastiera.
191 *   - type="button" e' obbligatorio: il pager sta dentro il <form runat="server"> e un
192 *     <button> senza type ne farebbe il submit;
193 *   - il click del plugin e' legato al <li>, mai al figlio (createPager lega $pager = i
194 *     <li>, e in tutto il plugin non c'e' un solo selettore sull'<a>): sostituire il
195 *     figlio non tocca il comportamento;
196 *   - createPager() riscrive gli <a> a ogni resize, ma a11yCarouselUpdate e' richiamata
197 *     dal resize handler qui sotto (250 ms, dopo i 200 ms del plugin) oltre che da
198 *     onSliderLoad/onAfterSlide: e' lo stesso riaggancio da cui dipendono gia' i nomi.
199 *  Idempotente: se il <button> c'e' gia', lo restituisce e basta. */
200function a11yPagerControl($li) {
201    var $controllo = $li.children('button');
202    if ($controllo.length) {
203        return $controllo;
204    }
205    var $a = $li.children('a');
206    if (!$a.length) {
207        return $();
208    }
209    $controllo = $('<button type="button"></button>').append($a.contents());
210    $a.replaceWith($controllo);
211    return $controllo;
212}
213
214function a11yCarouselUpdate($el) {
215    var $outer = $el.closest('.lSSlideOuter');
216    var dati = $outer.data('a11yCarousel');
217    if (!dati) {
218        return;
219    }
220    var opzioni = dati.opzioni;
221    var wrapper = $outer.find('.lSSlideWrapper')[0];
222    var box = wrapper ? wrapper.getBoundingClientRect() : null;
223    var $slide = $el.children().not('.clone');
224    var totale = $slide.length;
225
226    $slide.each(function (i) {
227        var $s = $(this);
228        var visibile;
229        if (opzioni.mode === 'fade') {
230            visibile = $s.hasClass('active');
231        } else {
232            var r = this.getBoundingClientRect();
233            visibile = !box || (Math.min(r.right, box.right) - Math.max(r.left, box.left) > 1);
234        }
235        $s.attr({ 'role': 'group', 'aria-roledescription': a11yCarouselTesto('slide'), 'aria-label': a11yFormat(a11yCarouselTesto('slideOf'), i + 1, totale) });
236        if (visibile) {
237            $s.removeAttr('aria-hidden');
238        } else {
239            $s.attr('aria-hidden', 'true');
240        }
241        a11yTabOrder($s, visibile);
242    });
243    $el.children('.clone').each(function () {
244        $(this).attr('aria-hidden', 'true');
245        a11yTabOrder($(this), false);
246    });
247
248    if (opzioni.a11yControls && box && $slide.length) {
249        var primo = $slide.first()[0].getBoundingClientRect();
250        var ultimo = $slide.last()[0].getBoundingClientRect();
251        var inizio = !opzioni.loop && primo.left >= box.left - 1;
252        var fine = !opzioni.loop && ultimo.right <= box.right + 1;
253        $outer.find('.a11y-carousel-prev').attr('aria-disabled', inizio ? 'true' : 'false');
254        $outer.find('.a11y-carousel-next').attr('aria-disabled', fine ? 'true' : 'false');
255        $outer.find('.a11y-carousel-controls').prop('hidden', inizio && fine);
256    }
257
258    $outer.find('.lSPager.lSpg > li').each(function (i) {
259        var $p = a11yPagerControl($(this));
260        $p.attr('aria-label', a11yFormat(a11yCarouselTesto('goTo'), i + 1));
261        if ($(this).hasClass('active')) {
262            $p.attr('aria-current', 'true');
263        } else {
264            $p.removeAttr('aria-current');
265        }
266    });
267    $outer.find('.lSPager.lSGallery > li').each(function (i) {
268        var $p = a11yPagerControl($(this));
269        $p.attr('aria-label', a11yFormat(a11yCarouselTesto('image'), i + 1)).find('img').attr('alt', '');
270        if ($(this).hasClass('active')) {
271            $p.attr('aria-current', 'true');
272        } else {
273            $p.removeAttr('aria-current');
274        }
275    });
276}
277
278(function ($) {
279    if (!$ || !$.fn.lightSlider) {
280        return;
281    }
282    var lightSliderOriginale = $.fn.lightSlider;
283    $.fn.lightSlider = function (opzioni) {
284        var o = $.extend({}, opzioni);
285        var onLoad = o.onSliderLoad;
286        var onAfter = o.onAfterSlide;
287        // keyPress del plugin ascolta keyup su tutto il documento e fa preventDefault su
288        // qualunque tasto fuori da input/textarea: annullava lo Spazio sui <button> della
289        // pagina (che si attivano al keyup) e le frecce spostavano tutti gli slider insieme.
290        // Si spegne e le frecce funzionano solo con il focus dentro il carousel.
291        var frecce = !!o.keyPress;
292        o.keyPress = false;
293        o.onSliderLoad = function ($el) {
294            a11yCarouselInit($el, o);
295            if (frecce) {
296                $el.closest('.lSSlideOuter').off('keydown.a11yCarousel').on('keydown.a11yCarousel', function (e) {
297                    if ($(e.target).is('input, textarea, select, [role="combobox"]')) {
298                        return;
299                    }
300                    if (e.key === 'ArrowLeft' || e.keyCode === 37) {
301                        e.preventDefault();
302                        $el.goToPrevSlide();
303                    } else if (e.key === 'ArrowRight' || e.keyCode === 39) {
304                        e.preventDefault();
305                        $el.goToNextSlide();
306                    }
307                });
308            }
309            if (typeof onLoad === 'function') {
310                onLoad.apply(this, arguments);
311            }
312            a11yCarouselUpdate($el);
313        };
314        o.onAfterSlide = function ($el) {
315            if (typeof onAfter === 'function') {
316                onAfter.apply(this, arguments);
317            }
318            a11yCarouselUpdate($el);
319            // la transizione CSS puo' non essere ancora finita: secondo passaggio
320            setTimeout(function () { a11yCarouselUpdate($el); }, (o.speed || 400) + 50);
321        };
322        return lightSliderOriginale.call(this, o);
323    };
324
325    var timer = null;
326    $(window).on('resize.a11yCarousel', function () {
327        clearTimeout(timer);
328        timer = setTimeout(function () {
329            $('.lSSlideOuter[data-a11y-carousel]').each(function () {
330                var dati = $(this).data('a11yCarousel');
331                if (dati) {
332                    a11yCarouselUpdate(dati.$el);
333                }
334            });
335        }, 250);
336    });
337})(window.jQuery);
338
339
340// Cookie Consent by https://www.TermsFeed.com
341if (typeof cookieconsentDisable == "undefined" || cookieconsentDisable === false) {
342
343    document.addEventListener('DOMContentLoaded', function () {
344
345        cookieconsent.run({
346            "notice_banner_type": "simple",
347            "consent_type": "express",
348            "palette": "dark",
349            "language": $("body").data("language-iso"),
350            "page_load_consent_levels": ["strictly-necessary"],
351            "notice_banner_reject_button_hide": false,
352            "preferences_center_close_button_hide": false,
353            "page_refresh_confirmation_buttons": false,
354            "website_name": "",
355            "callbacks": {
356                "scripts_specific_loaded": (level) => {
357                    switch (level) {
358                        case 'targeting':
359                            gtag('consent', 'update', {
360                                'ad_storage': 'granted',
361                                'ad_user_data': 'granted',
362                                'ad_personalization': 'granted',
363                                'analytics_storage': 'granted',
364                                'functionality_storage': 'granted',
365                                'security_storage': 'granted',
366                                'personalization_storage': 'granted'
367                            });
368                            break;
369                    }
370                },
371                // audit riga 10: a consenso espresso si aprono gli overlay rimasti in coda
372                // (modale paese), uno alla volta e mai sopra il banner. Vedi modalCountryShow().
373                "i_agree_button_clicked": function () { a11yOverlayNext(); },
374                "i_decline_button_clicked": function () { a11yOverlayNext(); },
375                "user_consent_saved": function () { a11yOverlayNext(); }
376            },
377            "callbacks_force": true
378        });
379    });
380}
381
382var lsslider;
383var lsnews;
384var lscsellingcart;
385var scroolTopOffset = 90;
386
387$(function () {
388
389    //cart (fuori dal percorso critico: vedi cart_get_deferred in default.js)
390    cart_get_deferred();
391    
392    var lastScrollTop = 0;
393    $(window).scroll(function() {
394        var currentScrollTop = $(this).scrollTop();
395        
396        if (currentScrollTop >= scroolTopOffset) {
397            
398            $("body").addClass("scroll");
399            
400            if (currentScrollTop > lastScrollTop) {
401                $("body").removeClass("scrolltop");
402                $("body").addClass("scrolldown");
403                $("body").addClass("scrolldownfirst");
404            } else {
405                $("body").removeClass("scrolldown");
406                $("body").addClass("scrolltop");
407                $("body").addClass("scrolltopfirst");
408            }
409            
410        } else {
411            
412            $("body").removeClass("scroll");
413            $("body").removeClass("scrolldown");
414            $("body").removeClass("scrolltop");
415            $("body").removeClass("scrolltopfirst");
416            $("body").removeClass("scrolldownfirst");
417            
418        }
419        
420        lastScrollTop = currentScrollTop;
421        
422    });
423
424    //if ($(window).width() > 768) {
425    //    $(".dropdown").mouseenter(function () {
426    //        $(this).addClass('open');
427    //    });
428
429    //    $(".dropdown").mouseleave(function () {
430    //        $(this).removeClass("open");
431    //    });
432    //}
433
434    //$(".dropdown").on("click", ".dropdown-menu", function (e) {
435    //    e.stopPropagation();
436    //});
437
438    //scrollTo
439    initScrollTo();
440
441    //language_url sostituisci in: link accettazione privacy, cookies, terms & conditions
442    var language_url = $("body").data("language-url");
443    $(".reg_privacy a, .form_privacy a, .newsletter_privacy a, .alert-cookies a, .order_privacy a, .order_terms a").each(function () {
444        $(this).attr("href", $(this).attr("href").replace("{language_url}", language_url));
445    });
446    
447    var el_gallery = document.getElementsByClassName('lg-gallery');
448    for (let item of el_gallery) {
449        lightGallery(item, {
450          plugins: [lgVideo],
451          selector: '.lg-gallery-item',
452          share:false,
453          download:false,
454          iframeMaxWidth: '80%'    
455        })
456    }
457    
458    var el_video = document.getElementsByClassName('lg-video');
459    for (let item of el_video) {
460        lightGallery(item, {
461          plugins: [lgVideo],    
462          iframeMaxWidth: '80%'    
463        })
464    }
465
466
467    ////lightgallery
468    //$('.lg-gallery').lightGallery({
469    //    selector: '.lg-gallery-item',
470    //    videoMaxWidth: '80%',
471    //    iframeMaxWidth: '80%',
472    //    thumbnail: false,
473    //    animateThumb: false,
474    //    showThumbByDefault: false,
475    //    share: true,
476    //    googlePlus: false,
477    //    download: false
478    //});
479
480    //$('#video-gallery').lightGallery();
481
482    ////lightgallery single image
483    //$('.lg-image').lightGallery({
484    //    selector: 'this',
485    //    download: false
486    //});
487
488    ////lightgallery Video
489    //$('.lg-video').lightGallery({
490    //    selector: 'this',
491    //    videoMaxWidth: '80%'
492    //});
493
494    ////lightgallery Page
495    //$('.lg-page').lightGallery({
496    //    selector: 'this',
497    //    iframeMaxWidth: '80%'
498    //});
499
500   
501    //primary nav
502    // ACCESSIBILITA' (audit righe 118, 119): il trigger e' ora un <button> con
503    // aria-expanded/aria-controls (vedi MasterPage.master). Qui si tiene aggiornato lo
504    // stato, si contiene il focus dentro il pannello aperto e si chiude con Esc
505    // riportando il focus sul trigger: senza, superato l'ultimo link il focus usciva dal
506    // menu senza tornare al punto di partenza.
507    $('#header').on('click', '.primary-nav-trigger', function (e) {
508        e.preventDefault();
509        var $trigger = $(this);
510        var aperto = !$('body').hasClass('navigation-is-open');
511
512        $('body').toggleClass('navigation-is-open');
513        $trigger.find('.menu-icon').toggleClass('is-clicked');
514        $trigger.attr('aria-expanded', aperto ? 'true' : 'false');
515
516        var $pannello = $('#' + ($trigger.attr('aria-controls') || 'primary-nav'));
517        if (aperto) {
518            a11yFocusTrap($pannello, $trigger);
519            $pannello.find(a11yFocusabili).first().trigger('focus');
520        } else {
521            a11yFocusTrapOff($pannello);
522            $trigger.trigger('focus');
523        }
524    });
525
526    // Esc chiude il menu mobile, come ci si aspetta da un pannello sovrapposto.
527    $(document).on('keydown.a11yNav', function (e) {
528        if (e.key === 'Escape' && $('body').hasClass('navigation-is-open')) {
529            $('#header .primary-nav-trigger').trigger('click');
530        }
531    });
532
533    // ACCESSIBILITA' (audit riga 8): il focus trap di Bootstrap 3 riporta il focus sul
534    // contenitore del dialog (tabindex="-1") invece di ciclare tra i controlli. Il
535    // risultato e' che Shift+Tab si incastra e non si puo' tornare indietro.
536    // Qui il ciclo e' esplicito e bidirezionale, e resta confinato agli elementi
537    // realmente interattivi della modale. Vale per tutte le modali del tema.
538    $(document).on('shown.bs.modal', '.modal', function () {
539        var $modale = $(this);
540        a11yFocusTrap($modale, $(document.activeElement));
541        $modale.find(a11yFocusabili).first().trigger('focus');
542    });
543
544    $(document).on('hidden.bs.modal', '.modal', function () {
545        a11yFocusTrapOff($(this));
546    });
547
548    //newseletter open
549    $("#newsletter_open").on("click", function () {
550        $("#newsletter .newsletter_email").val($("#newsletter_open_email").val());
551    });
552     
553
554    //Slider principale
555    if ($(".ls-banner .ls-item").length > 1) {
556        $(".ls-banner").lightSlider({
557            keyPress: true,
558            auto: true,
559            mode: 'fade',
560            useCSS: true,
561            loop: true,
562            item: 1,
563            speed: 800,
564            pause: 4000,
565            controls: true,
566            pager: false,
567            centerSlide: false,
568            prevHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-left-white.svg" alt="next" width="40" height="20" />',
569            nextHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-right-white.svg" alt="prev" width="40" height="20" />'
570        });
571    }
572    if ($(".ls-banner .ls-item").length === 0) {
573        $('#header').addClass('menu-transparent-dark');
574    }
575
576    //NEWS
577    if ($(".ls-news .ls-item").length > 0) {
578        lsnews = $(".ls-news").lightSlider({
579            keyPress: true,
580            auto: false,
581            mode: 'slide',
582            useCSS: true,
583            cssEasing: 'ease',
584            easing: 'ease',
585            loop: false,
586            item: 3,
587            slideMargin: 30,
588            speed: 400,
589            pause: 4000,
590            controls: false,
591            pager: false,
592            centerSlide: false,
593            prevHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-left-grey.png" alt="prev" />',
594            nextHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-right-grey.png" alt="next" />',
595            responsive: [{
596                breakpoint: 992,
597                settings: {
598                    item: 2
599                }
600            }, {
601                breakpoint: 768,
602                settings: {
603                    item: 1
604                }
605            }]
606        });
607        //$('.ls-news-next').click(function (e) {
608        //    e.preventDefault();
609        //    lsnews.goToNextSlide();
610        //});
611    }
612
613    //Slider principale
614    
615    if ($(".ls-slider .ls-item").length > 1) {
616        lsslider = $(".ls-slider").lightSlider({
617            keyPress: true,
618            auto: true,
619            mode: 'fade',
620            useCSS: true,
621            loop: true,
622            item: 1,
623            speed: 800,
624            pause: 4000,
625            controls: true,
626            pager: false,
627            centerSlide: false,
628            prevHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-left-white.svg" alt="next" width="40" height="20" />',
629            nextHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-right-white.svg" alt="prev" width="40" height="20" />'
630        });
631    }
632    
633    //Slider New
634    if ($(".ls-slider-new .ls-item").length > 1) {
635        $(".ls-slider-new").lightSlider({
636            keyPress: true,
637            auto: false,
638            mode: 'slide',
639            useCSS: true,
640            cssEasing: 'ease',
641            easing: 'ease',
642            loop: true,
643            item: 1,
644            speed: 800,
645            pause: 4000,
646            controls: false,
647            pager: true,
648            centerSlide: false,
649            prevHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-left-white.svg" alt="next" width="40" height="20" />',
650            nextHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-right-white.svg" alt="prev" width="40" height="20" />'
651        });
652    }
653
654    //CROSS SELLING CARRELLO
655    if ($(".ls-cselling_cart .ls-item").length > 0) {
656        lscsellingcart = $(".ls-cselling_cart").lightSlider({
657            keyPress: true,
658            auto: false,
659            mode: 'slide',
660            useCSS: true,
661            cssEasing: 'ease',
662            easing: 'ease',
663            loop: false,
664            item: 3,
665            slideMargin: 30,
666            speed: 400,
667            pause: 4000,
668            controls: false,
669            pager: true,
670            centerSlide: false,
671            prevHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-left-grey.png" alt="prev" />',
672            nextHtml: '<img class="hidden-xs" src="/site/vinora.1.0/theme/img/ico-arrow-right-grey.png" alt="next" />',
673            responsive: [{
674                breakpoint: 1200,
675                settings: {
676                    item: 2
677                }
678            }, {
679                breakpoint: 768,
680                settings: {
681                    item: 1
682                }
683            }]
684        });
685    }
686    // La guardia su $.fn.coverVid non e' difensiva a caso: con il setting "covervidEnabled" a
687    // false (k_setting.covervidEnabled) la MasterPage non include piu' covervid.konico.js, e
688    // senza guardia qui partirebbe un "coverVid is not a function" che interromperebbe il resto
689    // di initAfterPageLoad. In quel caso l'effetto cover lo fa il CSS:
690    // .covervid-wrapper video { object-fit: cover } in theme/css/theme.css.
691    setTimeout(function () {
692        if ($.fn.coverVid && $(".covervid-wrapper video").length > 0) {
693            $(".covervid-wrapper video").coverVid(1920, 1080);
694        }
695    }, 100);
696   
697
698    //video background attiva/disattiva audio
699    //da rimuovere da custom classe hidden a video-play
700    $(".video-play").on("click", function (e) {
701        e.preventDefault();
702        var v = $(this).parents(".video-wrapper").find("video").get(0);
703        $(this).find(".volume-off").addClass("hidden");
704        $(this).find(".volume-on").addClass("hidden");
705        if (v.paused) { v.play(); }
706        if (v.muted) {
707            v.muted = false;
708            $(this).find(".volume-on").removeClass("hidden");
709        } else {
710            v.muted = true;
711            $(this).find(".volume-off").removeClass("hidden");
712        }
713    });
714    
715    
716    
717    
718
719}); //chiusura document ready
720
721
722//variabili per abilitare da custom imagesloaded e il delay
723let imgload = true;
724let imgload_delay = 200;
725let imgload_delay_div = 0;
726
727//da eseguire quando il contenuto e' visibile: animazioni allo scroll e risorse lazy
728function initAfterPageLoad() {
729
730    $(".wpl").each(function () {
731        var wp = $(this);
732        wp.css("animation-delay", wp.data("delay"));
733        wp.waypoint(function () {
734            $(this.element).addClass("animated " + wp.data("animation"));
735        }, { offset: wp.data("offset") });
736    });
737
738    $(".lazy-loading-video").each(function () {
739        $(this).html("<video controls='controls' style='height:auto; max-width:100%'></video>");
740        $(this).find("video").attr("src", $(this).data("src"));
741        $(this).find("video").attr("poster", $(this).data("poster"));
742        $(this).find("video")[0].load();
743    });
744
745    // "data-lazy" porta la variante base (big per gli slider a piena larghezza), "data-lazy-full"
746    // quella grande da usare sopra i 1200 px: stessa soglia delle media query emesse da
747    // k_image.slider_bg_css per gli stessi slider in home e page/1. Qui serve il JS e non una
748    // regola CSS perche' il lazy scrive lo style inline, che vincerebbe comunque sulla media query.
749    // Gli elementi senza "data-lazy-full" (le gallery, e tutto con seoOptimize off) si comportano
750    // esattamente come prima.
751    $(".lazy-loading-background").each(function () {
752        var full = $(this).data("lazy-full");
753        var src = (full && window.matchMedia("(min-width:1201px)").matches) ? full : $(this).data("lazy");
754        $(this).css("background-image", "url('" + src + "')");
755    });
756
757}
758
759function initPageLoad() {
760    //Con "pageLoader" = "false" in Settings.config la MasterPage non renderizza il velo di
761    //caricamento. Il controllo sulla lunghezza e' obbligatorio: jQuery non esegue la callback di
762    //fadeOut su una selezione vuota, quindi senza questo ramo initAfterPageLoad() non partirebbe e
763    //resterebbero fermi i waypoint delle animazioni, i video e gli sfondi lazy.
764    var loader = $(".page-loader");
765    if (loader.length === 0) {
766        initAfterPageLoad();
767        return;
768    }
769    loader.find("div").delay(imgload_delay_div).fadeOut();
770    loader.delay(imgload_delay).fadeOut("slow", initAfterPageLoad);
771}
772$(function () {
773    //senza loader da nascondere non ha senso aspettare window.load
774    if (imgload && $(".page-loader").length) {
775        $(window).on("load", initPageLoad);
776    } else {
777        initPageLoad();
778    }
779});
780
781
782
783/*  ACCESSIBILITA' (audit riga 10) - WCAG 2.4.3 Focus Order
784 *  Alla prima visita partivano insieme il banner cookie (dialog aria-modal) e la
785 *  modale paese, che con l'enforceFocus di Bootstrap si riprendeva il focus: da
786 *  tastiera il banner non era piu' raggiungibile. Ora gli overlay sono in sequenza:
787 *  finche' il consenso cookie non e' stato espresso la modale paese resta in coda e
788 *  si apre dai callback di TermsFeed (vedi cookieconsent.run in cima al file). */
789var a11yOverlayQueue = [];
790
791function a11yCookieConsentPending() {
792    if (typeof cookieconsentDisable !== "undefined" && cookieconsentDisable === true) {
793        return false;
794    }
795    if (typeof cookieconsent === "undefined") {
796        return false;
797    }
798    return Cookies.get('cookie_consent_user_accepted') !== 'true';
799}
800
801function a11yOverlayNext() {
802    // Il callback arriva prima che TermsFeed tolga il banner: si lascia finire.
803    setTimeout(function () {
804        // TermsFeed lancia user_consent_saved anche al caricamento, quando per un utente
805        // nuovo salva da solo il livello "strictly-necessary" (page_load_consent_levels):
806        // in quel momento il banner e' ancora aperto e la coda deve restare ferma.
807        // Si procede solo quando la scelta dell'utente e' registrata.
808        if (a11yCookieConsentPending()) {
809            return;
810        }
811        while (a11yOverlayQueue.length) {
812            a11yOverlayQueue.shift()();
813        }
814    }, 0);
815}
816
817function modalCountryShow() {
818    if (Cookies.get('modal-country') == 1) {
819        return;
820    }
821    var show = function () {
822        $('#modal-country').modal('show');
823        Cookies.set('modal-country', '1', { expires: 7 });
824    };
825    if (a11yCookieConsentPending()) {
826        a11yOverlayQueue.push(show);
827    } else {
828        // ACCESSIBILITA' (audit riga 8): $(show) e non show(). Questa funzione e' chiamata da uno
829        // <script> inline (RegisterStartupScript in MasterPage.master.vb) che gira DURANTE il parsing:
830        // aprendo la modale li', shown.bs.modal scattava ~2 ms prima di DOMContentLoaded e il gestore
831        // che aggancia il focus trap, registrato dentro il $(function(){...}) piu' sopra, arrivava
832        // tardi. Risultato: modale senza trap, focus fermo sul contenitore e Tab che usciva dalla
833        // modale. $() esegue subito se il DOM e' gia' pronto, altrimenti al ready: la modale si apre
834        // qualche ms dopo, quando il gestore c'e'. Il ramo in coda non ha il problema perche' viene
835        // svuotato dai callback di TermsFeed, molto dopo il ready. Vedi voce 8 del report Ruffino.
836        $(show);
837    }
838}
839
840
841/*  ACCESSIBILITA' (audit riga 41) - link che aprono una nuova finestra
842 *  Ogni a[target=_blank] riceve un testo .sr-only con l'avviso, a meno che il
843 *  testo del link non lo contenga gia' (icone social della MasterPage).
844 *  Copre anche i link scritti dopo il caricamento (consensi e privacy iniettati da
845 *  custom/theme.js, banner TermsFeed, postback asincroni) con un MutationObserver. */
846function a11yNewWindowLinks() {
847    if (typeof a11y_new_window === "undefined" || !a11y_new_window) {
848        return;
849    }
850    var avviso = a11y_new_window.toLowerCase();
851    $('a[target="_blank"]').each(function () {
852        var $a = $(this);
853        if ($a.children('.a11y-new-window').length || $a.text().toLowerCase().indexOf(avviso) !== -1) {
854            return;
855        }
856        $a.append('<span class="sr-only a11y-new-window"> (' + $('<div>').text(a11y_new_window).html() + ')</span>');
857    });
858}
859
860(function () {
861    if (!window.MutationObserver) {
862        return;
863    }
864    var timer = null;
865    $(function () {
866        new MutationObserver(function () {
867            clearTimeout(timer);
868            timer = setTimeout(a11yNewWindowLinks, 50);
869        }).observe(document.body, { childList: true, subtree: true });
870    });
871})();
872
873
874/*  ACCESSIBILITA' (audit riga 69) - WCAG 1.3.1
875 *  La voce corrente era comunicata solo dalla classe .active (colore/sottolineatura).
876 *  - menu header, pannello mobile e menu footer: aria-current="page" sul link della
877 *    pagina aperta, aria-current="true" sulle voci .active che la contengono;
878 *  - filtri categoria e paginazione dello shop: aria-current="page" sulla voce attiva. */
879function a11yPath(p) {
880    return (p || '').replace(/\/+$/, '').toLowerCase();
881}
882
883function a11yAriaCurrent() {
884    var path = a11yPath(location.pathname);
885    $('#header .menu a[href], #primary-nav a[href], #footer-menu a[href]').each(function () {
886        var $a = $(this);
887        var href = $a.attr('href');
888        // href="#" o "javascript:" (voci contenitore, controlli): il pathname risolto
889        // coinciderebbe con la pagina corrente senza esserlo.
890        if (href.charAt(0) === '#' || href.indexOf('javascript:') === 0) {
891            $a.removeAttr('aria-current');
892            return;
893        }
894        if (this.host === location.host && a11yPath(this.pathname) === path) {
895            $a.attr('aria-current', 'page');
896        } else if ($a.parent('li').hasClass('active')) {
897            $a.attr('aria-current', 'true');
898        } else {
899            $a.removeAttr('aria-current');
900        }
901    });
902    $('#category-filters li.active > a').attr('aria-current', 'page');
903    $('.paging .btn-link.active').attr('aria-current', 'page');
904}
905
906/*  ACCESSIBILITA' (audit righe 63, 64, 65, 66) - postback asincroni e pulsanti-link
907 *  - Un UpdatePanel che si aggiorna sostituisce il controllo che aveva il focus (le select
908 *    AutoPostBack sono anche disabilitate da KoalaInitializeRequest): il focus cadeva su <body>.
909 *    Finito il postback, se il focus e' perso torna sul controllo che l'ha lanciato, ricreato
910 *    con lo stesso id; per ricerca e annulla ricerca va sul campo di ricerca.
911 *  - Listing prodotti (variabile a11y_listing della pagina categoria): quando il pannello del
912 *    listing si aggiorna, la live region #listing-status annuncia il risultato.
913 *  - LinkButton con role="button" (classe a11y-button): Invio funziona gia' come link, qui
914 *    si aggiunge lo Spazio come per un <button>. */
915var a11yPostbackId = null;
916
917function a11yListingAnnounce(ricerca) {
918    var $status = $('#listing-status');
919    if (typeof a11y_listing === 'undefined' || !$status.length) {
920        return;
921    }
922    var n = parseInt($('#listing-count').attr('data-count'), 10) || 0;
923    var testo = ricerca ? (n > 0 ? a11yFormat(a11y_listing.found, n) : a11y_listing.none) : a11yFormat(a11y_listing.updated, n);
924    // svuotata e riscritta dopo un attimo: lo stesso testo ripetuto verrebbe ignorato
925    $status.text('');
926    setTimeout(function () { $status.text(testo); }, 100);
927}
928
929/*  ACCESSIBILITA' (audit righe 83, 85) - messaggi di stato
930 *  Scrive il testo in una delle live region fisse della MasterPage (#a11y-status cortese,
931 *  #a11y-alert urgente). Svuotata e riscritta dopo un attimo, cosi' anche lo stesso testo
932 *  ripetuto viene annunciato; ripulita dopo qualche secondo per non lasciare testo vecchio
933 *  in fondo alla pagina. */
934var a11yAnnounceTimer = {};
935
936function a11yAnnounce(testo, urgente) {
937    var id = urgente ? 'a11y-alert' : 'a11y-status';
938    var $r = $('#' + id);
939    testo = $.trim(testo || '');
940    if (!$r.length || !testo) {
941        return;
942    }
943    clearTimeout(a11yAnnounceTimer[id]);
944    $r.text('');
945    setTimeout(function () {
946        $r.text(testo);
947        a11yAnnounceTimer[id] = setTimeout(function () { $r.text(''); }, 7000);
948    }, 100);
949}
950
951/*  Errori dei validatori resi dal server dopo un postback asincrono (es. cv_cart "carrello non
952 *  valido"): il pannello arriva con il messaggio gia' dentro e role="alert" su un nodo appena
953 *  inserito non viene annunciato in modo affidabile. Si rilegge il testo dei validatori visibili
954 *  nei pannelli aggiornati e lo si annuncia nella live region urgente. */
955function a11yAnnounceValidators(pannelli) {
956    if (!pannelli || !pannelli.length) {
957        return;
958    }
959    setTimeout(function () {
960        var candidati = $.makeArray(window.Page_Validators || []).concat($.makeArray(window.Page_ValidationSummaries || []));
961        $(pannelli).find('[role="alert"]').each(function () { candidati.push(this); });
962        var visti = [], testi = [];
963        $.each(candidati, function (i, el) {
964            if (!el || $.inArray(el, visti) !== -1 || !document.body.contains(el)) {
965                return;
966            }
967            visti.push(el);
968            var dentro = $.grep(pannelli, function (p) { return p.contains(el); }).length > 0;
969            var stile = window.getComputedStyle(el);
970            var testo = $.trim($(el).text());
971            if (dentro && testo && stile.visibility !== 'hidden' && stile.display !== 'none' && (el.offsetWidth > 0 || el.offsetHeight > 0)) {
972                testi.push(testo);
973            }
974        });
975        if (testi.length) {
976            a11yAnnounce(testi.join(' '), true);
977        }
978    }, 0);
979}
980
981function a11yAsyncPostback() {
982    if (typeof Sys === 'undefined' || !Sys.WebForms || !Sys.WebForms.PageRequestManager) {
983        return;
984    }
985    var prm = Sys.WebForms.PageRequestManager.getInstance();
986    prm.add_initializeRequest(function (sender, args) {
987        var el = args.get_postBackElement();
988        a11yPostbackId = el && el.id ? el.id : null;
989    });
990    prm.add_pageLoaded(function (sender, args) {
991        a11yAnnounceValidators(args.get_panelsUpdated());
992        var id = a11yPostbackId;
993        a11yPostbackId = null;
994        if (!id) {
995            return;
996        }
997        var listing = typeof a11y_listing !== 'undefined' ? a11y_listing : null;
998        var ricerca = !!(listing && $.inArray(id, listing.searchTriggers) !== -1);
999        if (listing) {
1000            var aggiornati = $.map(args.get_panelsUpdated() || [], function (p) { return p.id; });
1001            if ($.inArray(listing.panel, aggiornati) !== -1) {
1002                // "N prodotti trovati" solo dopo una ricerca; annulla ricerca, ordinamento e filtri: "Lista aggiornata"
1003                a11yListingAnnounce(id === listing.searchTriggers[0]);
1004            }
1005        }
1006        var attivo = document.activeElement;
1007        if (attivo && attivo !== document.body && document.body.contains(attivo)) {
1008            return;
1009        }
1010        var target = ricerca ? document.getElementById(listing.search) : document.getElementById(id);
1011        if (target && (target.offsetWidth > 0 || target.offsetHeight > 0)) {
1012            target.focus();
1013        }
1014    });
1015}
1016
1017$(document).on('keydown', 'a.a11y-button[role="button"]', function (e) {
1018    if (e.key === ' ' || e.keyCode === 32) {
1019        e.preventDefault();
1020        this.click();
1021    }
1022});
1023
1024$(function () {
1025    a11yAsyncPostback();
1026});
1027
1028function themePageLoad() {
1029
1030    $(function () {
1031        a11yAriaCurrent();
1032        a11yNewWindowLinks();
1033
1034        //selectordie	
1035        $("#block-product .sod").selectOrDie({
1036            cycle: true,
1037            size: 5
1038        });
1039    });
1040    
1041}

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.