Advertisement
Guest User

Untitled

a guest
Dec 29th, 2021
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * Copyright © Magento, Inc. All rights reserved.
  3.  * See COPYING.txt for license details.
  4.  */
  5.  
  6.  define([
  7.     'jquery',
  8.     'underscore',
  9.     'mage/template',
  10.     'mage/smart-keyboard-handler',
  11.     'mage/translate',
  12.     'priceUtils',
  13.     'jquery-ui-modules/widget',
  14.     'jquery/jquery.parsequery',
  15.     'mage/validation/validation'
  16. ], function ($, _, mageTemplate, keyboardHandler, $t, priceUtils) {
  17.     // 'use strict';
  18.  
  19.     /**
  20.      * Extend form validation to support swatch accessibility
  21.      */
  22.     $.widget('mage.validation', $.mage.validation, {
  23.         /**
  24.          * Handle form with swatches validation. Focus on first invalid swatch block.
  25.          *
  26.          * @param {jQuery.Event} event
  27.          * @param {Object} validation
  28.          */
  29.         listenFormValidateHandler: function (event, validation) {
  30.             var swatchWrapper, firstActive, swatches, swatch, successList, errorList, firstSwatch;
  31.  
  32.             this._superApply(arguments);
  33.  
  34.             swatchWrapper = '.swatch-attribute-options';
  35.             swatches = $(event.target).find(swatchWrapper);
  36.  
  37.             if (!swatches.length) {
  38.                 return;
  39.             }
  40.  
  41.             swatch = '.swatch-attribute';
  42.             firstActive = $(validation.errorList[0].element || []);
  43.             successList = validation.successList;
  44.             errorList = validation.errorList;
  45.             firstSwatch = $(firstActive).parent(swatch).find(swatchWrapper);
  46.  
  47.             keyboardHandler.focus(swatches);
  48.  
  49.             $.each(successList, function (index, item) {
  50.                 $(item).parent(swatch).find(swatchWrapper).attr('aria-invalid', false);
  51.             });
  52.  
  53.             $.each(errorList, function (index, item) {
  54.                 $(item.element).parent(swatch).find(swatchWrapper).attr('aria-invalid', true);
  55.             });
  56.  
  57.             if (firstSwatch.length) {
  58.                 $(firstSwatch).focus();
  59.             }
  60.         }
  61.     });
  62.  
  63.     /**
  64.      * Render tooltips by attributes (only to up).
  65.      * Required element attributes:
  66.      *  - option-type (integer, 0-3)
  67.      *  - option-label (string)
  68.      *  - option-tooltip-thumb
  69.      *  - option-tooltip-value
  70.      *  - thumb-width
  71.      *  - thumb-height
  72.      */
  73.     $.widget('mage.SwatchRendererTooltip', {
  74.         options: {
  75.             delay: 200,                             //how much ms before tooltip to show
  76.             tooltipClass: 'swatch-option-tooltip'  //configurable, but remember about css
  77.         },
  78.  
  79.         /**
  80.          * @private
  81.          */
  82.         _init: function () {
  83.             var $widget = this,
  84.                 $this = this.element,
  85.                 $element = $('.' + $widget.options.tooltipClass),
  86.                 timer,
  87.                 type = parseInt($this.attr('option-type'), 10),
  88.                 label = $this.attr('option-label'),
  89.                 thumb = $this.attr('option-tooltip-thumb'),
  90.                 value = $this.attr('option-tooltip-value'),
  91.                 width = $this.attr('thumb-width'),
  92.                 height = $this.attr('thumb-height'),
  93.                 $image,
  94.                 $title,
  95.                 $corner;
  96.  
  97.             if (!$element.length) {
  98.                 $element = $('<div class="' +
  99.                     $widget.options.tooltipClass +
  100.                     '"><div class="image"></div><div class="title"></div><div class="corner"></div></div>'
  101.                 );
  102.                 $('body').append($element);
  103.             }
  104.  
  105.             $image = $element.find('.image');
  106.             $title = $element.find('.title');
  107.             $corner = $element.find('.corner');
  108.  
  109.             $this.hover(function () {
  110.                 if (!$this.hasClass('disabled')) {
  111.                     timer = setTimeout(
  112.                         function () {
  113.                             var leftOpt = null,
  114.                                 leftCorner = 0,
  115.                                 left,
  116.                                 $window;
  117.  
  118.                             if (type === 2) {
  119.                                 // Image
  120.                                 $image.css({
  121.                                     'background': 'url("' + thumb + '") no-repeat center', //Background case
  122.                                     'background-size': 'initial',
  123.                                     'width': width + 'px',
  124.                                     'height': height + 'px'
  125.                                 });
  126.                                 $image.show();
  127.                             } else if (type === 1) {
  128.                                 // Color
  129.                                 $image.css({
  130.                                     background: value
  131.                                 });
  132.                                 $image.show();
  133.                             } else if (type === 0 || type === 3) {
  134.                                 // Default
  135.                                 $image.hide();
  136.                             }
  137.  
  138.                             $title.text(label);
  139.  
  140.                             leftOpt = $this.offset().left;
  141.                             left = leftOpt + $this.width() / 2 - $element.width() / 2;
  142.                             $window = $(window);
  143.  
  144.                             // the numbers (5 and 5) is magick constants for offset from left or right page
  145.                             if (left < 0) {
  146.                                 left = 5;
  147.                             } else if (left + $element.width() > $window.width()) {
  148.                                 left = $window.width() - $element.width() - 5;
  149.                             }
  150.  
  151.                             // the numbers (6,  3 and 18) is magick constants for offset tooltip
  152.                             leftCorner = 0;
  153.  
  154.                             if ($element.width() < $this.width()) {
  155.                                 leftCorner = $element.width() / 2 - 3;
  156.                             } else {
  157.                                 leftCorner = (leftOpt > left ? leftOpt - left : left - leftOpt) + $this.width() / 2 - 6;
  158.                             }
  159.  
  160.                             $corner.css({
  161.                                 left: leftCorner
  162.                             });
  163.                             $element.css({
  164.                                 left: left,
  165.                                 top: $this.offset().top - $element.height() - $corner.height() - 18
  166.                             }).show();
  167.                         },
  168.                         $widget.options.delay
  169.                     );
  170.                 }
  171.             }, function () {
  172.                 $element.hide();
  173.                 clearTimeout(timer);
  174.             });
  175.  
  176.             $(document).on('tap', function () {
  177.                 $element.hide();
  178.                 clearTimeout(timer);
  179.             });
  180.  
  181.             $this.on('tap', function (event) {
  182.                 event.stopPropagation();
  183.             });
  184.         }
  185.     });
  186.  
  187.     /**
  188.      * Render swatch controls with options and use tooltips.
  189.      * Required two json:
  190.      *  - jsonConfig (magento's option config)
  191.      *  - jsonSwatchConfig (swatch's option config)
  192.      *
  193.      *  Tuning:
  194.      *  - numberToShow (show "more" button if options are more)
  195.      *  - onlySwatches (hide selectboxes)
  196.      *  - moreButtonText (text for "more" button)
  197.      *  - selectorProduct (selector for product container)
  198.      *  - selectorProductPrice (selector for change price)
  199.      */
  200.     $.widget('mage.SwatchRenderer', {
  201.         options: {
  202.             classes: {
  203.                 attributeClass: 'swatch-attribute',
  204.                 attributeLabelClass: 'swatch-attribute-label',
  205.                 attributeSelectedOptionLabelClass: 'swatch-attribute-selected-option',
  206.                 attributeOptionsWrapper: 'swatch-attribute-options',
  207.                 attributeInput: 'swatch-input',
  208.                 optionClass: 'swatch-option',
  209.                 selectClass: 'swatch-select',
  210.                 moreButton: 'swatch-more',
  211.                 loader: 'swatch-option-loading'
  212.             },
  213.             // option's json config
  214.             jsonConfig: {},
  215.  
  216.             // swatch's json config
  217.             jsonSwatchConfig: {},
  218.  
  219.             // selector of parental block of prices and swatches (need to know where to seek for price block)
  220.             selectorProduct: '.product-info-main',
  221.  
  222.             // selector of price wrapper (need to know where set price)
  223.             selectorProductPrice: '[data-role=priceBox]',
  224.  
  225.             //selector of product images gallery wrapper
  226.             mediaGallerySelector: '[data-gallery-role=gallery-placeholder]',
  227.  
  228.             // selector of category product tile wrapper
  229.             selectorProductTile: '.product-item',
  230.  
  231.             // number of controls to show (false or zero = show all)
  232.             numberToShow: false,
  233.  
  234.             // show only swatch controls
  235.             onlySwatches: false,
  236.  
  237.             // enable label for control
  238.             enableControlLabel: true,
  239.  
  240.             // control label id
  241.             controlLabelId: '',
  242.  
  243.             // text for more button
  244.             moreButtonText: $t('More'),
  245.  
  246.             // Callback url for media
  247.             mediaCallback: '',
  248.  
  249.             // Local media cache
  250.             mediaCache: {},
  251.  
  252.             // Cache for BaseProduct images. Needed when option unset
  253.             mediaGalleryInitial: [{}],
  254.  
  255.             // Use ajax to get image data
  256.             useAjax: false,
  257.  
  258.             /**
  259.              * Defines the mechanism of how images of a gallery should be
  260.              * updated when user switches between configurations of a product.
  261.              *
  262.              * As for now value of this option can be either 'replace' or 'prepend'.
  263.              *
  264.              * @type {String}
  265.              */
  266.             gallerySwitchStrategy: 'replace',
  267.  
  268.             // whether swatches are rendered in product list or on product page
  269.             inProductList: false,
  270.  
  271.             // sly-old-price block selector
  272.             slyOldPriceSelector: '.sly-old-price',
  273.  
  274.             // tier prise selectors start
  275.             tierPriceTemplateSelector: '#tier-prices-template',
  276.             tierPriceBlockSelector: '[data-role="tier-price-block"]',
  277.             tierPriceTemplate: '',
  278.             // tier prise selectors end
  279.  
  280.             // A price label selector
  281.             normalPriceLabelSelector: '.product-info-main .normal-price .price-label'
  282.         },
  283.  
  284.         /**
  285.          * Get chosen product
  286.          *
  287.          * @returns int|null
  288.          */
  289.         getProduct: function () {
  290.             var products = this._CalcProducts();
  291.  
  292.             return _.isArray(products) ? products[0] : null;
  293.         },
  294.  
  295.         /**
  296.          * @private
  297.          */
  298.         _init: function () {
  299.             // Don't render the same set of swatches twice
  300.             if ($(this.element).attr('data-rendered')) {
  301.                 return;
  302.             }
  303.             $(this.element).attr('data-rendered', true);
  304.  
  305.             if (_.isEmpty(this.options.jsonConfig.images)) {
  306.                 this.options.useAjax = true;
  307.                 // creates debounced variant of _LoadProductMedia()
  308.                 // to use it in events handlers instead of _LoadProductMedia()
  309.                 this._debouncedLoadProductMedia = _.debounce(this._LoadProductMedia.bind(this), 500);
  310.             }
  311.  
  312.             if (this.options.jsonConfig !== '' && this.options.jsonSwatchConfig !== '') {
  313.                 // store unsorted attributes
  314.                 this.options.jsonConfig.mappedAttributes = _.clone(this.options.jsonConfig.attributes);
  315.                 this._sortAttributes();
  316.                 this._RenderControls();
  317.                 this._setPreSelectedGallery();
  318.                 $(this.element).trigger('swatch.initialized');
  319.             } else {
  320.                 console.log('SwatchRenderer: No input data received');
  321.             }
  322.             this.options.tierPriceTemplate = $(this.options.tierPriceTemplateSelector).html();
  323.         },
  324.  
  325.         /**
  326.          * @private
  327.          */
  328.         _sortAttributes: function () {
  329.             this.options.jsonConfig.attributes = _.sortBy(this.options.jsonConfig.attributes, function (attribute) {
  330.                 return parseInt(attribute.position, 10);
  331.             });
  332.         },
  333.  
  334.         /**
  335.          * @private
  336.          */
  337.         _create: function () {
  338.             var options = this.options,
  339.                 gallery = $('[data-gallery-role=gallery-placeholder]', '.column.main'),
  340.                 productData = this._determineProductData(),
  341.                 $main = productData.isInProductView ?
  342.                     this.element.parents('.column.main') :
  343.                     this.element.parents('.product-item-info');
  344.  
  345.             if (productData.isInProductView) {
  346.                 gallery.data('gallery') ?
  347.                     this._onGalleryLoaded(gallery) :
  348.                     gallery.on('gallery:loaded', this._onGalleryLoaded.bind(this, gallery));
  349.             } else {
  350.                 options.mediaGalleryInitial = [{
  351.                     'img': $main.find('.product-image-photo').attr('src')
  352.                 }];
  353.             }
  354.  
  355.             this.productForm = this.element.parents(this.options.selectorProductTile).find('form:first');
  356.             this.inProductList = this.productForm.length > 0;
  357.         },
  358.  
  359.         /**
  360.          * Determine product id and related data
  361.          *
  362.          * @returns {{productId: *, isInProductView: bool}}
  363.          * @private
  364.          */
  365.         _determineProductData: function () {
  366.             // Check if product is in a list of products.
  367.             var productId,
  368.                 isInProductView = false;
  369.  
  370.             productId = this.element.parents('.product-item-details')
  371.                 .find('.price-box.price-final_price').attr('data-product-id');
  372.  
  373.             if (!productId) {
  374.                 // Check individual product.
  375.                 productId = $('[name=product]').val();
  376.                 isInProductView = productId > 0;
  377.             }
  378.  
  379.             return {
  380.                 productId: productId,
  381.                 isInProductView: isInProductView
  382.             };
  383.         },
  384.  
  385.         /**
  386.          * Render controls
  387.          *
  388.          * @private
  389.          */
  390.         _RenderControls: function () {
  391.             var $widget = this,
  392.                 container = this.element,
  393.                 classes = this.options.classes,
  394.                 chooseText = this.options.jsonConfig.chooseText,
  395.                 showTooltip = this.options.showTooltip;
  396.  
  397.             $widget.optionsMap = {};
  398.  
  399.             $.each(this.options.jsonConfig.attributes, function () {
  400.                 var item = this,
  401.                     controlLabelId = 'option-label-' + item.code + '-' + item.id,
  402.                     options = $widget._RenderSwatchOptions(item, controlLabelId),
  403.                     select = $widget._RenderSwatchSelect(item, chooseText),
  404.                     input = $widget._RenderFormInput(item),
  405.                     listLabel = '',
  406.                     label = '';
  407.  
  408.                 // Show only swatch controls
  409.                 if ($widget.options.onlySwatches && !$widget.options.jsonSwatchConfig.hasOwnProperty(item.id)) {
  410.                     return;
  411.                 }
  412.  
  413.                 if ($widget.options.enableControlLabel) {
  414.                     label +=
  415.                         '<span id="' + controlLabelId + '" class="' + classes.attributeLabelClass + '">' +
  416.                         $('<i></i>').text(item.label).html() +
  417.                         '</span>' +
  418.                         '<span class="' + classes.attributeSelectedOptionLabelClass + '"></span>';
  419.                 }
  420.  
  421.                 if ($widget.inProductList) {
  422.                     $widget.productForm.append(input);
  423.                     input = '';
  424.                     listLabel = 'aria-label="' + $('<i></i>').text(item.label).html() + '"';
  425.                 } else {
  426.                     listLabel = 'aria-labelledby="' + controlLabelId + '"';
  427.                 }
  428.  
  429.                 // Create new control
  430.                 container.append(
  431.                     '<div class="' + classes.attributeClass + ' ' + item.code + '" ' +
  432.                     'attribute-code="' + item.code + '" ' +
  433.                     'attribute-id="' + item.id + '">' +
  434.                     label +
  435.                     '<div aria-activedescendant="" ' +
  436.                     'tabindex="0" ' +
  437.                     'aria-invalid="false" ' +
  438.                     'aria-required="true" ' +
  439.                     'role="listbox" ' + listLabel +
  440.                     'class="' + classes.attributeOptionsWrapper + ' clearfix">' +
  441.                     options + select +
  442.                     '</div>' + input +
  443.                     '</div>'
  444.                 );
  445.  
  446.                 $widget.optionsMap[item.id] = {};
  447.  
  448.                 // Aggregate options array to hash (key => value)
  449.                 $.each(item.options, function () {
  450.                     if (this.products.length > 0) {
  451.                         $widget.optionsMap[item.id][this.id] = {
  452.                             price: parseInt(
  453.                                 $widget.options.jsonConfig.optionPrices[this.products[0]].finalPrice.amount,
  454.                                 10
  455.                             ),
  456.                             products: this.products
  457.                         };
  458.                     }
  459.                 });
  460.             });
  461.  
  462.             if (showTooltip === 1) {
  463.                 // Connect Tooltip
  464.                 container
  465.                     .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]')
  466.                     .SwatchRendererTooltip();
  467.             }
  468.  
  469.             // Hide all elements below more button
  470.             $('.' + classes.moreButton).nextAll().hide();
  471.  
  472.             // Handle events like click or change
  473.             $widget._EventListener();
  474.  
  475.             // Rewind options
  476.             $widget._Rewind(container);
  477.  
  478.             //Emulate click on all swatches from Request
  479.             $widget._EmulateSelected($.parseQuery());
  480.             $widget._EmulateSelected($widget._getSelectedAttributes());
  481.         },
  482.  
  483.         /**
  484.          * Render swatch options by part of config
  485.          *
  486.          * @param {Object} config
  487.          * @param {String} controlId
  488.          * @returns {String}
  489.          * @private
  490.          */
  491.         _RenderSwatchOptions: function (config, controlId) {
  492.             var optionConfig = this.options.jsonSwatchConfig[config.id],
  493.                 optionClass = this.options.classes.optionClass,
  494.                 sizeConfig = this.options.jsonSwatchImageSizeConfig,
  495.                 moreLimit = parseInt(this.options.numberToShow, 10),
  496.                 moreClass = this.options.classes.moreButton,
  497.                 moreText = this.options.moreButtonText,
  498.                 countAttributes = 0,
  499.                 html = '';
  500.  
  501.             if (!this.options.jsonSwatchConfig.hasOwnProperty(config.id)) {
  502.                 return '';
  503.             }
  504.  
  505.             $.each(config.options, function (index) {
  506.                 var id,
  507.                     type,
  508.                     value,
  509.                     thumb,
  510.                     label,
  511.                     width,
  512.                     height,
  513.                     attr,
  514.                     swatchImageWidth,
  515.                     swatchImageHeight;
  516.  
  517.                 if (!optionConfig.hasOwnProperty(this.id)) {
  518.                     return '';
  519.                 }
  520.  
  521.                 // Add more button
  522.                 if (moreLimit === countAttributes++) {
  523.                     html += '<a href="#" class="' + moreClass + '"><span>' + moreText + '</span></a>';
  524.                 }
  525.  
  526.                 id = this.id;
  527.                 type = parseInt(optionConfig[id].type, 10);
  528.                 value = optionConfig[id].hasOwnProperty('value') ?
  529.                     $('<i></i>').text(optionConfig[id].value).html() : '';
  530.                 thumb = optionConfig[id].hasOwnProperty('thumb') ? optionConfig[id].thumb : '';
  531.                 width = _.has(sizeConfig, 'swatchThumb') ? sizeConfig.swatchThumb.width : 110;
  532.                 height = _.has(sizeConfig, 'swatchThumb') ? sizeConfig.swatchThumb.height : 90;
  533.                 label = this.label ? $('<i></i>').text(this.label).html() : '';
  534.                 attr =
  535.                     ' id="' + controlId + '-item-' + id + '"' +
  536.                     ' index="' + index + '"' +
  537.                     ' aria-checked="false"' +
  538.                     ' aria-describedby="' + controlId + '"' +
  539.                     ' tabindex="0"' +
  540.                     ' option-type="' + type + '"' +
  541.                     ' option-id="' + id + '"' +
  542.                     ' option-label="' + label + '"' +
  543.                     ' aria-label="' + label + '"' +
  544.                     ' option-tooltip-thumb="' + thumb + '"' +
  545.                     ' option-tooltip-value="' + value + '"' +
  546.                     ' role="option"' +
  547.                     ' thumb-width="' + width + '"' +
  548.                     ' thumb-height="' + height + '"';
  549.  
  550.                 swatchImageWidth = _.has(sizeConfig, 'swatchImage') ? sizeConfig.swatchImage.width : 30;
  551.                 swatchImageHeight = _.has(sizeConfig, 'swatchImage') ? sizeConfig.swatchImage.height : 20;
  552.  
  553.                 if (!this.hasOwnProperty('products') || this.products.length <= 0) {
  554.                     attr += ' option-empty="true"';
  555.                 }
  556.  
  557.                 if (type === 0) {
  558.                     // Text
  559.                     html += '<div class="' + optionClass + ' text" ' + attr + '>' + (value ? value : label) +
  560.                         '</div>';
  561.                 } else if (type === 1) {
  562.                     // Color
  563.                     html += '<div class="' + optionClass + ' color" ' + attr +
  564.                         ' style="background: ' + value +
  565.                         ' no-repeat center; background-size: initial;">' + '' +
  566.                         '</div>';
  567.                 } else if (type === 2) {
  568.                     // Image
  569.                     html += '<div class="' + optionClass + ' image" ' + attr +
  570.                         ' style="background: url(' + value + ') no-repeat center; background-size: initial;width:' +
  571.                         swatchImageWidth + 'px; height:' + swatchImageHeight + 'px">' + '' +
  572.                         '</div>';
  573.                 } else if (type === 3) {
  574.                     // Clear
  575.                     html += '<div class="' + optionClass + '" ' + attr + '></div>';
  576.                 } else {
  577.                     // Default
  578.                     html += '<div class="' + optionClass + '" ' + attr + '>' + label + '</div>';
  579.                 }
  580.             });
  581.             return html;
  582.         },
  583.  
  584.         /**
  585.          * Render select by part of config
  586.          *
  587.          * @param {Object} config
  588.          * @param {String} chooseText
  589.          * @returns {String}
  590.          * @private
  591.          */
  592.         _RenderSwatchSelect: function (config, chooseText) {
  593.             var html;
  594.  
  595.             if (this.options.jsonSwatchConfig.hasOwnProperty(config.id)) {
  596.                 return '';
  597.             }
  598.  
  599.             html =
  600.                 '<select class="' + this.options.classes.selectClass + ' ' + config.code + '">' +
  601.                 '<option value="0" option-id="0">' + chooseText + '</option>';
  602.  
  603.             $.each(config.options, function () {
  604.                 var label = this.label,
  605.                     attr = ' value="' + this.id + '" option-id="' + this.id + '"';
  606.  
  607.                 if (!this.hasOwnProperty('products') || this.products.length <= 0) {
  608.                     attr += ' option-empty="true"';
  609.                 }
  610.  
  611.                 html += '<option ' + attr + '>' + label + '</option>';
  612.             });
  613.  
  614.             html += '</select>';
  615.  
  616.             return html;
  617.         },
  618.  
  619.         /**
  620.          * Input for submit form.
  621.          * This control shouldn't have "type=hidden", "display: none" for validation work :(
  622.          *
  623.          * @param {Object} config
  624.          * @private
  625.          */
  626.         _RenderFormInput: function (config) {
  627.             return '<input class="' + this.options.classes.attributeInput + ' super-attribute-select" ' +
  628.                 'name="super_attribute[' + config.id + ']" ' +
  629.                 'type="text" ' +
  630.                 'value="" ' +
  631.                 'data-selector="super_attribute[' + config.id + ']" ' +
  632.                 'data-validate="{required: true}" ' +
  633.                 'aria-required="true" ' +
  634.                 'aria-invalid="false">';
  635.         },
  636.  
  637.         /**
  638.          * Event listener
  639.          *
  640.          * @private
  641.          */
  642.         _EventListener: function () {
  643.             var $widget = this,
  644.                 options = this.options.classes,
  645.                 target;
  646.  
  647.             $widget.element.on('click', '.' + options.optionClass, function () {
  648.                 return $widget._OnClick($(this), $widget);
  649.             });
  650.  
  651.             $widget.element.on('change', '.' + options.selectClass, function () {
  652.                 return $widget._OnChange($(this), $widget);
  653.             });
  654.  
  655.             $widget.element.on('click', '.' + options.moreButton, function (e) {
  656.                 e.preventDefault();
  657.  
  658.                 return $widget._OnMoreClick($(this));
  659.             });
  660.  
  661.             $widget.element.on('keydown', function (e) {
  662.                 if (e.which === 13) {
  663.                     target = $(e.target);
  664.  
  665.                     if (target.is('.' + options.optionClass)) {
  666.                         return $widget._OnClick(target, $widget);
  667.                     } else if (target.is('.' + options.selectClass)) {
  668.                         return $widget._OnChange(target, $widget);
  669.                     } else if (target.is('.' + options.moreButton)) {
  670.                         e.preventDefault();
  671.  
  672.                         return $widget._OnMoreClick(target);
  673.                     }
  674.                 }
  675.             });
  676.         },
  677.  
  678.         /**
  679.          * Load media gallery using ajax or json config.
  680.          *
  681.          * @private
  682.          */
  683.         _loadMedia: function () {
  684.             var $main = this.inProductList ?
  685.                 this.element.parents('.product-item-info') :
  686.                 this.element.parents('.column.main'),
  687.                 images;
  688.  
  689.             if (this.options.useAjax) {
  690.                 this._debouncedLoadProductMedia();
  691.             } else {
  692.                 images = this.options.jsonConfig.images[this.getProduct()];
  693.  
  694.                 if (!images) {
  695.                     images = this.options.mediaGalleryInitial;
  696.                 }
  697.                 this.updateBaseImage(this._sortImages(images), $main, !this.inProductList);
  698.             }
  699.         },
  700.  
  701.         /**
  702.          * Sorting images array
  703.          *
  704.          * @private
  705.          */
  706.         _sortImages: function (images) {
  707.             return _.sortBy(images, function (image) {
  708.                 return parseInt(image.position, 10);
  709.             });
  710.         },
  711.  
  712.         /**
  713.          * Event for swatch options
  714.          *
  715.          * @param {Object} $this
  716.          * @param {Object} $widget
  717.          * @private
  718.          */
  719.         _OnClick: function ($this, $widget) {
  720.             var $parent = $this.parents('.' + $widget.options.classes.attributeClass),
  721.                 $wrapper = $this.parents('.' + $widget.options.classes.attributeOptionsWrapper),
  722.                 $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass),
  723.                 attributeId = $parent.attr('attribute-id'),
  724.                 $input = $parent.find('.' + $widget.options.classes.attributeInput),
  725.                 checkAdditionalData = JSON.parse(this.options.jsonSwatchConfig[attributeId]['additional_data']);
  726.  
  727.             if ($widget.inProductList) {
  728.                 $input = $widget.productForm.find(
  729.                     '.' + $widget.options.classes.attributeInput + '[name="super_attribute[' + attributeId + ']"]'
  730.                 );
  731.             }
  732.  
  733.             if ($this.hasClass('disabled')) {
  734.                 return;
  735.             }
  736.  
  737.             if ($this.hasClass('selected')) {
  738.                 $parent.removeAttr('option-selected').find('.selected').removeClass('selected');
  739.                 $input.val('');
  740.                 $label.text('');
  741.                 $this.attr('aria-checked', false);
  742.             } else {
  743.                 $parent.attr('option-selected', $this.attr('option-id')).find('.selected').removeClass('selected');
  744.                 $label.text($this.attr('option-label'));
  745.                 $input.val($this.attr('option-id'));
  746.                 $input.attr('data-attr-name', this._getAttributeCodeById(attributeId));
  747.                 $this.addClass('selected');
  748.                 $widget._toggleCheckedAttributes($this, $wrapper);
  749.             }
  750.  
  751.             $widget._Rebuild();
  752.  
  753.             if ($widget.element.parents($widget.options.selectorProduct)
  754.                 .find(this.options.selectorProductPrice).is(':data(mage-priceBox)')
  755.             ) {
  756.                 $widget._UpdatePrice();
  757.             }
  758.  
  759.             $(document).trigger('updateMsrpPriceBlock',
  760.                 [
  761.                     _.findKey($widget.options.jsonConfig.index, $widget.options.jsonConfig.defaultValues),
  762.                     $widget.options.jsonConfig.optionPrices
  763.                 ]);
  764.  
  765.             if (parseInt(checkAdditionalData['update_product_preview_image'], 10) === 1) {
  766.                 $widget._loadMedia();
  767.             }
  768.             $input.trigger('change');
  769.         },
  770.  
  771.         /**
  772.          * Get human readable attribute code (eg. size, color) by it ID from configuration
  773.          *
  774.          * @param {Number} attributeId
  775.          * @returns {*}
  776.          * @private
  777.          */
  778.         _getAttributeCodeById: function (attributeId) {
  779.             var attribute = this.options.jsonConfig.mappedAttributes[attributeId];
  780.  
  781.             return attribute ? attribute.code : attributeId;
  782.         },
  783.  
  784.         /**
  785.          * Toggle accessibility attributes
  786.          *
  787.          * @param {Object} $this
  788.          * @param {Object} $wrapper
  789.          * @private
  790.          */
  791.         _toggleCheckedAttributes: function ($this, $wrapper) {
  792.             $wrapper.attr('aria-activedescendant', $this.attr('id'))
  793.                 .find('.' + this.options.classes.optionClass).attr('aria-checked', false);
  794.             $this.attr('aria-checked', true);
  795.         },
  796.  
  797.         /**
  798.          * Event for select
  799.          *
  800.          * @param {Object} $this
  801.          * @param {Object} $widget
  802.          * @private
  803.          */
  804.         _OnChange: function ($this, $widget) {
  805.             var $parent = $this.parents('.' + $widget.options.classes.attributeClass),
  806.                 attributeId = $parent.attr('attribute-id'),
  807.                 $input = $parent.find('.' + $widget.options.classes.attributeInput);
  808.  
  809.             if ($widget.productForm.length > 0) {
  810.                 $input = $widget.productForm.find(
  811.                     '.' + $widget.options.classes.attributeInput + '[name="super_attribute[' + attributeId + ']"]'
  812.                 );
  813.             }
  814.  
  815.             if ($this.val() > 0) {
  816.                 $parent.attr('option-selected', $this.val());
  817.                 $input.val($this.val());
  818.             } else {
  819.                 $parent.removeAttr('option-selected');
  820.                 $input.val('');
  821.             }
  822.  
  823.             $widget._Rebuild();
  824.             $widget._UpdatePrice();
  825.             $widget._loadMedia();
  826.             $input.trigger('change');
  827.         },
  828.  
  829.         /**
  830.          * Event for more switcher
  831.          *
  832.          * @param {Object} $this
  833.          * @private
  834.          */
  835.         _OnMoreClick: function ($this) {
  836.             $this.nextAll().show();
  837.             $this.blur().remove();
  838.         },
  839.  
  840.         /**
  841.          * Rewind options for controls
  842.          *
  843.          * @private
  844.          */
  845.         _Rewind: function (controls) {
  846.             controls.find('div[option-id], option[option-id]').removeClass('disabled').removeAttr('disabled');
  847.             controls.find('div[option-empty], option[option-empty]')
  848.                 .attr('disabled', true)
  849.                 .addClass('disabled')
  850.                 .attr('tabindex', '-1');
  851.         },
  852.  
  853.         /**
  854.          * Rebuild container
  855.          *
  856.          * @private
  857.          */
  858.         _Rebuild: function () {
  859.             var $widget = this,
  860.                 controls = $widget.element.find('.' + $widget.options.classes.attributeClass + '[attribute-id]'),
  861.                 selected = controls.filter('[option-selected]');
  862.  
  863.             // Enable all options
  864.             $widget._Rewind(controls);
  865.  
  866.             // done if nothing selected
  867.             if (selected.length <= 0) {
  868.                 return;
  869.             }
  870.  
  871.             // Disable not available options
  872.             controls.each(function () {
  873.                 var $this = $(this),
  874.                     id = $this.attr('attribute-id'),
  875.                     products = $widget._CalcProducts(id);
  876.  
  877.                 if (selected.length === 1 && selected.first().attr('attribute-id') === id) {
  878.                     return;
  879.                 }
  880.  
  881.                 $this.find('[option-id]').each(function () {
  882.                     var $element = $(this),
  883.                         option = $element.attr('option-id');
  884.  
  885.                     if (!$widget.optionsMap.hasOwnProperty(id) || !$widget.optionsMap[id].hasOwnProperty(option) ||
  886.                         $element.hasClass('selected') ||
  887.                         $element.is(':selected')) {
  888.                         return;
  889.                     }
  890.  
  891.                     if (_.intersection(products, $widget.optionsMap[id][option].products).length <= 0) {
  892.                         $element.attr('disabled', true).addClass('disabled');
  893.                     }
  894.                 });
  895.             });
  896.         },
  897.  
  898.         /**
  899.          * Get selected product list
  900.          *
  901.          * @returns {Array}
  902.          * @private
  903.          */
  904.         _CalcProducts: function ($skipAttributeId) {
  905.             var $widget = this,
  906.                 products = [];
  907.  
  908.             // Generate intersection of products
  909.             $widget.element.find('.' + $widget.options.classes.attributeClass + '[option-selected]').each(function () {
  910.                 var id = $(this).attr('attribute-id'),
  911.                     option = $(this).attr('option-selected');
  912.  
  913.                 if ($skipAttributeId !== undefined && $skipAttributeId === id) {
  914.                     return;
  915.                 }
  916.  
  917.                 if (!$widget.optionsMap.hasOwnProperty(id) || !$widget.optionsMap[id].hasOwnProperty(option)) {
  918.                     return;
  919.                 }
  920.  
  921.                 if (products.length === 0) {
  922.                     products = $widget.optionsMap[id][option].products;
  923.                 } else {
  924.                     products = _.intersection(products, $widget.optionsMap[id][option].products);
  925.                 }
  926.             });
  927.  
  928.             return products;
  929.         },
  930.  
  931.         _getTierPrices: function () {
  932.             const qtyIdentifier = '#qty';
  933.             const priceIdentifier = '.normal-price .price';
  934.             var tierPrices = [];
  935.             tierPrices.length = 0;
  936.             $('.prices-tier.items li.item').each(function (index) {
  937.                 $this = $(this);
  938.                 if ((first = $this.find('span.tier-price-qty')).length && (last = $this.find('.price')).length) {
  939.                     quantity = first.text()
  940.                     price = last.text()
  941.                     tierPrices.push({
  942.                         'qty': quantity,
  943.                         'price': price
  944.                     })
  945.                 }
  946.             })
  947.  
  948.             const priceContainer = $(priceIdentifier);
  949.             const originalPriceHtml = priceContainer.html();
  950.  
  951.             $(qtyIdentifier).on('change', function (event) {
  952.                 const qty = parseInt(event.target.value);
  953.                 const price = $(priceIdentifier);
  954.                 let isUpdated = false;
  955.                 tierPrices.forEach(function (tierPrice) {
  956.                     if (tierPrice.qty <= qty) {
  957.                         price.html(tierPrice.price);
  958.                         isUpdated = true;
  959.                     }
  960.                 });
  961.  
  962.                 if (!isUpdated) {
  963.                     price.html(originalPriceHtml);
  964.                 }
  965.  
  966.                 isUpdated = true;
  967.             });
  968.             var formatedPrices = tierPrices;
  969.  
  970.             function formatPrice(item) {
  971.                 var formatedArray = {
  972.                     "qty": item.qty,
  973.                     "price": parseFloat((item.price.replace(/,/, '.')).replace(/[^0-9.-]+/g, ""))
  974.                 };
  975.                 return formatedArray;
  976.             }
  977.             formatedPrices = formatedPrices.map(formatPrice);
  978.  
  979.             if (formatedPrices.length) {
  980.                 minQty = 1;
  981.                 price = $('.price').html();
  982.                 if (formatedPrices[0].qty !== minQty) {
  983.                     formatedPrices.unshift({
  984.                         'qty': parseInt(minQty),
  985.                         'price': parseFloat(price.replace(/,/, '.').replace(/[^0-9.-]+/g, ""))
  986.                     })
  987.                 }
  988.  
  989.                 tooltipConfig = {
  990.                     "enable": true,
  991.                     "percentage": 1
  992.                 };
  993.  
  994.                 $tooltipDiv = $('#tier-price-qty-tooltip');
  995.                 templateTooltip = $('#tier-price-qty-tooltip-template');
  996.                 $input = $('input#qty');
  997.                 tooltipTriggered = true;
  998.  
  999.                 function showTooltip() {
  1000.                     value = formatedPrices.find(function (element) {
  1001.                         return element.qty > parseInt(this.val());
  1002.                     }, $input);
  1003.                     index = formatedPrices.indexOf(value);
  1004.                     if (index > 0) {
  1005.                         if (!tooltipTriggered && ($input.val() >= Math.floor(value.qty * (1 - tooltipConfig.percentage)))) {
  1006.                             newPrice = value.price;
  1007.                             oldPrice = formatedPrices[index - 1].price;
  1008.                             if (!isNaN(oldPrice) && !isNaN(newPrice)) {
  1009.                                 message = templateTooltip.html();
  1010.                                 message = message.replace('%1$s', value.qty - $input.val());
  1011.                                 message = message.replace('%2$s', Math.round((1 - newPrice / oldPrice) * 100));
  1012.                                 $tooltipDiv.html(message);
  1013.                                 $yesSpan = $tooltipDiv.find('span[data-choice="yes"]');
  1014.                                 $yesSpan.on('click', function () {
  1015.                                     tooltipTriggered = true;
  1016.                                     $input.val(value.qty);
  1017.                                     $input.trigger('change');
  1018.                                 });
  1019.                                 $noSpan = $tooltipDiv.find('span[data-choice="no"]');
  1020.                                 $noSpan.on('click', function () {
  1021.                                     $tooltipDiv.removeClass('visible');
  1022.                                     $tooltipDiv.addClass('blocked');
  1023.                                     $('.blocked').css('display', 'none');
  1024.                                 });
  1025.                                 $tooltipDiv.addClass('visible');
  1026.                             } else {
  1027.                                 console.error('Unable to retrieve price');
  1028.                             }
  1029.                         } else {
  1030.                             $tooltipDiv.removeClass('visible');
  1031.                             tooltipTriggered = false;
  1032.                         }
  1033.                     } else {
  1034.                         $tooltipDiv.removeClass('visible');
  1035.                     }
  1036.                 };
  1037.                 $input.on('change', showTooltip);
  1038.                 $('.control-qty-cart').mouseover(showTooltip);
  1039.                 $('.action.primary.tocart').mouseover(showTooltip);
  1040.                 $("#qty").keydown(function (event) {
  1041.                     if (event.keyCode == 13) {
  1042.                         $(this).blur();
  1043.                         showTooltip();
  1044.                     }
  1045.                 });
  1046.                 $(".tooltip").mouseover(function () {
  1047.                     $("#qty").blur();
  1048.                 })
  1049.                 $input.trigger('change');
  1050.             }
  1051.         },
  1052.  
  1053.         /**
  1054.          * Update total price
  1055.          *
  1056.          * @private
  1057.          */
  1058.  
  1059.         _UpdatePrice: function () {
  1060.             var $widget = this,
  1061.                 $product = $widget.element.parents($widget.options.selectorProduct),
  1062.                 $productPrice = $product.find(this.options.selectorProductPrice),
  1063.                 result = $widget._getNewPrices(),
  1064.                 tierPriceHtml,
  1065.                 isShow;
  1066.  
  1067.             $productPrice.trigger(
  1068.                 'updatePrice',
  1069.                 {
  1070.                     'prices': $widget._getPrices(result, $productPrice.priceBox('option').prices)
  1071.                 }
  1072.             );
  1073.  
  1074.             isShow = typeof result != 'undefined' && result.oldPrice.amount !== result.finalPrice.amount;
  1075.  
  1076.             $product.find(this.options.slyOldPriceSelector)[isShow ? 'show' : 'hide']();
  1077.  
  1078.             if (typeof result != 'undefined' && result.tierPrices && result.tierPrices.length) {
  1079.                 if (this.options.tierPriceTemplate) {
  1080.                     tierPriceHtml = mageTemplate(
  1081.                         this.options.tierPriceTemplate,
  1082.                         {
  1083.                             'tierPrices': result.tierPrices,
  1084.                             '$t': $t,
  1085.                             'currencyFormat': this.options.jsonConfig.currencyFormat,
  1086.                             'priceUtils': priceUtils
  1087.                         }
  1088.                     );
  1089.                     $(this.options.tierPriceBlockSelector).html(tierPriceHtml).show();
  1090.                     $('.price-tier_caption').show();
  1091.                     $('.price-tier_warning').hide();
  1092.                 }
  1093.                 $widget._getTierPrices();
  1094.  
  1095.             } else {
  1096.                 $(this.options.tierPriceBlockSelector).hide();
  1097.                 $('.price-tier_caption').hide();
  1098.                 $('.price-tier_warning').show();
  1099.             }
  1100.  
  1101.  
  1102.             $(this.options.normalPriceLabelSelector).hide();
  1103.  
  1104.             _.each($('.' + this.options.classes.attributeOptionsWrapper), function (attribute) {
  1105.                 if ($(attribute).find('.' + this.options.classes.optionClass + '.selected').length === 0) {
  1106.                     if ($(attribute).find('.' + this.options.classes.selectClass).length > 0) {
  1107.                         _.each($(attribute).find('.' + this.options.classes.selectClass), function (dropdown) {
  1108.                             if ($(dropdown).val() === '0') {
  1109.                                 $(this.options.normalPriceLabelSelector).show();
  1110.                             }
  1111.                         }.bind(this));
  1112.                     } else {
  1113.                         $(this.options.normalPriceLabelSelector).show();
  1114.                     }
  1115.                 }
  1116.             }.bind(this));
  1117.         },
  1118.  
  1119.         /**
  1120.          * Get new prices for selected options
  1121.          *
  1122.          * @returns {*}
  1123.          * @private
  1124.          */
  1125.         _getNewPrices: function () {
  1126.             var $widget = this,
  1127.                 newPrices = $widget.options.jsonConfig.prices,
  1128.                 allowedProduct = this._getAllowedProductWithMinPrice(this._CalcProducts());
  1129.  
  1130.             if (!_.isEmpty(allowedProduct)) {
  1131.                 newPrices = this.options.jsonConfig.optionPrices[allowedProduct];
  1132.             }
  1133.  
  1134.             return newPrices;
  1135.         },
  1136.  
  1137.         /**
  1138.          * Get prices
  1139.          *
  1140.          * @param {Object} newPrices
  1141.          * @param {Object} displayPrices
  1142.          * @returns {*}
  1143.          * @private
  1144.          */
  1145.         _getPrices: function (newPrices, displayPrices) {
  1146.             var $widget = this;
  1147.  
  1148.             if (_.isEmpty(newPrices)) {
  1149.                 newPrices = $widget._getNewPrices();
  1150.             }
  1151.             _.each(displayPrices, function (price, code) {
  1152.  
  1153.                 if (newPrices[code]) {
  1154.                     displayPrices[code].amount = newPrices[code].amount - displayPrices[code].amount;
  1155.                 }
  1156.             });
  1157.  
  1158.             return displayPrices;
  1159.         },
  1160.  
  1161.         /**
  1162.          * Get product with minimum price from selected options.
  1163.          *
  1164.          * @param {Array} allowedProducts
  1165.          * @returns {String}
  1166.          * @private
  1167.          */
  1168.         _getAllowedProductWithMinPrice: function (allowedProducts) {
  1169.             var optionPrices = this.options.jsonConfig.optionPrices,
  1170.                 product = {},
  1171.                 optionFinalPrice, optionMinPrice;
  1172.  
  1173.             _.each(allowedProducts, function (allowedProduct) {
  1174.                 optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount);
  1175.  
  1176.                 if (_.isEmpty(product) || optionFinalPrice < optionMinPrice) {
  1177.                     optionMinPrice = optionFinalPrice;
  1178.                     product = allowedProduct;
  1179.                 }
  1180.             }, this);
  1181.  
  1182.             return product;
  1183.         },
  1184.  
  1185.         /**
  1186.          * Gets all product media and change current to the needed one
  1187.          *
  1188.          * @private
  1189.          */
  1190.         _LoadProductMedia: function () {
  1191.             var $widget = this,
  1192.                 $this = $widget.element,
  1193.                 productData = this._determineProductData(),
  1194.                 mediaCallData,
  1195.                 mediaCacheKey,
  1196.  
  1197.                 /**
  1198.                  * Processes product media data
  1199.                  *
  1200.                  * @param {Object} data
  1201.                  * @returns void
  1202.                  */
  1203.                 mediaSuccessCallback = function (data) {
  1204.                     if (!(mediaCacheKey in $widget.options.mediaCache)) {
  1205.                         $widget.options.mediaCache[mediaCacheKey] = data;
  1206.                     }
  1207.                     $widget._ProductMediaCallback($this, data, productData.isInProductView);
  1208.                     setTimeout(function () {
  1209.                         $widget._DisableProductMediaLoader($this);
  1210.                     }, 300);
  1211.                 };
  1212.  
  1213.             if (!$widget.options.mediaCallback) {
  1214.                 return;
  1215.             }
  1216.  
  1217.             mediaCallData = {
  1218.                 'product_id': this.getProduct()
  1219.             };
  1220.  
  1221.             mediaCacheKey = JSON.stringify(mediaCallData);
  1222.  
  1223.             if (mediaCacheKey in $widget.options.mediaCache) {
  1224.                 $widget._XhrKiller();
  1225.                 $widget._EnableProductMediaLoader($this);
  1226.                 mediaSuccessCallback($widget.options.mediaCache[mediaCacheKey]);
  1227.             } else {
  1228.                 mediaCallData.isAjax = true;
  1229.                 $widget._XhrKiller();
  1230.                 $widget._EnableProductMediaLoader($this);
  1231.                 $widget.xhr = $.ajax({
  1232.                     url: $widget.options.mediaCallback,
  1233.                     cache: true,
  1234.                     type: 'GET',
  1235.                     dataType: 'json',
  1236.                     data: mediaCallData,
  1237.                     success: mediaSuccessCallback
  1238.                 }).done(function () {
  1239.                     $widget._XhrKiller();
  1240.                 });
  1241.             }
  1242.         },
  1243.  
  1244.         /**
  1245.          * Enable loader
  1246.          *
  1247.          * @param {Object} $this
  1248.          * @private
  1249.          */
  1250.         _EnableProductMediaLoader: function ($this) {
  1251.             var $widget = this;
  1252.  
  1253.             if ($('body.catalog-product-view').length > 0) {
  1254.                 $this.parents('.column.main').find('.photo.image')
  1255.                     .addClass($widget.options.classes.loader);
  1256.             } else {
  1257.                 //Category View
  1258.                 $this.parents('.product-item-info').find('.product-image-photo')
  1259.                     .addClass($widget.options.classes.loader);
  1260.             }
  1261.         },
  1262.  
  1263.         /**
  1264.          * Disable loader
  1265.          *
  1266.          * @param {Object} $this
  1267.          * @private
  1268.          */
  1269.         _DisableProductMediaLoader: function ($this) {
  1270.             var $widget = this;
  1271.  
  1272.             if ($('body.catalog-product-view').length > 0) {
  1273.                 $this.parents('.column.main').find('.photo.image')
  1274.                     .removeClass($widget.options.classes.loader);
  1275.             } else {
  1276.                 //Category View
  1277.                 $this.parents('.product-item-info').find('.product-image-photo')
  1278.                     .removeClass($widget.options.classes.loader);
  1279.             }
  1280.         },
  1281.  
  1282.         /**
  1283.          * Callback for product media
  1284.          *
  1285.          * @param {Object} $this
  1286.          * @param {String} response
  1287.          * @param {Boolean} isInProductView
  1288.          * @private
  1289.          */
  1290.         _ProductMediaCallback: function ($this, response, isInProductView) {
  1291.             var $main = isInProductView ? $this.parents('.column.main') : $this.parents('.product-item-info'),
  1292.                 $widget = this,
  1293.                 images = [],
  1294.  
  1295.                 /**
  1296.                  * Check whether object supported or not
  1297.                  *
  1298.                  * @param {Object} e
  1299.                  * @returns {*|Boolean}
  1300.                  */
  1301.                 support = function (e) {
  1302.                     return e.hasOwnProperty('large') && e.hasOwnProperty('medium') && e.hasOwnProperty('small');
  1303.                 };
  1304.  
  1305.             if (_.size($widget) < 1 || !support(response)) {
  1306.                 this.updateBaseImage(this.options.mediaGalleryInitial, $main, isInProductView);
  1307.  
  1308.                 return;
  1309.             }
  1310.  
  1311.             images.push({
  1312.                 full: response.large,
  1313.                 img: response.medium,
  1314.                 thumb: response.small,
  1315.                 isMain: true
  1316.             });
  1317.  
  1318.             if (response.hasOwnProperty('gallery')) {
  1319.                 $.each(response.gallery, function () {
  1320.                     if (!support(this) || response.large === this.large) {
  1321.                         return;
  1322.                     }
  1323.                     images.push({
  1324.                         full: this.large,
  1325.                         img: this.medium,
  1326.                         thumb: this.small
  1327.                     });
  1328.                 });
  1329.             }
  1330.  
  1331.             this.updateBaseImage(images, $main, isInProductView);
  1332.         },
  1333.  
  1334.         /**
  1335.          * Check if images to update are initial and set their type
  1336.          * @param {Array} images
  1337.          */
  1338.         _setImageType: function (images) {
  1339.             var initial = this.options.mediaGalleryInitial[0].img;
  1340.  
  1341.             if (images[0].img === initial) {
  1342.                 images = $.extend(true, [], this.options.mediaGalleryInitial);
  1343.             } else {
  1344.                 images.map(function (img) {
  1345.                     if (!img.type) {
  1346.                         img.type = 'image';
  1347.                     }
  1348.                 });
  1349.             }
  1350.  
  1351.             return images;
  1352.         },
  1353.  
  1354.         /**
  1355.          * Update [gallery-placeholder] or [product-image-photo]
  1356.          * @param {Array} images
  1357.          * @param {jQuery} context
  1358.          * @param {Boolean} isInProductView
  1359.          */
  1360.         updateBaseImage: function (images, context, isInProductView) {
  1361.             var justAnImage = images[0],
  1362.                 initialImages = this.options.mediaGalleryInitial,
  1363.                 imagesToUpdate,
  1364.                 gallery = context.find(this.options.mediaGallerySelector).data('gallery'),
  1365.                 isInitial;
  1366.  
  1367.             if (isInProductView) {
  1368.                 imagesToUpdate = images.length ? this._setImageType($.extend(true, [], images)) : [];
  1369.                 isInitial = _.isEqual(imagesToUpdate, initialImages);
  1370.  
  1371.                 if (this.options.gallerySwitchStrategy === 'prepend' && !isInitial) {
  1372.                     imagesToUpdate = imagesToUpdate.concat(initialImages);
  1373.                 }
  1374.  
  1375.                 imagesToUpdate = this._setImageIndex(imagesToUpdate);
  1376.  
  1377.                 if (!_.isUndefined(gallery)) {
  1378.                     gallery.updateData(imagesToUpdate);
  1379.                 } else {
  1380.                     context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) {
  1381.                         loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery');
  1382.                         loadedGallery.updateData(imagesToUpdate);
  1383.                     }.bind(this));
  1384.                 }
  1385.  
  1386.                 if (isInitial) {
  1387.                     $(this.options.mediaGallerySelector).AddFotoramaVideoEvents();
  1388.                 } else {
  1389.                     $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({
  1390.                         selectedOption: this.getProduct(),
  1391.                         dataMergeStrategy: this.options.gallerySwitchStrategy
  1392.                     });
  1393.                 }
  1394.  
  1395.                 if (gallery) {
  1396.                     gallery.first();
  1397.                 }
  1398.             } else if (justAnImage && justAnImage.img) {
  1399.                 context.find('.product-image-photo').attr('src', justAnImage.img);
  1400.             }
  1401.         },
  1402.  
  1403.         /**
  1404.          * Set correct indexes for image set.
  1405.          *
  1406.          * @param {Array} images
  1407.          * @private
  1408.          */
  1409.         _setImageIndex: function (images) {
  1410.             var length = images.length,
  1411.                 i;
  1412.  
  1413.             for (i = 0; length > i; i++) {
  1414.                 images[i].i = i + 1;
  1415.             }
  1416.  
  1417.             return images;
  1418.         },
  1419.  
  1420.         /**
  1421.          * Kill doubled AJAX requests
  1422.          *
  1423.          * @private
  1424.          */
  1425.         _XhrKiller: function () {
  1426.             var $widget = this;
  1427.  
  1428.             if ($widget.xhr !== undefined && $widget.xhr !== null) {
  1429.                 $widget.xhr.abort();
  1430.                 $widget.xhr = null;
  1431.             }
  1432.         },
  1433.  
  1434.         /**
  1435.          * Emulate mouse click on all swatches that should be selected
  1436.          * @param {Object} [selectedAttributes]
  1437.          * @private
  1438.          */
  1439.         _EmulateSelected: function (selectedAttributes) {
  1440.             $.each(selectedAttributes, $.proxy(function (attributeCode, optionId) {
  1441.                 var elem = this.element.find('.' + this.options.classes.attributeClass +
  1442.                     '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]'),
  1443.                     parentInput = elem.parent();
  1444.  
  1445.                 if (elem.hasClass('selected')) {
  1446.                     return;
  1447.                 }
  1448.  
  1449.                 if (parentInput.hasClass(this.options.classes.selectClass)) {
  1450.                     parentInput.val(optionId);
  1451.                     parentInput.trigger('change');
  1452.                 } else {
  1453.                     elem.trigger('click');
  1454.                 }
  1455.             }, this));
  1456.         },
  1457.  
  1458.         /**
  1459.          * Emulate mouse click or selection change on all swatches that should be selected
  1460.          * @param {Object} [selectedAttributes]
  1461.          * @private
  1462.          */
  1463.         _EmulateSelectedByAttributeId: function (selectedAttributes) {
  1464.             $.each(selectedAttributes, $.proxy(function (attributeId, optionId) {
  1465.                 var elem = this.element.find('.' + this.options.classes.attributeClass +
  1466.                     '[attribute-id="' + attributeId + '"] [option-id="' + optionId + '"]'),
  1467.                     parentInput = elem.parent();
  1468.  
  1469.                 if (elem.hasClass('selected')) {
  1470.                     return;
  1471.                 }
  1472.  
  1473.                 if (parentInput.hasClass(this.options.classes.selectClass)) {
  1474.                     parentInput.val(optionId);
  1475.                     parentInput.trigger('change');
  1476.                 } else {
  1477.                     elem.trigger('click');
  1478.                 }
  1479.             }, this));
  1480.         },
  1481.  
  1482.         /**
  1483.          * Get default options values settings with either URL query parameters
  1484.          * @private
  1485.          */
  1486.         _getSelectedAttributes: function () {
  1487.             var hashIndex = window.location.href.indexOf('#'),
  1488.                 selectedAttributes = {},
  1489.                 params;
  1490.  
  1491.             if (hashIndex !== -1) {
  1492.                 params = $.parseQuery(window.location.href.substr(hashIndex + 1));
  1493.  
  1494.                 selectedAttributes = _.invert(_.mapObject(_.invert(params), function (attributeId) {
  1495.                     var attribute = this.options.jsonConfig.mappedAttributes[attributeId];
  1496.  
  1497.                     return attribute ? attribute.code : attributeId;
  1498.                 }.bind(this)));
  1499.             }
  1500.  
  1501.             return selectedAttributes;
  1502.         },
  1503.  
  1504.         /**
  1505.          * Callback which fired after gallery gets initialized.
  1506.          *
  1507.          * @param {HTMLElement} element - DOM element associated with a gallery.
  1508.          */
  1509.         _onGalleryLoaded: function (element) {
  1510.             var galleryObject = element.data('gallery');
  1511.  
  1512.             this.options.mediaGalleryInitial = galleryObject.returnCurrentImages();
  1513.         },
  1514.  
  1515.         /**
  1516.          * Sets mediaCache for cases when jsonConfig contains preSelectedGallery on layered navigation result pages
  1517.          *
  1518.          * @private
  1519.          */
  1520.         _setPreSelectedGallery: function () {
  1521.             var mediaCallData;
  1522.  
  1523.             if (this.options.jsonConfig.preSelectedGallery) {
  1524.                 mediaCallData = {
  1525.                     'product_id': this.getProduct()
  1526.                 };
  1527.  
  1528.                 this.options.mediaCache[JSON.stringify(mediaCallData)] = this.options.jsonConfig.preSelectedGallery;
  1529.             }
  1530.         }
  1531.     });
  1532.  
  1533.     return $.mage.SwatchRenderer;
  1534. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement