var Labels = BoostPFS.Labels;
var jQ = BoostPFS.jQ;
var Settings = BoostPFS.Settings;
var Utils = BoostPFS.Utils;
import BaseComponent from "../../components/base-component";
import AjaxCart from "../ajax-cart/ajax-cart";
/**
* Quick view
* @extends BaseComponent
*/
class QuickView extends BaseComponent {
getTemplate() {
return `
<button class="boost-pfs-quickview-btn boost-pfs-filter-button" data-href="{{productUrl}}" aria-label="{{label.quickview}}">
<span>
{{icoQuickView}}
</span>
</button>
`
}
getModalTemplate() {
return `
<div class="boost-pfs-modal-backdrop">
<div class="boost-pfs-modal-container">
<div class="boost-pfs-modal-content"></div>
</div>
</div>
`;
}
compileTemplate() {
var productData = this.parent.data;
var productListData = this.parent.parent.data;
var productIsFirst = productData.id == productListData[0].id;
var icoQuickView = '<svg width="32" height="32" viewBox="0 0 32 32"><g id="boost-pfs-icon-quick-view" transform="scale(0.03125 0.03125)"><path d="M1009.004 493.256c-2.256-2.82-56.254-69.828-143.786-137.492-51.696-39.962-104.462-71.87-156.832-94.834-66.48-29.152-132.556-43.932-196.386-43.932-63.832 0-129.904 14.782-196.386 43.932-52.37 22.962-105.136 54.87-156.834 94.834-87.53 67.666-141.528 134.674-143.784 137.494l-14.996 18.742 14.998 18.744c2.256 2.82 56.252 69.828 143.784 137.492 51.696 39.962 104.462 71.87 156.834 94.834 66.48 29.152 132.554 43.932 196.386 43.932 63.83 0 129.904-14.782 196.386-43.932 52.37-22.962 105.136-54.87 156.832-94.834 87.53-67.666 141.53-134.674 143.786-137.492l14.994-18.744-14.996-18.744zM827.402 621.624c-74.24 57.196-189.226 125.374-315.402 125.374-126.18 0-241.162-68.178-315.402-125.374-55.36-42.65-97.042-85.794-118.512-109.612 52.994-58.698 229.246-235.006 433.916-235.006 126.178 0 241.162 68.178 315.402 125.374 55.366 42.654 97.050 85.8 118.522 109.622-21.474 23.82-63.158 66.968-118.524 109.622z"></path><path d="M512 309.976c-111.396 0-202.024 90.63-202.024 202.024s90.63 202.024 202.024 202.024 202.026-90.628 202.026-202.024-90.63-202.024-202.026-202.024zM512 654.018c-78.308 0-142.018-63.71-142.018-142.018s63.71-142.018 142.018-142.018 142.018 63.71 142.018 142.018c0 78.308-63.71 142.018-142.018 142.018z"></path><path d="M512 419.322c-51.102 0-92.678 41.576-92.678 92.678s41.576 92.68 92.678 92.68 92.678-41.576 92.678-92.68c0-51.104-41.574-92.678-92.678-92.678zM512 544.672c-18.014 0-32.67-14.656-32.67-32.672s14.656-32.67 32.67-32.67 32.67 14.656 32.67 32.67c0.002 18.014-14.654 32.672-32.67 32.672z"></path></g></svg>';
var icoQuickViewLink = '<svg width="32" height="32" viewBox="0 0 32 32"><use xlink:href="#boost-pfs-icon-quick-view"></use></svg>';
var productUrl = Utils.buildProductItemUrl(this.parent.data) + '?view=boost-pfs-quickview';
return this.getTemplate()
.replace(/{{label.quickview}}/g, Labels.action_list.qvBtnLabel)
.replace(/{{productUrl}}/g, productUrl)
.replace(/{{icoQuickView}}/g, productIsFirst ? icoQuickView : icoQuickViewLink);
}
render() {
if (!this.$element) {
this.$element = jQ(this.compileTemplate());
}
}
bindEvents() {
if (this.$element) {
this.$element.on('click', this.getQuickViewModalContent.bind(this));
}
}
getQuickViewModalContent(e) {
if (!e) return;
e.stopPropagation();
e.preventDefault();
// Get quickview url
var url = jQ(e.currentTarget).data('href');
this.isGetQuickViewOption = jQ(e.currentTarget).attr('data-get-quickview-option') ? true : false;
// Clear all select option in product item
jQ('.boost-pfs-select-option-wrapper').remove();
jQ('.boost-pfs-select-option-show').removeClass('boost-pfs-select-option-show');
if (this.isGetQuickViewOption) {
jQ(e.currentTarget).removeAttr('data-get-quickview-option');
url += '-option';
this.parent.$element.addClass('boost-pfs-select-option-show');
} else {
// Hide the quickview button so it doesn't show on top of modal
this.$element.closest('.boost-pfs-action-list-wrapper').css('visibility', 'hidden');
// Render the backdrop and modal empty html
this.renderQuickViewBackdrop();
}
// Set up HTTP request
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.setRequestHeader("Content-Type", "text/html;charset=UTF-8");
xhr.onload = function () {
// On sucess, render the modal
if (xhr.readyState > 3 && xhr.status == 200) {
this.renderQuickViewModal(xhr.responseText);
this.bindQuickViewModalEvents();
}
}.bind(this);
// Send request
xhr.send();
if (boostPFS.filter && !this.isGetQuickViewOption) {
boostPFS.filter.filterLoadingIcon.setShow(true);
}
}
renderQuickViewBackdrop() {
if (jQ('.boost-pfs-modal-backdrop').length == 0) {
jQ('body').append(this.getModalTemplate());
jQ('.boost-pfs-modal-backdrop').on('click', this.closeModal.bind(this));
}
jQ('.boost-pfs-modal-backdrop').show();
jQ('.boost-pfs-modal-container').hide();
jQ('body').addClass('boost-pfs-body-no-scroll');
jQ('html').addClass('boost-pfs-body-no-scroll');
}
renderQuickViewModal(modalHtml) {
if (boostPFS.filter) {
boostPFS.filter.filterLoadingIcon.setShow(false);
}
if (this.isGetQuickViewOption) {
var $container = this.parent.$element;
if (Settings.getSettingValue('general.selectOptionContainer')) {
$container = this.parent.$element.find(Settings.getSettingValue('general.selectOptionContainer'));
}
if ($container.length > 0) {
$container.find('.boost-pfs-select-option-wrapper').remove();
$container.append('<div class="boost-pfs-select-option-wrapper"><div class="boost-pfs-select-option-close"></div>'+ modalHtml +'</div>');
}
} else {
jQ('.boost-pfs-modal-content').html(modalHtml);
jQ('.boost-pfs-modal-container').show();
}
}
bindQuickViewModalEvents() {
if (this.isGetQuickViewOption) {
if (Settings.getSettingValue('general.enableAjaxCart')) {
this.parent.$element.find('#boost-pfs-quickview-cart-form-' + this.parent.data.id).on('submit', this.onClickAddToCart.bind(this));
}
jQ('.boost-pfs-select-option-close').on('click', function(){
jQ('.boost-pfs-select-option-wrapper').hide();
jQ('.boost-pfs-action-list-enabled').removeClass('boost-pfs-select-option-show');
});
} else {
this.initImageSlider();
jQ('.boost-pfs-quickview-close').on('click', this.closeModal.bind(this));
// Bind changing options
jQ('.boost-pfs-quickview-select-option').on('change', this.onChangeVariant.bind(this));
jQ('.boost-pfs-quickview-select-option-color').on('change', this.onChangeVariant.bind(this));
// Bind changing options with enter/space key for ADA
jQ('.boost-pfs-swatch-element label').on('keydown', (event) => {
if (event.target && (event.keyCode == 13 || event.keyCode == 32)) {
jQ(event.target).click();
}
});
if (Settings.getSettingValue('general.enableAjaxCart')) {
jQ('#boost-pfs-quickview-cart-form').on('submit', this.onClickAddToCart.bind(this));
}
// Focus inside quickview
jQ('.boost-pfs-quickview-close').focus();
}
}
closeModal(e) {
var $target = jQ(e.target);
if (!$target.hasClass('boost-pfs-modal-backdrop')
&& !$target.hasClass('boost-pfs-modal-container')
&& !$target.hasClass('boost-pfs-quickview-close')) return;
jQ('.boost-pfs-modal-backdrop').hide();
jQ('.boost-pfs-modal-container').hide();
jQ('body').removeClass('boost-pfs-body-no-scroll');
jQ('html').removeClass('boost-pfs-body-no-scroll');
// Focus on the product item (ADA)
if (jQ('body').hasClass('boost-pfs-ada') && this.parent.$element) {
this.parent.$element.find('.boost-pfs-action-list-wrapper').css({visibility: 'visible'});
setTimeout(() => {
console.log(this.$element);
this.$element.focus();
}, 200);
}
}
initImageSlider() {
var $itemsWrapper = jQ('.boost-pfs-quickview-featured-image-wrapper');
var $items = jQ('.boost-pfs-quickview-featured-image');
var $prev = jQ('.boost-pfs-quickview-slider-prev');
var $next = jQ('.boost-pfs-quickview-slider-next');
var $dots = jQ('.boost-pfs-quickview-slider-dot');
if ($itemsWrapper.length == 0 || $items.length == 0) return;
this.imageSlider = {
$itemsWrapper: $itemsWrapper,
$prev: $prev,
$next: $next,
$dots: $dots,
posX1: 0,
posX2: 0,
posInitial: 0,
posFinal: 0,
threshold: 50,
slidesLength: $items.length,
slideSize: $items[0].offsetWidth,
index: 0,
allowShift: true,
isDragging: false
}
// Init css classes
if (this.imageSlider.index == 0) {
this.imageSlider.$prev.addClass('disabled');
}
if (this.imageSlider.index == this.imageSlider.slidesLength - 1) {
this.imageSlider.$next.addClass('disabled');
}
this.imageSlider.$itemsWrapper.css('left', '0px');
this.imageSlider.$dots.first().addClass('active');
// Mouse events
this.imageSlider.$itemsWrapper.on('mousedown', this.dragStart.bind(this));
jQ(document).off('mousemove');
jQ(document).off('mouseup');
jQ(document).on('mousemove', this.dragAction.bind(this));
jQ(document).on('mouseup', this.dragEnd.bind(this));
// Click events
this.imageSlider.$prev.on('click', this.shiftSlide.bind(this, null, -1));
this.imageSlider.$next.on('click', this.shiftSlide.bind(this, null, 1));
this.imageSlider.$dots.on('click', function (e) {
var index = jQ(e.currentTarget).data('index');
this.shiftSlide(index, null);
}.bind(this))
}
dragStart(e) {
e = e || window.event;
e.preventDefault();
this.imageSlider.posInitial = this.imageSlider.$itemsWrapper[0].offsetLeft;
this.imageSlider.posX1 = e.clientX;
this.imageSlider.isDragging = true;
}
dragAction(e) {
if (!this.imageSlider.isDragging) return;
e = e || window.event;
this.imageSlider.posX2 = this.imageSlider.posX1 - e.clientX;
this.imageSlider.posX1 = e.clientX;
var offset = this.imageSlider.$itemsWrapper[0].offsetLeft - this.imageSlider.posX2;
var offsetMax = this.imageSlider.slideSize * 0.2;
var offsetMin = -this.imageSlider.slideSize * (this.imageSlider.slidesLength - 0.8);
if (offset > offsetMax) {
offset = offsetMax;
} else if (offset < offsetMin) {
offset = offsetMin;
}
this.imageSlider.$itemsWrapper.css('left', offset + "px");
}
dragEnd(e) {
if (!this.imageSlider.isDragging) return;
this.imageSlider.isDragging = false;
this.imageSlider.posFinal = this.imageSlider.$itemsWrapper[0].offsetLeft;
if (this.imageSlider.posFinal - this.imageSlider.posInitial < -this.imageSlider.threshold) {
this.shiftSlide(null, 1);
} else if (this.imageSlider.posFinal - this.imageSlider.posInitial > this.imageSlider.threshold) {
this.shiftSlide(null, -1);
} else {
this.imageSlider.$itemsWrapper.css('left', (this.imageSlider.posInitial) + "px");
}
}
shiftSlide(slideIndex, direction) {
if (slideIndex == null) {
slideIndex = this.imageSlider.index + direction;
}
if (this.imageSlider.allowShift) {
var currentOffset = this.imageSlider.$itemsWrapper.css('left');
var newOffset;
if (slideIndex > -1 && slideIndex < this.imageSlider.slidesLength) {
newOffset = (-1 * slideIndex * this.imageSlider.slideSize) + "px";
this.imageSlider.index = slideIndex;
} else {
newOffset = (-1 * this.imageSlider.index * this.imageSlider.slideSize) + "px";
}
if (currentOffset != newOffset) {
// Update previous/next
if (this.imageSlider.index == 0) {
this.imageSlider.$prev.addClass('disabled');
} else {
this.imageSlider.$prev.removeClass('disabled');
}
if (this.imageSlider.index == this.imageSlider.slidesLength - 1) {
this.imageSlider.$next.addClass('disabled');
} else {
this.imageSlider.$next.removeClass('disabled');
}
// Update dots
this.imageSlider.$dots.removeClass('active');
this.imageSlider.$dots.eq(this.imageSlider.index).addClass('active');
// Slide image
this.imageSlider.$itemsWrapper.addClass('boost-pfs-quickview-slider-shifting');
this.imageSlider.$itemsWrapper.css('left', newOffset);
this.imageSlider.allowShift = false;
setTimeout(this.afterShiftSlide.bind(this), 300);
}
}
}
afterShiftSlide() {
this.imageSlider.$itemsWrapper.removeClass('boost-pfs-quickview-slider-shifting');
this.imageSlider.allowShift = true;
}
onClickAddToCart(e) {
if (e) e.preventDefault();
var selectedVariant = this._getSelectedVariant();
var quantity = 1;
var $addingLabel = null;
var $errorLabel = null;
if (this.isGetQuickViewOption) {
$addingLabel = jQ('.boost-pfs-quickview-cart-btn-text');
$errorLabel = jQ('.boost-pfs-select-option-wrapper .boost-pfs-addtocart-error-label');
} else {
quantity = jQ('#boost-pfs-quickview-cart-quantity').val();
$addingLabel = jQ('#boost-pfs-quickview-cart-btn-text');
$errorLabel = jQ('#boost-pfs-addtocart-error-label');
}
if (selectedVariant) {
AjaxCart.instance.addToCart(selectedVariant.id, quantity, $addingLabel, $errorLabel);
}
}
onChangeVariant() {
var selectedVariant = this._getSelectedVariant();
this._updateQuickViewModal(selectedVariant);
if (selectedVariant != null && selectedVariant.image) {
jQ('.boost-pfs-quickview-slider-dot[data-image="' + Utils.optimizeImage(selectedVariant.image, 'small').replace('https:', '') + '"]').click();
}
}
_getSelectedVariant() {
// Option name field (fixed by shopify)
var optionNames = ['option1', 'option2', 'option3'];
// Get the selected options from the quickview form
var formData = null;
if (this.isGetQuickViewOption) {
formData = jQ('#boost-pfs-quickview-cart-form-'+ this.parent.data.id).serializeArray();
} else {
formData = jQ('#boost-pfs-quickview-cart-form').serializeArray();
}
var selectedOptions = [];
formData.forEach(entry => {
if (optionNames.includes(entry.name)) {
selectedOptions.push(entry);
}
})
// Back end only return "merged_options", so we need to get original option data from liquid
var variantOptionData = [];
if (this.isGetQuickViewOption) {
variantOptionData = JSON.parse(jQ('.boost-pfs-quickview-variants-data-' + this.parent.data.id).html());
} else {
variantOptionData = JSON.parse(jQ('#boost-pfs-quickview-variants-data').html());
}
// Find the selected variant
var selectedVariant = null;
variantOptionData.forEach(variant => {
// Return if found variant
if (selectedVariant) return;
// Check if variant's option match with the selected option
var isMatchAllOptions = true;
selectedOptions.forEach(option => {
if (!isMatchAllOptions) return;
var variantValue = variant[option.name];
var optionValue = option.value;
if (variantValue != null && optionValue != null && variantValue != optionValue) isMatchAllOptions = false;
})
if (isMatchAllOptions) selectedVariant = variant;
})
// Merge variant data from liquid with variant data from our API
if (selectedVariant && selectedVariant.id) {
if (this.parent && this.parent.data && Array.isArray(this.parent.data.variants)) {
var variantData = this.parent.data.variants.find(data => data.id == selectedVariant.id);
if (variantData) {
Object.assign(selectedVariant, variantData);
}
}
}
return selectedVariant;
}
_updateQuickViewModal(selectedVariant) {
// Update variant input & button text
var $selectElement = jQ('#boost-pfs-quickview-variants-selector')
var $cartButton = jQ('#boost-pfs-quickview-cart-btn');
var $cartButtonText = jQ('#boost-pfs-quickview-cart-btn-text');
var $priceWrapper = jQ('.boost-pfs-quickview-price-wrapper');
if (selectedVariant && selectedVariant.available) {
$selectElement.val(selectedVariant.id);
$cartButton.removeAttr("disabled");
$cartButtonText.html(Labels.action_list.qvAddToCartBtnLabel);
// Get the price labels
var $option = $selectElement.find(":selected");
var priceHtml = $option.data('current-price');
var compareAtPriceHtml = $option.data('was-price');
// Check if onsale or not
var price = $option.data('current-price-without-currency');
if (price && typeof price == 'string') {
price = price.replace(/[ ,.]/g, "");
}
var compareAtPrice = $option.data('was-price-without-currency');
if (compareAtPrice && typeof compareAtPrice == 'string') {
compareAtPrice = compareAtPrice.replace(/[ ,.]/g, "");
}
var onSale = false;
if (price && compareAtPrice) {
onSale = parseFloat(compareAtPrice) > parseFloat(price);
}
// Replace price labels in DOM
if (onSale) {
jQ('#boost-pfs-quickview-current-price').html(priceHtml);
jQ('#boost-pfs-quickview-was-price').html(compareAtPriceHtml);
$priceWrapper.addClass('boost-pfs-quickview-price-on-sale');
} else {
jQ('#boost-pfs-quickview-current-price').html(priceHtml);
jQ('#boost-pfs-quickview-was-price').html('');
$priceWrapper.removeClass('boost-pfs-quickview-price-on-sale');
}
} else {
$cartButton.attr("disabled", true);
$cartButtonText.html(Labels.action_list.qvSoldOutLabel);
}
}
}
export default QuickView;