PageSourceSearch

https://locawo.com/templates/easyTemplate360/js/jtl.evo.js?v=2.0.28

js locawo.com collected 2026-09-24 14:20:13 UTC 51,029 bytes, 1,290 lines download raw bytes

1(function () {
2    'use strict';
3
4    if (!$.evo) {
5        $.evo = {};
6    }
7
8    var EvoClass = function() {};
9
10    EvoClass.prototype = {
11        options: {
12            captcha: {},
13            scrollSearch: '.smoothscroll-top-search'
14        },
15
16        constructor: EvoClass,
17
18        generateSlickSlider: function() {
19            let self = this;
20
21            const observer = new window.IntersectionObserver(entries => {
22                for (const entry of entries) {
23                    if (entry.isIntersecting) {
24                        let mainNode = $(entry.target);
25                        mainNode.removeClass('slick-lazy');
26                        if (!mainNode.hasClass('slick-initialized')) {
27                            mainNode.find('.product-wrapper').removeClass('mx-auto ml-auto-util mr-auto');
28                            self.initSlick(mainNode, mainNode.data('slick-type'), mainNode.data('display-counts'));
29                            mainNode.find('.slick-active a:first-child').attr({
30                                'tabindex': '-1'
31                            });
32                        }
33                    }
34                }
35            }, {
36                root: null,
37                threshold: 0.1, // set offset 0.1 means trigger if atleast 10% of element in viewport
38            })
39
40            document.querySelectorAll('.slick-lazy').forEach(function(slickItem) {
41                observer.observe(slickItem)
42            });
43
44            document.querySelectorAll('.slick-lazy').forEach(function(slickItem) {
45                let startX;
46                let supportsPassive = false;
47                try {
48                    let opts = Object.defineProperty({}, 'passive', {
49                        get: function() {
50                            supportsPassive = true;
51                        }
52                    });
53                    window.addEventListener("testPassive", null, opts);
54                    window.removeEventListener("testPassive", null, opts);
55                } catch (e) {}
56
57                slickItem.addEventListener('touchstart', function (e) {
58                    startX = e.changedTouches[0].pageX;
59
60                },supportsPassive ? { passive: true } : false);
61                slickItem.addEventListener('touchmove', function (e) {
62                    let mainNode = $(this);
63                    if (!mainNode.hasClass('slick-initialized')
64                        && Math.abs(startX - e.changedTouches[0].pageX) > 80
65                    ) {
66                        mainNode.removeClass('slick-lazy');
67                        mainNode.find('.product-wrapper').removeClass('mx-auto ml-auto-util mr-auto');
68                        self.initSlick(mainNode, mainNode.data('slick-type'), mainNode.data('display-counts'));
69                        let slickOptions = mainNode.slick('getSlick');
70                        if(slickOptions.slideCount > mainNode.slick('slickGetOption', 'slidesToShow')) {
71                            let goTo;
72                            $.each(slickOptions.originalSettings.responsive, function (key, value) {
73                                if (value.breakpoint === slickOptions.activeBreakpoint
74                                    && value.settings.slidesToShow !== undefined
75                                ) {
76                                    goTo = value.settings.slidesToShow;
77                                }
78                            });
79                            if (goTo === undefined) {
80                                goTo = slickOptions.originalSettings.slidesToScroll
81                            }
82                            mainNode.slick('slickGoTo', goTo || 2);
83                        }
84                    }
85                }, supportsPassive ? { passive: true } : false);
86            });
87        },
88
89        initSlick: function (node, sliderType, displayCounts = '') {
90            displayCounts = displayCounts.split(',');
91            displayCounts = [
92                parseInt(displayCounts[0]) || 2,
93                parseInt(displayCounts[1]) || 3,
94                parseInt(displayCounts[2]) || 5,
95                parseInt(displayCounts[3]) || 7,
96            ];
97            let sliderOptions = {
98                'box-slider' : {
99                    arrows:         false,
100                    lazyLoad:       'ondemand',
101                    slidesToShow:   1,
102                    slidesToScroll: 1,
103                    mobileFirst:    true,
104                    responsive: [
105                        {
106                            breakpoint: 992,
107                            settings: {
108                                arrows: true,
109                            }
110                        }
111                    ]
112                },
113                'slider-half' : {
114                    arrows:       false,
115                    lazyLoad:     'ondemand',
116                    mobileFirst:    true,
117                    slidesToShow: 2,
118                    slidesToScroll: 2,
119                    responsive:   [
120                        {
121                            breakpoint: 992,
122                            settings: {
123                                arrows: true,
124                            }
125                        },
126                        {
127                            breakpoint: 1300,
128                            settings: {
129                                slidesToShow: 3,
130                                slidesToScroll: 3,
131                                arrows: true,
132                            }
133                        }
134                    ]
135                },
136                'slider-three' : {
137                    arrows:       false,
138                    lazyLoad:     'ondemand',
139                    mobileFirst:    true,
140                    slidesToShow: 1,
141                    slidesToScroll: 1,
142                    responsive:   [
143                        {
144                            breakpoint: 768,
145                            settings: {
146                                slidesToShow: 2,
147                                slidesToScroll: 2,
148                            }
149                        },
150                        {
151                            breakpoint: 992,
152                            settings: {
153                                arrows: true,
154                            }
155                        },
156                        {
157                            breakpoint: 1300,
158                            settings: {
159                                arrows: true,
160                                slidesToShow: 3,
161                                slidesToScroll: 3,
162                            }
163                        }
164                    ]
165                },
166                'product-slider' : {
167                    rows:           0,
168                    arrows:         false,
169                    lazyLoad:       'ondemand',
170                    slidesToShow:   displayCounts[0] || 2,
171                    slidesToScroll: displayCounts[0] || 2,
172                    mobileFirst:    true,
173                    responsive:     [
174                        {
175                            breakpoint: 768,
176                            settings: {
177                                slidesToShow: displayCounts[1] || 3,
178                                slidesToScroll: displayCounts[1] || 3
179                            }
180                        },
181                        {
182                            breakpoint: 992,
183                            settings: {
184                                slidesToShow: displayCounts[2] || 5,
185                                arrows: true,
186                                slidesToScroll: displayCounts[2] || 5
187                            }
188                        },
189                        {
190                            breakpoint: 1300,
191                            settings: {
192                                slidesToShow: displayCounts[3] || 7,
193                                arrows: true,
194                                slidesToScroll: displayCounts[3] || 7
195                            }
196                        }
197                    ]
198                },
199                'news-slider' : {
200                    rows:           0,
201                    slidesToShow:   1,
202                    slidesToScroll: 1,
203                    arrows:         false,
204                    infinite:       false,
205                    lazyLoad:       'ondemand',
206                    mobileFirst:    true,
207                    responsive:     [
208                        {
209                            breakpoint: 768,
210                            settings: {
211                                slidesToShow: 2,
212                                slidesToScroll: 2,
213                            }
214                        },
215                        {
216                            breakpoint: 992,
217                            settings: {
218                                slidesToShow:3,
219                                slidesToScroll: 3,
220                                arrows: true
221                            }
222                        },
223                        {
224                            breakpoint: 1300,
225                            settings: {
226                                slidesToShow:5,
227                                slidesToScroll: 5,
228                                arrows: true
229                            }
230                        }
231                    ]
232                },
233                'freegift' : {
234                    slidesToShow:   3,
235                    slidesToScroll: 3,
236                    arrows:   false,
237                    infinite: false,
238                    responsive: [
239                        {
240                            breakpoint: 768,
241                            settings: {
242                                slidesToShow: 2,
243                                slidesToScroll: 2,
244                            }
245                        },
246                        {
247                            breakpoint: 992,
248                            settings: {
249                                arrows: true
250                            }
251                        }
252                    ]
253                },
254                'gallery' : {
255                    lazyLoad: 'ondemand',
256                    infinite: true,
257                    dots:     false,
258                    arrows:   true,
259                    speed: 500,
260                    fade: true,
261                    cssEase: 'linear',
262                    asNavFor: '#gallery_preview',
263                    responsive:     [
264                        {
265                            breakpoint: 992,
266                            settings: {
267                                dots: true
268                            }
269                        }
270                    ]
271                },
272                'gallery_preview' : {
273                    lazyLoad:       'ondemand',
274                    slidesToShow:   5,
275                    slidesToScroll: 1,
276                    asNavFor:       '#gallery',
277                    dots:           false,
278                    arrows:         true,
279                    focusOnSelect:  true,
280                    responsive:     [
281                        {
282                            breakpoint: 768,
283                            settings:   {
284                                slidesToShow: 4,
285                                slidesToScroll: 1,
286                            }
287                        },
288                        {
289                            breakpoint: 576,
290                            settings: {
291                                slidesToShow: 3,
292                                slidesToScroll: 1,
293                            }
294                        }
295                    ]
296                }
297            };
298            if ($('#content').hasClass('col-lg-9')) {
299                sliderOptions['news-slider']['slidesToShow'] = 2;
300            }
301            if (sliderType === 'gallery_preview') {
302                sliderType = 'gallery';
303                node = $('#gallery');
304            }
305            if (sliderType === 'gallery') {
306                $('.js-gallery-images').removeClass('d-none');
307                $('.slick-inital-arrow').remove();
308                $('.initial-slick-dots').remove();
309                $('#gallery_preview').slick(sliderOptions['gallery_preview']);
310            }
311
312            return node.slick(sliderOptions[sliderType]);
313        },
314
315        scrollStuff: function() {
316            var breakpoint = 0,
317                pos,
318                sidePanel = $('#sidepanel_left');
319
320            if(sidePanel.length) {
321                breakpoint = sidePanel.position().top + sidePanel.hiddenDimension('height');
322            }
323
324            pos = breakpoint - $(this).scrollTop();
325
326            if ($(this).scrollTop() > 200 && !$('#to-top').hasClass('active')) {
327                $('#to-top').addClass('active');
328            } else if($(this).scrollTop() < 200 && $('#to-top').hasClass('active')) {
329                $('#to-top').removeClass('active');
330            }
331
332            if ($(window).width() > 768) {
333                var $document = $(document),
334                    $element = $('.navbar-fixed-top'),
335                    className = 'nav-closed';
336
337                $document.scroll(function() {
338                    $element.toggleClass(className, $document.scrollTop() >= 150);
339                });
340
341            }
342        },
343
344        productTabsPriceFlow: function() {
345            var dateFormat = 'DD.MM.YYYY';
346            if ($('html').attr('lang') !== 'de') {
347                dateFormat = 'MM/DD/YYYY';
348            }
349            var chartOptions = {
350                responsive:       true,
351                scaleBeginAtZero: false,
352                aspectRatio:3,
353                tooltips: {
354                    callbacks: {
355                        label: function (tooltipItem, data) {
356                            var label = window.chartDataTooltip;
357                            label += Math.round(tooltipItem.yLabel * 100) / 100;
358                            label += ' '+window.chartDataCurrency;
359                            return label;
360                        }
361                    }
362                },
363                scales: {
364                    xAxes: [{
365                        type: 'time',
366                        time: {
367                            parser: 'DD.MM.YYYY',
368                            // round: 'day'
369                            tooltipFormat: dateFormat
370                        },
371                        display: false
372                    }],
373                }
374            };
375            var initPriceChartFunction = function() {
376                if (typeof window.priceHistoryChart !== 'undefined' && window.priceHistoryChart === null) {
377                    window.priceHistoryChart = new Chart(window.ctx, {
378                        type: 'line',
379                        data: window.chartData,
380                        options: chartOptions
381                    });
382                }
383            };
384            initPriceChartFunction();
385            $(document).on('priceChartUpdated', initPriceChartFunction);
386        },
387
388        tooltips: function() {
389            $('[data-toggle="tooltip"]').tooltip();
390        },
391
392        bootlint: function() {
393            (function(){
394                var p = window.alert;
395                var s = document.createElement("script");
396                window.alert = function() {
397                    console.info(arguments);
398                };
399                s.onload = function() {
400                    bootlint.showLintReportForCurrentDocument([]);
401                    window.alert = p;
402                };
403                s.src = "https://maxcdn.bootstrapcdn.com/bootlint/latest/bootlint.min.js";
404                document.body.appendChild(s);
405            })();
406        },
407
408        showNotify: function(options) {
409            eModal.alert({
410                size: 'xl',
411                buttons: false,
412                title: options.title,
413                message: options.text,
414                keyboard: true,
415                tabindex: -1})
416                .then(
417                 function() {
418                    $.evo.generateSlickSlider();
419                    $(document).trigger('notification-modal-shown.et');
420                }
421            );
422        },
423
424        popupDep: function() {
425            $('#main-wrapper').on('click', '.popup-dep', function(e) {
426                var id    = '#popup' + $(this).attr('id'),
427                    title = $(this).attr('title'),
428                    html  = $(id).html();
429                eModal.alert({
430                    message: html,
431                    title: title,
432                    keyboard: true,
433                    buttons: false,
434                    tabindex: -1})
435                    .then(
436                        function () {
437                            //the modal just copies all the html. so we got duplicate IDs which confuses recaptcha
438                            var recaptcha = $('.tmp-modal-content .g-recaptcha');
439                            if (recaptcha.length === 1) {
440                                var siteKey = recaptcha.data('sitekey'),
441                                    newRecaptcha = $('<div />');
442                                if (typeof  siteKey !== 'undefined') {
443                                    //create empty recapcha div, give it a unique id and delete the old one
444                                    newRecaptcha.attr('id', 'popup-recaptcha').addClass('g-recaptcha form-group');
445                                    recaptcha.replaceWith(newRecaptcha);
446                                    grecaptcha.render('popup-recaptcha', {
447                                        'sitekey' : siteKey,
448                                        'callback' : 'captcha_filled'
449
450                                    });
451                                }
452                            }
453                            addValidationListener();
454                            $('.g-recaptcha-response').attr('required', true);
455                        }
456                    );
457                return false;
458            });
459        },
460
461        popover: function() {
462            /*
463             * <a data-toggle="popover" data-ref="#popover-content123">Click me</a>
464             * <div id="popover-content123" class="popover">content here</div>
465             */
466            $('[data-toggle="popover"]').popover({
467                html: true,
468                sanitize: false,
469                content: function() {
470                    var ref = $(this).attr('data-ref');
471                    return $(ref).html();
472                }
473            });
474        },
475
476        smoothScrollToAnchor: function(href, pushToHistory) {
477            var anchorRegex = /^#[\w\-]+$/;
478            if (!anchorRegex.test(href)) {
479                return false;
480            }
481
482            var target, targetOffset;
483            target = $('#' + href.slice(1));
484
485            if (target.length > 0) {
486                // scroll below the static megamenu
487                // JTL logic
488                // var nav         = $('#jtl-nav-wrapper.sticky-top');
489                // var fixedOffset = nav.length > 0 ? nav.outerHeight() : 0;
490                // ET Logic: get the offset for all (possibly!) sticky elements that are above the target
491                var fixedOffset = 0;
492                $('.et-sticky:not(.et-sticky-affix)').each(function () {
493                    var $this = $(this);
494                    fixedOffset = fixedOffset + $this.outerHeight(true);
495                });
496
497                targetOffset = target.offset().top - fixedOffset - parseInt(target.css('margin-top'));
498                $('html, body').animate({scrollTop: targetOffset});
499
500                if (pushToHistory) {
501                    history.pushState({}, document.title, location.pathname + href);
502                }
503
504                return true;
505            }
506
507            return false;
508        },
509
510        smoothScroll: function() {
511            var supportHistory = (history && history.pushState) ? true : false;
512            var that = this;
513
514            this.smoothScrollToAnchor(location.hash, false);
515            $(document).delegate('a[href^="#"]', 'click', function(e) {
516                var elem = e.target;
517                if (!e.isDefaultPrevented()) {
518                    // only runs if no other click event is fired
519                    if (that.smoothScrollToAnchor(elem.getAttribute('href'), supportHistory)) {
520                        e.preventDefault();
521                    }
522                }
523            });
524        },
525
526        initSkipToScroll: function() {
527            // set focus on skip-to links
528            let links = $('.btn-skip-to');
529            var that = this;
530
531            links.on('click', function(e) {
532                let url    = new URL(e.target.href);
533                let target = url.hash;
534
535                that.smoothScrollToAnchor(target, false);
536                $(target).focus();
537            });
538        },
539
540        initScrollSearchEvent: function() {
541            this.destroyScrollSearchEvent();
542            let lastScroll       = 0,
543                $scrollTopSearch = $(this.options.scrollSearch);
544            if ($scrollTopSearch.length) {
545                $(document).on('scroll.search', function () {
546                    let newScroll = $(this).scrollTop();
547                    if (newScroll < lastScroll) {
548                        if ($(window).scrollTop() > 100) {
549                            $scrollTopSearch.removeClass('d-none');
550                        } else {
551                            $scrollTopSearch.addClass('d-none');
552                        }
553                    } else {
554                        $scrollTopSearch.addClass('d-none');
555                    }
556                    lastScroll = newScroll;
557                });
558            }
559        },
560
561        destroyScrollSearchEvent: function() {
562            $(this.options.scrollSearch).addClass('d-none');
563            $(document).off('scroll.search');
564        },
565
566        initScrollEvents: function() {
567            this.initScrollSearchEvent();
568
569            //scroll top button
570            let toTopbuttonVisible     = false,
571                $toTopbutton           = $('.smoothscroll-top'),
572                toTopbuttonActiveClass = 'show';
573
574            function scrolltoTop() {
575                $(window).scrollTop(0);
576            }
577
578            function handleVisibilityTopButton() {
579                let currentPosition = $(window).scrollTop();
580                if (currentPosition > 800) {
581                    if (!toTopbuttonVisible) {
582                        $toTopbutton.addClass(toTopbuttonActiveClass);
583                        toTopbuttonVisible = true;
584                    }
585                } else if (toTopbuttonVisible) {
586                    toTopbuttonVisible = false;
587                    $toTopbutton.removeClass(toTopbuttonActiveClass)
588                }
589            }
590
591            if ($toTopbutton.length) {
592                $(window).on('scroll', handleVisibilityTopButton);
593                $toTopbutton.on('click', scrolltoTop);
594                $toTopbutton.on('keyup', (e) => {
595                    if(e.key === 'Enter' || e.key === ' ') {
596                        scrolltoTop();
597                    }
598                })
599                handleVisibilityTopButton();
600            }
601        },
602
603        resetForParallax: function() {
604            $(window).trigger('resize').trigger('scroll');
605        },
606
607        addCartBtnAnimation: function() {
608            var animating = false;
609
610            initCustomization();
611
612            function initCustomization() {
613                var addToCartBtn = $('#add-to-cart button[type="submit"]'),
614                    form         = $('#buy_form');
615                //detect click on the add-to-cart button
616                form.on('submit', function(e) {
617                    // ET360: Fixes a bug where the basket button is getting animated even when clicking on product action elements (which also trigger a form submit but with a different intention
618                    if(document.activeElement && document.activeElement.getAttribute('name') === 'Wunschliste'
619                        || document.activeElement.getAttribute('name') === 'Vergleichsliste') {
620                        return true;
621                    }
622                    if(!animating) {
623                        //animate if not already animating
624                        animating =  true;
625
626                        addToCartBtn.addClass('is-added').find('path').eq(0).animate({
627                            //draw the check icon
628                            'stroke-dashoffset':0
629                        }, 300, function(){
630                            setTimeout(function(){
631                                addToCartBtn.removeClass('is-added').find('span.btn-basket-check').on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(){
632                                    //wait for the end of the transition to reset the check icon
633                                    addToCartBtn.find('path').eq(0).css('stroke-dashoffset', '19.79');
634                                    animating =  false;
635                                });
636
637                                if( $('.no-csstransitions').length > 0 ) {
638                                    // check if browser doesn't support css transitions
639                                    addToCartBtn.find('path').eq(0).css('stroke-dashoffset', '19.79');
640                                    animating =  false;
641                                }
642                            }, 600);
643                        });
644                    }
645                });
646            }
647
648        },
649
650        checkout: function() {
651            // show only the first submit button (i.g. the button from payment plugin)
652            var $submits = $('#checkout-shipping-payment')
653                .closest('form')
654                .find('button[type="submit"]');
655            $submits.addClass('d-none');
656            $submits.first().removeClass('d-none');
657
658            $('input[name="Versandart"]', '#checkout-shipping-payment').on('change', function() {
659                var id    = parseInt($(this).val());
660                var $form = $(this).closest('form');
661
662                if (isNaN(id)) {
663                    return;
664                }
665
666                $form.find('fieldset, button[type="submit"]')
667                    .attr('disabled', true);
668
669                var urlInstance = null;
670                if ($form.length === 1 && $form.attr('action') !== undefined) {
671                    urlInstance = new URL($form.attr('action'));
672                    urlInstance.searchParams.append('kVersandart', '' + id);
673                }
674                var url = urlInstance === null
675                    ? $('#jtl-io-path').data('path') + '/bestellvorgang.php?kVersandart=' + id
676                    : urlInstance.href;
677                $.evo.loadContent(url, function() {
678                    $.evo.checkout();
679                }, null, true);
680            });
681
682            $('#country').on('change', function (e) {
683                var val = $(this).find(':selected').val();
684
685                $.evo.io().call('checkDeliveryCountry', [val], {}, function (error, data) {
686                    var $shippingSwitch = $('#checkout_register_shipping_address');
687
688                    if (data.response) {
689                        $shippingSwitch.removeAttr('disabled');
690                        $shippingSwitch.parent().removeClass('d-none');
691                    } else {
692                        $shippingSwitch.attr('disabled', true);
693                        $shippingSwitch.parent().addClass('d-none');
694                        if ($shippingSwitch.prop('checked')) {
695                            $shippingSwitch.prop('checked', false);
696                            $('#select_shipping_address').collapse('show');
697                        }
698                    }
699                });
700            });
701        },
702
703        loadContent: function(url, callback, error, animation, wrapper) {
704            var that        = this;
705            var $wrapper    = (typeof wrapper === 'undefined' || wrapper.length === 0) ? $('#result-wrapper') : $(wrapper);
706            var ajaxOptions = {data: 'isAjax'};
707            if (animation) {
708                $wrapper.addClass('loading');
709            }
710
711            that.trigger('load.evo.content', { url: url });
712
713            $.ajax(url, ajaxOptions).done(function(html) {
714                var $data = $(html);
715                if (animation) {
716                    $data.addClass('loading');
717                }
718                $wrapper.replaceWith($data);
719                $wrapper = $data;
720                if (typeof callback === 'function') {
721                    callback();
722                }
723            })
724            .fail(function() {
725                if (typeof error === 'function') {
726                    error();
727                }
728            })
729            .always(function() {
730                $wrapper.removeClass('loading');
731                that.trigger('contentLoaded'); // compatibility
732                that.trigger('loaded.evo.content', { url: url });
733            });
734        },
735
736        startSpinner: function (target) {
737            target = target || $('body');
738            if ($('.jtl-spinner').length === 0) {
739                target.append('<div class="jtl-spinner"><i class="fa fa-spinner fa-pulse"></i></div>');
740            }
741        },
742
743        stopSpinner: function () {
744            $('.jtl-spinner').remove();
745        },
746
747        trigger: function(event, args) {
748            $(document).trigger('evo:' + event, args);
749            return this;
750        },
751
752        error: function() {
753            if (console && console.error) {
754                if (1 in arguments && arguments[1] === 'abort') {
755                    console.info(arguments);
756                } else {
757                    console.error(arguments);
758                }
759            }
760        },
761
762        addInactivityCheck: function(wrapper, timeoutMS = 500, stopEnter = false) {
763            var timeoutID,
764                that = this,
765                currentBox;
766
767            setup();
768
769            function setup() {
770                $(wrapper + ' .form-counter input, ' + wrapper + ' .choose_quantity input').on('change',resetTimer);
771                $(wrapper + ' .form-counter .btn-decrement, ' + wrapper + ' .form-counter .btn-increment')
772                    .on('click keydown',resetTimer)
773                    .on('touchstart',resetTimer,{passive: true});
774                if (stopEnter) {
775                    $(wrapper + ' input.quantity').on('keypress', function (e) {
776                        if (e.key === 'Enter') {
777                            return false;
778                        } else {
779                            resetTimer(e);
780                        }
781                    });
782                }
783            }
784
785            function startTimer() {
786                timeoutID = window.setTimeout(goInactive, timeoutMS);
787            }
788
789            function resetTimer(e) {
790                // prevents form submit on navigating via tab-key
791                if (e.keyCode !== 9) {
792                    if (wrapper === '#wl-items-form') {
793                        currentBox = $(e.target).closest('.productbox-inner');
794                    }
795                    if (timeoutID == undefined) {
796                        startTimer();
797                    }
798                    window.clearTimeout(timeoutID);
799
800                    startTimer();
801                }
802            }
803
804            function goInactive() {
805                if (wrapper === '#cart-form') {
806                    $(wrapper).submit();
807                } else if (wrapper === '#wl-items-form') {
808                    that.updateWishlistItem(currentBox);
809                }
810            }
811        },
812
813        updateWishlistItem: function($wrapper) {
814            let formID   = 'wl-items-form';
815            $.evo.extended().startSpinner($wrapper);
816            $.evo.io().call(
817                'updateWishlistItem',
818                [
819                    parseInt($('#' + formID + ' input[name="kWunschliste"]').val()),
820                    $.evo.io().getFormValues(formID)
821                ],
822                $(this) , function(error, data) {
823                    $.evo.extended().stopSpinner();
824                    $wrapper.removeClass('loading');
825                });
826        },
827
828        fixStickyElements: function() {
829            /* This functionality is handled by the sticky plugin!
830            var sticky    = '.cart-summary';
831            var navHeight = $('#jtl-nav-wrapper').outerHeight(true);
832            navHeight = navHeight === undefined ? 0 : parseInt(navHeight + 40);
833            $(sticky).css('top', navHeight);
834             */
835        },
836
837        setWishlistVisibilitySwitches: function() {
838            $('.wl-visibility-switch').on('change', function () {
839                $.evo.io().call(
840                    'setWishlistVisibility',
841                    [$(this).data('wl-id'), $(this).is(":checked"), window.globals.jtl_token],
842                    $(this),
843                    function(error, data) {
844                    if (error) {
845                        return;
846                    }
847                    var $wlPrivate    = $('span[data-switch-label-state="private-' + data.response.wlID + '"]'),
848                        $wlPublic     = $('span[data-switch-label-state="public-' + data.response.wlID + '"]'),
849                        $wlURLWrapper = $('#wishlist-url-wrapper'),
850                        $wlURL        = $('#wishlist-url');
851                    if (data.response.state) {
852                        $wlPrivate.addClass('d-none');
853                        $wlPublic.removeClass('d-none');
854                        $wlURLWrapper.removeClass('d-none');
855                        $wlURL.val($wlURL.data('static-route') + data.response.url)
856                    } else {
857                        $wlPrivate.removeClass('d-none');
858                        $wlPublic.addClass('d-none');
859                        $wlURLWrapper.addClass('d-none');
860                    }
861                });
862            });
863        },
864
865        setDeliveryAdressDefaultSwitches: function() {
866            $('.la-default-switch').on('change', function () {
867                let input = $(this);
868                $('.la-default-switch').each(function() {
869                    if (input.attr('id') !== $(this).attr('id')) {
870                        $(this).prop('checked', false);
871                    } else {
872                        $(this).prop('checked', true);
873                    }
874                });
875                $.evo.io().call(
876                    'setDeliveryaddressDefault',
877                    [$(this).data('la-id'), $('.jtl_token').val()],
878                    $(this), function(error, data) {
879                        if (error) {
880                            return;
881                        }
882                        if (!data.response.result) {
883                            input.prop('checked', false);
884                        }
885                    }
886                );
887            });
888        },
889
890        initEModals: function () {
891            $('.author-modal').on('click', function (e) {
892                e.preventDefault();
893                let modalID = $(this).data('target');
894                eModal.alert({
895                    title: $(modalID).attr('title'),
896                    message: $(modalID).html(),
897                    buttons: false
898                });
899            });
900        },
901
902        initPriceSlider: function ($wrapper, redirect) {
903            let priceRange      = $wrapper.find('[data-id="js-price-range"]').val(),
904                priceRangeID    = $wrapper.find('[data-id="js-price-range-id"]').val(),
905                priceRangeMin   = 0,
906                priceRangeMax   = $wrapper.find('[data-id="js-price-range-max"]').val(),
907                currentPriceMin = priceRangeMin,
908                currentPriceMax = priceRangeMax,
909                $priceRangeFrom = $("#" + priceRangeID + "-from"),
910                $priceRangeTo = $("#" + priceRangeID + "-to"),
911                $priceSlider = document.getElementById(priceRangeID);
912
913            if($priceSlider === null) {
914                return;
915            }
916
917            if(typeof $priceSlider !== 'undefined' && $priceSlider.noUiSlider) {
918                return;
919            }
920
921            if (priceRange) {
922                let priceRangeMinMax = priceRange.split('_');
923                currentPriceMin = priceRangeMinMax[0];
924                currentPriceMax = priceRangeMinMax[1];
925                $priceRangeFrom.val(currentPriceMin);
926                $priceRangeTo.val(currentPriceMax);
927            }
928            // NOTE: noUiSlider cannot work with identical min and max range, therefore min is always rounded down and max is always rounded up
929            noUiSlider.create($priceSlider, {
930                start: [Math.floor(parseFloat(currentPriceMin)), Math.ceil(parseFloat(currentPriceMax))],
931                connect: true,
932                range: {
933                    'min': Math.floor(parseFloat(priceRangeMin)),
934                    'max': Math.ceil(parseFloat(priceRangeMax))
935                },
936                step: 1,
937                format: {
938                    to: function (value) {
939                        return Math.ceil(parseFloat(value));
940                    },
941                    from: function (value) {
942                        return Math.floor(parseFloat(value));
943                    }
944                },
945                handleAttributes: [
946                    { 'aria-label': 'lower' },
947                    { 'aria-label': 'upper' },
948                ]
949            });
950            $priceSlider.noUiSlider.on('change', function (values, handle) {
951                setTimeout(function(){
952                    $.evo.redirectToNewPriceRange(values[0] + '_' + values[1], redirect, $wrapper);
953                },0);
954            });
955            $priceSlider.noUiSlider.on('update', function (values, handle) {
956                $priceRangeFrom.val(values[0]);
957                $priceRangeTo.val(values[1]);
958            });
959            $('.price-range-input').on('change', function () {
960                let prFrom = parseInt($priceRangeFrom.val()),
961                    prTo = parseInt($priceRangeTo.val());
962                $.evo.redirectToNewPriceRange(
963                    (prFrom > 0 ? prFrom : priceRangeMin) + '_' + (prTo > 0 ? prTo : priceRangeMax),
964                    redirect,
965                    $wrapper
966                );
967            });
968        },
969
970        initFilters: function (href) {
971            let $wrapper = $('.js-collapse-filter');
972            $('#submitMobileFilters').addClass('disabled');
973            $.evo.extended().startSpinner($wrapper);
974
975            $.ajax(href, {data: {'useMobileFilters':1}})
976                .done(function(data) {
977                    $wrapper.html(data);
978                    $.evo.initPriceSlider($wrapper, false);
979                    $.evo.initItemSearch('filter');
980                })
981                .always(function() {
982                    $('#submitMobileFilters').removeClass('disabled');
983                    $.evo.extended().stopSpinner();
984                });
985        },
986
987        initFilterEvents: function() {
988            let initiallized = false;
989            $('#js-filters').on('click', function() {
990                if (!initiallized) {
991                    $.evo.initFilters(window.location.href);
992                    initiallized = true;
993                }
994            });
995        },
996
997        redirectToNewPriceRange: function (priceRange, redirect, $wrapper) {
998            let currentURL  = window.location.href;
999            if (!redirect) {
1000                currentURL  = $wrapper.find('[data-id="js-price-range-url"]').val();
1001            }
1002            let redirectURL = $.evo.updateURLParameter(
1003                currentURL,
1004                'pf',
1005                priceRange
1006            );
1007            // Short-Cut for AJAX Filtering in ET360v2
1008            if(typeof window.etUpdatePriceFilterUrl === 'function') {
1009                window.etUpdatePriceFilterUrl(redirectURL);
1010                return;
1011            }
1012
1013            if (redirect) {
1014                window.location.href = redirectURL;
1015            } else {
1016                $.evo.initFilters(redirectURL);
1017            }
1018        },
1019
1020        updateURLParameter: function (url, param, paramVal) {
1021
1022            if(typeof URL === 'undefined' || typeof URLSearchParams === 'undefined') {
1023                // IE11 fallback, does not support updating get params if a # is present
1024                let newAdditionalURL = '',
1025                    tempArray = url.split('?'),
1026                    baseURL = tempArray[0],
1027                    additionalURL = tempArray[1],
1028                    temp = '';
1029                if (additionalURL) {
1030                    tempArray = additionalURL.split('&');
1031                    for (let i = 0; i < tempArray.length; i++) {
1032                        if (tempArray[i].split('=')[0] != param) {
1033                            newAdditionalURL += temp + tempArray[i];
1034                            temp = '&';
1035                        }
1036                    }
1037                }
1038                return baseURL + '?' + newAdditionalURL + temp + param + '=' + paramVal;
1039            }
1040
1041            let newUrl = new URL(url);
1042            newUrl.searchParams.set(param, paramVal);
1043            return newUrl.toString();
1044        },
1045
1046        updateReviewHelpful: function(item) {
1047            let formData = $.evo.io().getFormValues('reviews-list');
1048            formData[item.prop('name')] = '';
1049            formData['reviewID'] = item.data('review-id');
1050
1051            $.evo.io().call(
1052                'updateReviewHelpful',
1053                [formData],
1054                $(this) , function(error, data) {
1055                    if (error) {
1056                        return;
1057                    }
1058                    let review = data.response.review;
1059
1060                    $('[data-review-id="' + review.kBewertung + '"]').removeClass('on-list');
1061                    item.addClass('on-list');
1062                    $('[data-review-count-id="hilfreich_' + review.kBewertung + '"]').html(review.nHilfreich);
1063                    $('[data-review-count-id="nichthilfreich_' + review.kBewertung + '"]').html(review.nNichtHilfreich);
1064                });
1065        },
1066
1067        initReviewHelpful: function() {
1068            $('.js-helpful').on('click', function (e) {
1069                e.preventDefault();
1070                $.evo.extended().updateReviewHelpful($(this));
1071            });
1072        },
1073
1074        initWishlist: function() {
1075            let wlFormID = '#wl-items-form';
1076            if ($(wlFormID).length) {
1077                $.evo.extended().addInactivityCheck(wlFormID, 300, true);
1078                $('.js-update-wl').on('change', function () {
1079                    $.evo.extended().updateWishlistItem($(this).closest('.productbox-inner'));
1080                });
1081                // Disabled in ET for it does not seem reasonable
1082                /*
1083                $('.product-list').children().each(function() {
1084                    $(this).css('height', $(this).height());
1085                });
1086                */
1087            }
1088        },
1089
1090        initPaginationEvents: function() {
1091            $('.pagination-wrapper select').on('change', function () {
1092                this.form.submit();
1093            });
1094        },
1095
1096        initItemSearch: function(context) {
1097            let searchWrapper  = '.' + context + '-search-wrapper',
1098                searchInput    = '.' + context + '-search',
1099                itemValue      = '.' + context + '-item-value',
1100                item           = '.' + context + '-item',
1101                clear          = '.form-clear',
1102                inputSelected  = 'input-group-selected',
1103                $searchWrapper = $(searchWrapper);
1104
1105            if ($searchWrapper.length === 0) {
1106                return;
1107            }
1108            $searchWrapper.each((i, itemWrapper) => {
1109                $(itemWrapper).find(searchInput).on('input', function () {
1110                    filterSearch($(itemWrapper));
1111                    $(itemWrapper).find('.collapse').collapse('show');
1112                }).on('keydown', e => {
1113                    if (e.key === 'Escape') {
1114                        e.stopPropagation();
1115                    }
1116                });
1117            });
1118            $(searchWrapper + ' ' + clear).on('click', function() {
1119                $(this).prev().val('');
1120                $(this).addClass('d-none');
1121                filterSearch($(this).closest(searchWrapper));
1122            });
1123            $(searchInput).on('focusin', function() {
1124                $(this).closest(searchWrapper).addClass(inputSelected);
1125            }).on('focusout', function() {
1126                $(this).closest(searchWrapper).removeClass(inputSelected);
1127            });
1128
1129            function filterSearch (itemWrapper) {
1130                let searchTerm = itemWrapper.find(searchInput).val().toLowerCase();
1131                itemWrapper.find(itemValue).each((i, itemTMP) => {
1132                    itemTMP = $(itemTMP);
1133                    let text = itemTMP.text().toLowerCase();
1134                    if (text.indexOf(searchTerm) === -1) {
1135                        itemTMP.closest(item).hide();
1136                    } else {
1137                        itemTMP.closest(item).show();
1138                    }
1139                    if (searchTerm.length === 0) {
1140                        itemWrapper.find(clear).addClass('d-none');
1141                    } else {
1142                        itemWrapper.find(clear).removeClass('d-none');
1143                    }
1144                });
1145            }
1146        },
1147
1148        initSliders: function() {
1149            let wrapper  = '.js-slider-wrapper',
1150                buttonF  = '.js-btn-slider-sf',
1151                buttonB  = '.js-btn-slider-sb',
1152                items    = '.js-slider-items';
1153
1154            $(wrapper).each(function (e) {
1155                let $buttonF   = $(this).find(buttonF),
1156                    $buttonB   = $(this).find(buttonB),
1157                    $items     = $(this).find(items);
1158
1159                $buttonF.on('click', function () {
1160                    $items.animate(
1161                        { scrollLeft: $items.scrollLeft() + $items.width() },
1162                        300,
1163                        'swing',
1164                        function () {
1165                            checkButtonDisable($buttonB, $buttonF, $items);
1166                        });
1167                });
1168                $buttonB.on('click', function () {
1169                    $items.animate(
1170                        { scrollLeft: $items.scrollLeft() - $items.width() },
1171                        300,
1172                        'swing',
1173                        function () {
1174                            checkButtonDisable($buttonB, $buttonF, $items);
1175                        });
1176                });
1177                $items.on('touchend', function (e) {
1178                    checkButtonDisable($buttonB, $buttonF, $items);
1179                });
1180            });
1181
1182            function checkButtonDisable($buttonB, $buttonF, $items) {
1183                let currentScroll = $items.scrollLeft();
1184                if (currentScroll <= 1) {
1185                    $buttonB.prop('disabled', true);
1186                } else {
1187                    $buttonB.prop('disabled', false);
1188                }
1189                if (currentScroll >= $items[0].scrollWidth - $items.width()) {
1190                    $buttonF.prop('disabled', true);
1191                } else {
1192                    $buttonF.prop('disabled', false);
1193                }
1194            }
1195        },
1196
1197        initSwatchFocus: function($wrapper) {
1198            $('.swatches-image>input, .swatches-text>input', $wrapper).on('focusin', function () {
1199                $(this).parent('label').addClass('focus');
1200            }).on('focusout', function () {
1201                $(this).parent('label').removeClass('focus');
1202            });
1203        },
1204
1205        registerImageHover: function($wrapper) {
1206            $(window).on("load resize", function() {
1207                let productWrapper = $('.product-wrapper');
1208                $.each(productWrapper, function() {
1209                    // Skip if element is inside a closed collapse.
1210                    let collapse = $(this).closest('.collapse');
1211                    if (collapse.length === 0 || collapse.hasClass('show')) {
1212                        let boxHeight = $(this).height();
1213                        $(this).height(boxHeight);
1214                    }
1215                })
1216            })
1217        },
1218
1219        /**
1220         * $.evo.extended() is deprecated, please use $.evo instead
1221         */
1222        extended: function() {
1223            return $.evo;
1224        },
1225
1226        register: function() {
1227            this.productTabsPriceFlow();
1228            this.generateSlickSlider();
1229            // setTimeout(() => {
1230            //     $('.nav-tabs').tabdrop();
1231            // }, 200);
1232            this.tooltips();
1233            this.popupDep();
1234            this.popover();
1235            this.addCartBtnAnimation();
1236            this.smoothScroll();
1237            this.checkout();
1238            if ($('body').data('page') == 3) {
1239                this.addInactivityCheck('#cart-form',500,true);
1240            }
1241            this.fixStickyElements();
1242            this.setWishlistVisibilitySwitches();
1243            this.setDeliveryAdressDefaultSwitches();
1244            this.initEModals();
1245            $.evo.article().initConfigListeners();
1246            this.initScrollEvents();
1247            this.initSkipToScroll();
1248            this.initReviewHelpful();
1249            this.initWishlist();
1250            this.initPaginationEvents();
1251            this.initFilterEvents();
1252            this.initItemSearch('filter');
1253            this.initSwatchFocus();
1254            // NOVA-specific: this.initSliders();
1255            // NOVA-specific: this.registerImageHover();
1256            this.wireUpPRGLinks();
1257        },
1258
1259        wireUpPRGLinks: function()
1260        {
1261            for (const prgLink of document.querySelectorAll('[data-prg-href]')) {
1262                prgLink.addEventListener('click', e => {
1263                    e.preventDefault();
1264                    let targetUrl = atob(prgLink.dataset.prgHref);
1265                    let urlInput = document.createElement('input');
1266                    let virtualForm = document.createElement('form');
1267                    document.body.append(virtualForm);
1268                    urlInput.name = 'prgTargetUrl';
1269                    urlInput.value = targetUrl;
1270                    virtualForm.action = window.jtlShopBaseUrl + '/prg';
1271                    virtualForm.method = 'post';
1272                    virtualForm.append(urlInput);
1273                    virtualForm.submit();
1274                });
1275            }
1276        }
1277    };
1278
1279    $(document).ready(function () {
1280        $.evo.register();
1281    });
1282
1283    // PLUGIN DEFINITION
1284    // =================
1285    $.evo = new EvoClass();
1286})(jQuery);
1287
1288function g_recaptcha_callback() {
1289    $.evo.renderCaptcha();
1290}

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.