PageSourceSearch

https://montagut.com/themes/child_classic_montagut/assets/js/checkout_new2.js

js montagut.com collected 2026-10-08 01:32:16 UTC 13,982 bytes, 456 lines download raw bytes

1/**
2 * Montagut - Cart Unified Handler
3 * Gère le sélecteur de quantité intelligent sur desktop ET mobile
4 * + Titre panier dynamique
5 * + Message livraison dynamique
6 * + Sticky sidebar CSS (position: sticky)
7 */
8
9(function() {
10  'use strict';
11
12  // Configuration
13  var FREE_SHIPPING_THRESHOLD = 250;
14  var isUpdating = false;
15  var STICKY_TOP_OFFSET = 80;
16
17  // Initialisation au chargement
18  document.addEventListener('DOMContentLoaded', function() {
19    disableTouchSpin();
20    initQuantityButtons();
21    cleanColorAttributes();
22    updateCartTitle();
23    updateShippingMessage();
24    initPromoCodeToggle();
25    initStickySidebar();
26  });
27
28  // Réinitialiser après refresh AJAX du panier
29  if (typeof prestashop !== 'undefined') {
30    prestashop.on('updatedCart', function() {
31      isUpdating = false;
32      setTimeout(function() {
33        disableTouchSpin();
34        initQuantityButtons();
35        cleanColorAttributes();
36        hideAllLoaders();
37        updateCartTitle();
38        updateShippingMessage();
39        initPromoCodeToggle();
40        initStickySidebar();
41      }, 100);
42    });
43  }
44
45  /**
46   * Sticky Sidebar — CSS position:sticky
47   *
48   * Pour que sticky fonctionne, le parent (.cart-summary-column) doit être
49   * PLUS GRAND que le sticky. On force align-self:stretch pour que la colonne
50   * s'étire sur toute la hauteur du grid (celle de la colonne produits).
51   * Le sticky "voyage" alors dans cet espace.
52   */
53  function initStickySidebar() {
54    var sidebar = document.querySelector('.cart-summary-sticky');
55    var sidebarColumn = document.querySelector('.cart-summary-column');
56
57    if (!sidebar || !sidebarColumn) return;
58
59    // Injecter le CSS une seule fois
60    if (!document.getElementById('sticky-sidebar-styles')) {
61      var style = document.createElement('style');
62      style.id = 'sticky-sidebar-styles';
63      style.textContent =
64        '@media (min-width: 992px) {' +
65          '.cart-summary-column.sticky-active {' +
66            'align-self: stretch !important;' +
67          '}' +
68          '.cart-summary-sticky.is-sticky-enabled {' +
69            'position: -webkit-sticky !important;' +
70            'position: sticky !important;' +
71            'top: ' + STICKY_TOP_OFFSET + 'px !important;' +
72          '}' +
73        '}';
74      document.head.appendChild(style);
75    }
76
77    // Nettoyer tout style inline résiduel (ancienne logique JS)
78    sidebar.style.position = '';
79    sidebar.style.top = '';
80    sidebar.style.width = '';
81
82    // Appliquer ou retirer selon les conditions
83    applyStickyState(sidebar, sidebarColumn);
84
85    // Nettoyer les anciens listeners
86    if (window._stickyResizeHandler) {
87      window.removeEventListener('resize', window._stickyResizeHandler);
88    }
89    if (window._stickyScrollHandler) {
90      window.removeEventListener('scroll', window._stickyScrollHandler);
91      window._stickyScrollHandler = null;
92    }
93
94    // Resize : activer/désactiver selon viewport
95    window._stickyResizeHandler = function() {
96      applyStickyState(sidebar, sidebarColumn);
97    };
98    window.addEventListener('resize', window._stickyResizeHandler);
99  }
100
101  /**
102   * Active ou désactive le sticky selon viewport et nombre de produits
103   */
104  function applyStickyState(sidebar, sidebarColumn) {
105    var productCount = document.querySelectorAll('.cart-items > .cart-item').length;
106
107    if (window.innerWidth <= 991 || productCount < 3) {
108      sidebar.classList.remove('is-sticky-enabled');
109      sidebarColumn.classList.remove('sticky-active');
110    } else {
111      sidebar.classList.add('is-sticky-enabled');
112      sidebarColumn.classList.add('sticky-active');
113    }
114  }
115
116  /**
117   * Désactive le plugin TouchSpin de PrestaShop
118   */
119  function disableTouchSpin() {
120    if (typeof jQuery !== 'undefined' && jQuery.fn.TouchSpin) {
121      jQuery('.cart-quantity-selector input').each(function() {
122        var $input = jQuery(this);
123        if ($input.data('bts.touchspin')) {
124          try {
125            $input.TouchSpin('destroy');
126          } catch(e) {}
127        }
128      });
129    }
130
131    var elementsToRemove = [
132      '.cart-quantity-selector .input-group-btn-vertical',
133      '.cart-quantity-selector .bootstrap-touchspin-prefix',
134      '.cart-quantity-selector .bootstrap-touchspin-postfix',
135      '.cart-quantity-selector .touchspin-up',
136      '.cart-quantity-selector .touchspin-down',
137      '.cart-quantity-selector .btn-touchspin'
138    ];
139
140    elementsToRemove.forEach(function(selector) {
141      document.querySelectorAll(selector).forEach(function(el) {
142        el.remove();
143      });
144    });
145
146    document.querySelectorAll('.cart-quantity-selector .bootstrap-touchspin').forEach(function(wrapper) {
147      var input = wrapper.querySelector('input');
148      if (input && wrapper.parentNode) {
149        input.style.display = 'inline-flex';
150        input.style.visibility = 'visible';
151        input.style.opacity = '1';
152      }
153    });
154  }
155
156  /**
157   * Met à jour le titre "Mon Panier (X)"
158   */
159  function updateCartTitle() {
160    var titleElement = document.querySelector('.cart-title .cart-items-count');
161    if (!titleElement) return;
162
163    var totalItems = 0;
164
165    if (typeof prestashop !== 'undefined' && prestashop.cart && prestashop.cart.products_count !== undefined) {
166      totalItems = parseInt(prestashop.cart.products_count) || 0;
167    } else {
168      var productLines = document.querySelectorAll('.cart-items > .cart-item > .product-line-unified');
169
170      if (productLines.length === 0) {
171        productLines = document.querySelectorAll('.cart-item .product-line-unified');
172      }
173
174      var countedProducts = new Set();
175
176      productLines.forEach(function(item) {
177        var productId = item.dataset.idProduct || item.closest('[data-id-product]')?.dataset.idProduct;
178        var productAttrId = item.dataset.idProductAttribute || item.closest('[data-id-product-attribute]')?.dataset.idProductAttribute || '0';
179        var uniqueKey = productId + '-' + productAttrId;
180
181        if (productId && !countedProducts.has(uniqueKey)) {
182          countedProducts.add(uniqueKey);
183
184          var input = item.querySelector('.qty-input');
185          if (input && input.value) {
186            totalItems += parseInt(input.value) || 0;
187          } else {
188            var display = item.querySelector('.qty-display');
189            if (display) {
190              totalItems += parseInt(display.textContent) || 1;
191            } else {
192              var giftQty = item.querySelector('.gift-quantity');
193              if (giftQty) {
194                totalItems += parseInt(giftQty.textContent) || 1;
195              } else {
196                totalItems += 1;
197              }
198            }
199          }
200        }
201      });
202    }
203
204    titleElement.textContent = '(' + totalItems + ')';
205  }
206
207  /**
208   * Met à jour le message de livraison
209   */
210  function updateShippingMessage() {
211    var messageContainer = document.querySelector('.js-shipping-message');
212    if (!messageContainer) return;
213
214    var cartTotal = 0;
215
216    if (typeof prestashop !== 'undefined' && prestashop.cart) {
217      cartTotal = parseFloat(prestashop.cart.totals.total.amount) || 0;
218    }
219
220    if (cartTotal === 0) {
221      var totalElement = document.querySelector('.cart-total .value, .cart-summary-line.cart-total .value');
222      if (totalElement) {
223        var totalText = totalElement.textContent.replace(/[^\d,\.]/g, '').replace(',', '.');
224        cartTotal = parseFloat(totalText) || 0;
225      }
226    }
227
228    var freeShippingMsg = messageContainer.querySelector('.free-shipping-message');
229    var nextStepMsg = messageContainer.querySelector('.shipping-next-step-message');
230
231    if (freeShippingMsg && nextStepMsg) {
232      if (cartTotal >= FREE_SHIPPING_THRESHOLD) {
233        freeShippingMsg.style.display = 'inline';
234        nextStepMsg.style.display = 'none';
235      } else {
236        freeShippingMsg.style.display = 'none';
237        nextStepMsg.style.display = 'inline';
238      }
239    }
240  }
241
242  /**
243   * Nettoie les noms de couleurs (supprime préfixe numérique)
244   */
245  function cleanColorAttributes() {
246    var selectors = [
247      '.product-attribute .attr-value',
248      '.cart-product-attribute .attr-value',
249      '.product-line-info.couleur .value'
250    ];
251
252    selectors.forEach(function(selector) {
253      document.querySelectorAll(selector).forEach(function(el) {
254        var text = el.textContent.trim();
255        var cleaned = text.replace(/^\d{1,3}-/, '');
256        if (cleaned !== text) {
257          el.textContent = cleaned;
258        }
259      });
260    });
261  }
262
263  /**
264   * Initialise tous les boutons de quantité via délégation d'événements
265   */
266  function initQuantityButtons() {
267    if (window.cartQuantityInitialized) return;
268    window.cartQuantityInitialized = true;
269
270    document.addEventListener('click', function(e) {
271      var target = e.target.closest('.qty-btn');
272      if (!target) return;
273
274      if (isUpdating) {
275        e.preventDefault();
276        e.stopPropagation();
277        return;
278      }
279
280      if (target.classList.contains('qty-increase')) {
281        e.preventDefault();
282        e.stopPropagation();
283
284        var upUrl = target.getAttribute('data-up-url');
285        if (upUrl) {
286          var selector = target.closest('.cart-quantity-selector');
287          showSpinner(selector);
288          isUpdating = true;
289          updateCart(upUrl);
290        }
291        return;
292      }
293
294      if (target.classList.contains('qty-decrease')) {
295        e.preventDefault();
296        e.stopPropagation();
297
298        var downUrl = target.getAttribute('data-down-url');
299        if (downUrl) {
300          var selector = target.closest('.cart-quantity-selector');
301          showSpinner(selector);
302          isUpdating = true;
303          updateCart(downUrl);
304        }
305        return;
306      }
307
308      if (target.classList.contains('qty-trash')) {
309        var selector = target.closest('.cart-quantity-selector');
310        showSpinner(selector);
311        isUpdating = true;
312        return;
313      }
314    });
315
316    document.addEventListener('change', function(e) {
317      var input = e.target;
318      if (!input.classList.contains('qty-input') && !input.classList.contains('js-cart-line-product-quantity')) {
319        return;
320      }
321
322      if (isUpdating) return;
323
324      var newQty = parseInt(input.value) || 1;
325      if (newQty < 1) newQty = 1;
326
327      var updateUrl = input.getAttribute('data-update-url');
328      if (updateUrl) {
329        var selector = input.closest('.cart-quantity-selector');
330        showSpinner(selector);
331        isUpdating = true;
332        updateCart(updateUrl + '&qty=' + newQty);
333      }
334    });
335  }
336
337  /**
338   * Affiche le spinner sur un sélecteur de quantité
339   */
340  function showSpinner(selector) {
341    if (!selector) return;
342
343    selector.classList.add('is-loading');
344
345    selector.querySelectorAll('.qty-btn').forEach(function(btn) {
346      btn.disabled = true;
347      btn.classList.add('disabled');
348    });
349
350    var input = selector.querySelector('.qty-input');
351    if (input) {
352      input.disabled = true;
353    }
354
355    if (!selector.querySelector('.qty-spinner')) {
356      var spinner = document.createElement('div');
357      spinner.className = 'qty-spinner';
358      spinner.innerHTML = '<svg viewBox="0 0 24 24" width="18" height="18"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-dasharray="31.4 31.4" transform="rotate(-90 12 12)"><animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="0.6s" repeatCount="indefinite"/></circle></svg>';
359      selector.appendChild(spinner);
360    }
361  }
362
363  /**
364   * Cache tous les loaders
365   */
366  function hideAllLoaders() {
367    isUpdating = false;
368
369    document.querySelectorAll('.cart-quantity-selector').forEach(function(selector) {
370      selector.classList.remove('is-loading');
371
372      selector.querySelectorAll('.qty-btn').forEach(function(btn) {
373        btn.disabled = false;
374        btn.classList.remove('disabled');
375      });
376
377      var input = selector.querySelector('.qty-input');
378      if (input) {
379        input.disabled = false;
380      }
381
382      var spinner = selector.querySelector('.qty-spinner');
383      if (spinner) {
384        spinner.remove();
385      }
386    });
387  }
388
389  /**
390   * Met à jour le panier via AJAX
391   */
392  function updateCart(url) {
393    var ajaxUrl = url + (url.indexOf('?') > -1 ? '&' : '?') + 'ajax=1&action=update';
394
395    fetch(ajaxUrl, {
396      method: 'POST',
397      headers: {
398        'Accept': 'application/json, text/javascript, */*; q=0.01',
399        'X-Requested-With': 'XMLHttpRequest'
400      }
401    })
402    .then(function(response) {
403      return response.json();
404    })
405    .then(function(data) {
406      if (typeof prestashop !== 'undefined') {
407        prestashop.emit('updateCart', {
408          reason: data.id_product ? { idProduct: data.id_product } : {},
409          resp: data
410        });
411      } else {
412        window.location.reload();
413      }
414    })
415    .catch(function(error) {
416      console.error('Cart: Erreur', error);
417      isUpdating = false;
418      hideAllLoaders();
419      window.location.reload();
420    });
421  }
422
423  /**
424   * Toggle du champ code promo
425   */
426  function initPromoCodeToggle() {
427    var toggleBtns = document.querySelectorAll('.js-promo-toggle');
428
429    toggleBtns.forEach(function(btn) {
430      if (btn.dataset.initialized) return;
431      btn.dataset.initialized = 'true';
432
433      btn.addEventListener('click', function(e) {
434        e.preventDefault();
435
436        var form = this.nextElementSibling;
437        if (!form || !form.classList.contains('js-promo-form')) {
438          form = this.parentElement.querySelector('.js-promo-form');
439        }
440
441        if (form) {
442          this.classList.toggle('is-open');
443          form.classList.toggle('is-open');
444
445          if (form.classList.contains('is-open')) {
446            var input = form.querySelector('input[type="text"], input[name="discount_name"], .promo-input');
447            if (input) {
448              setTimeout(function() { input.focus(); }, 100);
449            }
450          }
451        }
452      });
453    });
454  }
455
456})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.