PageSourceSearch

https://www.zoga-beauty.com/templates/NOVA/js/jtl.article.js?v=5.5.3

js zoga-beauty.com collected 2026-10-04 13:25:17 UTC 74,884 bytes, 1,766 lines download raw bytes

1(function($, document, window, viewport){
2    'use strict';
3
4    var _stock_info = ['out-of-stock', 'in-short-supply', 'in-stock'],
5        $v,
6        ArticleClass = function () {
7            this.init();
8        };
9
10    ArticleClass.DEFAULTS = {
11        input: {
12            id: 'a',
13            childId: 'VariKindArtikel',
14            quantity: 'anzahl'
15        },
16        action: {
17            compareList: 'Vergleichsliste',
18            compareListRemove: 'Vergleichsliste.remove',
19            wishList: 'Wunschliste',
20            wishListRemove: 'Wunschliste.remove'
21        },
22        selector: {
23            navUpdateCompare: '#comparelist-dropdown-content',
24            navBadgeUpdateCompare: '#comparelist-badge',
25            navCompare: '#shop-nav-compare',
26            navContainerWish: '#wishlist-dropdown-container',
27            navBadgeWish: '#badge-wl-count',
28            navBadgeAppend: '#shop-nav li.cart-menu',
29            boxContainer: '#sidebox',
30            boxContainerWish: '#sidebox',
31            quantity: 'input.quantity'
32        },
33        modal: {
34            id: 'modal-article-dialog',
35            wrapper: '#result-wrapper',
36            wrapper_modal: '#result-wrapper-modal'
37        }
38    };
39
40    ArticleClass.prototype = {
41        modalShown: false,
42        modalView: null,
43
44        constructor: ArticleClass,
45
46        init: function () {
47            this.options = ArticleClass.DEFAULTS;
48            this.gallery = null;
49        },
50
51        onLoad: function() {
52            if (this.isSingleArticle()) {
53                var that = this;
54                var form = $.evo.io().getFormValues('buy_form');
55
56                if (typeof history.replaceState === 'function') {
57                    history.replaceState({
58                        a: form.a,
59                        a2: form.VariKindArtikel || form.a,
60                        url: document.location.href,
61                        variations: {}
62                    }, document.title, document.location.href);
63                }
64
65                window.addEventListener('popstate', function (event) {
66                    if (event.state) {
67                        that.setArticleContent(event.state.a, event.state.a2, event.state.url, event.state.variations);
68                    }
69                }, false);
70            }
71        },
72
73        isSingleArticle: function() {
74            return $('#buy_form').length > 0;
75        },
76
77        getWrapper: function(wrapper) {
78            return typeof wrapper === 'undefined' ? $(this.options.modal.wrapper) : $(wrapper);
79        },
80
81        getCurrent: function($item) {
82            var $current = $item.hasClass('variation') || ($item.length === 1 && $item[0].tagName === 'SELECT')
83                ? $item
84                : $item.closest('.variation');
85            if ($current.length === 1 && $current[0].tagName === 'SELECT') {
86                $current = $item.find('option:selected');
87            } else if ($current.length === 0) {
88                $current = $item.next('.variation');
89            }
90
91            return $current;
92        },
93
94        incrementProductVisitCount: function() {
95            console.log('This function is deprecated since JTL Shop 5.4.0');
96        },
97
98        navigateBackToList: function() {
99            console.log('This function is deprecated since JTL Shop 5.4.0');
100        },
101
102        register: function(wrapper) {
103            var $wrapper = this.getWrapper(wrapper);
104
105            if (this.isSingleArticle()) {
106                this.registerGallery($wrapper);
107                this.registerConfig();
108                this.registerHoverVariations($wrapper);
109            }
110
111            this.registerSimpleVariations($wrapper);
112            this.registerSwitchVariations($wrapper);
113            this.registerBulkPrices($wrapper);
114            this.registerAccordion();
115            // this.registerImageSwitch($wrapper);
116            this.registerArticleOverlay($wrapper);
117            this.registerImageHover($wrapper);
118            this.registerFinish($wrapper);
119            window.initNumberInput();
120            this.initAbnahmeIntervallError();
121            this.setClickableProductbox();
122        },
123
124        setClickableProductbox: function() {
125            $('.productbox[tabindex="0"]').on('keydown', function(e) {
126                // on Enter or Space
127                if (e.keyCode === 13 || e.keyCode === 32) {
128                    e.preventDefault();
129                    $(this).find('a')[0].click();
130                }
131            });
132        },
133
134        registerAccordion: function() {
135            $('.is-mobile .accordion [id^="tab-"]').on('shown.bs.collapse', function () {
136                let self = $(this);
137                $.evo.destroyScrollSearchEvent();
138                setTimeout(function() {
139                    $('html').animate(
140                        {scrollTop: self.offset().top-40},
141                        100,
142                        'linear',
143                        function () {
144                            setTimeout(function() {
145                                $.evo.initScrollSearchEvent();
146                            }, 500);
147                        });
148                }, 400);
149            });
150        },
151
152        registerArticleOverlay: function($wrapper) {
153            var that         = this;
154
155            $('.configpreview', $wrapper)
156                .each(function(i, item) {
157                    let $item   = $(item);
158                    let wrapper = that.options.modal.wrapper_modal + '_modal';
159
160                    $item.on('click', function (event) {
161                        event.preventDefault();
162                        that.modalArticleDetail(this, wrapper);
163                    });
164                });
165            $wrapper.hover(null, function() {
166                $(this).removeClass('active');
167            })
168        },
169
170        registerGallery: function(wrapper) {
171            /*
172             * product slider and zoom (details)
173             */
174            function slickinit()
175            {
176                $('.initial-slick-dots').on('click', function () {
177                    let mainNode = $('#gallery');
178                    mainNode.removeClass('slick-lazy');
179                    $.evo.extended().initSlick(mainNode, mainNode.data('slick-type'));
180                    if(mainNode.slick('getSlick').slideCount > mainNode.slick('slickGetOption', 'slidesToShow')) {
181                        mainNode.slick('slickGoTo', 1);
182                    }
183                });
184            }
185
186            function toggleFullscreen(fullscreen = false)
187            {
188                let $imgWrapper = $('#image_wrapper'),
189                    $gallery    = $('#gallery');
190                if (!$gallery.hasClass('slick-initialized') || (fullscreen && $imgWrapper.hasClass('fullscreen'))){
191                    return;
192                }
193
194                let maxHeight       = Math.max(document.documentElement.clientHeight, window.innerHeight || 0
194),
195                    otherElemHeight = 0,
196                    current         = ($('#gallery .slick-current').data('slick-index')),
197                    $galleryImages  = $('#gallery img, #gallery picture source'),
198                    hidePreview     = maxHeight < 700,
199                    previewHeight   = $('#gallery_preview_wrapper').length > 0 && !hidePreview ? 170 : 30,
200                    $previewBar     = $('.product-detail-image-preview-bar');
201
202                if (fullscreen) {
203                    $imgWrapper.addClass('fullscreen');
204                    let $galleryTopbar = $('#image_wrapper .product-detail-image-topbar');
205
206                    otherElemHeight = $galleryTopbar.outerHeight()
207                        + 2*parseInt($imgWrapper.css('paddingTop'))
208                        + previewHeight;
209
210                    $galleryImages.removeAttr('sizes');
211                    // lazySizes.autoSizer.updateElem($galleryImages);
212
213                    if (hidePreview) {
214                        $previewBar.addClass('d-none');
215                    }
216
217                    $galleryImages.css('max-height', maxHeight-otherElemHeight);
218                    $gallery.css('max-height', maxHeight-otherElemHeight);
219
220                    $('body').off('click.toggleFullscreen').on('click.toggleFullscreen', function (event) {
221                        if (!($(event.target).hasClass('product-image') || $(event.target).hasClass('slick-arrow'))) {
222                            toggleFullscreen(false);
223                            $('body').off('click.toggleFullscreen');
224                        }
225                    });
226                } else {
227                    $imgWrapper.removeClass('fullscreen');
228                    $galleryImages.css('max-height', '100%');
229                    $gallery.css('max-height', '100%');
230                    $previewBar.removeClass('d-none');
231                }
232
233                $gallery.slick('slickSetOption','initialSlide', current, true);
234                $('#gallery_preview').slick('slickGoTo', current, true);
235
236                //fix firefox height bug
237                $('.slick-slide, .slick-arrow').css({'display': 'none'});
238                setTimeout(function(){
239                    $('.slick-slide, .slick-arrow').css({'display': 'block'});
240                }, 50);
241            }
242
243            function addClickListener() {
244                $('#gallery img').off('click').on('click', e => {
245                    if (window.innerWidth > globals.breakpoints.lg) {
246                        toggleFullscreen(true);
247                    }
248                });
249            }
250
251            slickinit();
252
253            if (wrapper[0].id.indexOf(this.options.modal.wrapper_modal.slice(1)) === -1) {
254                addClickListener();
255
256                $(document).on('keyup', e => {
257                    if (e.key === "Escape" && $('#image_wrapper').hasClass('fullscreen')) {
258                        toggleFullscreen();
259                        addClickListener();
260                    }
261                });
262            }
263        },
264
265        registerConfig: function() {
266            var that   = this,
267                config = $('#product-configurator')
268                    .closest('form')
269                    .find('input[type="radio"], input[type="text"], input[type="checkbox"], input[type="number"], select'),
270                dropdown = $('#product-configurator')
271                    .closest('form')
272                    .find('select');
273            let offset = $('#jtl-nav-wrapper').height();
274
275            if (dropdown.length > 0) {
276                dropdown.on('change', function () {
277                    var item = $(this).val();
278                    $(this).parents('.cfg-group').find('.cfg-drpdwn-item.collapse.show').collapse('hide');
279                    $('#drpdwn_qnt_' + item).collapse('show');
280                })
281            }
282
283            if (config.length > 0) {
284                config.on('change', function() {
285                    that.configurator();
286                })
287                    .on('keypress', function (e) {
288                        if (e.key === 'Enter') {
289                            return false;
290                        }
291                    });
292                // timeout fixes problem with loading order of bootstrap dropdowns
293                setTimeout(function(){
294                    that.configurator(true);
295                },0);
296            }
297
298            function scroll (selector) {
299                $(selector).bind('click', function(event) {
300                    event.preventDefault();
301                    let anchor = $(this).attr('href');
302                    $('html, body').stop().animate({
303                        scrollTop: $(anchor).offset().top - offset - 15
304                    });
305                });
306            }
307            scroll('a[href="#cfg-container"]');
308
309
310            $('#cfg-nav nav a').on('click', function() {
311                $('html, body').animate({
312                    scrollTop: $('#cfg-container').offset().top - offset - 15
313                });
314                let anchor = $(this).attr('href'),
315                    elOffset = $(anchor)[0].offsetTop;
316                $('#cfg-container .cfg-options').stop().animate({
317                    scrollTop: elOffset
318                });
319            });
320
321            $(document).on('scroll', function (e) {
322                let headerHeight = $('#jtl-nav-wrapper').outerHeight() + 10;
323                $('.cfg-position-details.cfg-layout-list #product-configuration-sidebar, ' +
324                    '.cfg-position-details.cfg-layout-list .cfg-group .cfg-group-info').css('top', headerHeight + 'px');
325            });
326        },
327
328        registerSimpleVariations: function($wrapper) {
329            var that = this;
330
331            $('.variations select', $wrapper).selectpicker({
332                iconBase: 'fa',
333                tickIcon: 'fa-check',
334                hideDisabled: true,
335                showTick: true
336            });
337
338            $('.simple-variations input[type="radio"]', $wrapper)
339                .on('change', function() {
340                    var val = $(this).val(),
341                        key = $(this).parent().data('key');
342                    $('.simple-variations [data-key="' + key + '"]').removeClass('active');
343                    $('.simple-variations [data-value="' + val + '"]').addClass('active');
344                    $(this).closest(".swatches").addClass("radio-selected");
345                });
346
347            $('.simple-variations input[type="radio"], .simple-variations select', $wrapper)
348                .each(function(i, item) {
349                    var $item   = $(item),
350                        wrapper = '#' + $item.closest('form').closest('div[data-wrapper="true"]').attr('id');
351
352                    $item.on('change', function () {
353                        that.variationPrice($(this), true, wrapper);
354                    });
355                });
356            $('.simple-variations input[type="text"]', $wrapper)
357                .each(function(i, item) {
358                    let $item   = $(item),
359                        wrapper = '#' + $item.closest('form').closest('div[data-wrapper="true"]').attr('id'),
360                        timeout = null;
361                    $item.on('keyup', function (e) {
362                        clearTimeout(timeout);
363                        let self = $(this);
364
365                        timeout = setTimeout(function () {
366                            that.variationPrice(self, true, wrapper);
367                        }, 500);
368                    });
369                });
370        },
371
372        registerBulkPrices: function($wrapper) {
373            var $bulkPrice = $('.bulk-price', $wrapper),
374                that       = this,
375                $config    = $('#product-configurator');
376
377            if (($bulkPrice.length > 0 && $config.length === 0) || $('#product-list').length > 0) {
378                $('#quantity, [data-bulk="1"] .quantity', $wrapper)
379                    .each(function(i, item) {
380                        let $item   = $(item),
381                            wrapper = '#' + $item.closest('form').closest('div[data-wrapper="true"]').attr('id');
382
383                        $item.on('change', function () {
384                            that.variationPrice($(this), true, wrapper);
385                        });
386                        that.variationPrice($(this), true, wrapper);
387                    });
388            }
389        },
390
391        registerSwitchVariations: function($wrapper) {
392            var that = this;
393
394            $('.switch-variations input[type="radio"], .switch-variations select', $wrapper)
395                .each(function(i, item) {
396                    var $item   = $(item),
397                        wrapper = '#' + $item.closest('form').closest('div[id]').attr('id');
398
399                    $item.on('change', function () {
400                        that.variationSwitch($(this), false, wrapper);
401                    });
402                });
403
404            if (isTouchCapable()) {
405                $('.variations .swatches .variation', $wrapper)
406                    .on('mouseover', function() {
407                        $(this).trigger('click');
408                    });
409            }
410
411            // ie11 fallback
412            if (typeof document.body.style.msTransform === 'string') {
413                $('.variations label.variation', $wrapper)
414                    .on('click', function (e) {
415                        if (e.target.tagName === 'IMG') {
416                            $(this).trigger('click');
417                        }
418                    });
419            }
420        },
421
422        registerHoverVariations: function ($wrapper) {
423            let delay=300, setTimeoutConst;
424            $('.variations label.variation', $wrapper)
425                .on('mouseenter', function (e) {
426                    setTimeoutConst = setTimeout(function () {
427                        let mainImageHeight = $('.js-gallery-images').innerHeight();
428                        $('.variation-image-preview.vt' + $(e.currentTarget).data('value')).addClass('show d-md-block')
429                            .css('top', $(e.currentTarget).offset().top - $(e.currentTarget).closest('#content').position().top - mainImageHeight / 2 - 12);
430                    }, delay)
431                })
432                .on('mouseleave', function (e) {
433                    clearTimeout(setTimeoutConst);
434                    $('.variation-image-preview.vt' + $(this).data('value')).removeClass('show d-md-block');
435                });
436
437            $('.variations .selectpicker')
438                .on('show.bs.select', function () {
439                    let selectPicker = $(this);
440                    $(this).parent().find('li .variation')
441                        .on('mouseenter', function (e) {
442                            setTimeoutConst = setTimeout(function () {
443                                let mainImageHeight = $('.js-gallery-images').innerHeight();
444                                let selectOptionValue = selectPicker.find('option').eq(
445                                    e.currentTarget.ariaPosInSet
446                                ).data("value");
447                                $('.variation-image-preview.vt' + selectOptionValue)
448                                    .addClass('show d-md-block')
449                                    .css('top', $(e.currentTarget).offset().top - $(e.currentTarget).closest('#content').position().top - mainImageHeight / 2 - 12);
450                            }, delay)
451                        })
452                        .on('mouseleave', function (e) {
453                            clearTimeout(setTimeoutConst);
454                            let selectOptionValue = selectPicker.find('option').eq(
455                                e.currentTarget.ariaPosInSet
456                            ).data("value");
457                            $('.variation-image-preview.vt' + selectOptionValue)
458                                .removeClass('show d-md-block');
459                    });
460                })
461                .on('hide.bs.select', function () {
462                    $(this).parent().find('li .variation').off('mouseenter mouseleave');
463                    $('.variation-image-preview').removeClass('show');
464                });
465        },
466
467        registerImageSwitch: function($wrapper) {
468            var that     = this,
469                imgSwitch,
470                gallery  = this.gallery;
471
472            if (gallery !== null) {
473                imgSwitch = function (context, temporary, force) {
474                    var $context = $(context),
475                        id       = $context.attr('data-key'),
476                        value    = $context.attr('data-value'),
477                        data     = $context.data('list'),
478                        title    = $context.attr('data-title');
479
480                    if (typeof temporary === 'undefined') {
481                        temporary = true;
482                    }
483
484                    if ((!$context.hasClass('active') || force) && !!data) {
485                        gallery.setItems([data], value);
486
487                        if (!temporary) {
488                            var items  = [data],
489                                stacks = gallery.getStacks();
490                            for (var s in stacks) {
491                                if (stacks.hasOwnProperty(s) && s.match(/^_[0-9a-zA-Z]*$/) && s !== '_' + id) {
492                                    items = $.merge(items, stacks[s]);
493                                }
494                            }
495
496                            gallery.setItems([data], '_' + id);
497                            gallery.setItems(items, '__');
498                            gallery.render('__');
499
500                            that.galleryIndex     = gallery.index;
501                            that.galleryLastIdent = gallery.ident;
502                        } else {
503                            gallery.render(value);
504                        }
505                    }
506                }
507            } else {
508                imgSwitch = function (context, temporary) {
509                    var $context = $(context),
510                        value    = $context.attr('data-value'),
511                        data     = $context.data('list'),
512                        title    = $context.attr('data-title');
513
514                    if (typeof temporary === 'undefined') {
515                        temporary = true;
516                    }
517
518                    if (!!data) {
519                        var $wrapper = $(context).closest('.product-wrapper'),
520                            $img     = $('.image-box img', $wrapper);
521                        if ($img.length === 1) {
522                            $img.attr('src', data.md.src);
523                            if (!temporary) {
524                                $img.data('src', data.md.src);
525                            }
526                        }
527                    }
528                };
529            }
530
531            $('.variations .bootstrap-select select', $wrapper)
532                .on('change', function() {
533                    var sel  = $(this).find('[value=' + this.value + ']'),
534                        cont = $(this).closest('.variations');
535
536                    if (cont.hasClass('simple-variations')) {
537                        imgSwitch(sel, false, false);
538                    } else {
539                        imgSwitch(sel, true, false);
540                    }
541                });
542
543            if (!isTouchCapable() || ResponsiveBootstrapToolkit.current() !== 'xs') {
544                $('.variations .bootstrap-select .dropdown-menu li', $wrapper)
545                    .on('hover', function () {
546                        var tmp_idx = parseInt($(this).attr('data-original-index')) + 1,
547                            rule    = 'select option:nth-child(' + tmp_idx + ')',
548                            sel     = $(this).closest('.bootstrap-select').find(rule);
549                        imgSwitch(sel);
550                    }, function () {
551                        var tmp_idx = parseInt($(this).attr('data-original-index')) + 1,
552                            rule    = 'select option:nth-child(' + tmp_idx + ')',
553                            sel     = $(this).closest('.bootstrap-select').find(rule),
554                            gallery = that.gallery,
555                            active;
556
557                        if (gallery !== null) {
558                            active = $(sel).find('.variation.active');
559                            gallery.render(that.galleryLastIdent);
560                            gallery.activate(that.galleryIndex);
561                        } else {
562                            var $wrapper = $(sel).closest('.product-wrapper'),
563                                $img     = $('.image-box img', $wrapper);
564                            if ($img.length === 1) {
565                                $img.attr('src', $img.data('src'));
566                            }
567                        }
568                    });
569            }
570
571            $('.variations.simple-variations .variation', $wrapper)
572                .on('click', function () {
573                    imgSwitch(this, false);
574                });
575
576            if (!isTouchCapable() || ResponsiveBootstrapToolkit.current() !== 'xs') {
577                $('.variations .variation', $wrapper)
578                    .on('hover', function () {
579                        imgSwitch(this);
580                    }, function () {
581                        var sel     = $(this).closest('.variation'),
582                            gallery = that.gallery;
583
584                        if (gallery !== null) {
585                            gallery.render(that.galleryLastIdent);
586                            gallery.activate(that.galleryIndex);
587                        } else {
588                            var $wrapper = $(sel).closest('.product-wrapper'),
589                                $img     = $('.image-box img', $wrapper);
590                            if ($img.length === 1) {
591                                $img.attr('src', $img.data('src'));
592                            }
593                        }
594                    });
595            }
596        },
597
598        registerImageHover: function($wrapper) {
599            $('.product-list .productbox-hover:not(.focus), .product-list .productbox-show-variations:not(.focus)',
600                $wrapper).on('mouseenter focusin', function () {
601                $(this).closest('.product-list').find('.focus').removeClass('focus');
602            });
603            $('.productbox-show-variations', $wrapper).on('mouseenter focusin', function () {
604                let collapse = $(this).find('.productbox-variations>.collapse')
605
606                $(this).addClass('focus');
607                $(collapse).collapse('show');
608            });
609            $('.productlist-page-nav', $wrapper).on('focusin', function () {
610                $('.productbox-show-variations.focus', $wrapper).removeClass('focus');
611            });
612            $('.productbox-show-variations', $wrapper).on('mouseleave', function () {
613                let collapse = $(this).not('.focus').find('.productbox-variations>.collapse')
614                let box = $(this)
615
616                box.removeClass('focus');
617                if (collapse.length === 1) {
618                    box.addClass('transition');
619                    $(collapse).collapse('hide');
620                    $(collapse).on('hidden.bs.collapse', function () {
621                        box.removeClass('transition');
622                    });
623                }
624            });
625            $('.is-mobile .show-variation-collapse a').on('click', function (e) {
626                if (!$(this).closest('.show-variation-collapse').find('.collapse').hasClass('show')) {
627                    e.preventDefault();
628                }
629            });
630        },
631
632        registerFinish: function($wrapper) {
633            $('#jump-to-votes-tab', $wrapper).on('click', function () {
634                let $tabID = $('#content a[href="#tab-votes"]');
635                if ($tabID.length > 0) {
636                    $tabID.tab('show');
637                } else {
638                    $tabID = $('#tab-votes');
639                    $tabID.collapse('show');
640                }
641
642                $([document.documentElement, document.body]).animate({
643                    scrollTop: $tabID.offset().top
644                }, 200);
645            });
646
647            let $tabID = $('#product-tabs a[href="' + window.location.hash + '"]');
648            if ($tabID.length) {
649                $tabID.tab('show');
650                $([document.documentElement, document.body]).animate({
651       
651             scrollTop: $tabID.offset().top
652                }, 200);
653            }
654
655            if (this.isSingleArticle()) {
656                if ($('.switch-variations .form-group', $wrapper).length === 1) {
657                    var wrapper = '#' + $($wrapper).attr('id');
658                    this.variationSwitch($('.switch-variations', $wrapper), false, wrapper);
659                }
660            }
661            else {
662                var that = this;
663
664                $('.product-cell.hover-enabled')
665                    .on('click', function (event) {
666                        if (isTouchCapable() && ResponsiveBootstrapToolkit.current() !== 'xs') {
667                            var $this = $(this);
668
669                            if (!$this.hasClass('active')) {
670                                event.preventDefault();
671                                event.stopPropagation();
672                                $('.product-cell').removeClass('active');
673                                $this.addClass('active');
674                            }
675                        }
676                    })
677                    .on('mouseenter', function (event) {
678                        var $this = $(this),
679                            wrapper = '#' + $this.attr('id');
680
681                        if (!$this.data('varLoaded') && $('.switch-variations .form-group', $this).length === 1) {
682                            that.variationSwitch($('.switch-variations', $this), false, wrapper);
683                        }
684                        $this.data('varLoaded', true);
685                    });
686            }
687
688            this.registerProductActions($('#sidepanel_left'));
689            this.registerProductActions($('#footer'));
690            this.registerProductActions($('#shop-nav'));
691            this.registerProductActions($wrapper);
692            this.registerProductActions('#cart-form');
693        },
694
695        registerProductActions: function($wrapper) {
696            var that = this;
697
698            $('*[data-toggle="product-actions"] button', $wrapper)
699                .on('click', function(event) {
700                    var data = $(this.form).serializeObject();
701
702                    if ($wrapper === '#cart-form') {
703                        data.wlPos = $(this).data('wl-pos');
704                        data.a = $(this).data('product-id-wl');
705                    }
706
707                    if (that.handleProductAction(this, data)) {
708                        event.preventDefault();
709                    }
710                });
711            $('a[data-toggle="product-actions"]', $wrapper)
712                .on('click', function(event) {
713                    var data  = $(this).data('value');
714                    this.name = $(this).data('name');
715
716                    if (that.handleProductAction(this, data)) {
717                        event.preventDefault();
718                    }
719                });
720        },
721
722        loadModalArticle: function(url, wrapper, done, fail) {
723            var that       = this,
724                id         = wrapper.slice(1),
725                $modalBody = $('.modal-body', this.modalView);
726
727            $.ajax(url, {data: {'isAjax':1, 'quickView':1}})
728                .done(function(data) {
729                    var $html      = $('<div />').html(data);
730                    var $headerCSS = $html.find('link[type="text/css"]');
731                    var $headerJS  = $html.find('script[src][src!=""]');
732                    var content    = $html.find(that.options.modal.wrapper).html();
733
734                    $headerCSS.each(function (pos, item) {
735                        var $cssLink = $('head link[href="' + item.href + '"]');
736                        if ($cssLink.length === 0) {
737                            $('head').append('<link rel="stylesheet" type="text/css" href="' + item.href + '" >');
738                        }
739                    });
740
741                    $headerJS.each(function (pos, item) {
742                        if (typeof item.src !== 'undefined' && item.src.length > 0) {
743                            var $jsLink = $('head script[src="' + item.src + '"]');
744                            if ($jsLink.length === 0) {
745                                $('head').append('<script defer src="' + item.src + '" >');
746                            }
747                        }
748                    });
749
750                    $modalBody.html($('<div id="' + id + '" />').html(content));
751
752                    var $modal  = $modalBody.closest(".modal-dialog"),
753                        title   = $modal.find('.modal-body h1'),
754                        $config = $('#product-configurator', $modalBody);
755
756                    if ($config.length > 0) {
757                        // Configurator in child article!? Currently not supported!
758                        $config.remove();
759                        $.evo.extended().startSpinner($modalBody);
760                        location.href = url;
761                    }
762                    if (title.length > 0 && title.text().length > 0) {
763                        $modal.find('.modal-title').text(title.text());
764                        title.remove();
765                    }
766
767                    $('form', $modalBody).on('submit', function(event) {
768                        event.preventDefault();
769
770                        var $form = $(this);
771                        var data  = $form.serializeObject();
772                        if (data['VariKindArtikel']) {
773                            data['a'] = data['VariKindArtikel'];
774                        }
775
776                        $.evo.basket().addToBasket($form, data);
777                        that.modalView.modal('hide');
778                    });
779
780                    if (typeof done === 'function') {
781                        done();
782                    }
783                })
784                .fail(function() {
785                    if (typeof fail === 'function') {
786                        fail();
787                    }
788                })
789                .always(function() {
790                    $.evo.extended().stopSpinner();
791                });
792        },
793
794        addToComparelist: function(data, $action) {
795            var productId = parseInt(data[this.options.input.id]);
796            var childId = parseInt(data[this.options.input.childId]);
797            if (childId > 0) {
798                productId = childId;
799            }
800            if (productId > 0) {
801                var that = this;
802                $.evo.io().call('pushToComparelist', [productId], that, function(error, data) {
803                    if (error) {
804                        return;
805                    }
806
807                    var response = data.response;
808
809                    if (response) {
810                        switch (response.nType) {
811                            case 0: // error
812                                var errorlist = '<ul><li>' + response.cHints.join('</li><li>') + '</li></ul>';
813                                eModal.alert({
814                                    title: response.cTitle,
815                                    message: errorlist,
816                                    keyboard: true,
817                                    tabindex: -1,
818                                    buttons: false
819                                });
820                                break;
821                            case 1: // forwarding
822                                window.location.href = response.cLocation;
823                                break;
824                            case 2: // added to comparelist
825                                that.updateComparelist(response);
826                                break;
827                        }
828                    }
829                });
830
831                return true;
832            }
833
834            return false;
835        },
836
837        removeFromCompareList: function(data) {
838            var productId = parseInt(data[this.options.input.id]);
839            if (productId > 0) {
840                var that = this;
841                $.evo.io().call('removeFromComparelist', [productId], that, function(error, data) {
842                    if (error) {
843                        return;
844                    }
845
846                    var response = data.response;
847
848                    if (response) {
849                        switch (response.nType) {
850                            case 0: // error
851                                var errorlist = '<ul><li>' + response.cHints.join('</li><li>') + '</li></ul>';
852                                eModal.alert({
853                                    title: response.cTitle,
854                                    message: errorlist,
855                                    keyboard: true,
856                                    tabindex: -1,
857                                    buttons: false
858                                });
859                                break;
860                            case 1: // forwarding
861                                window.location.href = response.cLocation;
862                                break;
863                            case 2: // removed from comparelist
864                                that.updateComparelist(response);
865                                break;
866                        }
867                    }
868                });
869
870                return true;
871            }
872
873            return false;
874        },
875
876        updateComparelist: function(data) {
877            var $badgeUpd = $(this.options.selector.navUpdateCompare);
878
879            var badge = $(data.navDropdown);
880            $badgeUpd.html(badge);
881            $(this.options.selector.navBadgeUpdateCompare).html(data.nCount);
882
883            if (data.nCount > 0) {
884                $(this.options.selector.navCompare).removeClass('d-none');
885            } else {
886                $(this.options.selector.navCompare).addClass('d-none');
887                $('#nav-comparelist-collapse').removeClass('show');
888            }
889            if (data.nCount > 1) {
890                $('#nav-comparelist-goto').removeClass('d-none');
891            } else {
892                $('#nav-comparelist-goto').addClass('d-none');
893            }
894            this.registerProductActions($('#shop-nav'));
895
896            if (data.productID) {
897                let $action = $('button[data-product-id-cl="' + data.productID + '"]')
898                $action.removeClass("on-list");
899                $action.next().removeClass("press");
900                $('.comparelist [data-product-id-cl="' + data.productID + '"]').remove();
901            }
902
903            for (var ind in data.cBoxContainer) {
904                var $list = $(this.options.selector.boxContainer+ind);
905
906                if ($list.length > 0) {
907                    if (data.cBoxContainer[ind].length) {
908                        var $boxContent = $(data.cBoxContainer[ind]);
909                        this.registerProductActions($boxContent);
910                        $list.replaceWith($boxContent).removeClass('d-none');
911                    } else {
912                        $list.html('').addClass('d-none');
913                    }
914                }
915            }
916        },
917
918        addToWishlist: function(data, $action) {
919            let productId = parseInt(data[this.options.input.id]),
920                childId = parseInt(data[this.options.input.childId]),
921                qty =  parseFloat(data[this.options.input.quantity]);
922            if (childId > 0) {
923                productId = childId;
924            }
925            if (isNaN(qty)) {
926                qty = 1;
927            }
928            if (productId > 0) {
929                var that = this;
930                $.evo.io().call('pushToWishlist', [productId, qty, data], that, function(error, data) {
931                    if (error) {
932                        $action.closest('form')[0].reportValidity();
933                        return;
934                    }
935                    if ($action.hasClass('action-tip-animation-b')) {
936                        $action.addClass("on-list");
937                        $action.next().addClass("press");
938                        $action.next().next().removeClass("press");
939                    }
940                    var response = data.response;
941
942                    if (response) {
943                        switch (response.nType) {
944                            case 0: // error
945                                var errorlist = '<ul><li>' + response.cHints.join('</li><li>') + '</li></ul>';
946                                eModal.alert({
947                                    title: response.cTitle,
948                                    message: errorlist,
949                                    keyboard: true,
950                                    tabindex: -1,
951                                    buttons: false
952                                });
953                                break;
954                            case 1: // forwarding
955                                window.location.href = response.cLocation;
956                                break;
957                            case 2: // added to wishlist
958                                that.updateWishlist(response);
959                                break;
960                        }
961                    }
962                });
963
964                return true;
965            }
966
967            return false;
968        },
969
970        removeFromWishList: function(data) {
971            var productId = parseInt(data[this.options.input.id]);
972            if (productId > 0) {
973                var that = this;
974                $.evo.io().call('removeFromWishlist', [productId], that, function(error, data) {
975                    if (error) {
976                        return;
977                    }
978
979                    var response = data.response;
980
981                    if (response) {
982                        switch (response.nType) {
983                            case 0: // error
984                                var errorlist = '<ul><li>' + response.cHints.join('</li><li>') + '</li></ul>';
985                                eModal.alert({
986                                    title: response.cTitle,
987                                    message: errorlist,
988                                    keyboard: true,
989                                    tabindex: -1,
990                                    buttons: false
991                                });
992                                break;
993                            case 1: // forwarding
994                                window.location.href = response.cLocation;
995                                break;
996                            case 2: // removed from wishlist
997                                that.updateWishlist(response);
998                                break;
999                        }
1000                    }
1001                });
1002
1003                return true;
1004            }
1005
1006            return false;
1007        },
1008
1009        updateWishlist: function(data) {
1010            var $navContainerWish = $(this.options.selector.navContainerWish);
1011            var $navBadgeWish = $(this.options.selector.navBadgeWish);
1012
1013            if (data.wlPosRemove) {
1014                let $action = $('button[data-wl-pos="' + data.wlPosRemove + '"]');
1015                $action.removeClass("on-list");
1016                $action.next().removeClass("press");
1017                $action.find('.wishlist-icon').addClass('far').removeClass('fas');
1018            }
1019            if (data.wlPosAdd) {
1020                let $action = $('button[data-product-id-wl="' + data.productID + '"]');
1021                $action.attr('data-wl-pos', data.wlPosAdd);
1022                $action.data('wl-pos', data.wlPosAdd);
1023                $action.closest('form').find('input[name="wlPos"]').val(data.wlPosAdd)
1024                $action.find('.wishlist-icon').addClass('fas').removeClass('far');
1025            }
1026            $.evo.io().call('updateWishlistDropdown', [$navContainerWish, $navBadgeWish], this, function(error, data) {
1027                if (error) {
1028                    return;
1029                }
1030                if (data.response.currentPosCount > 0) {
1031                    $navBadgeWish.removeClass('d-none');
1032                } else {
1033                    $navBadgeWish.addClass('d-none');
1034                }
1035                $navContainerWish.html(data.response.content);
1036                $navBadgeWish.html(data.response.currentPosCount);
1037                setClickableRow();
1038            });
1039
1040            for (var ind in data.cBoxContainer) {
1041                var $list = $(this.options.selector.boxContainerWish+ind);
1042                if ($list.length > 0) {
1043                    if (data.cBoxContainer[ind].length) {
1044                        var $boxContent = $(data.cBoxContainer[ind]);
1045                        this.registerProductActions($boxContent);
1046                        $list.replaceWith($boxContent).removeClass('d-none');
1047                    } else {
1048                        $list.html('').addClass('d-none');
1049                    }
1050                }
1051            }
1052        },
1053
1054        handleProductAction: function(action, data) {
1055            let $action = $(action);
1056            switch (action.name) {
1057                case this.options.action.compareList:
1058                    if ($action.hasClass('action-tip-animation-b')) {
1059                        if ($action.hasClass('on-list')) {
1060                            $action.removeClass("on-list");
1061                            $action.next().removeClass("press");
1062                            $action.next().next().addClass("press");
1063                            return this.removeFromCompareList(data);
1064                        } else {
1065                            $action.addClass("on-list");
1066                            $action.next().addClass("press");
1067                            $action.next().next().removeClass("press");
1068                            $(this.options.selector.navCompare).removeClass('d-none');
1069
1070                            let $moveTo = isMobileByBodyClass()
1071                                ? $('.wish-compare-animation-mobile #burger-menu')
1072                                : $('.wish-compare-animation-desktop #shop-nav-compare');
1073                            $.evo.article().moveItemAnimation($action, $moveTo);
1074
1075                            return this.addToComparelist(data, $action);
1076                        }
1077                    } else {
1078                        return this.addToComparelist(data, $action);
1079                    }
1080                case this.options.action.compareListRemove:
1081                    return this.removeFromCompareList(data);
1082                case this.options.action.wishList:
1083                    data[this.options.input.quantity] = $('input[data-product-id="' + data.a + '"]').val()
1084                        || $('#buy_form_'+data.a+' '+this.options.selector.quantity).val()
1085                        || $('#quantity').val();
1086                    if ($action.hasClass('on-list')) {
1087                        $action.removeClass("on-list");
1088                        $action.next().removeClass("press");
1089                        $action.next().next().addClass("press");
1090                        data.a = data.wlPos;
1091                        return this.removeFromWishList(data);
1092                    } else {
1093                        $action.addClass("on-list");
1094                        let $moveTo = isMobileByBodyClass()
1095                            ? $('.wish-compare-animation-mobile #burger-menu')
1096                            : $('.wish-compare-animation-desktop #shop-nav-wish');
1097                        $.evo.article().moveItemAnimation($action, $moveTo);
1098                        return this.addToWishlist(data, $action);
1099                    }
1100                case this.options.action.wishListRemove:
1101                    return this.removeFromWishList(data);
1102            }
1103
1104            return false;
1105        },
1106
1107        configurator: function(init) {
1108            if (this.isSingleArticle()) {
1109                var that      = this,
1110                    container = $('#cfg-container'),
1111                    sidebar   = $('#cfg-sticky-sidebar'),
1112                    width,
1113                    form;
1114
1115                if (container.length === 0) {
1116                    return;
1117                }
1118
1119                $.evo.extended().startSpinner(container);
1120
1121                $('#buy_form').find('*[data-selected="true"]')
1122                    .attr('checked', true)
1123                    .attr('selected', true)
1124                    .attr('data-selected', null);
1125
1126                form = $.evo.io().getFormValues('buy_form');
1127
1128                $.evo.io().call('buildConfiguration', [form], that, function (error, data) {
1129                    var result,
1130                        i,
1131                        j,
1132                        item,
1133                        cBeschreibung,
1134                        quantityWrapper,
1135                        grp,
1136                        value,
1137                        enableQuantity,
1138                        nNetto,
1139                        quantityInput;
1140                    $('.js-start-configuration').prop('disabled', !(data.response.variationsSelected && data.response.inStock));
1141                    $('.js-choose-variations-wrapper').toggleClass('d-none', data.response.variationsSelected);
1142                    $('.js-cfg-group').each(function (i, item) {
1143                        let iconChecked     = $('a[href="#'+this.id+'"] .js-group-checked'),
1144                            badgeInfoDanger = 'alert-info';
1145                        if (data.response.invalidGroups && data.response.invalidGroups.includes($(this).data('id'))) {
1146                            iconChecked.addClass('d-none');
1147                            if ($(this).hasClass('visited')) {
1148                                badgeInfoDanger = 'alert-danger';
1149                            }
1150                            $(this).find('.js-group-badge-checked')
1151                                .removeClass('alert-success alert-info')
1152                                .addClass(badgeInfoDanger);
1153                        } else {
1154                            iconChecked.removeClass('d-none');
1155                            $(this).find('.js-group-badge-checked')
1156                                .addClass('alert-success')
1157                                .removeClass('alert-danger alert-info');
1158                        }
1159                    });
1160                    $('.js-cfg-group-error').addClass('d-none').html('');
1161                    $.each(data.response.errorMessages, function (i, item) {
1162                        $('.js-cfg-group-error[data-id="' + item.group + '"]').removeClass('d-none').html(item.message);
1163                    });
1164                    let $summaryFinish = $('#cfg-tab-summary-finish');
1165                    if (data.response.valid) {
1166                        $('.js-cfg-validate').prop('disabled', false);
1167                        $summaryFinish.children().removeClass('disabled');
1168                        $summaryFinish.removeClass('disabled');
1169                    } else {
1170                        $('.js-cfg-validate').prop('disabled', true);
1171                        $summaryFinish.children().addClass('disabled');
1172                        $summaryFinish.addClass('disabled');
1173                    }
1174                    $.evo.extended().stopSpinner();
1175                    if (error) {
1176                        $.evo.error(data);
1177                        return;
1178                    }
1179                    result = data.response;
1180
1181                    if (!result.oKonfig_arr) {
1182                        $.evo.error('Missing configuration groups');
1183                        return;
1184                    }
1185
1186                    // global price
1187                    nNetto = result.nNettoPreise;
1188                    that.setPrice(result.fGesamtpreis[nNetto], result.cPreisLocalized[nNetto], result.cPreisString);
1189                    that.setStockInformation(result.cEstimatedDelivery);
1190
1191                    $('#content .summary').html(result.cTemplate);
1192                    $.evo.tooltips();
1193                    $.evo.extended()
1194                        .trigger('priceChanged', result);
1195                });
1196            }
1197        },
1198
1199        initConfigListeners: function () {
1200            $('.js-cfg-group').on('click', function () {
1201                $(this).addClass('visited');
1202            });
1203            $('#cfg-tab-summary-finish').on('click', function () {
1204                if (!$(this).hasClass('disabled')) {
1205                    $('#cfg-modal-tabs').find('.nav-link').removeClass('active');
1206                    $('#cfg-tab-summary').children().addClass('active');
1207                    $(this).children().removeClass('active');
1208                }
1209            });
1210            $('.js-cfg-group-collapse').on('click', function () {
1211                $(this).addClass('visited');
1212            });
1213        },
1214
1215        variationRefreshAll: function($wrapper) {
1216            $('.variations select', $wrapper).selectpicker('refresh');
1217            $.evo.initSliders()
1218        },
1219
1220        getConfigGroupQuantity: function (groupId) {
1221            return $('.cfg-group[data-id="' + groupId + '"] .quantity');
1222        },
1223
1224        getConfigGroupQuantityInput: function (groupId) {
1225            return $('.cfg-group[data-id="' + groupId + '"] .quantity input');
1226        },
1227
1228        getConfigGroupImage: function (groupId) {
1229            return $('.cfg-group[data-id="' + groupId + '"] .group-image img');
1230        },
1231
1232        setConfigItemImage: function (groupId, img) {
1233            $('.cfg-group[data-id="' + groupId + '"] .group-image img').attr('src', img).first();
1234        },
1235
1236        setConfigItemDescription: function (groupId, itemBeschreibung) {
1237            var groupItems                       = $('.cfg-group[data-id="' + groupId + '"] .group-items');
1238            var descriptionDropdownContent       = groupItems.find('#filter-collapsible_dropdown_' + groupId + '');
1239            var descriptionDropdownContentHidden = groupItems.find('.d-none');
1240            var descriptionCheckdioContent       = groupItems.find('div[id^="filter-collapsible_checkdio"]');
1241            var multiselect                      = groupItems.find('select').attr("multiple");
1242
1243            //  Bisher kein Content mit einer Beschreibung vorhanden, aber ein Artikel mit Beschreibung ausgewählt
1244            if (descriptionDropdownContentHidden.length > 0 && descriptionCheckdioContent.length === 0 && itemBeschreibung.length > 0 && multiselect !== "multiple") {
1245                groupItems.find('a[href="#filter-collapsible_dropdown_' + groupId + '"]').removeClass('d-none');
1246                descriptionDropdownContent.replaceWith('<div id="filter-collapsible_dropdown_' + groupId + '" class="collapse top10 panel-body">' + itemBeschreibung + '</div>');
1247                //  Bisher Content mit einer Beschreibung vorhanden, aber ein Artikel ohne Beschreibung ausgewählt
1248            } else if (descriptionDropdownContentHidden.length === 0 && descriptionCheckdioContent.length === 0 && itemBeschreibung.length === 0 && multiselect !== "multiple") {
1249                groupItems.find('a[href="#filter-collapsible_dropdown_' + groupId + '"]').addClass('d-none');
1250                descriptionDropdownContent.addClass('d-none');
1251                //  Bisher Content mit einer Beschreibung vorhanden und ein Artikel mit Beschreibung ausgewählt
1252            } else if (descriptionDropdownContentHidden.length === 0 && descriptionCheckdioContent.length === 0 && itemBeschreibung.length > 0 && multiselect !== "multiple") {
1253                descriptionDropdownContent.replaceWith('<div id="filter-collapsible_dropdown_' + groupId + '" class="collapse top10 panel-body">' + itemBeschreibung + '</div>');
1254            }
1255        },
1256
1257        setPrice: function(price, fmtPrice, priceLabel, wrapper) {
1258            var $wrapper = this.getWrapper(wrapper);
1259
1260            if (this.isSingleArticle()) {
1261                $('#product-offer .price', $wrapper).html(fmtPrice);
1262                $('#product-offer meta[itemprop="price"]', $wrapper).attr('content',price);
1263                if (priceLabel.length > 0) {
1264                    $('#product-offer .price_label', $wrapper).html(priceLabel);
1265                }
1266            } else {
1267                var $price = $('.price_wrapper', $wrapper);
1268
1269                $('.price span:first-child', $price).html(fmtPrice);
1270                if (priceLabel.length > 0) {
1271                    $('.price_label', $price).html(priceLabel);
1272                }
1273            }
1274
1275            $.evo.trigger('changed.article.price', { price: price });
1276        },
1277
1278        setStockInformation: function(cEstimatedDelivery, wrapper) {
1279            var $wrapper = this.getWrapper(wrapper);
1280
1281            $('.delivery-status .estimated-delivery span', $wrapper).html(cEstimatedDelivery);
1282        },
1283
1284        setStaffelPrice: function(prices, fmtPrices, wrapper) {
1285            var $wrapper   = this.getWrapper(wrapper),
1286                $container = $('#product-offer', $wrapper);
1287
1288            $.each(fmtPrices, function(index, value){
1289                $('.bulk-price-' + index + ' .bulk-price', $container).html(value);
1290            });
1291        },
1292
1293        setVPEPrice: function(fmtVPEPrice, VPEPrices, fmtVPEPrices, wrapper) {
1294            var $wrapper   = this.getWrapper(wrapper),
1295                $container = $('#product-offer', $wrapper);
1296
1297            $('.base-price .value', $container).html(fmtVPEPrice);
1298            $.each(fmtVPEPrices, function(index, value){
1299                $('.bulk-price-' + index + ' .bulk-base-price', $container).html(value);
1300            });
1301        },
1302
1303        /**
1304         * @deprecated since 4.05 - use setArticleWeight instead
1305         */
1306        setUnitWeight: function(UnitWeight, newUnitWeight) {
1307            $('#article-tabs .product-attributes .weight-unit').html(newUnitWeight);
1308        },
1309
1310        setArticleWeight: function(ArticleWeight, wrapper) {
1311            if (this.isSingleArticle()) {
1312                var $articleTabs = $('#article-tabs');
1313
1314                if ($.isArray(ArticleWeight)) {
1315                    $('.product-attributes .weight-unit', $articleTabs).html(ArticleWeight[0][1]);
1316                    $('.product-attributes .weight-unit-article', $articleTabs).html(ArticleWeight[1][1]);
1317                } else {
1318                    $('.product-attributes .weight-unit', $articleTabs).html(ArticleWeight);
1319                }
1320            } else {
1321                var $wrapper = this.getWrapper(wrapper);
1322
1323                if ($.isArray(ArticleWeight)) {
1324                    $('.attr-weight .value', $wrapper).html(ArticleWeight[0][1]);
1325                    $('.attr-weight.weight-unit-article .value', $wrapper).html(ArticleWeight[1][1]);
1326                } else {
1327                    $('.attr-weight .value', $wrapper).html(ArticleWeight);
1328                }
1329            }
1330
1331        },
1332
1333        setProductNumber: function(productNumber, wrapper) {
1334            var $wrapper = this.getWrapper(wrapper);
1335
1336            $('#product-offer span[itemprop="sku"]', $wrapper).html(productNumber);
1337        },
1338
1339        setArticleContent: function(id, variation, url, variations, wrapper) {
1340            var $wrapper  = this.getWrapper(wrapper),
1341                listStyle = $('#product-list-type').val();
1342
1343                if (listStyle === 'undefined') {
1344                    listStyle = $('#ed_list.active').length > 0 ? 'list' : 'gallery';
1345                }
1346
1347                $.evo.extended().startSpinner($wrapper);
1348
1349            if (this.modalShown) {
1350                this.loadModalArticle(url, wrapper,
1351                    function() {
1352                        var article = new ArticleClass();
1353                        article.register(wrapper);
1354                        $.evo.extended().stopSpinner();
1355                    },
1356                    function() {
1357                        $.evo.extended().stopSpinner();
1358                        $.evo.error('Error loading ' + url);
1359                    }
1360                );
1361            } else if (this.isSingleArticle()) {
1362                if($('#opc-startmenu').length > 0) {
1363                    return this.redirectToArticle(id, variation, url, variations, wrapper);
1364                }
1365
1366                $.evo.extended().loadContent(url, () => {
1367                    $.evo.extended().register();
1368                    $.evo.article().register(wrapper);
1369
1370                    $(variations).each(function (i, item) {
1371                        $.evo.article().variationSetVal(item.key, item.value, wrapper);
1372                    });
1373
1374                    if (document.location.href !== url) {
1375                        history.pushState({a: id, a2: variation, url: url, variations: variations}, "", url);
1376                    }
1377                    $.evo.extended().stopSpinner();
1378                }, function () {
1379                    $.evo.error('Error loading ' + url);
1380                    $.evo.extended().stopSpinner();
1381                }, false, wrapper);
1382            } else {
1383                $.evo.extended().loadContent(url + (url.indexOf('?') >= 0 ? '&' : '?') + 'isListStyle=' + listStyle, function (content) {
1384                    $.evo.article().register(wrapper);
1385
1386                    $('[data-toggle="basket-add"]', $(wrapper)).on('submit', function(event) {
1387                        event.preventDefault();
1388                        event.stopPropagation();
1389
1390                        var $form = $(this);
1391                        var data  = $form.serializeObject();
1392                        data['a'] = variation;
1393
1394                        $.evo.basket().addToBasket($form, data);
1395                    });
1396
1397                    $(variations).each(function (i, item) {
1398                        $.evo.article().variationSetVal(item.key, item.value, wrapper);
1399                    });
1400
1401                    $.evo.extended().stopSpinner();
1402                }, function () {
1403                    $.evo.error('Error loading ' + url);
1404                    $.evo.extended().stopSpinner();
1405                }, false, wrapper);
1406            }
1407        },
1408
1409        redirectToArticle: function(id, variation, url, variations, wrapper) {
1410            var $wrapper  = this.getWrapper(wrapper),
1411                listStyle = $('#product-list-type').val();
1412
1413            $.evo.extended().startSpinner($wrapper);
1414
1415            window.open(url, '_top');
1416
1417            $.evo.extended().stopSpinner($wrapper);
1418        },
1419
1420        variationResetAll: function(wrapper) {
1421            var $wrapper = this.getWrapper(wrapper);
1422
1423            $('.variation[data-value] input:checked', $wrapper).prop('checked', false);
1424            $('.variations select option', $wrapper).prop('selected', false);
1425            $('.variations select', $wrapper).selectpicker('refresh');
1426        },
1427
1428        variationDisableAll: function(wrapper) {
1429            let $wrapper = this.getWrapper(wrapper);
1430
1431            $('.swatches-selected', $wrapper).text('');
1432            $('[data-value].variation', $wrapper).each(function(i, item) {
1433                $(item)
1434                    .removeClass('active loading')
1435                    .addClass('not-available');
1436                $.evo.article()
1437                    .removeStockInfo($(item));
1438            });
1439        },
1440
1441        variationSetVal: function(key, value, wrapper) {
1442            var $wrapper = this.getWrapper(wrapper);
1443
1444            $('[data-key="' + key + '"]', $wrapper).val(value);
1445        },
1446
1447        variationEnable: function(key, value, wrapper) {
1448            var $wrapper = this.getWrapper(wrapper),
1449                $item    = $('[data-value="' + value + '"].variation', $wrapper);
1450
1451            $item.removeClass('not-available swatches-sold-out swatches-not-in-stock');
1452        },
1453
1454        showGalleryVariation: function(key, wrapper) {
1455            var $wrapper = this.getWrapper(wrapper),
1456                $item    = $('.variation-' + key, $wrapper);
1457
1458            $item.collapse();
1459        },
1460
1461        variationActive: function(key, value, def, wrapper) {
1462            var $wrapper = this.getWrapper(wrapper),
1463                $item    = $('[data-value="' + value + '"].variation', $wrapper);
1464            $item.addClass('active')
1465                .find('input')
1466                .prop('checked', true)
1467                .end()
1468                .prop('selected', true);
1469
1470            $('[data-id="'+key+'"].swatches-selected')
1471                .text($item.attr('data-original'));
1472        },
1473
1474        removeStockInfo: function($item) {
1475            if (this.isSingleArticle()) {
1476                var type = $item.attr('data-type'),
1477                    elem,
1478                    label,
1479                    wrapper;
1480
1481                switch (type) {
1482                    case 'option':
1483                        label = $item.data('content');
1484                        wrapper = $('<div />').append(label);
1485                        $(wrapper)
1486                            .find('.badge-not-available')
1487                            .remove();
1488                        label = $(wrapper).html();
1489                        $item.data('content', label)
1490                            .attr('data-content', label);
1491
1492                        break;
1493                    case 'radio':
1494                        elem = $item.find('.badge-not-available');
1495                        if (elem.length === 1) {
1496                            $(elem).remove();
1497                        }
1498                        break;
1499                    case 'swatch':
1500                        $item.tooltip('dispose');
1501                        break;
1502                }
1503
1504                $item.removeAttr('data-stock');
1505            }
1506        },
1507
1508        variationInfo: function(value, status, note, notExists) {
1509            let $item = $('[data-value="' + value + '"].variation'),
1510                type = $item.attr('data-type'),
1511                text,
1512                content,
1513                $wrapper,
1514                label;
1515
1516            $item.attr('data-stock', _stock_info[status]);
1517
1518            switch (type) {
1519                case 'option':
1520                    text     = ' (' + note + ')';
1521                    content  = $item.data('content');
1522                    $wrapper = $('<div />');
1523
1524                    $wrapper.append(content);
1525                    $wrapper
1526                        .find('.badge-not-available')
1527                        .remove();
1528
1529                    label = $('<span />')
1530                        .addClass('badge badge-danger badge-not-available')
1531                        .text(' '+note);
1532
1533                    $wrapper.append(label);
1534
1535                    $item.data('content', $wrapper.html())
1536                        .attr('data-content', $wrapper.html());
1537
1538                    $item.closest('select')
1539                        .selectpicker('refresh');
1540                    break;
1541                case 'radio':
1542                    $item.find('.badge-not-available')
1543                        .remove();
1544
1545                    label = $('<span />')
1546                        .addClass('badge badge-danger badge-not-available')
1547                        .text(' '+note);
1548
1549                    $item.append(label);
1550                    break;
1551                case 'swatch':
1552                    if (!$item.hasClass('gall-preview')) {
1553                        $item.tooltip({
1554                            title: note,
1555                            trigger: 'hover',
1556                            container: 'body'
1557                        });
1558                    }
1559                    if (notExists) {
1560                        $item.addClass('swatches-not-in-stock');
1561                    } else {
1562                        $item.addClass('swatches-sold-out');
1563                    }
1564                    break;
1565            }
1566        },
1567
1568        variationSwitch: function($item, animation, wrapper) {
1569            if ($item) {
1570                var formID   = $item.closest('form').attr('id'),
1571                    $current = this.getCurrent($item),
1572                    key      = $current.data('key'),
1573                    value    = $current.data('value'),
1574                    io       = $.evo.io(),
1575                    args     = io.getFormValues(formID),
1576                    layout   = $('#product-list-type').val(),
1577                    $wrapper = this.getWrapper(wrapper);
1578
1579                if (animation) {
1580                    $.evo.extended().startSpinner();
1581                } else {
1582                    $('.updatingStockInfo', $wrapper).show();
1583                }
1584
1585                $('.tooltip.show').remove();
1586                args.wrapper = wrapper;
1587                args.layout  = layout;
1588
1589                $.evo.article()
1590                    .variationDispose(wrapper);
1591
1592                io.call('checkVarkombiDependencies', [args, key, value], $item, function (error, data) {
1593                    if (animation) {
1594                        $.evo.extended().stopSpinner();
1595                    }
1596                    $('.updatingStockInfo', $wrapper).hide();
1597                    if (error) {
1598                        $.evo.error('checkVarkombiDependencies');
1599                    }
1600                });
1601            }
1602        },
1603
1604        modalArticleDetail: function(item, wrapper)
1605        {
1606            let $item = $(item);
1607            let title = $item.data('title');
1608            let url   = $item.data('src');
1609
1610            if (typeof this.modalView === 'undefined' || this.modalView === null) {
1611                this.modalView = $(
1612                    '<div id="' + this.options.modal.id + '" class="modal fade" role="dialog" tabindex="-1" >' +
1613                    '   <div class="modal-dialog modal-lg">' +
1614                    '       <div class="modal-content">' +
1615                    '           <div class="modal-header">' +
1616                    '               <button type="button" class="x close" data-dismiss="modal"><i class="fa fa-times"></i></button>' +
1617                    '               <div class="modal-title h4">' + title + '</div>' +
1618                    '           </div>' +
1619                    '           <div class="modal-body">' +
1620                    '               <div id="' + wrapper.slice(1) + '" style="min-height:100px">' +
1621                    '<div class="jtl-spinner"><i class="fa fa-spinner fa-pulse"></i></div>' +
1622                    '               </div>' +
1623                    '           </div>' +
1624                    '       </div>' +
1625                    '   </div>' +
1626                    '</div>');
1627                this.modalView
1628                    .on('hidden.bs.modal', () => {
1629                        $('.modal-body', this.modalView)
1630                            .html('<div id="' + wrapper.slice(1) + '" style="min-height:100px" />');
1631                        $('.modal-title', this.modalView).html('');
1632                        this.modalView
1633                            .off('shown.bs.modal');
1634                        this.modalShown = false;
1635                    });
1636            } else {
1637                $('.modal-title', this.modalView).html(title);
1638                $('.modal-body', this.modalView)
1639                    .html('<div id="' + wrapper.slice(1) + '" style="min-height:100px">' +
1640                        '<div class="jtl-spinner"><i class="fa fa-spinner fa-pulse"></i></div></div>');
1641            }
1642
1643            this.modalView
1644                .on('shown.bs.modal', () => {
1645                    this.modalShown = true;
1646                    this.loadModalArticle(url, wrapper,
1647                        function() {
1648                            // no functionality needed in quickview
1649                            // var article = new ArticleClass();
1650                            // article.register(wrapper);
1651                            $.evo.extended().stopSpinner();
1652                            $('[data-toggle="popover"]', wrapper).popover({ html: true });
1653                            $('.product-image', wrapper).css('cursor', 'default');
1654                        },
1655                        function() {
1656                            $.evo.extended().stopSpinner();
1657                            $.evo.error('Error loading ' + params.url);
1658                        }
1659                    );
1660                })
1661                .modal('show');
1662        },
1663
1664        variationPrice: function($item, animation, wrapper) {
1665            var formID   = $item.closest('form').attr('id'),
1666                $wrapper = this.getWrapper(wrapper),
1667                io       = $.evo.io(),
1668                args     = io.getFormValues(formID);
1669
1670            if (animation) {
1671                $.evo.extended().startSpinner($wrapper);
1672            }
1673
1674            args.wrapper = wrapper;
1675            io.call('checkDependencies', [args], $(this), function (error, data) {
1676                let $action = $('button[data-product-id-wl="' + data.response.itemID + '"]');
1677                if (data.response.check > 0) {
1678                    $action.attr('data-wl-pos', data.response.check);
1679                    $action.data('wl-pos', data.response.check);
1680                    $action.closest('form').find('input[name="wlPos"]').val(data.response.check)
1681                    $action.addClass('on-list');
1682                } else {
1683                    $action.removeClass('on-list');
1684                }
1685
1686                if (animation) {
1687                    $.evo.extended().stopSpinner();
1688                }
1689                if (error) {
1690                    $.evo.error('checkDependencies');
1691                }
1692            });
1693        },
1694
1695        variationDispose: function(wrapper) {
1696            var $wrapper = this.getWrapper(wrapper);
1697
1698            $('[role="tooltip"]', $wrapper).remove();
1699        },
1700
1701        moveItemAnimation: function(item, moveTo) {
1702            if (!item.length || !moveTo.length || $(this).hasClass('on-list')) {
1703                return;
1704            }
1705            setTimeout(function() {
1706                let itemClone = item.clone()
1707                    .offset({
1708                        top: item.offset().top,
1709                        left: item.offset().left
1710                    }).css({
1711                        'opacity': '0.5',
1712                        'position': 'absolute',
1713                        'z-index': '10000'
1714                    })
1715                    .appendTo($('body'))
1716                    .animate({
1717                        'top': moveTo.offset().top + 5,
1718                        'left': moveTo.offset().left + 5,
1719                    }, 700);
1720
1721                itemClone.animate({
1722                    'width': 0,
1723                    'height': 0
1724                }, function () {
1725                    $(this).detach()
1726                });
1727            }, 0);
1728        },
1729
1730        initAbnahmeIntervallError: function() {
1731            let $intervallNotice = $('#intervall-notice');
1732            if ($intervallNotice.length > 0) {
1733                $('#quantity').on('change', function () {
1734                    let $step   = $(this).attr('step'),
1735                        diff    = Math.abs(($(this).val() % $step) - $step),
1736                        epsilon = 0.00000001;
1737                    if (diff < epsilon || diff + epsilon > $step) {
1738                        $('#intervall-notice-danger').remove();
1739                    } else {
1740                        $('#quantity-grp').after('<div id="intervall-notice-danger" class="alert alert-danger mt-2">'
1741                            + $intervallNotice.html() + '</div>');
1742                    }
1743                });
1744            }
1745        }
1746    };
1747
1748    $v = new ArticleClass();
1749
1750    $(document).ready(function () {
1751        $v.onLoad();
1752        $v.register();
1753    });
1754
1755    $(window).on('resize',
1756        viewport.changed(function(){
1757            $v.configurator();
1758        })
1759    );
1760
1761    // PLUGIN DEFINITION
1762    // =================
1763    $.evo.article = function () {
1764       return $v;
1765    };
1766})(jQuery, document, window, ResponsiveBootstrapToolkit);

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.