PageSourceSearch

https://guideposts.org/wp-content/plugins/guideposts-slide-cart/assets/js/custom-mini-cart.js?ver=1.0.0

js guideposts.org collected 2026-10-01 10:36:09 UTC 67,249 bytes, 1,930 lines download raw bytes

1(function($) {
2    'use strict';
3    
4    // Configuration
5    var config = {
6        ajaxUrl: custom_mini_cart_params.ajax_url,
7        nonce: custom_mini_cart_params.nonce,
8        isUpdating: false,
9        miniCartOpen: false
10    };
11    
12    // Donation-specific configuration
13    var donationConfig = {
14        isProcessing: false
15    };
16    
17    // Global cart state
18    let miniCartOpen = false;
19    let isClosing = false;
20    
21    // Initialize
22    $(document).ready(function() {
23        initMiniCartAJAX();
24        initDonationAJAX();
25        initCouponAJAX(); // Add this line
26        initCartOpenClose();
27        
28        // Initialize donation form values
29        initializeDonationForm();
30        
31        // Initialize cart state
32        $('.headerminicart').removeClass('open cartactive fast-open');
33        $('html').removeClass('cart-open');
34    });
35    
36    // =========================
37    // MINI CART AJAX HANDLERS
38    // =========================
39    
40    function initMiniCartAJAX() {
41    // Handle quantity buttons
42    $(document).on('click', '.qib-button', function(e) {
43        e.preventDefault();
44        e.stopPropagation();
45        
46        // Global protection check
47        if (config.isUpdating) {
48            console.log('Cart is already updating, please wait...');
49            showMessage('Please wait, cart is updating...', 'info');
50            return false;
51        }
52        
53        var $button = $(this);
54        
55        // Check if button is already disabled
56        if ($button.hasClass('disabled') || $button.prop('disabled')) {
57            console.log('Button is disabled, ignoring click');
58            return false;
59        }
60        
61        var $wrapper = $button.closest('.qib-button-wrapper');
62        var cartItemKey = $wrapper.data('cart-item-key');
63        var $input = $wrapper.find('.cart-quantity-input');
64        var action = $button.data('action');
65        var currentQty = parseInt($input.val()) || 1;
66        
67        // Check if input is disabled
68        if ($input.prop('disabled')) {
69            console.log('Quantity input is disabled, ignoring click');
70            return false;
71        }
72        
73        if (action === 'increase') {
74            currentQty++;
75        } else if (action === 'decrease') {
76            currentQty--;
77            if (currentQty < 1) currentQty = 1;
78        }
79        
80        $input.val(currentQty);
81        updateCartItem(cartItemKey, currentQty, $wrapper);
82        
83        return false;
84    });
85    
86    // Handle quantity input directly
87    $(document).on('change', '.cart-quantity-input', function(e) {
88    e.stopPropagation();
89    
90    // Global protection check
91    if (config.isUpdating) {
92        console.log('Cart is already updating, please wait...');
93        showMessage('Please wait, cart is updating...', 'info');
94        return false;
95    }
96    
97    var $input = $(this);
98    
99    // Check if input is disabled
100    if ($input.prop('disabled')) {
101        console.log('Input is disabled, ignoring change');
102        return false;
103    }
104    
105    var cartItemKey = $input.data('cart-item-key');
106    var quantity = parseInt($input.val()) || 1;
107    
108    // VALIDATION: Minimum quantity is 1
109    if (quantity < 1) {
110        quantity = 1;
111        $input.val(1); // Reset to minimum
112        showMessage('Minimum quantity must be 1', 'error');
113        updateCartItem(cartItemKey, quantity, $input.closest('.qib-button-wrapper'));
114        //return false; // Don't proceed with AJAX
115    }
116    
117    updateCartItem(cartItemKey, quantity, $input.closest('.qib-button-wrapper'));
118    
119    return false;
120});
121
122// Add input event handler for real-time validation
123$(document).on('input', '.cart-quantity-input', function(e) {
124    var $input = $(this);
125    var value = $input.val();
126    
127    // Remove non-numeric characters
128    var numericValue = value.replace(/[^0-9]/g, '');
129    
130    // Don't allow 0 as the first digit
131    if (numericValue === '0') {
132        $input.val('1');
133        return;
134    }
135    
136    // Remove leading zeros
137    if (numericValue.length > 1 && numericValue.charAt(0) === '0') {
138        numericValue = numericValue.replace(/^0+/, '');
139    }
140    
141    $input.val(numericValue);
142});
143
144// Add blur event handler to validate when user leaves the field
145$(document).on('blur', '.cart-quantity-input', function(e) {
146    var $input = $(this);
147    var quantity = parseInt($input.val()) || 0;
148    
149    if (quantity < 1) {
150        $input.val(1);
151        // Optional: Show a subtle message
152        // showMessage('Minimum quantity is 1', 'error');
153    }
154});
155    
156    // Handle remove items - INCLUDES DONATION ITEMS
157    $(document).on('click', '.remove-cart-item, .remove-donation-btn', function(e) {
158        e.preventDefault();
159        e.stopPropagation();
160        
161        // Global protection check
162        if (config.isUpdating) {
163            console.log('Cart is already updating, please wait...');
164            showMessage('Please wait, cart is updating...', 'info');
165            return false;
166        }
167        
168        var $link = $(this);
169        
170        // Check if link is already in loading state
171        if ($link.hasClass('loading')) {
172            console.log('Item is already being removed, please wait...');
173            return false;
174        }
175        
176        var cartItemKey = '';
177        
178        // Check if this is a regular remove link or donation remove button
179        if ($link.hasClass('remove-donation-btn')) {
180            cartItemKey = $link.data('cart-item-key');
181        } else {
182            cartItemKey = $link.data('cart_item_key');
183        }
184        
185        if (!cartItemKey) {
186            console.error('No cart item key found for removal');
187            return false;
188        }
189        
190        $link.text('Removing...').addClass('loading');
191        removeCartItem(cartItemKey, $link);
192        
193        return false;
194    });
195}
196    
197    // =========================
198    // DONATION AJAX HANDLERS
199    // =========================
200    
201    function initDonationAJAX() {
202    // Handle donation amount selection
203    $(document).on('click', '.donatecart .price-select, .donatecart .donation-select', function(e) {
204        e.preventDefault();
205        e.stopPropagation();
206        
207        // Check global processing state
208        if (donationConfig.isProcessing || config.isUpdating) {
209            console.log('Donation is processing, please wait...');
210            return;
211        }
212        
213        var $this = $(this);
214        
215        // Check if element is disabled
216        if ($this.hasClass('disabled') || $this.prop('disabled')) {
217            console.log('Donation option is disabled, ignoring click');
218            return;
219        }
220        
221        //var amount = $this.data('amount');
222        var amount = 5;
223        var $container = $this.closest('.donatecart');
224        
225        // Update UI
226        $container.find('.price-select, .donation-select').removeClass('selected').addClass('price-select');
227        $this.removeClass('price-select').addClass('donation-select selected');
228        
229        // Update hidden field with selected amount
230       // $container.find('#selected-donation-amount').val(amount);
231        
232        // Clear custom amount field
233        $container.find('#custom-donation-amount').val('');
234        
235        // Clear error messages
236        $container.find('.msg.error').hide().text('');
237    });
238    
239    // Handle custom amount input
240    $(document).on('input change', '.donatecart .custom-donation-input-field', function(e) {
241        // Check if donation is processing
242        if (donationConfig.isProcessing || config.isUpdating) {
243            // Prevent input while processing
244            e.preventDefault();
245            return;
246        }
247        
248        var $this = $(this);
249        var customAmount = $this.val();
250        var $container = $this.closest('.donatecart');
251        
252        if (customAmount && parseFloat(customAmount) > 0) {
253            // Update hidden field with custom amount
254            $container.find('#selected-donation-amount').val(customAmount);
255            
256            // Deselect preset amounts
257            $container.find('.price-select, .donation-select').removeClass('selected').addClass('price-select');
258            $container.find('.msg.error').hide().text('');
259        }
260    });
261    
262    // Handle donation "Add to Cart" button
263    $(document).on('click', '.donatecart .add-donation-btn', function(e) {
264        e.preventDefault();
265        e.stopPropagation();
266        
267        // Global protection check
268        if (donationConfig.isProcessing || config.isUpdating) {
269            console.log('Donation is already processing, please wait...');
270            showMessage('Please wait, donation is being processed...', 'info');
271            return;
272        }
273        
274        var $button = $(this);
275        
276        // Check if button is already disabled
277        if ($button.hasClass('loading') || $button.prop('disabled')) {
278            console.log('Donation button is already processing, ignoring click');
279            return;
280        }
281        
282        var $container = $button.closest('.donatecart');
283        var productId = $button.data('product-id');
284        var donationSku = $button.data('donation-sku');
285        var donationNonce = $button.data('nonce');
286        
287        // GET THE ACTUAL AMOUNT FROM THE FORM
288        var donationAmount = $container.find('#selected-donation-amount').val();
289        
290        // If no amount selected but custom field has value, use that
291        if (!donationAmount || donationAmount === '' || donationAmount === '10') {
292            var customAmount = $container.find('#custom-donation-amount').val();
293            if (customAmount && parseFloat(customAmount) > 0) {
294                donationAmount = customAmount;
295            }
296        }
297        
298        // Validate amount
299        if (!donationAmount || parseFloat(donationAmount) < 1) {
300            $container.find('.msg.error').text('Please enter a donation amount of at least $1').show();
301            return;
302        }
303        
304        console.log('Adding donation:', {
305            productId: productId,
306            amount: donationAmount,
307            sku: donationSku
308        });
309        
310        // Add donation to cart
311        addDonationToCart(productId, donationAmount, donationSku, donationNonce, $button);
312    });
313    
314    // Handle Enter key in custom donation field
315    $(document).on('keypress', '.donatecart .custom-donation-input-field', function(e) {
316        // Check if donation is processing
317        if (donationConfig.isProcessing || config.isUpdating) {
318            e.preventDefault();
319            return;
320        }
321        
322        if (e.which === 13) {
323            e.preventDefault();
324            var $container = $(this).closest('.donatecart');
325            var $button = $container.find('.add-donation-btn');
326            
327            // Check if button is disabled
328            if (!$button.hasClass('loading') && !$button.prop('disabled')) {
329                $button.click();
330            }
331        }
332    });
333}
334    
335    // =========================
336    // DONATION FUNCTIONS
337    // =========================
338    
339    function addDonationToCart(productId, amount, sku, nonce, $button) {
340    // Check if already processing
341    if (donationConfig.isProcessing || config.isUpdating) {
342        console.log('Donation already processing, ignoring click');
343        return;
344    }
345    
346    donationConfig.isProcessing = true;
347    config.isUpdating = true;
348    
349    console.log('Sending donation to cart:', {
350        productId: productId,
351        amount: amount,
352        sku: sku
353    });
354    
355    // Disable ALL quantity controls in the cart
356    disableAllQuantityControls();
357    
358    // Get the container
359    var $container = $button.closest('.donatecart');
360    
361    // Show loading state on button
362    $button.addClass('loading').html(
363        '<div class="loading-spinner"></div> Adding...'
364    );
365    
366    // Show cart loading if cart is open
367    if ($('.headerminicart').hasClass('open')) {
368        //$('.headerminicart').addClass('updating');
369    }
370    
371    // Prepare form data
372    var formData = new FormData();
373    formData.append('action', 'add_donation_to_cart');
374    formData.append('product_id', productId);
375    formData.append('donation_amount', amount);
376    formData.append('donation_sku', sku);
377    formData.append('nonce', nonce);
378    
379    // Use fetch API for better error handling
380    fetch(config.ajaxUrl, {
381        method: 'POST',
382        body: formData,
383        credentials: 'same-origin'
384    })
385    .then(response => {
386        if (!response.ok) {
387            throw new Error('Network response was not ok: ' + response.statusText);
388        }
389        return response.json();
390    })
391    .then(data => {
392        console.log('Donation Response:', data);
393        
394        if (data.success) {
395            // Clear any error messages
396            $container.find('.msg.error').hide().text('');
397            
398            // Show success message
399            showMessage(data.data.message || 'Donation of $' + parseFloat(amount).toFixed(2) + ' added to cart!', 'success');
400            
401            // Update cart totals IMMEDIATELY
402            if (data.data) {
403                updateCartTotals(data.data);
404            }
405            
406            // IMPORTANT: Trigger WooCommerce cart updates
407            if (typeof wc_cart_fragments_params !== 'undefined') {
408                $(document.body).trigger('wc_fragment_refresh');
409            }
410            
411            $(document.body).trigger('updated_wc_div');
412            $(document.body).trigger('updated_cart_totals');
413            
414            // Refresh mini cart to show updated donation
415            setTimeout(function() {
416                refreshMiniCart();
417                // Re-enable all controls after refresh
418                setTimeout(enableAllQuantityControls, 100);
419            }, 500);
420            
421            // Auto-open cart if closed
422            if (!miniCartOpen) {
423                setTimeout(function() {
424                    openMiniCart(true);
425                }, 1000);
426            }
427            
428        } else {
429            // Show error
430            var errorMsg = data.data && data.data.message ? 
431                         data.data.message : 'Error adding donation. Please try again.';
432            $container.find('.msg.error').text(errorMsg).show();
433            
434            // Re-enable all controls on error
435            enableAllQuantityControls();
436            resetDonationButton($button);
437        }
438    })
439    .catch(error => {
440        console.error('Donation AJAX Error:', error);
441        $container.find('.msg.error').text('Processing donation...').show();
442        
443        // Re-enable all controls on network error
444        enableAllQuantityControls();
445        resetDonationButton($button);
446        
447        // Refresh cart to see if donation was added
448        setTimeout(function() {
449            refreshMiniCart();
450            showMessage('Please check your cart - donation may have been added.', 'success');
451        }, 1000);
452    })
453    .finally(() => {
454        donationConfig.isProcessing = false;
455        config.isUpdating = false;
456        
457        // Remove cart loading
458        setTimeout(function() {
459            $('.headerminicart').removeClass('updating');
460        }, 1000);
461    });
462}
463    
464    function resetDonationButton($button) {
465        $button.removeClass('loading').prop('disabled', false).html(
466            'Add to Cart <svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"><g><g><path d="M20.756,5.345C20.565,5.126,20.29,5,20,5H6.181L5.986,3.836C5.906,3.354,5.489,3,5,3H2.75c-0.553,0-1,0.447-1,1 s0.447,1,1,1h1.403l1.86,11.164c0.008,0.045,0.031,0.082,0.045,0.124c0.016,0.053,0.029,0.103,0.054,0.151 c0.032,0.066,0.075,0.122,0.12,0.179c0.031,0.039,0.059,0.078,0.095,0.112c0.058,0.054,0.125,0.092,0.193,0.13 c0.038,0.021,0.071,0.049,0.112,0.065C6.748,16.972,6.87,17,6.999,17C7,17,18,17,18,17c0.553,0,1-0.447,1-1s-0.447-1-1-1H7.847 l-0.166-1H19c0.498,0,0.92-0.366,0.99-0.858l1-7C21.031,5.854,20.945,5.563,20.756,5.345z"/><circle cx="8.5" cy="19.5" r="1.5"/><circle cx="17.5" cy="19.5" r="1.5"/></g></g></svg>'
467        );
468    }
469    
470    function initializeDonationForm() {
471        $('.donatecart').each(function() {
472            var $container = $(this);
473            var selectedAmount = $container.find('#selected-donation-amount').val();
474            var customAmount = $container.find('#custom-donation-amount').val();
475            
476            console.log('Initializing donation form:', {
477                selectedAmount: selectedAmount,
478                customAmount: customAmount
479            });
480            
481            // If custom amount exists and is valid
482            if (customAmount && customAmount !== '' && parseFloat(customAmount) > 0) {
483                // Deselect all preset amounts
484                $container.find('.price-select, .donation-select').removeClass('selected').addClass('price-select');
485            } else if (selectedAmount) {
486                // Select the preset amount that matches
487                $container.find('[data-amount="' + selectedAmount + '"]')
488                    .removeClass('price-select')
489                    .addClass('donation-select selected');
490            }
491        });
492    }
493    
494    // =========================
495    // CART FUNCTIONS
496    // =========================
497    
498    function updateCartItem_old(cartItemKey, quantity, $element) {
499
500        
501    if (!cartItemKey || quantity < 1) return;
502    
503    config.isUpdating = true;
504    
505    // Disable ALL quantity controls in the cart
506    disableAllQuantityControls();
507    
508    // Get all relevant quantity elements for this item
509    var $wrapper = $element.closest('.qib-button-wrapper');
510    var $input = $wrapper.find('.cart-quantity-input');
511    var $buttons = $wrapper.find('.qib-button');
512    var $row = $wrapper.closest('tr');
513    
514    // Show loading state for this specific item
515    $row.addClass('updating');
516    $row.find('.product-subtotal-amount').html('<span class="loading">Updating...</span>');
517    $('.headerminicart').addClass('updating');
518
519    $.ajax({
520        url: config.ajaxUrl,
521        type: 'POST',
522        data: {
523            action: 'custom_update_cart_item_clean',
524            cart_item_key: cartItemKey,
525            quantity: quantity,
526            security: config.nonce
527        },
528        success: function(response) {
529            if (response.success) {
530                // Update cart totals
531                updateCartTotals(response.data);
532                
533                // Update individual item subtotal
534                if ($row.length && response.data.item_subtotal) {
535                    $row.find('.product-subtotal-amount').html(response.data.item_subtotal);
536                }
537                
538                showMessage(response.data.message || 'Quantity updated', 'success');
539            } else {
540                showMessage(response.data || 'Failed to update quantity', 'error');
541                refreshMiniCart();
542            }
543        },
544        error: function(xhr, status, error) {
545            showMessage('Error updating quantity. Please try again.', 'error');
546            //showMessage('Error updating quantity. Please try again.', 'success');
547            refreshMiniCart();
548        },
549        complete: function() {
550            config.isUpdating = false;
551            
552            // Re-enable ALL quantity controls after a brief delay
553            setTimeout(function() {
554                enableAllQuantityControls();
555                $row.removeClass('updating');
556            }, 500);
557            
558            $('.headerminicart').removeClass('updating');
559        }
560    });
561}
562
563function updateCartItem(cartItemKey, quantity, $element) {
564    if (!cartItemKey || quantity < 1) return;
565    
566    config.isUpdating = true;
567    
568    // Disable ALL quantity controls in the cart
569    disableAllQuantityControls();
570    
571    // Get all relevant quantity elements for this item
572    var $wrapper = $element.closest('.qib-button-wrapper');
573    var $input = $wrapper.find('.cart-quantity-input');
574    var $buttons = $wrapper.find('.qib-button');
575    var $row = $wrapper.closest('tr');
576    
577    // Show loading state for this specific item
578    $row.addClass('updating123');
579    $row.find('.product-subtotal-amount').html('<span class="loading">Updating...</span>');
580    $('.headerminicart').addClass('updating234');
581
582    $.ajax({
583        url: config.ajaxUrl,
584        type: 'POST',
585        data: {
586            action: 'custom_update_cart_item',
587            cart_item_key: cartItemKey,
588            quantity: quantity,
589            security: config.nonce
590        },
591        dataType: 'text', // Accept text first, then parse
592        success: function(rawResponse, status, xhr) {
593            console.log('Raw response received:', rawResponse.substring(0, 200));
594            
595            try {
596                // Clean the response - remove script tags and any HTML
597                var cleanedResponse = rawResponse.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
598                cleanedResponse = cleanedResponse.replace(/^\s*[\r\n]/gm, '').trim();
599                
600                // Parse JSON
601                var response = JSON.parse(cleanedResponse);
602                
603                console.log('Parsed response:', response);
604                
605                if (response && response.success) {
606                    // Update cart totals
607                    updateCartTotals(response.data);
608                    
609                    // Update individual item subtotal
610                    if ($row.length && response.data.item_subtotal) {
611                        $row.find('.product-subtotal-amount').html(response.data.item_subtotal);
612                    }
613                    
614                    showMessage(response.data.message || 'Quantity updated', 'success');
615                    refreshMiniCart();
616                } else {
617                    showMessage(response.data || 'Failed to update quantity', 'error');
618                    refreshMiniCart();
619                }
620            } catch (e) {
621                console.error('Failed to parse response:', e);
622                console.log('Cleaned response:', cleanedResponse);
623                showMessage('Server returned invalid response', 'error');
624                refreshMiniCart();
625            }
626        },
627        error: function(xhr, status, error) {
628            console.error('AJAX Error:', error);
629            showMessage('Error updating quantity. Please try again.', 'error');
630            refreshMiniCart();
631        },
632        complete: function() {
633            config.isUpdating = false;
634            
635            // Re-enable ALL quantity controls after a brief delay
636            setTimeout(function() {
637                enableAllQuantityControls();
638                $row.removeClass('updating');
639            }, 500);
640            
641            $('.headerminicart').removeClass('updating');
642        }
643    });
644}
645    
646    function removeCartItem(cartItemKey, $link) {
647    if (!cartItemKey) return;
648    
649    config.isUpdating = true;
650    
651    // Disable ALL quantity controls in the cart
652    disableAllQuantityControls();
653    
654    // Find the specific row
655    var $row = $('[data-cart-item-key="' + cartItemKey + '"]');
656    
657    // Update button text
658    $link.text('Removing...').addClass('loading');
659    $('.headerminicart').addClass('updating999');
660
661    $.ajax({
662        url: config.ajaxUrl,
663        type: 'POST',
664        data: {
665            action: 'custom_remove_cart_item',
666            cart_item_key: cartItemKey,
667            security: config.nonce
668        },
669        success: function(response) {
670            if (response.success) {
671                // Update cart totals
672                updateCartTotals(response.data);
673                
674                // Show success message
675                showMessage(response.data.message || 'Item removed successfully', 'success');
676                
677                // Refresh cart to get updated state
678                refreshMiniCart();
679            } else {
680                showMessage(response.data || 'Failed to remove item', 'error');
681                $link.text('Remove').removeClass('loading');
682                enableAllQuantityControls(); // Re-enable on error
683            }
684        },
685        error: function(xhr, status, error) {
686            console.error('Remove error:', error);
687            showMessage('Error removing item', 'error');
688            $link.text('Remove').removeClass('loading');
689            enableAllQuantityControls(); // Re-enable on error
690        },
691        complete: function() {
692            config.isUpdating = false;
693            $('.headerminicart').removeClass('updating');
694        }
695    });
696}
697    
698    // =========================
699    // UPDATE CART TOTALS TO INCLUDE COUPONS
700    // =========================
701
702    function updateCartTotals(data) {
703    // Update item count
704    if (data.item_count !== undefined) {
705        var countText = '(' + data.item_count + ' item' + (data.item_count !== 1 ? 's' : '') + ')';
706        $('.cart-count').text(countText);
707    }
708
709    // Preferred path: the server sends back the whole totals block already
710    // rendered, so subtotal, coupon rows, shipping, estimated tax and order
711    // total can never disagree with each other.
712    if (data.totals_html) {
713        $('.minicartTable').html(data.totals_html);
714
715        if (typeof wc_cart_fragments_params !== 'undefined') {
716            $(document.body).trigger('wc_fragment_refresh');
717        }
718        $(document.body).trigger('updated_wc_div');
719        $(document.body).trigger('updated_cart_totals');
720        return;
721    }
722
723    // Fallback: patch the rows individually.
724    // Update subtotal
725    if (data.subtotal) {
726        $('.cart-total-amount').html(data.subtotal);
727    }
728    
729    // Update discount row if exists
730    if (data.discount_total) {
731        if ($('.cart-discount').length) {
732            // Update existing discount row
733            $('.cart-discount td').html(data.discount_total);
734        } else {
735            // Add discount row
736            $('.cart-subtotal').after(
737                '<tr class="cart-discount">' +
738                '<th>Discount</th>' +
739                '<td class="cart-discount-amount">' + data.discount_total + '</td>' +
740                '</tr>'
741            );
742        }
743    } else {
744        // Remove discount row if no discount
745        $('.cart-discount').remove();
746    }
747    
748    // Update total if provided
749    if (data.total) {
750        if ($('.cart-order-total').length) {
751            $('.cart-order-total-amount').html(data.total);
752        } else {
753            // Add total row
754            var totalRow = '<tr class="cart-total">' +
755                          '<th>Total</th>' +
756                          '<td class="cart-final-amount">' + data.total + '</td>' +
757                          '</tr>';
758            
759            if ($('.cart-discount').length) {
760                $('.cart-discount').after(totalRow);
761            } else {
762                $('.cart-subtotal').after(totalRow);
763            }
764        }
765    } else {
766        $('.cart-total').remove();
767    }
768    
769    // Update coupon display if data has coupons_html
770    if (data.coupons_html !== undefined) {
771        $('.applied-coupons-container').html(data.coupons_html);
772    } else if (data.coupons) {
773        updateCouponDisplay(data.coupons);
774    }
775    
776    // Also update WooCommerce fragments if available
777    if (typeof wc_cart_fragments_params !== 'undefined') {
778        $(document.body).trigger('wc_fragment_refresh');
779    }
780    
781    // Trigger additional WooCommerce events
782    $(document.body).trigger('updated_wc_div');
783    $(document.body).trigger('updated_cart_totals');
784}
785    // Update your refreshMiniCart function to also update coupon display
786    // Update the refreshMiniCart function to also update coupons:
787function refreshMiniCart() {
788    $.ajax({
789        url: config.ajaxUrl,
790        type: 'POST',
791        data: {
792            action: 'custom_get_mini_cart',
793            security: config.nonce
794        },
795        success: function(response) {
796            if (response.success) {
797                // Replace the entire mini cart
798                $('.minicart').html(response.data.html);
799                $('.cart-quantity').html(response.data.count);
800                
801                // Update state
802                config.miniCartOpen = $('.minicart').is(':visible');
803                
804                // Re-initialize donation form
805                setTimeout(initializeDonationForm, 100);
806                
807                // Note: The mini cart HTML from custom_get_mini_cart already includes 
808                // the coupons section, so no need to update separately
809            }
810        },
811        error: function(xhr, status, error) {
812            console.error('Refresh error:', error);
813        }
814    });
815}
816    
817    function showMessage(message, type) {
818        // Remove existing messages
819        $('.mini-cart-message').remove();
820        
821        var $message = $('<div class="mini-cart-message mini-cart-' + type + '">' + message + '</div>');
822        
823        // Add to mini cart
824        $('.minicart').prepend($message);
825        
826        // Auto remove after 3 seconds
827        setTimeout(function() {
828            $message.fadeOut(300, function() {
829                $(this).remove();
830            });
831        }, 3000);
832    }
833    
834    // =========================
835    // CART OPEN/CLOSE FUNCTIONS
836    // =========================
837    
838    function initCartOpenClose() {
839        // Remove existing handlers
840        $(document).off('click.minicart');
841        $('.closeCart').off('click');
842
843        // 1. Click on overlay
844        $(document).on('click.minicart', '.headerminicart.cartactive', function(e) {
845            if (e.target === this) {
846                closeMiniCart();
847                return false;
848            }
849        });
850
851        // 2. Close button
852        $(document).on('click.minicart', '.closeCart', function(e) {
853            e.preventDefault();
854            e.stopPropagation();
855            closeMiniCart();
856            return false;
857        });
858
859        // 3. Open cart
860        // $(document).on('click.minicart', '#myCart, .cartBubble, .cartBubble a', function(e) {
861        //     e.preventDefault();
862        //     openMiniCart();
863        // });
864
865        $(document).on('click.minicart', '#myCart', function(e) {
866            e.preventDefault();
867            openMiniCart();
868        });
869
870        // 4. ESC key
871        $(document).on('keydown.minicart', function(e) {
872            if (e.key === 'Escape' && miniCartOpen && !isClosing) {
873                closeMiniCart();
874            }
875        });
876
877        // 5. Click outside
878        $(document).on('click.minicart', function(e) {
879            if (!miniCartOpen || isClosing) return;
880            
881            var $target = $(e.target);
882            var isInCart = $target.closest('.minicart').length > 0;
883            var isOpenButton = $target.closest('#myCart, .cartBubble, .cartBubble a').length > 0;
884            
885            if (!isInCart && !isOpenButton && !$target.closest('.headerminicart').length) {
886                //closeMiniCart();
887            }
888        });
889
890        // Auto open after add to cart
891        $(document.body).on('added_to_cart', function() {
892            openMiniCart(true);
893        });
894
895        // Non-AJAX fallback
896        $(document).on('click submit', '.add_to_cart_button, form.cart', function() {
897            sessionStorage.setItem('open_cart', '1');
898        });
899
900        if (sessionStorage.getItem('open_cart') === '1') {
901            sessionStorage.removeItem('open_cart');
902            setTimeout(() => openMiniCart(true), 300);
903        }
904
905        // Checkout button
906        $(document).on('click', '.checkout-button', function() {
907            window.location.href = $(this).attr('href');
908            return false;
909        });
910    }
911
912
913    // =========================
914    // SCROLL LOCK
915    // =========================
916
917    var gscScrollY = 0;
918    var gscScrollLocked = false;
919
920    function gscLockScroll() {
921        if (gscScrollLocked) return;
922
923        gscScrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
924        gscScrollLocked = true;
925
926        // Offsetting by the current scroll keeps the viewport showing the same
927        // content once the body is fixed.
928        document.body.style.top = (-gscScrollY) + 'px';
929    }
930
931    function gscUnlockScroll() {
932        if (!gscScrollLocked) return;
933
934        gscScrollLocked = false;
935        document.body.style.top = '';
936
937        // Jump straight back — 'auto' avoids a smooth-scroll animation if the
938        // theme sets scroll-behavior: smooth on html.
939        window.scrollTo({ top: gscScrollY, left: 0, behavior: 'auto' });
940    }
941    
942    function openMiniCart(fast = false) {
943        if (miniCartOpen || isClosing) return;
944        isClosing = false;
945
946        const el = document.querySelector('.cartBubble');
947        const display = window.getComputedStyle(el).display;
948        console.log('cartBubble is hidden (display: none)');
949
950        if (display === 'none') {
951        console.log('cartBubble is hidden (display: none)');
952        //return false;
953        } else {
954        console.log('cartBubble is visible, display is:', display);
955        gscLockScroll();
956        $('html').addClass('open cartactive');
957        
958        setTimeout(() => {
959            miniCartOpen = true;
960            
961            if (fast) {
962                $('html').addClass('open cartactive');
963            }
964            
965            $('.headerminicart').addClass('open cartactive');
966            
967            setTimeout(() => {
968                // Cleanup
969            }, fast ? 200 : 400);
970        }, 10);
971        }
972        
973        
974    }
975
976    function closeMiniCart() {
977        if (!miniCartOpen || isClosing) return;
978
979        isClosing = true;
980
981        $('.headerminicart').removeClass('open cartactive');
982
983        setTimeout(() => {
984            $('html').removeClass('open cartactive');
985            gscUnlockScroll();
986        }, 500);
987
988        setTimeout(() => {
989            $('.headerminicart').removeClass('cartactive open');
990
991            miniCartOpen = false;
992            isClosing = false;
993        }, 500);
994    }
995    
996    // =========================
997    // COUPON AJAX HANDLERS
998    // =========================
999
1000    function initCouponAJAX() {
1001    console.log('Initializing coupon AJAX handlers...');
1002    
1003    // Handle coupon apply button
1004    $(document).on('click', '.discount-code-apply-btn', function(e) {
1005        e.preventDefault();
1006        e.stopPropagation();
1007        console.log('Apply button clicked!');
1008        
1009        // Global protection check
1010        if (config.isUpdating) {
1011            console.log('Cart is already updating, please wait...');
1012            showMessage('Please wait, cart is updating...', 'info');
1013            return false;
1014        }
1015        
1016        var $button = $(this);
1017        
1018        // Check if button is disabled
1019        if ($button.prop('disabled')) {
1020            console.log('Coupon button is disabled, ignoring click');
1021            return false;
1022        }
1023        
1024        var $input = $button.closest('.discount-code-input-group').find('.discount-code-input');
1025        var couponCode = $input.val().trim();
1026        
1027        console.log('Coupon code entered:', couponCode);
1028        
1029        if (!couponCode) {
1030            console.log('No coupon code entered');
1031            showCouponMessage('Please enter a discount code', 'error');
1032            return false;
1033        }
1034        
1035        applyCoupon(couponCode, $button);
1036        
1037        return false;
1038    });
1039    
1040    // Handle Enter key in coupon input
1041    $(document).on('keypress', '.discount-code-input', function(e) {
1042        if (e.which === 13) {
1043            e.preventDefault();
1044            console.log('Enter key pressed in coupon input');
1045            var $button = $(this).closest('.discount-code-input-group').find('.discount-code-apply-btn');
1046            
1047            // Check if button is disabled
1048            if (!$button.prop('disabled')) {
1049                $button.click();
1050            }
1051        }
1052    });
1053    
1054    // Handle coupon removal
1055    $(document).on('click', '.remove-coupon', function(e) {
1056        e.preventDefault();
1057        e.stopPropagation();
1058        console.log('Remove coupon clicked');
1059        
1060        // Global protection check
1061        if (config.isUpdating) {
1062            console.log('Cart is already updating, please wait...');
1063            showMessage('Please wait, cart is updating...', 'info');
1064            return false;
1065        }
1066        
1067        var $link = $(this);
1068        var couponCode = $link.data('coupon-code');
1069        
1070        if (!couponCode) {
1071            console.log('No coupon code in remove link');
1072            return false;
1073        }
1074        
1075        removeCoupon(couponCode, $link);
1076        
1077        return false;
1078    });
1079}
1080
1081    // =========================
1082    // COUPON FUNCTIONS
1083    // =========================
1084
1085    // Update the applyCoupon function to handle display:
1086function applyCoupon(couponCode, $button) {
1087    if (config.isUpdating) {
1088        console.log('Cannot apply coupon: already updating');
1089        return;
1090    }
1091    
1092    config.isUpdating = true;
1093    console.log('Applying coupon:', couponCode, 'AJAX URL:', config.ajaxUrl);
1094    
1095    // Disable ALL quantity controls
1096    disableAllQuantityControls();
1097    
1098    // Show loading state
1099    $button.prop('disabled', true).text('Applying...');
1100    showCouponMessage('Applying coupon...', 'info');
1101    
1102    // Simple AJAX call
1103    $.ajax({
1104        url: config.ajaxUrl,
1105        type: 'POST',
1106        data: {
1107            action: 'custom_apply_coupon',
1108            coupon_code: couponCode,
1109            security: config.nonce
1110        },
1111        beforeSend: function() {
1112            console.log('AJAX request sending...');
1113        },
1114        success: function(response) {
1115            console.log('AJAX success response:', response);
1116            
1117            if (response.success) {
1118                console.log('Coupon applied successfully');
1119                showCouponMessage(response.data.message || 'Coupon applied successfully!', 'success');
1120                
1121                // Clear input field
1122                $button.closest('.discount-code-input-group').find('.discount-code-input').val('');
1123                
1124                // Update cart totals
1125                if (response.data) {
1126                    updateCartTotals(response.data);
1127                    
1128                    if (response.data.coupons_html) {
1129                        $('.applied-coupons-container').html(response.data.coupons_html);
1130                    } else if (response.data.coupons) {
1131                        updateCouponDisplay(response.data.coupons);
1132                    }
1133                }
1134                
1135            } else {
1136                console.log('Coupon failed:', response.data);
1137                showCouponMessage(response.data || 'Failed to apply coupon', 'error');
1138            }
1139        },
1140        error: function(xhr, status, error) {
1141            console.error('AJAX error:', {
1142                status: status,
1143                error: error,
1144                responseText: xhr.responseText
1145            });
1146            showCouponMessage('Error applying coupon. Please try again.', 'error');
1147        },
1148        complete: function() {
1149            console.log('AJAX complete');
1150            config.isUpdating = false;
1151            $button.prop('disabled', false).text('Apply');
1152            
1153            // Re-enable all controls
1154            setTimeout(enableAllQuantityControls, 500);
1155        }
1156    });
1157}
1158
1159function removeCoupon(couponCode, $link) {
1160    if (config.isUpdating) return;
1161    
1162    config.isUpdating = true;
1163    
1164    // Disable ALL quantity controls
1165    disableAllQuantityControls();
1166    
1167    // Show loading state
1168    $link.text('Removing...');
1169    showCouponMessage('Removing coupon...', 'info');
1170    
1171    $.ajax({
1172        url: config.ajaxUrl,
1173        type: 'POST',
1174        data: {
1175            action: 'custom_remove_coupon',
1176            coupon_code: couponCode,
1177            security: config.nonce
1178        },
1179        success: function(response) {
1180            if (response.success) {
1181                // Show success message
1182                showCouponMessage(response.data.message || 'Coupon removed successfully!', 'success');
1183                
1184                // Update cart totals
1185                updateCartTotals(response.data);
1186                
1187                // Update coupon display using HTML from response
1188                if (response.data.coupons_html !== undefined) {
1189                    $('.applied-coupons-container').html(response.data.coupons_html);
1190                } else if (response.data.coupons) {
1191                    updateCouponDisplay(response.data.coupons);
1192                }
1193                
1194            } else {
1195                // Show error message
1196                showCouponMessage(response.data || 'Failed to remove coupon', 'error');
1197            }
1198        },
1199        error: function(xhr, status, error) {
1200            showCouponMessage('Error removing coupon. Please try again.', 'error');
1201            console.error('Coupon remove error:', error);
1202        },
1203        complete: function() {
1204            config.isUpdating = false;
1205            
1206            // Re-enable all controls
1207            setTimeout(enableAllQuantityControls, 500);
1208        }
1209    });
1210}
1211
1212
1213// =========================
1214// GLOBAL QUANTITY CONTROL FUNCTIONS
1215// =========================
1216
1217function disableAllQuantityControls() {
1218    console.log('Disabling all quantity controls...');
1219    
1220    // Disable all quantity inputs
1221    $('.cart-quantity-input').prop('disabled', true).addClass('disabled');
1222    
1223    // Disable all quantity buttons
1224    $('.qib-button').prop('disabled', true).addClass('disabled');
1225    
1226    // Disable all remove buttons
1227    $('.remove-cart-item, .remove-donation-btn').prop('disabled', true).addClass('disabled');
1228    
1229    // Disable coupon apply button
1230    $('.discount-code-apply-btn').prop('disabled', true).addClass('disabled');
1231    
1232    // Disable donation form elements (if visible)
1233    $('.donatecart .price-select, .donatecart .donation-select')
1234        .prop('disabled', true)
1235        .addClass('disabled');
1236    
1237    $('.donatecart .custom-donation-input-field')
1238        .prop('disabled', true);
1239    
1240    $('.donatecart .add-donation-btn')
1241        .prop('disabled', true)
1242        .addClass('disabled');
1243    
1244    // Disable checkout buttons
1245    $('.checkout-button').prop('disabled', true).addClass('disabled');
1246    
1247    // Add global updating class to cart
1248    //$('.headerminicart').addClass('global-updating');
1249}
1250
1251function enableAllQuantityControls() {
1252    console.log('Enabling all quantity controls...');
1253    
1254    // Enable all quantity inputs
1255    $('.cart-quantity-input').prop('disabled', false).removeClass('disabled');
1256    
1257    // Enable all quantity buttons
1258    $('.qib-button').prop('disabled', false).removeClass('disabled');
1259    
1260    // Enable all remove buttons (except those in loading state)
1261    $('.remove-cart-item, .remove-donation-btn').not('.loading')
1262        .prop('disabled', false)
1263        .removeClass('disabled');
1264    
1265    // Enable coupon apply button
1266    $('.discount-code-apply-btn').prop('disabled', false).removeClass('disabled');
1267    
1268    // Enable donation form elements (if donation hasn't been added)
1269    $('.donatecart:visible .price-select, .donatecart:visible .donation-select')
1270        .prop('disabled', false)
1271        .removeClass('disabled');
1272    
1273    $('.donatecart:visible .custom-donation-input-field')
1274        .prop('disabled', false);
1275    
1276    $('.donatecart:visible .add-donation-btn')
1277        .prop('disabled', false)
1278        .removeClass('disabled');
1279    
1280    // Enable checkout buttons
1281    $('.checkout-button').prop('disabled', false).removeClass('disabled');
1282    
1283    // Remove global updating class
1284    $('.headerminicart').removeClass('global-updating');
1285}
1286
1287// Function to reset specific row after removal
1288function resetSpecificRowControls($row) {
1289    $row.find('.cart-quantity-input').prop('disabled', false).removeClass('disabled');
1290    $row.find('.qib-button').prop('disabled', false).removeClass('disabled');
1291    $row.find('.remove-cart-item').prop('disabled', false).removeClass('disabled');
1292}
1293
1294    function showCouponMessage(message, type) {
1295        // Remove existing coupon messages
1296        $('.coupon-message').remove();
1297        
1298        // Create message element
1299        var $message = $('<div class="coupon-message coupon-' + type + '">' + message + '</div>');
1300        
1301        // Add message above the discount code section
1302        $('.minicart-discount-code').before($message);
1303        
1304        // Auto remove after 5 seconds
1305        setTimeout(function() {
1306            $message.fadeOut(300, function() {
1307                $(this).remove();
1308            });
1309        }, 5000);
1310    }
1311
1312    function updateCouponDisplay(coupons) {
1313    console.log('Updating coupon display:', coupons);
1314    
1315    var $couponContainer = $('.applied-coupons-container');
1316    
1317    if (!coupons || coupons.length === 0) {
1318        // Hide applied coupons section if exists
1319        $couponContainer.html('');
1320        return;
1321    }
1322    
1323    // Build coupon HTML
1324    var couponHtml = '<div class="applied-coupons">' +
1325                     '<div class="applied-coupons-title">Applied Discounts</div>';
1326    
1327    $.each(coupons, function(index, coupon) {
1328        couponHtml += '<div class="applied-coupon-item" data-coupon-code="' + coupon.code + '">' +
1329                     '<span class="coupon-code">' + coupon.code + '</span>' +
1330                     '<span class="coupon-amount">-' + coupon.amount + '</span>' +
1331                     '<a href="#" class="remove-coupon" data-coupon-code="' + coupon.code + '">Remove</a>' +
1332                     '</div>';
1333    });
1334    
1335    couponHtml += '</div>';
1336    
1337    // Update container
1338    $couponContainer.html(couponHtml);
1339}
1340
1341    // =========================
1342    // GLOBAL EXPORTS
1343    // =========================
1344    
1345    window.miniCartAJAX = {
1346        isUpdating: function() {
1347            return config.isUpdating;
1348        },
1349        preventActions: function() {
1350            return config.isUpdating;
1351        },
1352        openCart: function(fast) {
1353            openMiniCart(fast);
1354        },
1355        closeCart: function() {
1356            closeMiniCart();
1357        }
1358    };
1359
1360    // =========================
1361    // ADDITIONAL DOM READY FUNCTIONS
1362    // =========================
1363    
1364    $(document).ready(function() {
1365        // Fix for specific product quantity display
1366        $('td.product-quantity.itemcartqty_1323857').each(function() {
1367            var style = document.createElement('style');
1368            style.innerHTML = '.woocommerce-js .minicartitems table.shop_table_responsive tr td.product-quantity.itemcartqty_1323857::before { content: attr(data-title) !important; }';
1369            document.head.appendChild(style);
1370        });
1371        
1372        // Add style tag with highest priority
1373        var style = document.createElement('style');
1374        style.id = 'remove-colon-fix';
1375        style.innerHTML = `
1376            td.product-quantity.itemcartqty_1323857::before {
1377                content: attr(data-title) !important;
1378                visibility: visible !important;
1379            }
1380            .woocommerce-js .minicartitems table.shop_table_responsive tr td.product-quantity.itemcartqty_1323857::before {
1381                content: attr(data-title) !important;
1382            }
1383        `;
1384        document.head.appendChild(style);
1385        
1386        // Function to check and toggle forms
1387        function toggleFormsBasedOnClass() {
1388            if ($('td').hasClass('itemcartqty_1323857')) {
1389                $('.donatecart').hide();
1390            } else {
1391                $('.donatecart').show();
1392            }
1393        }
1394        
1395        // Run on page load
1396        toggleFormsBasedOnClass();
1397        
1398        // Check periodically for dynamic changes
1399        setInterval(toggleFormsBasedOnClass, 1000);
1400    });
1401
1402    
1403
1404
1405})(jQuery);
1406
1407document.addEventListener("DOMContentLoaded", function () {
1408    const scrollbarWidth =
1409        window.innerWidth - document.documentElement.clientWidth;
1410
1411    document.documentElement.style.setProperty(
1412        "--scrollbar-width",
1413        scrollbarWidth + "px"
1414    );
1415});
1416
1417
1418// Nuclear option - completely override everything
1419jQuery(document).ready(function($) {
1420    console.log('=== DONATION CHECKBOX WITH EXISTING POPUP ===');
1421    
1422    // Track optimistic state
1423    var optimisticChecked = false;
1424    var lastActionTime = 0;
1425    
1426    // Function to show modal
1427    function showDonationModal() {
1428        $('.donation-modal-overlay').css('display', 'flex');
1429        // Prevent body scrolling
1430        $('body').css('overflow', 'hidden');
1431        console.log('Modal shown');
1432    }
1433    
1434    // Function to hide modal
1435    function hideDonationModal() {
1436        $('.donation-modal-overlay').hide();
1437        // Restore body scrolling
1438        $('body').css('overflow', '');
1439        console.log('Modal hidden');
1440    }
1441    
1442    // Modal event handlers
1443    $(document).on('click', '.info-icon', function(e) {
1444        e.preventDefault();
1445        e.stopPropagation();
1446        console.log('Info icon clicked - showing modal');
1447        showDonationModal();
1448        return false;
1449    });
1450    
1451    $(document).on('click', '.donation-modal-close', function(e) {
1452        e.preventDefault();
1453        e.stopPropagation();
1454        console.log('Modal close clicked');
1455        hideDonationModal();
1456        return false;
1457    });
1458    
1459    $(document).on('click', '.donation-modal-donate', function(e) {
1460        e.preventDefault();
1461        e.stopPropagation();
1462        console.log('Modal donate button clicked');
1463        hideDonationModal();
1464        
1465        // Check the donation checkbox if not already checked
1466        var $checkbox = $('#add-donation-checkbox');
1467        var hasDonation = $('td.itemcartqty_1323857').length > 0;
1468        
1469        if (!hasDonation) {
1470            // Update tracking
1471            lastActionTime = Date.now();
1472            optimisticChecked = true;
1473            
1474            // Update checkbox state
1475            $checkbox.prop('checked', true);
1476            updateCheckboxVisual(true);
1477            
1478            // Trigger donation button
1479            $('.add-donation-btn').click();
1480        }
1481        
1482        return false;
1483    });
1484    
1485    // Close modal when clicking outside
1486    $(document).on('click', '.donation-modal-overlay', function(e) {
1487        if ($(e.target).hasClass('donation-modal-overlay')) {
1488            hideDonationModal();
1489        }
1490    });
1491    
1492    // Close modal with Escape key
1493    $(document).on('keydown', function(e) {
1494        if (e.key === 'Escape' && $('.donation-modal-overlay').is(':visible')) {
1495            hideDonationModal();
1496        }
1497    });
1498    
1499    // Function to update checkbox based on .itemcartqty_1323857
1500    function syncCheckboxWithCart() {
1501        // If we just clicked (within last 2 seconds), don't sync yet
1502        if (Date.now() - lastActionTime < 2000) {
1503            console.log('Skipping sync - recent action');
1504            return;
1505        }
1506        
1507        var $checkbox = $('#add-donation-checkbox');
1508        if ($checkbox.length === 0) return;
1509        
1510        // Check if donation is in cart
1511        var hasDonation = $('td.itemcartqty_1323857').length > 0;
1512        
1513        console.log('Checking cart - hasDonation:', hasDonation);
1514        
1515        // Update checkbox based on REAL cart state
1516        $checkbox.prop('checked', hasDonation);
1517        optimisticChecked = hasDonation;
1518        
1519        // Update visual checkmark
1520        updateCheckboxVisual(hasDonation);
1521        
1522        // Update info icon visibility based on checkbox state
1523        // if (hasDonation) {
1524        //     $('.info-icon').show();
1525        // } else {
1526        //     $('.info-icon').hide();
1527        // }
1528    }
1529    
1530    // Function to update visual appearance
1531    function updateCheckboxVisual(isChecked) {
1532        var $label = $('label[for="add-donation-checkbox"], .donation-label');
1533        if ($label.length === 0) return;
1534        
1535        // Remove any existing visual
1536        $label.find('.check-visual').remove();
1537        
1538        // Add new visual
1539        $label.prepend('<span class="check-visual" style="display: inline-block; width: 18px; height: 18px; border: 2px solid ' + (isChecked ? '#4CAF50' : '#666') + '; border-radius: 3px; background: ' + (isChecked ? '#4CAF50' : 'white') + '; margin-right: 8px; vertical-align: middle; position: relative; color: white; font-size: 12px; text-align: center; line-height: 16px;">' + (isChecked ? '✓' : '') + '</span>');
1540    }
1541    
1542    // Click handler
1543    $(document).on('click', 'label[for="add-donation-checkbox"], .donation-label, .check-visual', function(e) {
1544        e.preventDefault();
1545        e.stopPropagation();
1546        
1547        console.log('=== CHECKBOX LABEL CLICKED ===');
1548        
1549        var $checkbox = $('#add-donation-checkbox');
1550        if ($checkbox.length === 0) {
1551            console.log('Checkbox not found');
1552            return false;
1553        }
1554        
1555        // Get current REAL state from cart
1556        var hasDonation = $('td.itemcartqty_1323857').length > 0;
1557        var isNowChecked = !hasDonation;
1558        
1559        console.log('Current cart state - hasDonation:', hasDonation);
1560        console.log('Setting checkbox to:', isNowChecked);
1561        
1562        // Update tracking
1563        lastActionTime = Date.now();
1564        optimisticChecked = isNowChecked;
1565        
1566        // OPTIMISTIC UPDATE: Show checked state immediately
1567        $checkbox.prop('checked', isNowChecked);
1568        updateCheckboxVisual(isNowChecked);
1569        
1570        // Update info icon visibility
1571        if (isNowChecked) {
1572            $('.info-icon').show();
1573        } else {
1574            $('.info-icon').hide();
1575        }
1576        
1577        // Actually perform the action
1578        if (isNowChecked) {
1579            console.log('Adding donation to cart');
1580            $('.add-donation-btn').click();
1581        } else {
1582            console.log('Removing donation from cart');
1583            $('td.itemcartqty_1323857').closest('tr').find('.remove').click();
1584        }
1585        
1586        // Check REAL cart state after longer delay (after AJAX should complete)
1587        setTimeout(function() {
1588            console.log('Checking REAL cart state after 3 seconds...');
1589            var newHasDonation = $('td.itemcartqty_1323857').length > 0;
1590            console.log('Real cart state:', newHasDonation);
1591            
1592            // Only update if optimistic state doesn't match real state
1593            if (optimisticChecked !== newHasDonation) {
1594                console.log('Correcting checkbox to match real cart state');
1595                $checkbox.prop('checked', newHasDonation);
1596                updateCheckboxVisual(newHasDonation);
1597                optimisticChecked = newHasDonation;
1598                
1599                // Update info icon visibility
1600                if (newHasDonation) {
1601                    $('.info-icon').show();
1602                } else {
1603                    $('.info-icon').hide();
1604                }
1605            }
1606        }, 3000);
1607        
1608        return false;
1609    });
1610    
1611    // Also handle checkbox clicks directly
1612    $(document).on('click', '#add-donation-checkbox', function(e) {
1613        e.preventDefault();
1614        e.stopPropagation();
1615        console.log('Checkbox directly clicked');
1616        $('label[for="add-donation-checkbox"], .donation-label').click();
1617        return false;
1618    });
1619    
1620    // Run on page load
1621    console.log('=== INITIALIZING ===');
1622    syncCheckboxWithCart();
1623    
1624    // Run after delay (cart might load async)
1625    setTimeout(syncCheckboxWithCart, 1000);
1626    
1627    // Run after AJAX - but wait a bit
1628    $(document).ajaxComplete(function(event, xhr, settings) {
1629        console.log('AJAX complete - will check cart in 1 second');
1630        setTimeout(syncCheckboxWithCart, 500);
1631    });
1632    
1633    // Debug: Check if modal exists
1634    console.log('Modal overlay exists:', $('.donation-modal-overlay').length);
1635    console.log('Info icon exists:', $('.info-icon').length);
1636    
1637    // Make show/hide functions globally available for debugging
1638    window.showDonationPopup = showDonationModal;
1639    window.hideDonationPopup = hideDonationModal;
1640    
1641    console.log('Donation checkbox with popup loaded successfully');
1642});
1643
1644 // Add this BEFORE any other scripts in the <head> or right after opening <body>
1645// (function() {
1646//     // Create and add style to hide elements immediately
1647//     var style = document.createElement('style');
1648//     style.id = 'cart-preloader-style';
1649//     style.textContent = `
1650//         .elementor-element-c02e1ca.toggle-icon--custom.elementor-menu-cart--cart-type-mini-cart,
1651//         .linkitem.cartBubble {
1652//             visibility: hidden !important;
1653//             opacity: 0 !important;
1654//             transition: none !important;
1655//         }
1656//     `;
1657//     document.head.appendChild(style);
1658// })();
1659
1660
1661//window.SlideCart.setState(state);
1662
1663(function () {
1664  if (window.SlideCart) return;
1665
1666  const STORAGE_KEY = 'site_cart_mode';
1667
1668  function normalize(val) {
1669    return Number(val) === 1 ? 1 : 0;
1670  }
1671
1672  function getStoredState() {
1673    const saved = localStorage.getItem(STORAGE_KEY);
1674    // if never set → default = 1 (popup cart)
1675    if (saved === null) return 1;
1676    return normalize(saved);
1677  }
1678
1679  function broadcast(state) {
1680    window.dispatchEvent(
1681      new CustomEvent('slidecart:stateChange', { detail: { state } })
1682    );
1683  }
1684
1685  // Initialize with current state BEFORE setting up API
1686  let currentState = getStoredState();
1687
1688  window.SlideCart = {
1689    setState: function (state) {
1690      const s = normalize(state);
1691      localStorage.setItem(STORAGE_KEY, s);
1692      currentState = s; // Update in-memory state
1693      broadcast(s);
1694      console.log('[SlideCart] setState →', s, `(${s === 1 ? 'Popup Cart' : 'Slide Cart'})`);
1695      return s;
1696    },
1697
1698    getState: function () {
1699      return currentState; // Return in-memory state, not localStorage
1700    },
1701
1702    toggle: function () {
1703      return this.setState(currentState === 1 ? 0 : 1);
1704    },
1705
1706    // Add a method to force refresh from localStorage if needed
1707    refresh: function() {
1708      const stored = getStoredState();
1709      if (stored !== currentState) {
1710        console.log('[SlideCart] Refreshing state from localStorage:', stored);
1711        currentState = stored;
1712        broadcast(stored);
1713      }
1714      return currentState;
1715    }
1716  };
1717
1718  // Broadcast initial state on DOMContentLoaded
1719  document.addEventListener('DOMContentLoaded', function () {
1720    // Don't broadcast if we already have the state set
1721    // Just ensure display updates
1722    setTimeout(() => {
1723      broadcast(currentState);
1724    }, 100);
1725  });
1726
1727})();
1728
1729// Cart display controller
1730document.addEventListener('DOMContentLoaded', function () {
1731  console.log('[CartDisplay] Initializing...');
1732
1733  if (!window.SlideCart || typeof window.SlideCart.getState !== 'function') {
1734    console.warn('SlideCart API not found');
1735    return;
1736  }
1737
1738  var elementorCart = document.querySelector(
1739    '.elementor-element.elementor-element-c02e1ca.toggle-icon--custom.elementor-menu-cart--cart-type-mini-cart'
1740  );
1741  var cartBubble = document.querySelector('.linkitem.cartBubble');
1742
1743  if (!elementorCart) {
1744    console.warn('[CartDisplay] Elementor cart not found');
1745  }
1746  if (!cartBubble) {
1747    console.warn('[CartDisplay] Cart bubble not found');
1748  }
1749  if (!elementorCart || !cartBubble) {
1750    // Try again after a short delay in case elements load later
1751    setTimeout(function() {
1752      //location.reload();
1753    }, 500);
1754    return;
1755  }
1756
1757  // Remove the preloader style
1758  var preloaderStyle = document.getElementById('cart-preloader-style');
1759  if (preloaderStyle) {
1760    preloaderStyle.remove();
1761    console.log('[CartDisplay] Removed preloader');
1762  }
1763
1764  function applyCartMode(state) {
1765    console.log('[CartDisplay] Applying cart mode:', state, `(${state === 1 ? 'Popup Cart' : 'Slide Cart'})`);
1766
1767    // State mapping:
1768    // 0 = slide cart (cartBubble)
1769    // 1 = popup cart (elementorCart)
1770
1771    if (state === 0) {
1772      // Show slide cart (cartBubble), hide popup cart (elementorCart)
1773      elementorCart.style.cssText = `
1774        display: none !important;
1775        visibility: hidden !important;
1776        opacity: 0 !important;
1777        pointer-events: none !important;
1778        position: absolute !important;
1779        width: 0 !important;
1780        height: 0 !important;
1781        overflow: hidden !important;
1782      `;
1783      
1784      cartBubble.style.cssText = `
1785        display: inline-flex !important;
1786        visibility: visible !important;
1787        opacity: 1 !important;
1788        pointer-events: auto !important;
1789        position: relative !important;
1790      `;
1791
1792    } else {
1793      // Show popup cart (elementorCart), hide slide cart (cartBubble)
1794      elementorCart.style.cssText = `
1795        display: block !important;
1796        visibility: visible !important;
1797        opacity: 1 !important;
1798        pointer-events: auto !important;
1799        position: relative !important;
1800      `;
1801      
1802      cartBubble.style.cssText = `
1803        display: none !important;
1804        visibility: hidden !important;
1805        opacity: 0 !important;
1806        pointer-events: none !important;
1807        position: absolute !important;
1808        width: 0 !important;
1809        height: 0 !important;
1810        overflow: hidden !important;
1811      `;
1812    }
1813
1814    // Add data attributes for debugging
1815    elementorCart.setAttribute('data-cart-state', state);
1816    cartBubble.setAttribute('data-cart-state', state);
1817    document.body.setAttribute('data-cart-mode', state === 1 ? 'popup' : 'slide');
1818  }
1819
1820  // Apply on load using global state
1821  var initialState = window.SlideCart.getState();
1822  console.log('[CartDisplay] Initial state:', initialState);
1823  applyCartMode(initialState);
1824
1825  // Auto-update when state changes
1826  window.addEventListener('slidecart:stateChange', function (e) {
1827    console.log('[CartDisplay] State change detected:', e.detail.state);
1828    applyCartMode(e.detail.state);
1829  });
1830
1831  // Add a small debug indicator (remove in production)
1832//   setTimeout(function() {
1833//     var debugIndicator = document.createElement('div');
1834//     debugIndicator.id = 'cart-debug-indicator';
1835//     debugIndicator.style.cssText = `
1836//       position: fixed;
1837//       bottom: 10px;
1838//       right: 10px;
1839//       background: #333;
1840//       color: white;
1841//       padding: 6px 10px;
1842//       font-size: 11px;
1843//       font-family: monospace;
1844//       border-radius: 3px;
1845//       z-index: 99999;
1846//       opacity: 0.8;
1847//       cursor: help;
1848//       border: 1px solid #666;
1849//     `;
1850//     debugIndicator.title = 'Click to toggle cart state\nRight-click to reset to default';
1851    
1852//     function updateDebugIndicator() {
1853//       var state = window.SlideCart.getState();
1854//       debugIndicator.textContent = `Cart: ${state === 1 ? 'Popup' : 'Slide'} [${state}]`;
1855//       debugIndicator.style.background = state === 1 ? '#4CAF50' : '#2196F3';
1856//     }
1857    
1858//     debugIndicator.onclick = function(e) {
1859//       e.preventDefault();
1860//       window.SlideCart.toggle();
1861//     };
1862    
1863//     debugIndicator.oncontextmenu = function(e) {
1864//       e.preventDefault();
1865//       window.SlideCart.setState(1); // Reset to default (popup)
1866//     };
1867    
1868//     document.body.appendChild(debugIndicator);
1869//     updateDebugIndicator();
1870    
1871//     // Update indicator on state changes
1872//     window.addEventListener('slidecart:stateChange', updateDebugIndicator);
1873    
1874//     console.log('[CartDisplay] Debug indicator added');
1875//   }, 1000);
1876});
1877
1878
1879// Set to popup cart (Elementor)
1880 //window.SlideCart.setState(1);
1881
1882// Set to slide cart (cartBubble)
1883 //window.SlideCart.setState(0);
1884
1885// Toggle between states
1886 //window.SlideCart.toggle();
1887
1888// Get current state
1889 //window.SlideCart.getState(); // Returns 0 or 1
1890
1891// Force refresh from localStorage (if needed)
1892 //window.SlideCart.refresh();
1893
1894/* -------------------------------------------------------------------------
1895 * Move the slide cart out of the header's stacking context.
1896 *
1897 * The panel is rendered inside the header shortcode, so its z-index competes
1898 * only with its header siblings. Any header element with a z-index of its own
1899 * (the mobile hamburger, a sticky bar) therefore paints over the overlay no
1900 * matter how high the panel's z-index goes. Reparenting to <body> puts it in
1901 * the root stacking context, where its z-index actually means something.
1902 *
1903 * Safe because every handler in this file is delegated from document and
1904 * matches on class, and the panel is position:fixed so layout is unaffected.
1905 * #myCart stays in the header.
1906 * ------------------------------------------------------------------------- */
1907jQuery(function ($) {
1908    function gscReparentCart() {
1909        var $all = $('.headerminicart');
1910
1911        if (!$all.length) {
1912            return;
1913        }
1914
1915        var $onBody = $all.filter(function () {
1916            return $(this).parent().is('body');
1917        }).first();
1918
1919        if (!$onBody.length) {
1920            $onBody = $all.first().appendTo('body');
1921        }
1922
1923        // Drop any stray copy a re-rendered header may have left behind.
1924        $all.not($onBody).remove();
1925    }
1926
1927    gscReparentCart();
1928
1929    $(document.body).on('wc_fragments_refreshed wc_fragments_loaded', gscReparentCart);
1930});

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.