PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.3.0
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.3.0
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / assets / js / modules / side-cart / side-cart.js

side-cart.js in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 2.3.0, at assets/js/modules/side-cart/side-cart.js

458 lines 18.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2
3 /**
4 * Merchant side cart
5 *
6 */
7 jQuery(document).ready(function ($) {
8 'use strict';
9
10 var _merchant;
11 var _ref = ((_merchant = merchant) === null || _merchant === void 0 ? void 0 : _merchant.setting) || {},
12 _ref$side_cart = _ref.side_cart,
13 sideCartObj = _ref$side_cart === void 0 ? {} : _ref$side_cart,
14 ajax_url = _ref.ajax_url,
15 nonce = _ref.nonce;
16 var $body = $('body');
17 $(document).on('click', '.js-merchant-side-cart-toggle-handler', function (e) {
18 e.preventDefault();
19 $body.toggleClass('merchant-side-cart-show');
20 $(window).trigger('merchant.side-cart-resize');
21 });
22
23 // Manually update Side Cart as for some reason `wc_fragment_refresh` doesn't refresh the Side Cart widget.
24 $body.on('wc_cart_emptied', function (e, context) {
25 $('.merchant_widget_shopping_cart_content').fadeOut(function () {
26 $(this).empty().append("<p class=\"woocommerce-mini-cart__empty-message\">".concat((sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.side_cart_empty_message) || '', "</p>")).fadeIn();
27 });
28 var $floatingCart = $('.merchant-side-cart-floating-cart');
29 if ($floatingCart.length) {
30 $floatingCart.find('.merchant-side-cart-floating-cart-counter').text(0);
31 if ((sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.icon_display) === 'cart-not-empty') {
32 $floatingCart.removeClass('merchant-show');
33 }
34 }
35 });
36
37 /**
38 * Check if the current device is allowed to show the side cart.
39 *
40 * @returns {boolean}
41 */
42 function merchant_is_allowed_device() {
43 var allowed_devices = sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.allowed_devices;
44 var screenWidth = window.innerWidth;
45 if (screenWidth <= 768 && allowed_devices.includes('mobile')) {
46 return true;
47 } else if (screenWidth > 768 && allowed_devices.includes('desktop')) {
48 return true;
49 }
50 return false;
51 }
52
53 // Toggle side cart
54 if (sideCartObj.hasOwnProperty('show_after_add_to_cart_single_product') && merchant_is_allowed_device()) {
55 var isSingleProductPage = $('body.single-product').length;
56 var isNoticeVisible = $('.woocommerce-notices-wrapper').is(':visible') && !$('.woocommerce-notices-wrapper').is(':empty');
57 var isBlockNoticeVisible = $('.wc-block-components-notice-banner').is(':visible') && !$('.wc-block-components-notice-banner').is(':empty');
58 if (isSingleProductPage && (isNoticeVisible || isBlockNoticeVisible)) {
59 $body.toggleClass('merchant-side-cart-show');
60 $(window).trigger('merchant.side-cart-resize');
61 }
62 }
63
64 // Add to cart AJAX event.
65 if (sideCartObj.hasOwnProperty('add_to_cart_slide_out') && merchant_is_allowed_device()) {
66 $(document.body).on('added_to_cart', function (event, fragments, cart_hash, $button, $context) {
67 if ($context !== 'side-cart' && $context !== 'free-gifts') {
68 $body.toggleClass('merchant-side-cart-show');
69 }
70 $(window).trigger('merchant.side-cart-resize');
71 });
72 }
73
74 // On cart URL click
75 if (sideCartObj.hasOwnProperty('cart_url') && merchant_is_allowed_device()) {
76 $('[href="' + (sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.cart_url) + '"]:not(.merchant-side-cart-view-cart-btn)').on('click', function (e) {
77 e.preventDefault();
78 $(window).trigger('merchant.side-cart-resize');
79 $body.toggleClass('merchant-side-cart-show');
80 });
81 }
82
83 // Update Product quantity in Side Cart
84 // if ( sideCartObj.hasOwnProperty('add_to_cart_slide_out') && merchant_is_allowed_device() ) {
85 // Update quantity on plus/minus click
86 $(document).on('click', '.js-merchant-quantity-btn', function (e) {
87 e.preventDefault();
88 var $btn = $(this);
89 var $input = $btn.closest('.merchant-quantity-wrap').find('.js-update-quantity');
90 if (!$input.length) {
91 return;
92 }
93 var quantity = +($input.val() || 1);
94 var minimum = +$input.attr('min');
95 var maximum = +$input.attr('max');
96 var stepSize = Math.round(parseFloat($input.attr('step')));
97 if ($btn.hasClass('merchant-quantity-plus')) {
98 quantity += stepSize;
99 quantity = maximum && maximum !== -1 ? Math.min(quantity, maximum) : quantity;
100 } else if ($btn.hasClass('merchant-quantity-minus')) {
101 quantity -= stepSize;
102 quantity = minimum ? Math.max(quantity, minimum) : quantity;
103 }
104 $input.val(quantity);
105 merchant_update_side_cart_quantity($input);
106 });
107
108 // Update quantity on input value change
109 $(document).on('input change', '.js-update-quantity', function (e) {
110 e.preventDefault();
111 merchant_update_side_cart_quantity($(this));
112 });
113
114 // Update quantity helper
115 var debounceTimer;
116 function merchant_update_side_cart_quantity($input) {
117 if (!$input.length || !ajax_url || !nonce) {
118 return;
119 }
120 var cartItemKey = $input.attr('name');
121 var quantity = Math.round(parseFloat($input.val() || 1));
122 var $cart_item = $input.closest('.js-side-cart-item');
123
124 // Clear previous timer
125 clearTimeout(debounceTimer);
126
127 // Set a new timer to delay the AJAX request
128 debounceTimer = setTimeout(function () {
129 $.ajax({
130 type: 'POST',
131 url: ajax_url,
132 data: {
133 action: 'update_side_cart_quantity',
134 cart_item_key: cartItemKey,
135 quantity: quantity,
136 nonce: nonce
137 },
138 beforeSend: function beforeSend() {
139 if ($cart_item.length) {
140 $cart_item.block({
141 message: null,
142 overlayCSS: {
143 background: '#fff',
144 opacity: 0.6
145 }
146 });
147 }
148 },
149 success: function success(response) {
150 if (!response || !response.fragments) {
151 return;
152 }
153 $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $input, 'side-cart']);
154 if ($cart_item.length) {
155 $cart_item.unblock();
156 $(document).trigger('merchant_destroy_carousel');
157 $(document).trigger('merchant_init_carousel');
158 }
159 },
160 error: function error(_error) {
161 console.log('Error:', _error);
162 }
163 });
164 }, 350);
165 }
166 // }
167
168 var merchant_upsells = {
169 init: function init() {
170 var self = this;
171 this.bindEvents();
172 setTimeout(function () {
173 $(document).trigger('merchant_init_carousel');
174
175 // Pause the slider on hover
176 $(document).find('.woocommerce-mini-cart-item.merchant-upsell-widget').on('mouseenter', function () {
177 if ($(document).find('.merchant-mini-cart-upsells.upsells-layout-carousel').hasClass('slick-initialized')) {
178 $('.merchant-mini-cart-upsells.upsells-layout-carousel').slick('slickPause');
179 }
180 });
181
182 // Resume the slider on mouse leave
183 $(document).find('.woocommerce-mini-cart-item.merchant-upsell-widget').on('mouseleave', function () {
184 if ($(document).find('.merchant-mini-cart-upsells.upsells-layout-carousel').hasClass('slick-initialized')) {
185 $('.merchant-mini-cart-upsells.upsells-layout-carousel').slick('slickPlay');
186 }
187 });
188 }, 500);
189 },
190 bindEvents: function bindEvents() {
191 $(document).on('change', '.merchant-mini-cart-upsell-item-wrap .variation-selector', this.handleVariationChange.bind(this));
192 $(document).on('click', '.add-to-cart-wrap .merchant-upsell-add-to-cart:not(.disabled)', this.handleAddToCartClick.bind(this));
193 $(document).on('click', '.merchant-coupon-form button', this.handleCouponBtnClick.bind(this));
194 $(document).on('click', '.merchant-remove-coupon', this.handleCouponRemoveClick.bind(this));
195 $(document).on('merchant_init_carousel', this.initCarousel.bind(this));
196 $(document).on('merchant_destroy_carousel', this.destroyCarousel.bind(this));
197 $(document).on('added_to_cart', this.handleAddToCart.bind(this));
198 $(document).on('removed_from_cart', this.handleRemoveFromCart.bind(this));
199 },
200 handleVariationChange: function handleVariationChange(event) {
201 var variationField = $(event.target),
202 container = variationField.closest('.merchant-mini-cart-upsell-item-wrap'),
203 variations = container.attr('data-variations') && JSON.parse(container.attr('data-variations')) || [],
204 messageEl = container.find('.upsell-variation-message');
205
206 // Reset state
207 container.attr('data-variation-id', 0);
208 messageEl.text('').hide();
209
210 // Capture original price on first interaction (image is already in data-thumbnail-url)
211 if (!container.data('original-price')) {
212 container.data('original-price', container.find('.product-price').html());
213 }
214
215 // Wait until all dropdowns have a value
216 if (!this.isAllVariationsSelected(container)) {
217 this.handleAddToCartBtnState(container, false);
218
219 // Reset image and price to parent product defaults
220 var originalImg = container.find('.product-thumbnail').attr('data-thumbnail-url');
221 if (originalImg) {
222 container.find('.product-thumbnail a img').attr('src', originalImg);
223 }
224 container.find('.product-price').html(container.data('original-price'));
225 return;
226 }
227
228 // Build selected attributes map
229 var selected = this.getSelectedAttributes(container);
230
231 // Find matching variation from enriched data
232 var match = variations.find(function (v) {
233 return Object.keys(selected).every(function (key) {
234 // Check key exists in variation attributes — don't fallback to "any" for missing keys
235 var hasKey = key in v.attributes;
236 var hasLowerKey = !hasKey && key.toLowerCase() in v.attributes;
237 if (!hasKey && !hasLowerKey) {
238 return false;
239 }
240 var varAttr = hasKey ? v.attributes[key] : v.attributes[key.toLowerCase()];
241
242 // Empty string means "any" — matches everything
243 return varAttr === '' || varAttr === selected[key];
244 });
245 });
246 if (!match) {
247 messageEl.text((sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.upsell_unavailable_message) || '').show();
248 this.handleAddToCartBtnState(container, false);
249 return;
250 }
251
252 // Update image and price for any matched variation (even out-of-stock)
253 if (match.image_url) {
254 container.find('.product-thumbnail a img').attr('src', match.image_url);
255 }
256 if (match.price_html) {
257 container.find('.product-price').html(match.price_html);
258 }
259 if (!match.is_in_stock) {
260 messageEl.text((sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.upsell_out_of_stock_message) || '').show();
261 this.handleAddToCartBtnState(container, false);
262 return;
263 }
264
265 // Valid in-stock combination — enable add to cart
266 container.attr('data-variation-id', match.variation_id);
267 this.handleAddToCartBtnState(container, true);
268 },
269 getSelectedAttributes: function getSelectedAttributes(container) {
270 var attributes = {};
271 container.find('.variation-selector').each(function () {
272 attributes[$(this).attr('data-attribute_name')] = $(this).val();
273 });
274 return attributes;
275 },
276 handleAddToCartBtnState: function handleAddToCartBtnState(container, allSelected) {
277 var btn = container.find('.add-to-cart-wrap .merchant-upsell-add-to-cart');
278 if (allSelected) {
279 btn.removeClass('disabled');
280 btn.prop('disabled', false);
281 } else {
282 btn.addClass('disabled');
283 btn.prop('disabled', true);
284 }
285 },
286 isAllVariationsSelected: function isAllVariationsSelected(container) {
287 var variationFields = container.find('.variation-selector');
288 return variationFields.length && variationFields.toArray().every(function (field) {
289 return $(field).val() !== '';
290 });
291 },
292 handleAddToCartClick: function handleAddToCartClick(event) {
293 event.preventDefault();
294 var self = this,
295 btn = $(event.currentTarget),
296 container = btn.closest('.merchant-mini-cart-upsell-item-wrap'),
297 productType = container.attr('data-product-type'),
298 productId = container.attr('data-product-id'),
299 variationId = container.attr('data-variation-id');
300 if (productType === 'variable' && variationId !== '0') {
301 this.addToCart(self, 'variable', productId, variationId, btn, container);
302 } else if (productType === 'simple') {
303 this.addToCart(self, 'simple', productId, variationId, btn, container);
304 } else {
305 console.log('Unsupported product type:', productType);
306 }
307 },
308 addToCart: function addToCart(self, productType, productId, variationId, btn, container) {
309 var data = {
310 action: 'merchant_side_cart_upsells_add_to_cart',
311 product_id: productId,
312 variation_id: variationId,
313 nonce: nonce
314 };
315
316 // Include selected attributes for variable products
317 if (productType === 'variable' && container) {
318 data.attributes = this.getSelectedAttributes(container);
319 }
320 $.ajax({
321 type: 'POST',
322 url: ajax_url,
323 data: data,
324 beforeSend: function beforeSend() {
325 btn.addClass('loading');
326 },
327 success: function success(response) {
328 self.handleSuccess(response);
329 $(document).trigger('add_to_cart', [response.data.fragments, response.data.cart_hash, btn, 'side-cart']);
330 },
331 error: function error(_error2) {
332 self.handleError(_error2);
333 },
334 complete: function complete() {
335 btn.removeClass('loading');
336 }
337 });
338 },
339 handleSuccess: function handleSuccess(response) {
340 if (response.data.fragments) {
341 $(document).trigger('merchant_destroy_carousel');
342 $(document.body).trigger('added_to_cart', [response.data.fragments, response.data.cart_hash, $('.merchant-upsell-add-to-cart'), 'side-cart']);
343 $(document).trigger('merchant_init_carousel');
344 }
345 },
346 handleError: function handleError(error) {
347 console.log('Error:', error);
348 },
349 handleAddToCart: function handleAddToCart(event, fragments, cart_hash, $button, $context) {
350 $(document).trigger('merchant_destroy_carousel');
351 $(document).trigger('merchant_init_carousel');
352 },
353 handleRemoveFromCart: function handleRemoveFromCart(event) {
354 $(document).trigger('merchant_destroy_carousel');
355 $(document).trigger('merchant_init_carousel');
356 },
357 initCarousel: function initCarousel() {
358 // check if slick is initialized
359 var carousel = $(document).find('.merchant-mini-cart-upsells.upsells-layout-carousel');
360 if ('carousel' === (sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.upsells_style) && !carousel.hasClass('slick-initialized')) {
361 carousel.slick({
362 infinite: true,
363 arrows: true,
364 slidesToShow: 1,
365 dots: false,
366 autoplay: false,
367 // autoplaySpeed: 2000,
368 fade: true,
369 cssEase: 'linear',
370 pauseOnFocus: true,
371 pauseOnHover: true,
372 prevArrow: '<button type="button" class="slick-prev"><</button>',
373 nextArrow: '<button type="button" class="slick-next">></button>',
374 rtl: (sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.is_rtl) === '1'
375 });
376 }
377 },
378 destroyCarousel: function destroyCarousel() {
379 // check if slick is initialized
380 var carousel = $(document).find('.merchant-mini-cart-upsells.upsells-layout-carousel');
381 if ('carousel' === (sideCartObj === null || sideCartObj === void 0 ? void 0 : sideCartObj.upsells_style) && carousel.hasClass('slick-initialized')) {
382 carousel.slick('unslick');
383 }
384 },
385 handleCouponBtnClick: function handleCouponBtnClick(event) {
386 event.preventDefault();
387 var self = this,
388 btn = $(event.currentTarget),
389 container = btn.closest('.merchant-coupon-form'),
390 couponCode = container.find('.coupon_code').val();
391 if (couponCode === '') {
392 return;
393 }
394 this.applyCoupon(self, couponCode, container);
395 },
396 applyCoupon: function applyCoupon(self, couponCode, container) {
397 var data = {
398 action: 'merchant_side_cart_apply_coupon',
399 coupon_code: couponCode,
400 nonce: nonce
401 };
402 $.ajax({
403 type: 'POST',
404 url: ajax_url,
405 data: data,
406 beforeSend: function beforeSend() {
407 container.addClass('loading');
408 },
409 success: function success(response) {
410 self.handleCouponSuccess(response);
411 },
412 error: function error(_error3) {
413 self.handleCouponError(_error3);
414 },
415 complete: function complete() {
416 container.removeClass('loading');
417 }
418 });
419 },
420 removeCoupon: function removeCoupon(self, couponCode) {
421 var data = {
422 action: 'merchant_side_cart_remove_coupon',
423 coupon_code: couponCode,
424 nonce: nonce
425 };
426 $.ajax({
427 type: 'POST',
428 url: ajax_url,
429 data: data,
430 beforeSend: function beforeSend() {},
431 success: function success(response) {
432 self.handleCouponSuccess(response);
433 },
434 error: function error(_error4) {
435 self.handleCouponError(_error4);
436 }
437 });
438 },
439 handleCouponSuccess: function handleCouponSuccess(response) {
440 if ((response === null || response === void 0 ? void 0 : response.fragments) !== undefined) {
441 $(document).trigger('merchant_destroy_carousel');
442 $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $('.merchant-coupon-form button'), 'side-cart']);
443 $(document).trigger('merchant_init_carousel');
444 }
445 },
446 handleCouponError: function handleCouponError(error) {
447 console.log('Error:', error);
448 },
449 handleCouponRemoveClick: function handleCouponRemoveClick(event) {
450 event.preventDefault();
451 var self = this,
452 btn = $(event.currentTarget),
453 couponCode = btn.attr('data-coupon');
454 this.removeCoupon(self, couponCode);
455 }
456 };
457 merchant_upsells.init();
458 });