Advertisement
Guest User

Untitled

a guest
Dec 29th, 2021
79
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.  
  582.             return html;
  583.         },
  584.  
  585.         /**
  586.          * Render select by part of config
  587.          *
  588.          * @param {Object} config
  589.          * @param {String} chooseText
  590.          * @returns {String}
  591.          * @private
  592.          */
  593.         _RenderSwatchSelect: function (config, chooseText) {
  594.             var html;
  595.  
  596.             if (this.options.jsonSwatchConfig.hasOwnProperty(config.id)) {
  597.                 return '';
  598.             }
  599.  
  600.             html =
  601.                 '<select class="' + this.options.classes.selectClass + ' ' + config.code + '">' +
  602.                 '<option value="0" option-id="0">' + chooseText + '</option>';
  603.  
  604.             $.each(config.options, function () {
  605.                 var label = this.label,
  606.                     attr = ' value="' + this.id + '" option-id="' + this.id + '"';
  607.  
  608.                 if (!this.hasOwnProperty('products') || this.products.length <= 0) {
  609.                     attr += ' option-empty="true"';
  610.                 }
  611.  
  612.                 html += '<option ' + attr + '>' + label + '</option>';
  613.             });
  614.  
  615.             html += '</select>';
  616.  
  617.             return html;
  618.         },
  619.  
  620.         /**
  621.          * Input for submit form.
  622.          * This control shouldn't have "type=hidden", "display: none" for validation work :(
  623.          *
  624.          * @param {Object} config
  625.          * @private
  626.          */
  627.         _RenderFormInput: function (config) {
  628.             return '<input class="' + this.options.classes.attributeInput + ' super-attribute-select" ' +
  629.                 'name="super_attribute[' + config.id + ']" ' +
  630.                 'type="text" ' +
  631.                 'value="" ' +
  632.                 'data-selector="super_attribute[' + config.id + ']" ' +
  633.                 'data-validate="{required: true}" ' +
  634.                 'aria-required="true" ' +
  635.                 'aria-invalid="false">';
  636.         },
  637.  
  638.         /**
  639.          * Event listener
  640.          *
  641.          * @private
  642.          */
  643.         _EventListener: function () {
  644.             var $widget = this,
  645.                 options = this.options.classes,
  646.                 target;
  647.  
  648.             $widget.element.on('click', '.' + options.optionClass, function () {
  649.                 return $widget._OnClick($(this), $widget);
  650.             });
  651.  
  652.             $widget.element.on('change', '.' + options.selectClass, function () {
  653.                 return $widget._OnChange($(this), $widget);
  654.             });
  655.  
  656.             $widget.element.on('click', '.' + options.moreButton, function (e) {
  657.                 e.preventDefault();
  658.  
  659.                 return $widget._OnMoreClick($(this));
  660.             });
  661.  
  662.             $widget.element.on('keydown', function (e) {
  663.                 if (e.which === 13) {
  664.                     target = $(e.target);
  665.  
  666.                     if (target.is('.' + options.optionClass)) {
  667.                         return $widget._OnClick(target, $widget);
  668.                     } else if (target.is('.' + options.selectClass)) {
  669.                         return $widget._OnChange(target, $widget);
  670.                     } else if (target.is('.' + options.moreButton)) {
  671.                         e.preventDefault();
  672.  
  673.                         return $widget._OnMoreClick(target);
  674.                     }
  675.                 }
  676.             });
  677.         },
  678.  
  679.         /**
  680.          * Load media gallery using ajax or json config.
  681.          *
  682.          * @private
  683.          */
  684.         _loadMedia: function () {
  685.             var $main = this.inProductList ?
  686.                 this.element.parents('.product-item-info') :
  687.                 this.element.parents('.column.main'),
  688.                 images;
  689.  
  690.             if (this.options.useAjax) {
  691.                 this._debouncedLoadProductMedia();
  692.             } else {
  693.                 images = this.options.jsonConfig.images[this.getProduct()];
  694.  
  695.                 if (!images) {
  696.                     images = this.options.mediaGalleryInitial;
  697.                 }
  698.                 this.updateBaseImage(this._sortImages(images), $main, !this.inProductList);
  699.             }
  700.         },
  701.  
  702.         /**
  703.          * Sorting images array
  704.          *
  705.          * @private
  706.          */
  707.         _sortImages: function (images) {
  708.             return _.sortBy(images, function (image) {
  709.                 return parseInt(image.position, 10);
  710.             });
  711.         },
  712.  
  713.         /**
  714.          * Event for swatch options
  715.          *
  716.          * @param {Object} $this
  717.          * @param {Object} $widget
  718.          * @private
  719.          */
  720.         _OnClick: function ($this, $widget) {
  721.             var $parent = $this.parents('.' + $widget.options.classes.attributeClass),
  722.                 $wrapper = $this.parents('.' + $widget.options.classes.attributeOptionsWrapper),
  723.                 $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass),
  724.                 attributeId = $parent.attr('attribute-id'),
  725.                 $input = $parent.find('.' + $widget.options.classes.attributeInput),
  726.                 checkAdditionalData = JSON.parse(this.options.jsonSwatchConfig[attributeId]['additional_data']);
  727.  
  728.             if ($widget.inProductList) {
  729.                 $input = $widget.productForm.find(
  730.                     '.' + $widget.options.classes.attributeInput + '[name="super_attribute[' + attributeId + ']"]'
  731.                 );
  732.             }
  733.  
  734.             if ($this.hasClass('disabled')) {
  735.                 return;
  736.             }
  737.  
  738.             if ($this.hasClass('selected')) {
  739.                 $parent.removeAttr('option-selected').find('.selected').removeClass('selected');
  740.                 $input.val('');
  741.                 $label.text('');
  742.                 $this.attr('aria-checked', false);
  743.             } else {
  744.                 $parent.attr('option-selected', $this.attr('option-id')).find('.selected').removeClass('selected');
  745.                 $label.text($this.attr('option-label'));
  746.                 $input.val($this.attr('option-id'));
  747.                 $input.attr('data-attr-name', this._getAttributeCodeById(attributeId));
  748.                 $this.addClass('selected');
  749.                 $widget._toggleCheckedAttributes($this, $wrapper);
  750.             }
  751.  
  752.             $widget._Rebuild();
  753.  
  754.             if ($widget.element.parents($widget.options.selectorProduct)
  755.                 .find(this.options.selectorProductPrice).is(':data(mage-priceBox)')
  756.             ) {
  757.                 $widget._UpdatePrice();
  758.             }
  759.  
  760.             $(document).trigger('updateMsrpPriceBlock',
  761.                 [
  762.                     _.findKey($widget.options.jsonConfig.index, $widget.options.jsonConfig.defaultValues),
  763.                     $widget.options.jsonConfig.optionPrices
  764.                 ]);
  765.  
  766.             if (parseInt(checkAdditionalData['update_product_preview_image'], 10) === 1) {
  767.                 $widget._loadMedia();
  768.             }
  769.  
  770.             $input.trigger('change');
  771.             $widget._getTierPrices();
  772.         },
  773.  
  774.         /**
  775.          * Get human readable attribute code (eg. size, color) by it ID from configuration
  776.          *
  777.          * @param {Number} attributeId
  778.          * @returns {*}
  779.          * @private
  780.          */
  781.         _getAttributeCodeById: function (attributeId) {
  782.             var attribute = this.options.jsonConfig.mappedAttributes[attributeId];
  783.  
  784.             return attribute ? attribute.code : attributeId;
  785.         },
  786.  
  787.         /**
  788.          * Toggle accessibility attributes
  789.          *
  790.          * @param {Object} $this
  791.          * @param {Object} $wrapper
  792.          * @private
  793.          */
  794.         _toggleCheckedAttributes: function ($this, $wrapper) {
  795.             $wrapper.attr('aria-activedescendant', $this.attr('id'))
  796.                 .find('.' + this.options.classes.optionClass).attr('aria-checked', false);
  797.             $this.attr('aria-checked', true);
  798.         },
  799.  
  800.         /**
  801.          * Event for select
  802.          *
  803.          * @param {Object} $this
  804.          * @param {Object} $widget
  805.          * @private
  806.          */
  807.         _OnChange: function ($this, $widget) {
  808.             var $parent = $this.parents('.' + $widget.options.classes.attributeClass),
  809.                 attributeId = $parent.attr('attribute-id'),
  810.                 $input = $parent.find('.' + $widget.options.classes.attributeInput);
  811.  
  812.             if ($widget.productForm.length > 0) {
  813.                 $input = $widget.productForm.find(
  814.                     '.' + $widget.options.classes.attributeInput + '[name="super_attribute[' + attributeId + ']"]'
  815.                 );
  816.             }
  817.  
  818.             if ($this.val() > 0) {
  819.                 $parent.attr('option-selected', $this.val());
  820.                 $input.val($this.val());
  821.             } else {
  822.                 $parent.removeAttr('option-selected');
  823.                 $input.val('');
  824.             }
  825.  
  826.             $widget._Rebuild();
  827.             $widget._UpdatePrice();
  828.             $widget._loadMedia();
  829.             $input.trigger('change');
  830.             $widget._getTierPrices();
  831.         },
  832.  
  833.         /**
  834.          * Event for more switcher
  835.          *
  836.          * @param {Object} $this
  837.          * @private
  838.          */
  839.         _OnMoreClick: function ($this) {
  840.             $this.nextAll().show();
  841.             $this.blur().remove();
  842.         },
  843.  
  844.         /**
  845.          * Rewind options for controls
  846.          *
  847.          * @private
  848.          */
  849.         _Rewind: function (controls) {
  850.             controls.find('div[option-id], option[option-id]').removeClass('disabled').removeAttr('disabled');
  851.             controls.find('div[option-empty], option[option-empty]')
  852.                 .attr('disabled', true)
  853.                 .addClass('disabled')
  854.                 .attr('tabindex', '-1');
  855.         },
  856.  
  857.         /**
  858.          * Rebuild container
  859.          *
  860.          * @private
  861.          */
  862.         _Rebuild: function () {
  863.             var $widget = this,
  864.                 controls = $widget.element.find('.' + $widget.options.classes.attributeClass + '[attribute-id]'),
  865.                 selected = controls.filter('[option-selected]');
  866.  
  867.             // Enable all options
  868.             $widget._Rewind(controls);
  869.  
  870.             // done if nothing selected
  871.             if (selected.length <= 0) {
  872.                 return;
  873.             }
  874.  
  875.             // Disable not available options
  876.             controls.each(function () {
  877.                 var $this = $(this),
  878.                     id = $this.attr('attribute-id'),
  879.                     products = $widget._CalcProducts(id);
  880.  
  881.                 if (selected.length === 1 && selected.first().attr('attribute-id') === id) {
  882.                     return;
  883.                 }
  884.  
  885.                 $this.find('[option-id]').each(function () {
  886.                     var $element = $(this),
  887.                         option = $element.attr('option-id');
  888.  
  889.                     if (!$widget.optionsMap.hasOwnProperty(id) || !$widget.optionsMap[id].hasOwnProperty(option) ||
  890.                         $element.hasClass('selected') ||
  891.                         $element.is(':selected')) {
  892.                         return;
  893.                     }
  894.  
  895.                     if (_.intersection(products, $widget.optionsMap[id][option].products).length <= 0) {
  896.                         $element.attr('disabled', true).addClass('disabled');
  897.                     }
  898.                 });
  899.             });
  900.         },
  901.  
  902.         /**
  903.          * Get selected product list
  904.          *
  905.          * @returns {Array}
  906.          * @private
  907.          */
  908.         _CalcProducts: function ($skipAttributeId) {
  909.             var $widget = this,
  910.                 products = [];
  911.  
  912.             // Generate intersection of products
  913.             $widget.element.find('.' + $widget.options.classes.attributeClass + '[option-selected]').each(function () {
  914.                 var id = $(this).attr('attribute-id'),
  915.                     option = $(this).attr('option-selected');
  916.  
  917.                 if ($skipAttributeId !== undefined && $skipAttributeId === id) {
  918.                     return;
  919.                 }
  920.  
  921.                 if (!$widget.optionsMap.hasOwnProperty(id) || !$widget.optionsMap[id].hasOwnProperty(option)) {
  922.                     return;
  923.                 }
  924.  
  925.                 if (products.length === 0) {
  926.                     products = $widget.optionsMap[id][option].products;
  927.                 } else {
  928.                     products = _.intersection(products, $widget.optionsMap[id][option].products);
  929.                 }
  930.             });
  931.  
  932.             return products;
  933.         },
  934.  
  935.         _getTierPrices: function () {
  936.             const qtyIdentifier = '#qty';
  937.             const priceIdentifier = '.normal-price .price';
  938.             var tierPrices = [];
  939.             tierPrices.length = 0;
  940.             $('.prices-tier.items li.item').each(function (index) {
  941.                 let $this = $(this);
  942.                 if ((first = $this.find('span.tier-price-qty')).length && (last = $this.find('.price')).length) {
  943.                     quantity = first.text()
  944.                     price = last.text()
  945.                     tierPrices.push({
  946.                         'qty': quantity,
  947.                         'price': price
  948.                     })
  949.                 }
  950.             })
  951.  
  952.             const priceContainer = $(priceIdentifier);
  953.             const originalPriceHtml = priceContainer.html();
  954.  
  955.             $(qtyIdentifier).on('change', function (event) {
  956.                 const qty = parseInt(event.target.value);
  957.                 const price = $(priceIdentifier);
  958.                 let isUpdated = false;
  959.                 tierPrices.forEach(function (tierPrice) {
  960.                     if (tierPrice.qty <= qty) {
  961.                         price.html(tierPrice.price);
  962.                         isUpdated = true;
  963.                     }
  964.                 });
  965.  
  966.                 if (!isUpdated) {
  967.                     price.html(originalPriceHtml);
  968.                 }
  969.  
  970.                 isUpdated = true;
  971.             });
  972.             var formatedPrices = tierPrices;
  973.  
  974.             function formatPrice(item) {
  975.                 var formatedArray = {
  976.                     "qty": item.qty,
  977.                     "price": parseFloat((item.price.replace(/,/, '.')).replace(/[^0-9.-]+/g, ""))
  978.                 };
  979.                 return formatedArray;
  980.             }
  981.             formatedPrices = formatedPrices.map(formatPrice);
  982.  
  983.             if (formatedPrices.length) {
  984.                 minQty = 1;
  985.                 price = $('.price').html();
  986.                 if (formatedPrices[0].qty !== minQty) {
  987.                     formatedPrices.unshift({
  988.                         'qty': parseInt(minQty),
  989.                         'price': parseFloat(price.replace(/,/, '.').replace(/[^0-9.-]+/g, ""))
  990.                     })
  991.                 }
  992.  
  993.                 tooltipConfig = {
  994.                     "enable": true,
  995.                     "percentage": 1
  996.                 };
  997.  
  998.                 $tooltipDiv = $('#tier-price-qty-tooltip');
  999.                 templateTooltip = $('#tier-price-qty-tooltip-template');
  1000.                 $input = $('input#qty');
  1001.                 tooltipTriggered = true;
  1002.  
  1003.                 function showTooltip() {
  1004.                     value = formatedPrices.find(function (element) {
  1005.                         return element.qty > parseInt(this.val());
  1006.                     }, $input);
  1007.                     index = formatedPrices.indexOf(value);
  1008.                     if (index > 0) {
  1009.                         if (!tooltipTriggered && ($input.val() >= Math.floor(value.qty * (1 - tooltipConfig.percentage)))) {
  1010.                             newPrice = value.price;
  1011.                             oldPrice = formatedPrices[index - 1].price;
  1012.                             if (!isNaN(oldPrice) && !isNaN(newPrice)) {
  1013.                                 message = templateTooltip.html();
  1014.                                 message = message.replace('%1$s', value.qty - $input.val());
  1015.                                 message = message.replace('%2$s', Math.round((1 - newPrice / oldPrice) * 100));
  1016.                                 $tooltipDiv.html(message);
  1017.                                 $yesSpan = $tooltipDiv.find('span[data-choice="yes"]');
  1018.                                 $yesSpan.on('click', function () {
  1019.                                     tooltipTriggered = true;
  1020.                                     $input.val(value.qty);
  1021.                                     $input.trigger('change');
  1022.                                 });
  1023.                                 $noSpan = $tooltipDiv.find('span[data-choice="no"]');
  1024.                                 $noSpan.on('click', function () {
  1025.                                     $tooltipDiv.removeClass('visible');
  1026.                                     $tooltipDiv.addClass('blocked');
  1027.                                     $('.blocked').css('display', 'none');
  1028.                                 });
  1029.                                 $tooltipDiv.addClass('visible');
  1030.                             } else {
  1031.                                 console.error('Unable to retrieve price');
  1032.                             }
  1033.                         } else {
  1034.                             $tooltipDiv.removeClass('visible');
  1035.                             tooltipTriggered = false;
  1036.                         }
  1037.                     } else {
  1038.                         $tooltipDiv.removeClass('visible');
  1039.                     }
  1040.                 };
  1041.                 $input.on('change', showTooltip);
  1042.                 $('.control-qty-cart').mouseover(showTooltip);
  1043.                 $('.action.primary.tocart').mouseover(showTooltip);
  1044.                 $("#qty").keydown(function (event) {
  1045.                     if (event.keyCode == 13) {
  1046.                         $(this).blur();
  1047.                         showTooltip();
  1048.                     }
  1049.                 });
  1050.                 $(".tooltip").mouseover(function () {
  1051.                     $("#qty").blur();
  1052.                 })
  1053.                 $input.trigger('change');
  1054.             }
  1055.         },
  1056.  
  1057.         /**
  1058.          * Update total price
  1059.          *
  1060.          * @private
  1061.          */
  1062.  
  1063.         _UpdatePrice: function () {
  1064.             var $widget = this,
  1065.                 $product = $widget.element.parents($widget.options.selectorProduct),
  1066.                 $productPrice = $product.find(this.options.selectorProductPrice),
  1067.                 result = $widget._getNewPrices(),
  1068.                 tierPriceHtml,
  1069.                 isShow;
  1070.  
  1071.             $productPrice.trigger(
  1072.                 'updatePrice',
  1073.                 {
  1074.                     'prices': $widget._getPrices(result, $productPrice.priceBox('option').prices)
  1075.                 }
  1076.             );
  1077.  
  1078.             isShow = typeof result != 'undefined' && result.oldPrice.amount !== result.finalPrice.amount;
  1079.  
  1080.             $product.find(this.options.slyOldPriceSelector)[isShow ? 'show' : 'hide']();
  1081.  
  1082.             if (typeof result != 'undefined' && result.tierPrices && result.tierPrices.length) {
  1083.                 if (this.options.tierPriceTemplate) {
  1084.                     tierPriceHtml = mageTemplate(
  1085.                         this.options.tierPriceTemplate,
  1086.                         {
  1087.                             'tierPrices': result.tierPrices,
  1088.                             '$t': $t,
  1089.                             'currencyFormat': this.options.jsonConfig.currencyFormat,
  1090.                             'priceUtils': priceUtils
  1091.                         }
  1092.                     );
  1093.                     $(this.options.tierPriceBlockSelector).html(tierPriceHtml).show();
  1094.                     $('.price-tier_caption').show();
  1095.                     $('.price-tier_warning').hide();
  1096.                 }
  1097.                 /* $('.swatch-option').on('click', function () { getTierPrices(); console.log("click work") });
  1098.                 $('.swatch-select').on('change', function () { getTierPrices(); console.log('change work') }); */
  1099.  
  1100.             } else {
  1101.                 $(this.options.tierPriceBlockSelector).hide();
  1102.                 $('.price-tier_caption').hide();
  1103.                 $('.price-tier_warning').show();
  1104.             }
  1105.  
  1106.  
  1107.             $(this.options.normalPriceLabelSelector).hide();
  1108.  
  1109.             _.each($('.' + this.options.classes.attributeOptionsWrapper), function (attribute) {
  1110.                 if ($(attribute).find('.' + this.options.classes.optionClass + '.selected').length === 0) {
  1111.                     if ($(attribute).find('.' + this.options.classes.selectClass).length > 0) {
  1112.                         _.each($(attribute).find('.' + this.options.classes.selectClass), function (dropdown) {
  1113.                             if ($(dropdown).val() === '0') {
  1114.                                 $(this.options.normalPriceLabelSelector).show();
  1115.                             }
  1116.                         }.bind(this));
  1117.                     } else {
  1118.                         $(this.options.normalPriceLabelSelector).show();
  1119.                     }
  1120.                 }
  1121.             }.bind(this));
  1122.         },
  1123.  
  1124.         /**
  1125.          * Get new prices for selected options
  1126.          *
  1127.          * @returns {*}
  1128.          * @private
  1129.          */
  1130.         _getNewPrices: function () {
  1131.             var $widget = this,
  1132.                 newPrices = $widget.options.jsonConfig.prices,
  1133.                 allowedProduct = this._getAllowedProductWithMinPrice(this._CalcProducts());
  1134.  
  1135.             if (!_.isEmpty(allowedProduct)) {
  1136.                 newPrices = this.options.jsonConfig.optionPrices[allowedProduct];
  1137.             }
  1138.  
  1139.             return newPrices;
  1140.         },
  1141.  
  1142.         /**
  1143.          * Get prices
  1144.          *
  1145.          * @param {Object} newPrices
  1146.          * @param {Object} displayPrices
  1147.          * @returns {*}
  1148.          * @private
  1149.          */
  1150.         _getPrices: function (newPrices, displayPrices) {
  1151.             var $widget = this;
  1152.  
  1153.             if (_.isEmpty(newPrices)) {
  1154.                 newPrices = $widget._getNewPrices();
  1155.             }
  1156.             _.each(displayPrices, function (price, code) {
  1157.  
  1158.                 if (newPrices[code]) {
  1159.                     displayPrices[code].amount = newPrices[code].amount - displayPrices[code].amount;
  1160.                 }
  1161.             });
  1162.  
  1163.             return displayPrices;
  1164.         },
  1165.  
  1166.         /**
  1167.          * Get product with minimum price from selected options.
  1168.          *
  1169.          * @param {Array} allowedProducts
  1170.          * @returns {String}
  1171.          * @private
  1172.          */
  1173.         _getAllowedProductWithMinPrice: function (allowedProducts) {
  1174.             var optionPrices = this.options.jsonConfig.optionPrices,
  1175.                 product = {},
  1176.                 optionFinalPrice, optionMinPrice;
  1177.  
  1178.             _.each(allowedProducts, function (allowedProduct) {
  1179.                 optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount);
  1180.  
  1181.                 if (_.isEmpty(product) || optionFinalPrice < optionMinPrice) {
  1182.                     optionMinPrice = optionFinalPrice;
  1183.                     product = allowedProduct;
  1184.                 }
  1185.             }, this);
  1186.  
  1187.             return product;
  1188.         },
  1189.  
  1190.         /**
  1191.          * Gets all product media and change current to the needed one
  1192.          *
  1193.          * @private
  1194.          */
  1195.         _LoadProductMedia: function () {
  1196.             var $widget = this,
  1197.                 $this = $widget.element,
  1198.                 productData = this._determineProductData(),
  1199.                 mediaCallData,
  1200.                 mediaCacheKey,
  1201.  
  1202.                 /**
  1203.                  * Processes product media data
  1204.                  *
  1205.                  * @param {Object} data
  1206.                  * @returns void
  1207.                  */
  1208.                 mediaSuccessCallback = function (data) {
  1209.                     if (!(mediaCacheKey in $widget.options.mediaCache)) {
  1210.                         $widget.options.mediaCache[mediaCacheKey] = data;
  1211.                     }
  1212.                     $widget._ProductMediaCallback($this, data, productData.isInProductView);
  1213.                     setTimeout(function () {
  1214.                         $widget._DisableProductMediaLoader($this);
  1215.                     }, 300);
  1216.                 };
  1217.  
  1218.             if (!$widget.options.mediaCallback) {
  1219.                 return;
  1220.             }
  1221.  
  1222.             mediaCallData = {
  1223.                 'product_id': this.getProduct()
  1224.             };
  1225.  
  1226.             mediaCacheKey = JSON.stringify(mediaCallData);
  1227.  
  1228.             if (mediaCacheKey in $widget.options.mediaCache) {
  1229.                 $widget._XhrKiller();
  1230.                 $widget._EnableProductMediaLoader($this);
  1231.                 mediaSuccessCallback($widget.options.mediaCache[mediaCacheKey]);
  1232.             } else {
  1233.                 mediaCallData.isAjax = true;
  1234.                 $widget._XhrKiller();
  1235.                 $widget._EnableProductMediaLoader($this);
  1236.                 $widget.xhr = $.ajax({
  1237.                     url: $widget.options.mediaCallback,
  1238.                     cache: true,
  1239.                     type: 'GET',
  1240.                     dataType: 'json',
  1241.                     data: mediaCallData,
  1242.                     success: mediaSuccessCallback
  1243.                 }).done(function () {
  1244.                     $widget._XhrKiller();
  1245.                 });
  1246.             }
  1247.         },
  1248.  
  1249.         /**
  1250.          * Enable loader
  1251.          *
  1252.          * @param {Object} $this
  1253.          * @private
  1254.          */
  1255.         _EnableProductMediaLoader: function ($this) {
  1256.             var $widget = this;
  1257.  
  1258.             if ($('body.catalog-product-view').length > 0) {
  1259.                 $this.parents('.column.main').find('.photo.image')
  1260.                     .addClass($widget.options.classes.loader);
  1261.             } else {
  1262.                 //Category View
  1263.                 $this.parents('.product-item-info').find('.product-image-photo')
  1264.                     .addClass($widget.options.classes.loader);
  1265.             }
  1266.         },
  1267.  
  1268.         /**
  1269.          * Disable loader
  1270.          *
  1271.          * @param {Object} $this
  1272.          * @private
  1273.          */
  1274.         _DisableProductMediaLoader: function ($this) {
  1275.             var $widget = this;
  1276.  
  1277.             if ($('body.catalog-product-view').length > 0) {
  1278.                 $this.parents('.column.main').find('.photo.image')
  1279.                     .removeClass($widget.options.classes.loader);
  1280.             } else {
  1281.                 //Category View
  1282.                 $this.parents('.product-item-info').find('.product-image-photo')
  1283.                     .removeClass($widget.options.classes.loader);
  1284.             }
  1285.         },
  1286.  
  1287.         /**
  1288.          * Callback for product media
  1289.          *
  1290.          * @param {Object} $this
  1291.          * @param {String} response
  1292.          * @param {Boolean} isInProductView
  1293.          * @private
  1294.          */
  1295.         _ProductMediaCallback: function ($this, response, isInProductView) {
  1296.             var $main = isInProductView ? $this.parents('.column.main') : $this.parents('.product-item-info'),
  1297.                 $widget = this,
  1298.                 images = [],
  1299.  
  1300.                 /**
  1301.                  * Check whether object supported or not
  1302.                  *
  1303.                  * @param {Object} e
  1304.                  * @returns {*|Boolean}
  1305.                  */
  1306.                 support = function (e) {
  1307.                     return e.hasOwnProperty('large') && e.hasOwnProperty('medium') && e.hasOwnProperty('small');
  1308.                 };
  1309.  
  1310.             if (_.size($widget) < 1 || !support(response)) {
  1311.                 this.updateBaseImage(this.options.mediaGalleryInitial, $main, isInProductView);
  1312.  
  1313.                 return;
  1314.             }
  1315.  
  1316.             images.push({
  1317.                 full: response.large,
  1318.                 img: response.medium,
  1319.                 thumb: response.small,
  1320.                 isMain: true
  1321.             });
  1322.  
  1323.             if (response.hasOwnProperty('gallery')) {
  1324.                 $.each(response.gallery, function () {
  1325.                     if (!support(this) || response.large === this.large) {
  1326.                         return;
  1327.                     }
  1328.                     images.push({
  1329.                         full: this.large,
  1330.                         img: this.medium,
  1331.                         thumb: this.small
  1332.                     });
  1333.                 });
  1334.             }
  1335.  
  1336.             this.updateBaseImage(images, $main, isInProductView);
  1337.         },
  1338.  
  1339.         /**
  1340.          * Check if images to update are initial and set their type
  1341.          * @param {Array} images
  1342.          */
  1343.         _setImageType: function (images) {
  1344.             var initial = this.options.mediaGalleryInitial[0].img;
  1345.  
  1346.             if (images[0].img === initial) {
  1347.                 images = $.extend(true, [], this.options.mediaGalleryInitial);
  1348.             } else {
  1349.                 images.map(function (img) {
  1350.                     if (!img.type) {
  1351.                         img.type = 'image';
  1352.                     }
  1353.                 });
  1354.             }
  1355.  
  1356.             return images;
  1357.         },
  1358.  
  1359.         /**
  1360.          * Update [gallery-placeholder] or [product-image-photo]
  1361.          * @param {Array} images
  1362.          * @param {jQuery} context
  1363.          * @param {Boolean} isInProductView
  1364.          */
  1365.         updateBaseImage: function (images, context, isInProductView) {
  1366.             var justAnImage = images[0],
  1367.                 initialImages = this.options.mediaGalleryInitial,
  1368.                 imagesToUpdate,
  1369.                 gallery = context.find(this.options.mediaGallerySelector).data('gallery'),
  1370.                 isInitial;
  1371.  
  1372.             if (isInProductView) {
  1373.                 imagesToUpdate = images.length ? this._setImageType($.extend(true, [], images)) : [];
  1374.                 isInitial = _.isEqual(imagesToUpdate, initialImages);
  1375.  
  1376.                 if (this.options.gallerySwitchStrategy === 'prepend' && !isInitial) {
  1377.                     imagesToUpdate = imagesToUpdate.concat(initialImages);
  1378.                 }
  1379.  
  1380.                 imagesToUpdate = this._setImageIndex(imagesToUpdate);
  1381.  
  1382.                 if (!_.isUndefined(gallery)) {
  1383.                     gallery.updateData(imagesToUpdate);
  1384.                 } else {
  1385.                     context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) {
  1386.                         loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery');
  1387.                         loadedGallery.updateData(imagesToUpdate);
  1388.                     }.bind(this));
  1389.                 }
  1390.  
  1391.                 if (isInitial) {
  1392.                     $(this.options.mediaGallerySelector).AddFotoramaVideoEvents();
  1393.                 } else {
  1394.                     $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({
  1395.                         selectedOption: this.getProduct(),
  1396.                         dataMergeStrategy: this.options.gallerySwitchStrategy
  1397.                     });
  1398.                 }
  1399.  
  1400.                 if (gallery) {
  1401.                     gallery.first();
  1402.                 }
  1403.             } else if (justAnImage && justAnImage.img) {
  1404.                 context.find('.product-image-photo').attr('src', justAnImage.img);
  1405.             }
  1406.         },
  1407.  
  1408.         /**
  1409.          * Set correct indexes for image set.
  1410.          *
  1411.          * @param {Array} images
  1412.          * @private
  1413.          */
  1414.         _setImageIndex: function (images) {
  1415.             var length = images.length,
  1416.                 i;
  1417.  
  1418.             for (i = 0; length > i; i++) {
  1419.                 images[i].i = i + 1;
  1420.             }
  1421.  
  1422.             return images;
  1423.         },
  1424.  
  1425.         /**
  1426.          * Kill doubled AJAX requests
  1427.          *
  1428.          * @private
  1429.          */
  1430.         _XhrKiller: function () {
  1431.             var $widget = this;
  1432.  
  1433.             if ($widget.xhr !== undefined && $widget.xhr !== null) {
  1434.                 $widget.xhr.abort();
  1435.                 $widget.xhr = null;
  1436.             }
  1437.         },
  1438.  
  1439.         /**
  1440.          * Emulate mouse click on all swatches that should be selected
  1441.          * @param {Object} [selectedAttributes]
  1442.          * @private
  1443.          */
  1444.         _EmulateSelected: function (selectedAttributes) {
  1445.             $.each(selectedAttributes, $.proxy(function (attributeCode, optionId) {
  1446.                 var elem = this.element.find('.' + this.options.classes.attributeClass +
  1447.                     '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]'),
  1448.                     parentInput = elem.parent();
  1449.  
  1450.                 if (elem.hasClass('selected')) {
  1451.                     return;
  1452.                 }
  1453.  
  1454.                 if (parentInput.hasClass(this.options.classes.selectClass)) {
  1455.                     parentInput.val(optionId);
  1456.                     parentInput.trigger('change');
  1457.                 } else {
  1458.                     elem.trigger('click');
  1459.                 }
  1460.             }, this));
  1461.         },
  1462.  
  1463.         /**
  1464.          * Emulate mouse click or selection change on all swatches that should be selected
  1465.          * @param {Object} [selectedAttributes]
  1466.          * @private
  1467.          */
  1468.         _EmulateSelectedByAttributeId: function (selectedAttributes) {
  1469.             $.each(selectedAttributes, $.proxy(function (attributeId, optionId) {
  1470.                 var elem = this.element.find('.' + this.options.classes.attributeClass +
  1471.                     '[attribute-id="' + attributeId + '"] [option-id="' + optionId + '"]'),
  1472.                     parentInput = elem.parent();
  1473.  
  1474.                 if (elem.hasClass('selected')) {
  1475.                     return;
  1476.                 }
  1477.  
  1478.                 if (parentInput.hasClass(this.options.classes.selectClass)) {
  1479.                     parentInput.val(optionId);
  1480.                     parentInput.trigger('change');
  1481.                 } else {
  1482.                     elem.trigger('click');
  1483.                 }
  1484.             }, this));
  1485.         },
  1486.  
  1487.         /**
  1488.          * Get default options values settings with either URL query parameters
  1489.          * @private
  1490.          */
  1491.         _getSelectedAttributes: function () {
  1492.             var hashIndex = window.location.href.indexOf('#'),
  1493.                 selectedAttributes = {},
  1494.                 params;
  1495.  
  1496.             if (hashIndex !== -1) {
  1497.                 params = $.parseQuery(window.location.href.substr(hashIndex + 1));
  1498.  
  1499.                 selectedAttributes = _.invert(_.mapObject(_.invert(params), function (attributeId) {
  1500.                     var attribute = this.options.jsonConfig.mappedAttributes[attributeId];
  1501.  
  1502.                     return attribute ? attribute.code : attributeId;
  1503.                 }.bind(this)));
  1504.             }
  1505.  
  1506.             return selectedAttributes;
  1507.         },
  1508.  
  1509.         /**
  1510.          * Callback which fired after gallery gets initialized.
  1511.          *
  1512.          * @param {HTMLElement} element - DOM element associated with a gallery.
  1513.          */
  1514.         _onGalleryLoaded: function (element) {
  1515.             var galleryObject = element.data('gallery');
  1516.  
  1517.             this.options.mediaGalleryInitial = galleryObject.returnCurrentImages();
  1518.         },
  1519.  
  1520.         /**
  1521.          * Sets mediaCache for cases when jsonConfig contains preSelectedGallery on layered navigation result pages
  1522.          *
  1523.          * @private
  1524.          */
  1525.         _setPreSelectedGallery: function () {
  1526.             var mediaCallData;
  1527.  
  1528.             if (this.options.jsonConfig.preSelectedGallery) {
  1529.                 mediaCallData = {
  1530.                     'product_id': this.getProduct()
  1531.                 };
  1532.  
  1533.                 this.options.mediaCache[JSON.stringify(mediaCallData)] = this.options.jsonConfig.preSelectedGallery;
  1534.             }
  1535.         }
  1536.     });
  1537.  
  1538.     return $.mage.SwatchRenderer;
  1539. });
  1540.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement