PageSourceSearch

https://wholesale.alspopcorn.com/skin/frontend/base/default/js/designcenter-app.js?v=7.3.173-0

js alspopcorn.com collected 2026-10-08 03:43:22 UTC 45,195 bytes, 1,144 lines download raw bytes

1/**
2 * Magento
3 *
4 * NOTICE OF LICENSE
5 *
6 * This source file is subject to the Academic Free License (AFL 3.0)
7 * that is bundled with this package in the file LICENSE_AFL.txt.
8 * It is also available through the world-wide-web at this URL:
9 * http://opensource.org/licenses/afl-3.0.php
10 * If you did not receive a copy of the license and are unable to
11 * obtain it through the world-wide-web, please send an email
12 * to [email protected] so we can send you a copy immediately.
13 *
14 * DISCLAIMER
15 *
16 * Do not edit or add to this file if you wish to upgrade Magento to newer
17 * versions in the future. If you wish to customize Magento for your
18 * needs please refer to http://www.magentocommerce.com for more information.
19 *
20 * @category    design
21 * @package     default_default
22 * @copyright   Copyright (c) 2014 Magento Inc. (http://www.magentocommerce.com)
23 * @license     http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
24 */
25
26// TODO refactor this to remove useless code and reduce dependencies
27window.Zoey.require(['Varien', 'jQuery', 'enquire', 'imagesLoaded', 'pix.core', 'pix.globalEvents', 'pix.media'], function(Varien, jQuery, enquire, imagesLoaded, pixCore, globalEvents, media) {
28
29    // =============================================
30    // Primary Break Points
31    // =============================================
32
33    // These should be used with the bp (max-width, xx) mixin
34    // where a min-width is used, remember to +1 to break correctly
35    // If these are changed, they must also be updated in _var.scss
36
37    var bp = {
38        xsmall: 479,
39        small: 599,
40        medium: 770,
41        large: 979,
42        xlarge: 1199
43    }
44
45    // ==============================================
46    // jQuery Init
47    // ==============================================
48
49    // Avoid PrototypeJS conflicts, assign jQuery to $j instead of $
50    var $j = jQuery.noConflict();
51
52    // Use $j(document).ready() because Magento executes Prototype inline
53    $j(function () {
54
55        // ==============================================
56        // Shared Vars
57        // ==============================================
58
59        // Document
60        var w = $j(window);
61        var d = $j(document);
62        var body = $j('body');
63
64        // =============================================
65        // Skip Links
66        // =============================================
67
68        // var skipContents = $j('.skip-content');
69        // var skipLinks = $j('.skip-link');
70
71        // skipLinks.on('click', function (e) {
72        //     e.preventDefault();
73
74        //     var self = $j(this);
75        //     var target = self.attr('href');
76
77        //     // Get target element
78        //     var elem = $j(target);
79
80        //     // Check if stub is open
81        //     var isSkipContentOpen = elem.hasClass('skip-active') ? 1 : 0;
82
83        //     // Hide all stubs
84        //     skipLinks.removeClass('skip-active');
85        //     skipContents.removeClass('skip-active');
86
87        //     // Toggle stubs
88        //     if (isSkipContentOpen) {
89        //         self.removeClass('skip-active');
90        //     } else {
91        //         self.addClass('skip-active');
92        //         elem.addClass('skip-active');
93        //     }
94        // });
95
96        // $j('#header-cart').on('click', '.skip-link-close', function(e) {
97        //     var parent = $j(this).parents('.skip-content');
98        //     var link = parent.siblings('.skip-link');
99
100        //     parent.removeClass('skip-active');
101        //     link.removeClass('skip-active');
102
103        //     e.preventDefault();
104        // });
105
106
107        // ==============================================
108        // Header Menus
109        // ==============================================
110
111        var nav = $j('#nav');
112
113        // ----------------------------------------------
114        // Top Menus
115
116        var MenuManagerState = {
117            TOUCH_SCROLL_THRESHOLD: 20,
118
119            touchStartPosition: null,
120
121            shouldCancelTouch: function() {
122                if(!this.touchStartPosition) {
123                    return false;
124                }
125
126                var scroll = $j(window).scrollTop() - this.touchStartPosition;
127                return Math.abs(scroll) > this.TOUCH_SCROLL_THRESHOLD;
128            }
129        };
130
131        var pointerEvent = 'touchend';
132        // If device has implemented touch/click agnostic event, use it instead of "click"
133        if (window.navigator.pointerEnabled) {
134            pointerEvent = 'pointerdown';
135        } else if (window.navigator.msPointerEnabled) {
136            pointerEvent = 'MSPointerDown';
137        }
138        nav.find('a.has-children.level0').on(pointerEvent,function (event) {
139            //scroll occurred, cancel event
140            if(MenuManagerState.shouldCancelTouch()) {
141                return;
142            }
143
144            // If mouse is being used on large viewport, use native hover state
145            if (
146                (window.navigator.msPointerEnabled || window.navigator.pointerEnabled)
147                && event.originalEvent.pointerType == 'mouse'
148                && window.matchMedia("screen and (min-width:" + (bp.medium + 1) + "px)")
149            ) {
150                $j(this).data('has-touch', false);
151                return;
152            }
153            $j(this).data('has-touch', true);
154            var elem = $j(this).parent();
155
156            var alreadyExpanded = elem.hasClass('menu-active');
157
158            nav.find('li.level0').removeClass('menu-active');
159
160            // Collapse all active sub-menus
161            nav.find('.sub-menu-active').removeClass('sub-menu-active');
162
163            if (!alreadyExpanded) {
164                elem.addClass('menu-active');
165            }
166
167            event.preventDefault();
168        }).on('click', function (event) {
169            var elem = $j(this);
170            if (elem.data('has-touch')) {
171                elem.data('has-touch', false);
172                event.preventDefault();
173                return;
174            }
175
176            if(window.matchMedia("screen and (max-width:" + bp.medium + "px)")) {
177                var elem = $j(this).parent();
178
179                var alreadyExpanded = elem.hasClass('menu-active');
180
181                nav.find('li.level0').removeClass('menu-active');
182
183                // Collapse all active sub-menus
184                nav.find('.sub-menu-active').removeClass('sub-menu-active');
185
186                if (!alreadyExpanded) {
187                    elem.addClass('menu-active');
188                     event.preventDefault();
189                } else {
190                    return true;
191                }
192
193
194            }
195        }).on('touchstart', function(event) {
196            $j(this).data('has-touch');
197            MenuManagerState.touchStartPosition = $j(window).scrollTop();
198        });
199
200        // ----------------------------------------------
201        // Sub Menus
202
203        nav.find('li.level1 a.has-children').on('click', function (e) {
204            //e.preventDefault();
205            e.stopPropagation();
206
207            var elem = $j(this).parent();
208
209            // Check if sub-menu is open
210            var isSubMenuActive = elem.hasClass('sub-menu-active') ? 1 : 0;
211
212            // On smaller screens, allow multiple sibling sub-menus to show at once,
213            // but this is a large touch device, avoid multiple sub-menus showing at once.
214            if (window.matchMedia("screen and (min-width:" + (bp.medium + 1) + "px)")) {
215                elem
216                    .siblings('.sub-menu-active')
217                    .removeClass('sub-menu-active')
218                    .find('.sub-menu-active')
219                    .removeClass('sub-menu-active');
220            }
221            if (isSubMenuActive) {
222                elem.removeClass('sub-menu-active');
223            } else {
224                elem.addClass('sub-menu-active');
225            }
226        });
227
228        // Prevent sub menus from spilling out of the window.
229        function preventMenuSpill() {
230            var windowWidth = $j(window).width();
231            $j('ul.level0').each(function(){
232                var ul = $j(this);
233                //Show it long enough to get info, then hide it.
234                ul.addClass('position-test');
235                ul.removeClass('spill');
236                var width = ul.outerWidth();
237                var offset = ul.offset().left;
238                ul.removeClass('position-test');
239                //Add the spill class if it will spill off the page.
240                if ((offset + width) > windowWidth) {
241                    ul.addClass('spill');
242                }
243            });
244        }
245        preventMenuSpill();
246        $j(window).on('delayed-resize', preventMenuSpill);
247
248        // ==============================================
249        // Enquire JS
250        // ==============================================
251
252        enquire.register('screen and (min-width: ' + (bp.medium + 1) + 'px)', {
253            match: function () {
254                $j('.menu-active').removeClass('menu-active');
255                $j('.sub-menu-active').removeClass('sub-menu-active');
256                $j('.skip-active').removeClass('skip-active');
257            },
258            unmatch: function () {
259                $j('.menu-active').removeClass('menu-active');
260                $j('.sub-menu-active').removeClass('sub-menu-active');
261                $j('.skip-active').removeClass('skip-active');
262            }
263        });
264
265        // ==============================================
266        // UI Pattern - Media Switcher
267        // ==============================================
268
269        // Used to swap primary product photo from thumbnails.
270
271        var mediaListLinks = $j('.media-list').find('a');
272        var mediaPrimaryImage = $j('.primary-image').find('img');
273
274        if (mediaListLinks.length) {
275            mediaListLinks.on('click', function (e) {
276                e.preventDefault();
277
278                var self = $j(this);
279
280                mediaPrimaryImage.attr('src', self.attr('href'));
281            });
282        }
283
284        // ==============================================
285        // UI Pattern - ToggleSingle
286        // ==============================================
287
288        // Use this plugin to toggle the visibility of content based on a toggle link/element.
289        // This pattern differs from the accordion functionality in the Toggle pattern in that each toggle group acts
290        // independently of the others. It is named so as not to be confused with the Toggle pattern below
291        //
292        // This plugin requires a specific markup structure. The plugin expects a set of elements that it
293        // will use as the toggle link. It then hides all immediately following siblings and toggles the sibling's
294        // visibility when the toggle link is clicked.
295        //
296        // Example markup:
297        // <div class="block">
298        //     <div class="block-title">Trigger</div>
299        //     <div class="block-content">Content that should show when </div>
300        // </div>
301        //
302        // JS: jQuery('.block-title').toggleSingle();
303        //
304        // Options:
305        //     destruct: defaults to false, but if true, the plugin will remove itself, display content, and remove event handlers
306
307
308        jQuery.fn.toggleSingle = function (options) {
309
310            // passing destruct: true allows
311            var settings = $j.extend({
312                destruct: false
313            }, options);
314
315            return this.each(function () {
316                if (!settings.destruct) {
317                    $j(this).on('click', function () {
318                        $j(this)
319                            .toggleClass('active')
320                            .next()
321                            .toggleClass('no-display');
322                    });
323                    // Hide the content
324                    $j(this).next().addClass('no-display');
325                } else {
326                    // Remove event handler so that the toggle link can no longer be used
327                    $j(this).off('click');
328                    // Remove all classes that were added by this plugin
329                    $j(this)
330                        .removeClass('active')
331                        .next()
332                        .removeClass('no-display');
333                }
334
335            });
336        };
337
338        var toggleElements = {
339            _items: null,
340            init: function(){
341                this._items = $j(
342                    // This selects the menu on the My Account and CMS pages
343                    '.col-left-first .block:not(.block-layered-nav) .block-title, ' +
344                    '.col-left-first .block-layered-nav .block-subtitle--filter, ' +
345                    '.sidebar:not(.col-left-first) .block .block-title'
346                );
347                return this;
348            },
349            get: function(){
350                return this._items;
351            }
352        };
353
354        // ==============================================
355        // UI Pattern - Toggle Content (tabs and accordions in one setup)
356        // ==============================================
357
358        var initToggleContent = function(root){
359            root = root || document;
360            $j(root).find('.toggle-content').each(function () {
361                var wrapper = jQuery(this);
362
363                var hasTabs = wrapper.hasClass('tabs');
364                var hasAccordion = wrapper.hasClass('accordion');
365                var startOpen = wrapper.hasClass('open');
366
367                var dl = wrapper.children('dl').first();
368                var dts = dl.children('dt');
369                var panes = dl.children('dd');
370                var groups = [dts, panes];
371
372                //Create a ul for tabs if necessary.
373                if (hasTabs) {
374                    var ul = jQuery('<ul class="toggle-tabs"></ul>');
375                    dts.each(function () {
376                        var dt = jQuery(this);
377                        var li = jQuery('<li></li>');
378                        li.html(dt.html());
379                        ul.append(li);
380                    });
381                    ul.insertBefore(dl);
382                    var lis = ul.children();
383                    groups.push(lis);
384                }
385
386                //Add "last" classes.
387                var i;
388                for (i = 0; i < groups.length; i++) {
389                    groups[i].last().addClass('last');
390                }
391
392                function toggleClasses(clickedItem, group) {
393                    var index = group.index(clickedItem);
394                    var i;
395                    for (i = 0; i < groups.length; i++) {
396                        groups[i].removeClass('current');
397                        groups[i].eq(index).addClass('current');
398                    }
399                }
400
401                //Toggle on tab (dt) click.
402                dts.on('click', function (e) {
403                    //They clicked the current dt to close it. Restore the wrapper to unclicked state.
404                    if (jQuery(this).hasClass('current') && wrapper.hasClass('accordion-open')) {
405                        wrapper.removeClass('accordion-open');
406                    } else {
407                        //They're clicking something new. Reflect the explicit user interaction.
408                        wrapper.addClass('accordion-open');
409                    }
410                    toggleClasses(jQuery(this), dts);
411                });
412
413                //Toggle on tab (li) click.
414                if (hasTabs) {
415                    lis.on('click', function (e) {
416                        toggleClasses(jQuery(this), lis);
417                    });
418                    //Open the first tab.
419                    lis.eq(0).trigger('click');
420                }
421
422                //Open the first accordion if desired.
423                if (startOpen) {
424                    dts.eq(0).trigger('click');
425                }
426
427            });
428        };
429        initToggleContent();
430        w.on('ajaxFilterComplete', function(){
431            var filters = d.find('.block-layered-nav-filters');
432
433            //toggleSingle for only mobile
434            if(enquire.queries['(max-width: 770px)'].matches())
435                filters.find('.block-subtitle--filter').toggleSingle();
436
437            //update toggleElements
438            toggleElements.init();
439
440            //init toggleContent
441            initToggleContent(filters);
442        });
443
444
445        // ==============================================
446        // Layered Navigation Block
447        // ==============================================
448
449        // On product list pages, we want to show the layered nav/category menu immediately above the product list.
450        // While it would make more sense to just move the .block-layered-nav block rather than .col-left-first
451        // (since other blocks can be inserted into left_first), it creates simpler code to move the entire
452        // .col-left-first block, so that is the approach we're taking
453        if ($j('.col-left-first > .block').length && $j('.category-products').length) {
454            enquire.register('screen and (max-width: ' + bp.medium + 'px)', {
455                match: function () {
456                    $j('.col-left-first').insertBefore($j('.category-products'))
457                },
458                unmatch: function () {
459                    // Move layered nav back to left column
460                    $j('.col-left-first').insertBefore($j('.col-main'))
461                }
462            });
463        }
464
465        // ==============================================
466        // 3 column layout
467        // ==============================================
468
469        // On viewports smaller than 1000px, move the right column into the left column
470        if ($j('.main-container.col3-layout').length > 0) {
471            enquire.register('screen and (max-width: 1000px)', {
472                match: function () {
473                    var rightColumn = $j('.col-right');
474                    var colWrapper = $j('.col-wrapper');
475
476                    rightColumn.appendTo(colWrapper);
477                },
478                unmatch: function () {
479                    var rightColumn = $j('.col-right');
480                    var main = $j('.main');
481
482                    rightColumn.appendTo(main);
483                }
484            });
485        }
486
487
488        // ==============================================
489        // Block collapsing (on smaller viewports)
490        // ==============================================
491
492        enquire.register('(max-width: ' + bp.medium + 'px)', {
493            setup: function () {
494                toggleElements.init();
495            },
496            match: function () {
497                toggleElements.get().toggleSingle();
498            },
499            unmatch: function () {
500                toggleElements.get().toggleSingle({destruct: true});
501            }
502        });
503
504
505        // NOTE: this doesn't do anything. #checkout-progress-wrapper doesn't exist on checkout-onepage-index. remove if not being used
506        // ==============================================
507        // OPC - Progress Block
508        // ==============================================
509
510        if ($j('body.checkout-onepage-index').length) {
511            enquire.register('(max-width: ' + bp.large + 'px)', {
512                match: function () {
513                    $j('#checkout-step-review').prepend($j('#checkout-progress-wrapper'));
514                },
515                unmatch: function () {
516                    $j('.col-right').prepend($j('#checkout-progress-wrapper'));
517                }
518            });
519        }
520
521
522        // ==============================================
523        // Checkout Cart - events
524        // ==============================================
525
526        if ($j('body.checkout-cart-index').length) {
527            // $j('input[name^="cart"]').on('focus change', function () {
528            //     $j(this).siblings('button').fadeIn();
529            // });
530
531            globalEvents.add('checkout__shoppingCart__updated', function(){
532                $j('input[name^="cart"]').on('focus change', function () {
533                    $j(this).siblings('button').fadeIn();
534                });
535            });
536        }
537
538        // ==============================================
539        // Product Listing - Align action buttons/links
540        // ==============================================
541
542        // Since the number of columns per grid will vary based on the viewport size, the only way to align the action
543        // buttons/links is via JS
544
545        // if ($j('.products-grid').length) {
546
547        //     var alignProductGridActions = function () {
548        //         var gridRows = []; // This will store an array per row
549        //         var tempRow = [];
550        //         productGridElements = $j('.products-grid > li');
551        //         productGridElements.each(function (index) {
552        //             // The JS ought to be agnostic of the specific CSS breakpoints, so we are dynamically checking to find
553        //             // each row by grouping all cells (eg, li elements) up until we find an element that is cleared.
554        //             // We are ignoring the first cell since it will always be cleared.
555        //             if ($j(this).css('clear') != 'none' && index != 0) {
556        //                 gridRows.push(tempRow); // Add the previous set of rows to the main array
557        //                 tempRow = []; // Reset the array since we're on a new row
558        //             }
559        //             tempRow.push(this);
560
561        //             // The last row will not contain any cells that clear that row, so we check to see if this is the last cell
562        //             // in the grid, and if so, we add its row to the array
563        //             if (productGridElements.length == index + 1) {
564        //                 gridRows.push(tempRow);
565        //             }
566        //         });
567
568        //         $j.each(gridRows, function () {
569        //             var tallestHeight = 0;
570        //             $j.each(this, function () {
571        //                 // Since this function is called every time the page is resized, we need to remove the min-height
572        //                 // so each cell can return to its natural size before being measured.
573        //                 $j(this).find('.product-info').css('min-height', '');
574        //                 // We are checking the height of .product-info (rather than the entire li), because the images
575        //                 // will not be loaded when this JS is run.
576        //                 elHeight = parseInt($j(this).find('.product-info').css('height'));
577        //                 if (elHeight > tallestHeight) {
578        //                     tallestHeight = elHeight;
579        //                 }
580        //             });
581        //             // Set the height of all .product-info elements in a row to the tallest height
582        //             $j.each(this, function () {
583        //                 $j(this).find('.product-info').css('minHeight', tallestHeight);
584        //             });
585        //         });
586        //     }
587        //     alignProductGridActions();
588
589        //     // Since the height of each cell and the number of columns per page may change when the page is resized, we are
590        //     // going to run the alignment function each time the page is resized.
591        //     $j(window).on('delayed-resize', function (e, resizeEvent) {
592        //         alignProductGridActions();
593        //     });
594        // }
595
596        // ==============================================
597        // Generic, efficient window resize handler
598        // ==============================================
599
600        // Using setTimeout since Web-Kit and some other browsers call the resize function constantly upon window resizing.
601        var resizeTimer;
602        $j(window).resize(function (e) {
603            clearTimeout(resizeTimer);
604            resizeTimer = setTimeout(function () {
605                $j(window).trigger('delayed-resize', e);
606            }, 250);
607        });
608    });
609
610    // ==============================================
611    // PDP - image zoom - needs to be available outside document.ready scope
612    // ==============================================
613
614    var ProductMediaManager = {
615        IMAGE_ZOOM_THRESHOLD: 20,
616        zoomEnabled: window.matchMedia("screen and (min-width:768px)"),
617        // imageWrapper: null,
618
619        _isInit: false,
620
621        isInit: function() {
622            return this._isInit;
623        },
624
625        destroyZoom: function() {
626            $j('.product-image-gallery .gallery-image').each(function(index, element){
627                var $img = $j(element);
628                var elevateZoom = $img.data('elevateZoom')
629                if(elevateZoom){
630                    elevateZoom.destroy();
631                    $img.parents('.product-image').removeClass('zoom-available').removeClass('enlarge-available');
632                }
633            });
634
635            $j('.zoomContainer').remove();
636            $j('.product-image-gallery .gallery-image').removeData('elevateZoom');
637        },
638
639        createZoom: function(image) {
640            ProductMediaManager.destroyZoom();
641            var config = image.parents('.product-image').attr('data-zoom-config') ? JSON.parse(image.parents('.product-image').attr('data-zoom-config')) : null;
642            //console.log(config);
643
644            if(!ProductMediaManager.zoomEnabled) { //zoom not enabled
645                return;
646            }
647
648            if(image.length <= 0) { //no image found
649                return;
650            }
651
652            if(!config){
653                return;
654            }
655
656            // allow click to work even if zoom is disabled.
657            if(config.clickToEnlarge){
658                // click to enlarge should work even if the naturalWi
658dth and naturalHeight are smaller than the image.
659                // Based on http://www.elevateweb.co.uk/image-zoom/examples#gallery-lightbox
660                // var moreViewsContainer = image.parents('.zoey-product-media').find(".more-views").first();
661                // var galleryId = moreViewsContainer.attr('id');
662                // if(!galleryId){
663                //     galleryId = 'zpm_' + Date.now();
664                //     moreViewsContainer.attr('id', galleryId);
665                // }
666
667                // config.gallery = galleryId;
668                // config.galleryActiveClass = 'active';
669                // config.imageCrossfade = true;
670
671                this.initFancyBox();
672
673                image.on('click', function(evt){
674                    var startIndex = -1;
675                    // capture the src by removing the filestack stuff.
676                    var mainSrcToCheck = evt.target.src.split(/https?:\/\//).pop();
677                    var galleryNodeList = evt.target.up('.component-content').querySelectorAll('.thumb-link');
678                    var galleryConfig = [];
679                    Array.prototype.slice.call(galleryNodeList).map(function(item) {
680                        const dataImageIndex = +item.getAttribute('data-image-index');
681                        const imageConfig = { href: item.getAttribute('data-zoom-image'), title: undefined };
682
683                        // capture the src by removing the filestack stuff.
684                        var srcToCheck = imageConfig.href.split(/https?:\/\//).pop();
685
686                        // capture the index of the gallery item it matches the main image.
687                        startIndex = mainSrcToCheck === srcToCheck ? dataImageIndex : startIndex;
688                        galleryConfig[dataImageIndex] = imageConfig;
689                    });
690
691                    if (!~startIndex) {
692                        // if image doesn't exist in the gallery then add it first and set the start index.
693                        galleryConfig.unshift({ href: image.get(0).src, title: undefined });
694                        startIndex = 0;
695                    }
696
697                    jQuery.fancyboxPlus(galleryConfig, {
698                        index: startIndex,
699                        cyclic: galleryConfig.length > 1,
700                        padding: 0,
701                    });
702                });
703
704                image.parents('.product-image').addClass('enlarge-available');
705            }
706
707            if(!config.enabled){
708                return;
709            }
710
711            if(image[0].naturalWidth && image[0].naturalHeight) {
712                var widthDiff = image[0].naturalWidth - image.width() - ProductMediaManager.IMAGE_ZOOM_THRESHOLD;
713                var heightDiff = image[0].naturalHeight - image.height() - ProductMediaManager.IMAGE_ZOOM_THRESHOLD;
714
715                if(widthDiff < 0 && heightDiff < 0) {
716                    //image not big enough
717
718                    image.parents('.product-image').removeClass('zoom-available');
719
720                    return;
721                } else {
722                    image.parents('.product-image').addClass('zoom-available');
723                }
724            }
725
726            if(!config.parentContainer){
727                config.parentContainer = image.siblings('.zoey-zoom-container-parent').get(0);
728            }
729
730            window.Zoey.require('jQuery.elevateZoom', function() {
731                if(image.elevateZoom !== undefined){
732                    // there are issues with swatches that will call this repeatedly.
733                    config.refresh = 1000;
734                    image.elevateZoom(config);
735                }
736            });
737        },
738
739        initFancyBox: function() {
740            if(!jQuery.fancyboxPlus){
741                // add fancybox to the page.
742                pixCore.domHelper.get$Body().append('<script type="text/javascript" src="/js/fancybox-plus/dist/jquery.fancybox-plus.min.js"></script>');
743                pixCore.domHelper.get$Body().append('<link rel="stylesheet" type="text/css" href="/js/fancybox-plus/css/jquery.fancybox-plus.css" />');
744            }
745        },
746
747        swapImage: function(targetImage) {
748            targetImage = $j(targetImage);
749            targetImage.addClass('gallery-image');
750
751            ProductMediaManager.destroyZoom();
752
753            var imageGallery = $j('.product-image-gallery');
754
755            if(targetImage[0].complete) { //image already loaded -- swap immediately
756
757                imageGallery.find('.gallery-image').removeClass('visible');
758
759                //move target image to correct place, in case it's necessary
760                imageGallery.append(targetImage);
761
762                //reveal new image
763                targetImage.addClass('visible');
764
765                //wire zoom on new image
766                ProductMediaManager.createZoom(targetImage);
767
768                globalEvents.notify('productMediaImageChange', targetImage.get(0));
769            } else { //need to wait for image to load
770
771                //add spinner
772                imageGallery.addClass('loading');
773
774                //move target image to correct place, in case it's necessary
775                imageGallery.append(targetImage);
776
777                this.toLoad = this.toLoad || 0;
778                this.toLoad++;
779                var self = this;
780                //wait until image is loaded
781                imagesLoaded(targetImage, function() {
782                    if(self.toLoad == this){
783                        //remove spinner
784                        imageGallery.removeClass('loading');
785
786                        //hide old image
787                        imageGallery.find('.gallery-image').removeClass('visible');
788
789                        //reveal new image
790                        targetImage.addClass('visible');
791
792                        //wire zoom on new image
793                        ProductMediaManager.createZoom(targetImage);
794                        globalEvents.notify('productMediaImageChange', targetImage.get(0));
795                    }
796                }.bind(this.toLoad));
797
798            }
799        },
800
801        _galleryUniqueKey: 1,
802        swapGalleryImages: function(images) {
803            const galleryContainer = document.querySelector('.product-image-thumbs');
804            const mainImageContainer = document.querySelector('.product-image-gallery')
805
806            // empty
807            if (galleryContainer) {
808                galleryContainer.innerHTML = '';
809            }
810
811            images.forEach((image, index) => {
812                image.__gallery_key = image.__gallery_key || `generated_gallery_key_${this._galleryUniqueKey++}`;
813
814                if (mainImageContainer) {
815                    if (!document.getElementById(`image-${image.__gallery_key}`)) {
816                        const img = document.createElement('img');
817                        img.setAttribute('id', `image-${image.__gallery_key}`);
818                        img.setAttribute('class', 'gallery-image');
819                        img.setAttribute('src', image.main);
820                        img.setAttribute('data-zoom-image', image.main);
821                        mainImageContainer.appendChild(img);
822                    }
823                }
824
825                if (galleryContainer) {
826                    const li = document.createElement('li');
827                    const a = document.createElement('a');
828                    a.classList.add('thumb-link');
829                    a.setAttribute('href', '#');
830                    a.setAttribute('title', image.title);
831                    a.setAttribute('data-image-index', image.__gallery_key);
832                    a.setAttribute('style', `background-image: url(${image.thumbnail});`);
833                    a.setAttribute('data-image', image.thumbnail);
834                    a.setAttribute('data-zoom-image', image.main);
835    
836                    li.appendChild(a);
837                    galleryContainer.appendChild(li);
838                }
839            });
840
841            document.dispatchEvent(
842                new CustomEvent('zoey:app:swapGalleryImages', {
843                    detail: {
844                        items: Array.from(galleryContainer.children)
845                    }
846                })
847            );
848
849            this.wireThumbnails();
850        },
851
852        wireThumbnails: function() {
853            //trigger image change event on thumbnail click
854            $j('.product-image-thumbs .thumb-link').click(function(e) {
855                e.preventDefault();
856                var jlink = $j(this);
857                var target = $j('#image-' + jlink.data('image-index'));
858
859                ProductMediaManager.swapImage(target);
860                document.dispatchEvent(new CustomEvent('zoey:product-essentials-thumbnail-after-click'));
861            });
862        },
863
864        initZoom: function() {
865            ProductMediaManager.createZoom($j(".no-touch .gallery-image.visible")); //set zoom on first image
866        },
867
868        init: function() {
869            this._isInit = true;
870            // ProductMediaManager.imageWrapper = $j('.product-img-box');
871
872            enquire.register("screen and (min-width:768px)", {
873                match : function() {
874                    ProductMediaManager.zoomEnabled = true;
875                    ProductMediaManager.initZoom();
876                },
877                unmatch : function() {
878                    ProductMediaManager.destroyZoom();
879                    ProductMediaManager.zoomEnabled = false;
880                }
881            });
882
883            //resizing the window causes problems with zoom -- reinitialize
884            $j(window).on('delayed-resize', function(e, resizeEvent) {
885                ProductMediaManager.destroyZoom();
886                ProductMediaManager.initZoom();
887            });
888
889            ProductMediaManager.wireThumbnails();
890
891            $j(document).trigger('product-media-loaded', ProductMediaManager);
892
893            // product media download link action
894            $j(document.body).on('click', '.zoey-product-media .download-link', function(e) {
895                // get visible img src
896                var img = $j('.zoey-product-media .product-image-gallery img:visible');
897                var imgSrc = img.data('dlImage');
898
899                // set it as href of current link
900                e.target.href = imgSrc;
901                e.target.download = imgSrc;
902
903                setTimeout(function() {
904                    // clear href
905                    e.target.href = '';
906                });
907            });
908        },
909
910        getBaseImage: function() {
911            return $j('.product-image-gallery .gallery-image#image-main').eq(0);
912        }
913    };
914
915
916    globalEvents.add('documentLoaded', function(){
917        ProductMediaManager.init();
918    });
919
920    globalEvents.add('initFancyBox', function(){
921        ProductMediaManager.initFancyBox();
922    });
923
924    // ==============================================
925    // Skip Links actions
926    // ==============================================
927
928    var skipLinks = {
929        COMPONENT_CLASS: 'pix-design-component',
930        HIDDEN_CLASS: 'hidden',
931        CLOSE_CLASS: 'skip-link-close',
932        TOGGLE_ATTR: 'data-toggle',
933        TOGGLE_POSITION_ATTR: 'data-toggle-position',
934        TOGGLE_ALIGN_ATTR: 'data-toggle-align',
935        POSITION_THRESHOLD: 200,
936
937        _isInit: false,
938
939        init: function() {
940            skipLinks.toggleEls = $j('['+skipLinks.TOGGLE_ATTR+']');
941
942            skipLinks.relocate();
943            this._mediaChangeListener = function(){
944                skipLinks.closeAll();
945            };
946            this._skipLinkOpenListener = function(evt){
947                skipLinks.open(this);
948            };
949            this._skipLinkCloseListener = function() {
950                skipLinks.closeAll();
951            };
952            this._skipLinkBodyClickListner = function(evt){
953                var target = evt.target;
954
955                var canClose = true;
956                if (skipLinks.toggleEls.get().indexOf(target) < 0 && skipLinks.toggleEls.has(target).length === 0) {
957                    canClose = skipLinks.toggleEls.get().every(function(el) {
958                        var toggle = $j(el).attr(skipLinks.TOGGLE_ATTR);
959                        if (toggle) {
960                            toggle = toggle.match(/^[.#[]/) ? toggle : '#' + toggle;
961                            var $toggleElem = $j(toggle);
962                            if ($toggleElem[0] && ($toggleElem[0] === target || $toggleElem.has(target).length)) {
963                                return false;
964                            }
965                        }
966                        return true;
967                    });
968                } else {
969                    canClose = false;
970                }
971
972                if (canClose) {
973                    skipLinks.closeAll();
974                }
975            };
976            skipLinks.attachEvents();
977            this._isInit = true;
978            return this;
979        },
980        reinit: function() {
981            if (!this._isInit) {
982                return this.init();
983            }
984
985            skipLinks.dettachEvents();
986            skipLinks.toggleEls = $j('['+skipLinks.TOGGLE_ATTR+']');
987            skipLinks.relocate();
988            skipLinks.attachEvents();
989            return this;
990        },
991        attachEvents: function(){
992            jQuery(window).on('pixMediaChange', this._mediaChangeListener);
993            skipLinks.toggleEls.on('click', this._skipLinkOpenListener);
994            pixCore.domHelper.get$Document().on('click', '.' + skipLinks.CLOSE_CLASS, this._skipLinkCloseListener);
995            globalEvents.add('bodyClick', this._skipLinkBodyClickListner);
996        },
997        dettachEvents: function() {
998            jQuery(window).off('pixMediaChange', this._mediaChangeListener);
999            skipLinks.toggleEls.off('click', this._skipLinkOpenListener);
1000            pixCore.domHelper.get$Document().off('click', '.' + skipLinks.CLOSE_CLASS, this._skipLinkCloseListener);
1001            globalEvents.remove('bodyClick', this._skipLinkBodyClickListner);
1002        },
1003        closeAll: function(){
1004            var self = this;
1005            skipLinks.toggleEls.each(function(index, el){
1006                var $el = $j('#'+$j(el).attr(skipLinks.TOGGLE_ATTR));
1007                if ($el.data('skip-link-status') === 'open')
1008                    return;
1009
1010                $el.addClass(self.HIDDEN_CLASS).removeAttr('style');
1011                $el.parents('.'+this.COMPONENT_CLASS).removeAttr('style');
1012            });
1013        },
1014        _setCss: function(el){
1015            var css = {};
1016
1017            if(el){
1018                el.css('position', 'relative');
1019
1020                var offset = el.offset();
1021
1022                // css['position'] = 'absolute';
1023                // css['left'] = '0';
1024                // css['top'] = '0';
1025                css['width'] = window.innerWidth+'px';
1026                css['margin-top'] = -offset.top+'px';
1027                css['margin-left'] = -offset.left+'px';
1028                css['min-height'] = '100px';
1029                css['z-index'] = 999999;
1030
1031                el.css(css);
1032            }
1033
1034            return this;
1035        },
1036        open: function(el, force){
1037            var $el = $j(el);
1038            var id = $el.attr(skipLinks.TOGGLE_ATTR);
1039            var togglePosition = $el.attr(skipLinks.TOGGLE_POSITION_ATTR) || 'static';
1040            var toggleAlign = $el.attr(skipLinks.TOGGLE_ALIGN_ATTR) || 'left';
1041            var toggleEl = $j('#'+id);
1042            var isHidden = toggleEl.hasClass(this.HIDDEN_CLASS) || force;
1043
1044            skipLinks.closeAll();
1045
1046            if(toggleEl.length){
1047                if(isHidden && $el.data('skip-link-status') !== 'close') {
1048                    toggleEl.removeClass(this.HIDDEN_CLASS);
1049
1050                    if(media.isMobile()){
1051
1052                        if(toggleEl.attr('data-href')){
1053                            window.location = toggleEl.attr('data-href');
1054                            toggleEl.addClass(this.HIDDEN_CLASS);
1055                            return;
1056                        }
1057
1058                        toggleEl.parents('.'+this.COMPONENT_CLASS).css({
1059                            'z-index': 99999999999999,
1060                            'position': 'absolute',
1061                            'top': '0',
1062                            'left': '0'
1063                        });
1064
1065                        this._setCss(toggleEl);
1066                    } else {
1067                        var position = $el.position();
1068                        var offset = $el.offset();
1069                        var bodyWidth = $j(document.body).outerWidth();
1070                        var css = {
1071                            zIndex: 99999999999999,
1072                            position: togglePosition === 'absolute' ? 'absolute' : 'fixed',
1073                            top: offset.top + $el.outerHeight() - (togglePosition === "fixed" ? jQuery(window).scrollTop() : 0)
1074                        };
1075
1076                        if(toggleAlign === 'center'){
1077                            css.left = (bodyWidth/2);
1078                        } else if(toggleAlign === 'full'){
1079                            css.left = 0;
1080                            css.width = '100%';
1081                        } else if(offset.left < bodyWidth/2){
1082                            css.left = offset.left;
1083                        } else {
1084                            if(toggleAlign === 'left') {
1085                                css.left = Math.max(0, offset.left);
1086                            } else if(toggleAlign === 'center-toggle'){
1087                                css.left = offset.left + ($el.outerWidth()/2);
1088                                css.transform = 'translateX(-50%)';
1089                            } else if( toggleAlign === 'right' ) {
1090                                css.right = Math.max(0, bodyWidth - (offset.left + $el.outerWidth()));
1091                            }
1092                            
1093                        }
1094
1095                        toggleEl.css(css);
1096
1097                        if(toggleEl.outerHeight() + toggleEl.position().top > window.innerHeight){
1098                            toggleEl.css({
1099                                top: 'auto',
1100                                bottom: '0px'
1101                            });
1102                        }
1103
1104                    }
1105
1106                }
1107            }
1108        },
1109        relocate: function(){
1110            var body = jQuery(document.body);
1111            skipLinks.toggleEls.each(function(){
1112                var id = $j(this).attr(skipLinks.TOGGLE_ATTR);
1113                body.append(jQuery('#'+id));
1114            });
1115        }
1116    };
1117
1118    globalEvents.add('documentLoaded', function(){
1119        skipLinks.init();
1120    });
1121
1122    window.Zoey.export('skipLinks', skipLinks);
1123    window.Zoey.export('ProductMediaManager', ProductMediaManager);
1124});
1125
1126window.Zoey.require(['pix.globalEvents'], function(globalEvents) {
1127    // ==============================================
1128    // Qty inputs
1129    // ==============================================
1130
1131    globalEvents.add('documentLoaded', function() {
1132        Array.from(document.querySelectorAll('input[name="qty"]')).forEach(function(el) {
1133            el.addEventListener('blur', function(e) {
1134                let input = e.target;
1135                let value = input.value;
1136
1137                if(value == 0) {
1138                    input.placeholder = '0';
1139                    input.value = '';
1140                }
1141            })
1142        })
1143    });
1144});

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.