PageSourceSearch

https://toborlife.ai/wp-content/themes/onzo-progression-child-1.2/js/custom.js?ver=1.2.1791182272

js toborlife.ai collected 2026-10-05 13:07:23 UTC 39,289 bytes, 1,092 lines download raw bytes

1
2jQuery(document).ready(function($) {
3  // Store the original content for later restoration.
4  var originalContent = $(".whats-in-the-box-wrapper").html();
5  // Variable to hold cached content from the AJAX request.
6  var cachedContent = null;
7  
8  
9
10
11
12
13  // Bind click events on the specified classes.
14  $(".homedog-air, .homedog-edu, .homedog-pro").on('click', function(e) {
15    e.preventDefault();
16
17    // If the clicked element is the "pro" button, restore the original content.
18    if ($(this).hasClass('homedog-pro') || $(this).hasClass('homedog-air')) {
19      $(".whats-in-the-box-wrapper").html(originalContent);
20      return;
21    }
22
23    // For .home-dog-air and .home-dog-edu: if content is cached, use it.
24    if (cachedContent !== null) {
25      $(".whats-in-the-box-wrapper").html(cachedContent);
26      return;
27    }
28
29    // Otherwise, perform the AJAX request.
30    $.ajax({
31      url: "https://toborlife.ai/product/go2-edu/",
32      method: "GET",
33      dataType: "html",
34      success: function(response) {
35        // Create a temporary container to hold the fetched HTML.
36        var $tempContainer = $("<div>").html(response);
37        // Extract the inner HTML of the .whats-in-the-box-wrapper element from the fetched content.
38        var newContent = $tempContainer.find(".whats-in-the-box-wrapper").html();
39        
40        if (newContent) {
41          // Cache the fetched content.
42          cachedContent = newContent;
43          // Replace the current page's .whats-in-the-box-wrapper content.
44          $(".whats-in-the-box-wrapper").html(newContent);
45        } else {
46          console.error("The specified element '.whats-in-the-box-wrapper' was not found in the response.");
47        }
48      },
49      error: function(xhr, status, error) {
50        console.error("Error fetching the content: " + error);
51      }
52    });
53  });
54});
55
56
57jQuery(document).ready(function ($) {
58    $('.tobor-nav-item').on('click', function (e) {
59        e.preventDefault();
60        const targetId = $(this).attr('href'); 
61        const offsetTop = $(targetId).offset().top;
62
63        $('html, body').animate({ scrollTop: offsetTop - 60 }, 800);
64    });
65});
66
67jQuery(document).ready(function ($) {
68    var $thumbContainer = $('.woocommerce-product-thumbnails');
69    var $thumbs = $('.woocommerce-product-thumbnail');
70    var $upBtn = $('.thumb-nav-up');
71    var $downBtn = $('.thumb-nav-down');
72    var $mainImg = $('.woocommerce-product-gallery__image a img');
73    var $mainLink = $('.woocommerce-product-gallery__image a');
74    var isTransitioning = false;
75
76    function switchMainImage(newSrc, direction, fullSrc) {
77        if (isTransitioning) return;
78        var currentSrc = $mainImg.attr('src');
79        if (currentSrc === newSrc) return;
80        isTransitioning = true;
81        var dir = direction || 'right';
82        var outClass = dir === 'right' ? 'slide-out-left' : 'slide-out-right';
83        var inClass = dir === 'right' ? 'slide-in-left' : 'slide-in-right';
84
85        var preload = new Image();
86        preload.src = newSrc;
87
88        function doSwap() {
89            $mainImg.attr('src', newSrc).attr('srcset', newSrc);
90            $mainLink.attr('href', fullSrc || newSrc);
91            $mainImg.removeClass(outClass).addClass(inClass);
92            requestAnimationFrame(function () {
93                requestAnimationFrame(function () {
94                    $mainImg.removeClass(inClass);
95                    setTimeout(function () { isTransitioning = false; }, 400);
96                });
97            });
98        }
99
100        $mainImg.addClass(outClass);
101        if (preload.complete) {
102            setTimeout(doSwap, 350);
103        } else {
104            preload.onload = function () { setTimeout(doSwap, 50); };
105            setTimeout(function () { if (!preload.complete) doSwap(); }, 2000);
106        }
107    }
108
109    function scrollToThumb($thumb) {
110        var container = $thumbContainer[0];
111        if (!container) return;
112        var thumbEl = $thumb[0];
113        if (window.innerWidth < 768) {
114            var thumbLeft = thumbEl.offsetLeft - container.offsetLeft;
115            container.scrollTo({ left: thumbLeft - (container.clientWidth / 2) + (thumbEl.offsetWidth / 2), behavior: 'smooth' });
116        } else {
117            var thumbTop = thumbEl.offsetTop - container.offsetTop;
118            container.scrollTo({ top: thumbTop - (container.clientHeight / 2) + (thumbEl.offsetHeight / 2), behavior: 'smooth' });
119        }
120    }
121
122    function selectThumb($thumb, direction) {
123        var oldIdx = getActiveIndex();
124        var newIdx = $thumbs.index($thumb);
125        var dir = direction || (newIdx > oldIdx ? 'right' : 'left');
126        $thumbs.removeClass('active');
127        $thumb.addClass('active');
128        var largeSrc = $thumb.data('large') || $thumb.find('a').attr('href');
129        var fullSrc = $thumb.data('full') || largeSrc;
130        switchMainImage(largeSrc, dir, fullSrc);
131        scrollToThumb($thumb);
132        updateArrowState();
133    }
134
135    function getActiveIndex() {
136        return $thumbs.index($thumbs.filter('.active'));
137    }
138
139    function updateArrowState() {
140        if (!$thumbs.length) return;
141        var idx = getActiveIndex();
142        $upBtn.prop('disabled', idx <= 0);
143        $downBtn.prop('disabled', idx >= $thumbs.length - 1);
144    }
145
146    $('.woocommerce-product-thumbnails a').on('click', function (e) {
147        e.preventDefault();
148        selectThumb($(this).parent());
149    });
150
151    $upBtn.on('click', function () {
152        var idx = getActiveIndex();
153        if (idx > 0) selectThumb($thumbs.eq(idx - 1), 'left');
154    });
155
156    $downBtn.on('click', function () {
157        var idx = getActiveIndex();
158        if (idx < $thumbs.length - 1) selectThumb($thumbs.eq(idx + 1), 'right');
159    });
160
161    $('.gallery-nav-prev').on('click', function () {
162        var idx = getActiveIndex();
163        var prev = idx >
163 0 ? idx - 1 : $thumbs.length - 1;
164        selectThumb($thumbs.eq(prev), 'left');
165    });
166
167    $('.gallery-nav-next').on('click', function () {
168        var idx = getActiveIndex();
169        var next = idx < $thumbs.length - 1 ? idx + 1 : 0;
170        selectThumb($thumbs.eq(next), 'right');
171    });
172
173    updateArrowState();
174
175    // Lightbox
176    var $lightbox = $('#tobor-lightbox');
177    var $lbImg = $lightbox.find('.tobor-lightbox-img');
178    var $lbCounter = $lightbox.find('.tobor-lightbox-counter');
179    var lbImages = [];
180    var lbIndex = 0;
181
182    $thumbs.each(function () {
183        lbImages.push($(this).data('full') || $(this).find('a').attr('href'));
184    });
185
186    if (!lbImages.length) {
187        var singleSrc = $mainLink.attr('href');
188        if (singleSrc) lbImages.push(singleSrc);
189    }
190
191    function showLightboxImage(idx, dir) {
192        var outClass = dir === 'prev' ? 'slide-out-right' : 'slide-out-left';
193        var inClass = dir === 'prev' ? 'slide-in-right' : 'slide-in-left';
194        $lbImg.addClass(outClass);
195        setTimeout(function () {
196            lbIndex = idx;
197            $lbImg.attr('src', lbImages[lbIndex]).removeClass(outClass).addClass(inClass);
198            requestAnimationFrame(function () {
199                requestAnimationFrame(function () { $lbImg.removeClass(inClass); });
200            });
201        }, 300);
202        $lbCounter.text((idx + 1) + ' / ' + lbImages.length);
203    }
204
205    function openLightbox(idx) {
206        lbIndex = idx;
207        $lbImg.attr('src', lbImages[lbIndex]).css('opacity', 1);
208        $lbCounter.text((lbIndex + 1) + ' / ' + lbImages.length);
209        $lightbox.addClass('is-open').attr('aria-hidden', 'false');
210        $('body').css('overflow', 'hidden');
211    }
212
213    function closeLightbox() {
214        $lightbox.removeClass('is-open').attr('aria-hidden', 'true');
215        $('body').css('overflow', '');
216    }
217
218    $mainLink.on('click', function (e) {
219        e.preventDefault();
220        var currentSrc = $(this).attr('href');
221        var idx = lbImages.indexOf(currentSrc);
222        openLightbox(idx >= 0 ? idx : 0);
223    });
224
225    $lightbox.find('.tobor-lightbox-close, .tobor-lightbox-overlay').on('click', closeLightbox);
226    $lightbox.find('.tobor-lightbox-prev').on('click', function () {
227        showLightboxImage((lbIndex - 1 + lbImages.length) % lbImages.length, 'prev');
228    });
229    $lightbox.find('.tobor-lightbox-next').on('click', function () {
230        showLightboxImage((lbIndex + 1) % lbImages.length, 'next');
231    });
232
233    $(document).on('keydown', function (e) {
234        if (!$lightbox.hasClass('is-open')) return;
235        if (e.key === 'Escape') closeLightbox();
236        if (e.key === 'ArrowLeft') showLightboxImage((lbIndex - 1 + lbImages.length) % lbImages.length, 'prev');
237        if (e.key === 'ArrowRight') showLightboxImage((lbIndex + 1) % lbImages.length, 'next');
238    });
239});
240
241
242jQuery(document).ready(function ($) {
243    $(".toborlife-custom-number-input").each(function () {
244        const $toborlifeWrapper = $(this);
245        const $toborlifeInput = $toborlifeWrapper.find(".toborlife-qty");
246        const $toborlifeBtnPlus = $toborlifeWrapper.find(".toborlife-qty-up");
247        const $toborlifeBtnMinus = $toborlifeWrapper.find(".toborlife-qty-down");
248
249        $toborlifeBtnPlus.on("click", function () {
250            const toborlifeMax = parseInt($toborlifeInput.attr("max")) || Infinity;
251            const toborlifeCurrentValue = parseInt($toborlifeInput.val()) || 0;
252            if (toborlifeCurrentValue < toborlifeMax) {
253                $toborlifeInput.val(toborlifeCurrentValue + 1);
254            }
255        });
256
257        $toborlifeBtnMinus.on("click", function () {
258            const toborlifeMin = parseInt($toborlifeInput.attr("min")) || 0;
259            const toborlifeCurrentValue = parseInt($toborlifeInput.val()) || 0;
260            if (toborlifeCurrentValue > toborlifeMin) {
261                $toborlifeInput.val(toborlifeCurrentValue - 1);
262            }
263        });
264    });
265});
266
267jQuery(document).ready(function ($) {
268    $('.toborlife-news-search-icon').on('click', function () {
269        $(this).closest('form').submit(); // Submit the form when the icon is clicked
270    });
271});
272
273jQuery(document).ready(function ($) {
274    var $button = $('#toborlife-press-load-more');
275    var $grid = $('#toborlife-press-grid');
276
277    if (!$button.length || !$grid.length || typeof toborPressRoomAjax === 'undefined') {
278        return;
279    }
280
281    $button.on('click', function () {
282        var currentPage = parseInt($button.data('page'), 10) || 1;
283        var maxPages = parseInt($button.data('max-pages'), 10) || 1;
284        var nextPage = currentPage + 1;
285
286        if (nextPage > maxPages || $button.hasClass('is-loading')) {
287            return;
288        }
289
290        $button.addClass('is-loading').text('Loading...');
291
292        $.ajax({
293            url: toborPressRoomAjax.ajaxUrl,
294            type: 'POST',
295            dataType: 'json',
296            data: {
297                action: 'toborlife_load_more_press_room',
298                nonce: toborPressRoomAjax.nonce,
299                page: nextPage,
300                posts_per_page: parseInt($button.data('posts-per-page'), 10) || 6,
301                year: parseInt($button.data('year'), 10) || '',
302                month: parseInt($button.data('month'), 10) || ''
303            },
304            success: function (response) {
305                if (response.success && response.data.html) {
306                    $grid.append(response.data.html);
307                    $button.data('page', nextPage);
308                    maxPages = parseInt(response.data.max_pages, 10) || maxPages;
309                    $button.data('max-pages', maxPages);
310
311                    if (nextPage >= maxPages) {
312                        $button.closest('.toborlife-press-load-more-wrap').remove();
313                    } else {
314                        $button.removeClass('is-loading').text('Load More');
315                    }
316                } else {
317                    $button.closest('.toborlife-press-load-more-wrap').remove();
318                }
319            },
320            error: function () {
321                $button.removeClass('is-loading').text('Load More');
322            }
323        });
324    });
325});
326
327jQuery(document).ready(function ($) {
328    function maskElavonCscField() {
329        var $cscField = $('#wc-elavon-converge-credit-card-csc');
330
331        if (!$cscField.length) {
332            return;
333        }
334
335        $cscField.attr({
336            placeholder: '***',
337            autocomplete: 'off',
338            autocorrect: 'off',
339            autocapitalize: 'off',
340            spellcheck: 'false'
341        });
342
343        $cscField.css({
344            '-webkit-text-security': 'disc',
345            'text-security': 'disc'
346        });
347    }
348
349    maskElavonCscField();
350    $(window).on('load', maskElavonCscField);
351
352    var cscMaskAttempts = 0;
353    var cscMaskTimer = setInterval(function () {
354        maskElavonCscField();
355        cscMaskAttempts += 1;
356
357        if (cscMaskAttempts >= 5) {
358            clearInterval(cscMaskTimer);
359        }
360    }, 1000);
361});
362
363
364document.addEventListener('DOMContentLoaded', function() {
365    const variationBtns = document.querySelectorAll('.variation-selector-btn');
366
367    const defaultBtn = variationBtns[0];
368    if (defaultBtn) {
369        defaultBtn.classList.add('active');
370        const defaultVariationData = JSON.parse(defaultBtn.dataset.variation);
371        updateProductDisplay(defaultVariationData);
372    }
373    
374    variationBtns.forEach(btn => {
375        btn.addEventListener('click', function() {
376            variationBtns.forEach(b => b.classList.remove('active'));
377            this.classList.add('active');
378            
379            const variationData = JSON.parse(this.dataset.variation);
380            updateProductDisplay(variationData);
381        });
382    });
383    
384    function updateProductDisplay(variationData) {
385        document.querySelector('.tobor-product-title').textContent = variationData.title_html;
386        
387        const description = variationData.description.replace(/<\/?p>/g, '');
388        document.querySelector('#variation-description').innerHTML = description;
389        var priceNode = document.querySelector('.variation_price');
390        var depositPrefix = priceNode.dataset.pricePrefix || '';
391        priceNode.innerHTML = (depositPrefix ? '<span class="deposit-label">' + depositPrefix + '</span> ' : '') + variationData.price_html;
392        document.querySelector('input[name="variation_id"]').value = variationData.variation_id;
393        
394        const affirmElement = document.querySelector('.affirm-as-low-as');
395        if (affirmElement) {
396            affirmElement.setAttribute('data-amount', variationData.display_price + '00');
397        }
398        document.querySelector('.tobor-variation-name').textContent = variationData.model;
399
400        const productImage = document.querySelector('.woocommerce-product-gallery__image img');
401        const productLink = document.querySelector('.woocommerce-product-gallery__image a');
402        if (productImage && variationData.image_url) {
403            productImage.classList.add('slide-out-left');
404            setTimeout(function () {
405                productImage.src = variationData.image_url;
406                productImage.srcset = variationData.image_url;
407                if (productLink) productLink.href = variationData.image_url;
408                productImage.classList.remove('slide-out-left');
409                productImage.classList.add('slide-in-left');
410                requestAnimationFrame(function () {
411                    requestAnimationFrame(function () {
412                        productImage.classList.remove('slide-in-left');
413                    });
414                });
415            }, 350);
416        }
417    }
418});
419
420
421//code for accessories panel view all button
422if(document.getElementById('view-all-products')){
423 document.getElementById('view-all-products').addEventListener('click', function() {
424    var items = document.querySelectorAll('.product-item');
425    var isShowingAll = this.textContent === 'Show Less'; 
426
427    items.forEach(function(item, index) {
428        item.style.display = isShowingAll ? (index < 6 ? 'block' : 'none') : 'block';
429    });
430
431    this.textContent = isShowingAll ? 'View All' : 'Show Less';
432
433    if (!isShowingAll) {
434        document.getElementById('product-list').scrollIntoView({ behavior: 'smooth' });
435    }
436});
437
438}
439
440
441//code for spec table row plus and minus button
442// jQuery(document).ready(function($) {
443//     $(document).on('click', '.viewAllButton', function() {
444//         var $button = $(this); 
445//         var $rows = $button.closest('.tab-pane').find('table.table tbody tr'); 
446//         var isShowingAll = $button.text() === '-'; 
447        
448//         $rows.each(function(index) {
449//             $(this).css('display', isShowingAll ? (index < 8 ? 'table-row' : 'none') : 'table-row'); 
450//         });
451
452//         $button.text(isShowingAll ? '+' : '-'); 
453
454//         if (!isShowingAll) {
455//             setTimeout(function() {
456//                 var $eighthRow = $rows.eq(10); 
457//                 if ($eighthRow.length) {
458//                     $('html, body').animate({
459//                         scrollTop: $eighthRow.offset().top - 100
460//                     }, 300);
461//                 }
462//             }, 100);
463//         }
464//     });
465// });
466
467
468//code for spec table row plus and minus button
469jQuery(document).ready(function($) {
470    $(document).on('click', '.viewAllButton', function() {
471        var $button = $(this);
472        // Find rows in either tab-pane or direct table
473        var $rows = $button.closest('.tab-pane, .container').find('table.table tbody tr:not(:last-child)'); 
474        var isShowingAll = $button.text() === '-';
475        
476        $rows.each(function(index) {
477            $(this).css('display', isShowingAll ? (index < 8 ? 'table-row' : 'none') : 'table-row');
478        });
479
480        $button.text(isShowingAll ? '+' : '-');
481
482        // Scroll down when expanding (clicking '+')
483        if (!isShowingAll) {
484            // Wait for DOM to update before scrolling
485            setTimeout(function() {
486                var $eighthRow = $rows.eq(10); // Index 7 is the 8th row (0-based index)
487                if ($eighthRow.length) {
488                    $('html, body').animate({
489                        scrollTop: $eighthRow.offset().top - 100
490                    }, 300);
491                }
492            }, 100);
493        }
494    });
495});
496
497
498// jQuery(document).ready(function($) {
499//     $('section#home-video-bg').css('cursor', 'pointer').click(function() {
500//         window.location.href = '/product/tobor-home-dog/';
501//     });
502// });
503// jQuery(document).ready(function($) {
504//     $('section#home-video-bg').css('cursor', 'pointer').click(function(e) {
505//         // Check if the click target is NOT the prev/next buttons or their children
506//         if (!$(e.target).closest('.boosted-elements-slider-nav-prev, .boosted-elements-slider-nav-next').length) {
507//             window.location.href = '/product/tobor-home-dog/';
508//         }
509//     });
510    
511//     // Optional: Remove pointer cursor from nav buttons
512//     $('.boosted-elements-slider-nav-prev, .boosted-elements-slider-nav-next').css('cursor', 'default');
513// });
514
515jQuery(document).ready(function($) {
516    $('#banner-bg-scroll-g1').css('cursor', 'pointer').click(function() {
517        $('html, body').animate({
518            scrollTop: $('#flexibility').offset().top 
519        }, 200); 
520    });
521});
522
523jQuery(document).ready(function($) {
524    $('#banner-bg-scroll').css('cursor', 'pointer').click(function() {
525        $('html, body').animate({
526            scrollTop: $('#overcoming').offset().top 
527        }, 200); 
528    });
529});
530
531
532
533jQuery(function ($) {
534  $(document).on('click', '.tobor-accessories-add-to-cart', function (e) {
535    e.preventDefault();
536    var $btn     = $(this);
537    var data     = {
538      action:     'woocommerce_ajax_add_to_cart',
539      product_id: $btn.data('product_id'),
540      quantity:   1
541    };
542
543    $('#tobor-accessories-loader').show();
544    $.ajax({
545      type:  'POST',
546      url:   wc_add_to_cart_params.ajax_url,
547      data:  data,
548      beforeSend()  { $btn.addClass('loading'); },
549      complete()    { $btn.removeClass('loading'); },
550      success(resp) {
551        if (resp.fragments) {
552          $.each(resp.fragments, function (k, v) { $(k).replaceWith(v); });
553        }
554        $('.woocommerce-notices-wrapper')
555          .html('<div class="woocommerce-message" role="alert">Product has been added to your cart.</div>');
556
557        //   ⬇️ NOW passes the button too
558        $(document.body).trigger('added_to_cart', [resp.fragments, resp.cart_hash, $btn]);
559      },
560      error() {
561        $('.woocommerce-notices-wrapper')
562          .html('<div class="woocommerce-error" role="alert">Error adding product to cart. Please try again.</div>');
563      }
564    });
565  });
566});
567
568jQuery(document).ready(function($) {
569    var $contactBtn = $('button#toborOpenContactForm');
570    var formSection = $('.product-contact-form');
571    var $productBar = $('.tobor-product-bar');
572    var scrollThreshold = 500;
573    var scrollTicking = false;
574
575    if ($(window).scrollTop() <= scrollThreshold) {
576        $productBar.addClass('hide-bar');
577        $('#chat-widget-container').removeClass('move_cwc');
578    }
579
580    $(window).on('scroll', function() {
581        if (scrollTicking) return;
582        scrollTicking = true;
583        requestAnimationFrame(function() {
584            var scrollTop = $(window).scrollTop();
585            var windowHeight = $(window).height();
586
587            if (formSection.length) {
588                var formTop = formSection.offset().top;
589                var formBottom = formTop + formSection.outerHeight();
590                var isFormVisible = (formBottom >= scrollTop && formTop <= scrollTop + windowHeight);
591                if (isFormVisible) {
592                    $contactBtn.fadeOut(300);
593                } else {
594                    $contactBtn.fadeIn(300);
595                }
596            } else {
597                $contactBtn.fadeIn(300);
598            }
599
600            if (scrollTop > scrollThreshold) {
601                $productBar.removeClass('hide-bar');
602                $('#chat-widget-container').addClass('move_cwc');
603            } else {
604                $productBar.addClass('hide-bar');
605                $('#chat-widget-container').removeClass('move_cwc');
606            }
607
608            scrollTicking = false;
609        });
610    });
611});
612        
613//product page spec table slider
614document.addEventListener('DOMContentLoaded', function() {
615  document.querySelectorAll('.product-specification').forEach(function(specSection) {
616      var specSwiper = specSection.querySelector('.spec-swiper');
617
618      if (!specSwiper) {
619          return;
620      }
621
622      new Swiper(specSwiper, {
623          slidesPerView: 1,
624          spaceBetween: 30,
625          navigation: {
626              nextEl: specSection.querySelector('.swiper-button-next'),
627              prevEl: specSection.querySelector('.swiper-button-prev'),
628          },
629          pagination: {
630              el: specSection.querySelector('.swiper-pagination'),
631              clickable: true,
632          },
633      });
634  });
635
636  document.querySelectorAll('.tobor-harness-description-toggle').forEach(function(toggle) {
637      toggle.addEventListener('click', function() {
638          var card = toggle.closest('.tobor-harness-description-card');
639          var isOpen = card.classList.toggle('is-open');
640
641          toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
642      });
643  });
644});
645
646
647// Add this to your theme's functions.php footer scripts or custom JS
648jQuery(function($) {
649    
650    function makeFirstActive() {
651        var firstLink = $('.category-nav .elementor-icon-list-item:first a');
652        if (firstLink.length && !firstLink.hasClass('active')) {
653            firstLink.addClass('active');
654        }
655    }
656    
657    // Try immediately
658    makeFirstActive();
659    
660    // Try after DOM is fully ready
661    setTimeout(makeFirstActive, 100);
662    setTimeout(makeFirstActive, 500);
663    setTimeout(makeFirstActive, 1000);
664    
665    // Click handler
666    $(document).on('click', '.category-nav .elementor-icon-list-item a', function(e) {
667        
668        $('.category-nav .elementor-icon-list-item a').removeClass('active');
669        $(this).addClass('active');
670    });
671});
672
673
674// PRODUCT CONFIGURATOR CODE START
675document.addEventListener('DOMContentLoaded', function() {
676    var configurators = document.querySelectorAll('.tobor-configurator');
677
678    configurators.forEach(function(configurator) {
679        var form = configurator.closest('form.cart');
680        if (!form) {
681            return;
682        }
683
684        var groups = Array.prototype.slice.call(configurator.querySelectorAll('.tobor-config-group'));
685        var addToCartButton = form.querySelector('.single_add_to_cart_button');
686
687        if (!groups.length || !addToCartButton) {
688            return;
689        }
690
691        configurator.classList.add('is-js-ready');
692        var priceNode = form.querySelector('.variation_price');
693        var basePrice = Number(configurator.dataset.basePrice || 0);
694        var pricePrefix = priceNode ? (priceNode.dataset.pricePrefix ? priceNode.dataset.pricePrefix + ' ' : '') : '';
695        var specsModal = configurator.querySelector('.tobor-config-specs-modal');
696        var specsModalBody = specsModal ? specsModal.querySelector('[data-tobor-config-modal-body]') : null;
697        var lastSpecsTrigger = null;
698
699        function formatCurrency(amount) {
700            return new Intl.NumberFormat('en-US', {
701                style: 'currency',
702                currency: 'USD',
703                minimumFractionDigits: 0,
704                maximumFractionDigits: 0
705            }).format(amount);
706        }
707
708        function updateDisplayedPrice() {
709            if (!priceNode) {
710                return;
711            }
712
713            var selectedTotal = 0;
714
715            groups.forEach(function(group) {
716                var checkedInput = getCheckedInput(group);
717                if (!checkedInput) {
718                    return;
719                }
720
721                var optionLabel = group.querySelector('label[for="' + checkedInput.id + '"]');
722                if (!optionLabel) {
723                    return;
724                }
725
726                selectedTotal += Number(optionLabel.dataset.optionTotal || 0);
727            });
728
729            var displayPrice = basePrice + selectedTotal;
730            if (displayPrice <= 0) {
731                priceNode.textContent = '';
732                priceNode.hidden = true;
733                priceNode.classList.add('is-price-hidden');
734                return;
735            }
736
737            priceNode.hidden = false;
738            priceNode.classList.remove('is-price-hidden');
739            priceNode.textContent = pricePrefix + formatCurrency(displayPrice);
740        }
741
742        function updateGalleryImageElement(imageElement, imageUrl, imageAlt) {
743            if (!imageElement || !imageUrl) {
744                return;
745            }
746
747            imageElement.src = imageUrl;
748            imageElement.srcset = imageUrl;
749            imageElement.alt = imageAlt || imageElement.alt;
750
751            imageElement.setAttribute('data-src', imageUrl);
752            imageElement.setAttribute('data-large_image', imageUrl);
753            imageElement.setAttribute('data-o_src', imageUrl);
754            imageElement.setAttribute('data-o_srcset', imageUrl);
755
756            var imageLink = imageElement.closest('a');
757            if (imageLink) {
758                imageLink.href = imageUrl;
759                imageLink.setAttribute('data-src', imageUrl);
760                imageLink.setAttribute('data-large_image', imageUrl);
761            }
762        }
763
764        // Product configurator image sync: show selected option image in default WooCommerce and WooGallery/WCGS galleries.
765        function updateMainProductImageFromOption(group) {
766            var checkedInput = getCheckedInput(group);
767            if (!checkedInput) {
768                return;
769            }
770
771            var optionLabel = group.querySelector('label[for="' + checkedInput.id + '"]');
772            var optionImage = optionLabel ? optionLabel.querySelector('.tobor-config-option-image img') : null;
773            if (!optionImage || !optionImage.src) {
774                return;
775            }
776
777            var imageUrl = optionImage.src;
778            var imageAlt = optionImage.alt || '';
779            var gallerySelectors = [
780                '.woocommerce-product-gallery__image img',
781                '.wcgs-photo img',
782                '.wcgs-slider-image img',
783                '.wcgs-slider .swiper-slide-active img',
784                '.wcgs-slider .slick-current img',
785                '.wcgs-gallery img.wp-post-image',
786                '.wcgs-gallery .wp-post-image',
787                '.wcgs-carousel .slick-current img',
788                '.wcgs-swiper .swiper-slide-active img'
789            ];
790            var updated = false;
791
792            gallerySelectors.forEach(function(selector) {
793                var imageElement = document.querySelector(selector);
794                if (imageElement) {
795                    updateGalleryImageElement(imageElement, imageUrl, imageAlt);
796                    updated = true;
797                }
798            });
799
800            document.querySelectorAll('.zoomImg').forEach(function(zoomImage) {
801                updateGalleryImageElement(zoomImage, imageUrl, imageAlt);
802                updated = true;
803            });
804
805            document.querySelectorAll('.wcgs-photo, .wcgs-slider-image').forEach(function(photoElement) {
806                if (photoElement.style.backgroundImage) {
807                    photoElement.style.backgroundImage = 'url("' + imageUrl + '")';
808                    updated = true;
809                }
810            });
811
812            if (!updated) {
813                return;
814            }
815
816            if (window.jQuery) {
817                window.jQuery(document.body).trigger('woocommerce_gallery_reset_slide_position');
818            }
819        }
820
821        function getCheckedInput(group) {
822            return group.querySelector('.tobor-config-radio:checked');
823        }
824
825        function getDefaultSummary(group) {
826            return group.dataset.required === '1' ? 'Selection required' : 'Optional';
827        }
828
829        function openSpecsModal(link) {
830            if (!specsModal || !specsModalBody || !link) {
831                return false;
832            }
833
834            var templateId = link.dataset.specsTemplateId;
835            var template = templateId ? document.getElementById(templateId) : null;
836            if (!template) {
837                return false;
838            }
839
840            specsModalBody.innerHTML = template.innerHTML;
841            specsModal.classList.add('is-open');
842            specsModal.setAttribute('aria-hidden', 'false');
843            document.body.classList.add('tobor-config-modal-open');
844            lastSpecsTrigger = link;
845
846            var closeButton = specsModal.querySelector('.tobor-config-specs-modal__close');
847            if (closeButton) {
848                closeButton.focus();
849            }
850
851            return true;
852        }
853
854        function closeSpecsModal() {
855            if (!specsModal) {
856                return;
857            }
858
859            specsModal.classList.remove('is-open');
860            specsModal.setAttribute('aria-hidden', 'true');
861
862            if (specsModalBody) {
863                specsModalBody.innerHTML = '';
864            }
865
866            document.body.classList.remove('tobor-config-modal-open');
867
868            if (lastSpecsTrigger) {
869                lastSpecsTrigger.focus();
870                lastSpecsTrigger = null;
871            }
872        }
873
874        // Product configurator redesign: keep groups open by default and only toggle from the group header.
875        function setGroupOpen(group, isOpen) {
876            if (!group) {
877                return;
878            }
879
880            group.classList.toggle('is-open', isOpen);
881
882            var toggle = group.querySelector('.tobor-config-group-toggle');
883            if (toggle) {
884                toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
885            }
886        }
887
888        function setOpenGroup(targetGroup) {
889            setGroupOpen(targetGroup, true);
890        }
891
892        function updatePrimaryGroup() {
893            var firstIncompleteGroup = null;
894
895            groups.forEach(function(group) {
896                group.classList.remove('is-primary');
897
898                if (firstIncompleteGroup || group.dataset.required !== '1' || getCheckedInput(group)) {
899                    return;
900                }
901
902                firstIncompleteGroup = group;
903            });
904
905            if (firstIncompleteGroup) {
906                firstIncompleteGroup.classList.add('is-primary');
907            }
908        }
909
910        function updateGroupState(group) {
911            var checkedInput = getCheckedInput(group);
912            var summary = group.querySelector('.tobor-config-toggle-summary');
913            var title = group.querySelector('.tobor-config-toggle-title');
914            var error = group.querySelector('.tobor-config-error');
915            var defaultTitle = title ? (title.dataset.defaultTitle || group.dataset.groupTitle || '') : '';
916
917            group.classList.toggle('has-selection', !!checkedInput);
918
919            if (title) {
920                title.textContent = defaultTitle;
921            }
922
923            if (summary) {
924                summary.textContent = checkedInput ? checkedInput.value.replace(/-/g, ' ') : getDefaultSummary(group);
925            }
926
927            if (checkedInput) {
928                var optionLabel = group.querySelector('label[for="' + checkedInput.id + '"]');
929                if (optionLabel && optionLabel.dataset.optionLabel && summary) {
930                    summary.textContent = optionLabel.dataset.optionLabel;
931                }
932            }
933
934            if (error && checkedInput) {
935                error.hidden = true;
936                error.textContent = '';
937            }
938
939            updateDisplayedPrice();
940        }
941
942        function validateConfigurator(showErrors) {
943            var firstInvalidGroup = null;
944
945            groups.forEach(function(group) {
946                var isRequired = group.dataset.required === '1';
947                var checkedInput = getCheckedInput(group);
948                var error = group.querySelector('.tobor-config-error');
949                var errorLabel = group.dataset.groupTitle.replace(/^select\s+/i, '');
950
951                updateGroupState(group);
952
953                if (isRequired && !checkedInput) {
954                    if (!firstInvalidGroup) {
955                        firstInvalidGroup = group;
956                    }
957
958                    if (showErrors) {
959                        group.classList.add('is-invalid');
960                        if (error) {
961                            error.hidden = false;
962                            error.textContent = 'Please select a ' + errorLabel + '.';
963                        }
964                    }
965                } else {
966                    group.classList.remove('is-invalid');
967                    if (error) {
968                        error.hidden = true;
969                        error.textContent = '';
970                    }
971                }
972            });
973
974            var isValid = !firstInvalidGroup;
975            addToCartButton.disabled = !isValid;
976            addToCartButton.classList.toggle('is-disabled', !isValid);
977            addToCartButton.setAttribute('aria-disabled', isValid ? 'false' : 'true');
978            updatePrimaryGroup();
979
980            return firstInvalidGroup;
981        }
982
983        groups.forEach(function(group) {
984            var toggle = group.querySelector('.tobor-config-group-toggle');
985            if (toggle) {
986                toggle.addEventListener('click', function() {
987                    var alreadyOpen = group.classList.contains('is-open');
988                    setGroupOpen(group, !alreadyOpen);
989                });
990            }
991
992            var radios = group.querySelectorAll('.tobor-config-radio');
993            radios.forEach(function(radio) {
994                radio.addEventListener('change', function() {
995                    validateConfigurator(false);
996                    setGroupOpen(group, true);
997                    updateMainProductImageFromOption(group);
998                });
999            });
1000
1001            var optionLabels = group.querySelectorAll('.tobor-config-option');
1002            optionLabels.forEach(function(optionLabel) {
1003                optionLabel.addEventListener('keydown', function(event) {
1004                    if (event.key !== 'Enter' && event.key !== ' ') {
1005                        return;
1006                    }
1007
1008                    event.preventDefault();
1009                    var inputId = optionLabel.getAttribute('for');
1010                    var input = inputId ? document.getElementById(inputId) : null;
1011                    if (!input) {
1012                        return;
1013                    }
1014
1015                    input.checked = true;
1016                    input.dispatchEvent(new Event('change', { bubbles: true }));
1017                });
1018            });
1019
1020        });
1021
1022        configurator.addEventListener('click', function(event) {
1023            var link = event.target.closest('.tobor-config-specs-link');
1024            if (!link || !configurator.contains(link)) {
1025                return;
1026            }
1027
1028            event.preventDefault();
1029            event.stopPropagation();
1030            event.stopImmediatePropagation();
1031
1032            openSpecsModal(link);
1033        }, true);
1034
1035        if (specsModal) {
1036            var modalCloseButtons = specsModal.querySelectorAll('[data-tobor-config-modal-close]');
1037            modalCloseButtons.forEach(function(closeButton) {
1038                closeButton.addEventListener('click', function(event) {
1039                    event.preventDefault();
1040                    closeSpecsModal();
1041                });
1042            });
1043
1044            document.addEventListener('keydown', function(event) {
1045                if (event.key !== 'Escape' || !specsModal.classList.contains('is-open')) {
1046                    return;
1047                }
1048
1049                closeSpecsModal();
1050            });
1051        }
1052
1053        groups.forEach(function(group) {
1054            setGroupOpen(group, group.classList.contains('is-open'));
1055        });
1056        validateConfigurator(false);
1057
1058        addToCartButton.addEventListener('click', function(event) {
1059            var firstInvalidGroup = validateConfigurator(true);
1060            if (firstInvalidGroup) {
1061                event.preventDefault();
1062                event.stopPropagation();
1063                event.stopImmediatePropagation();
1064                setOpenGroup(firstInvalidGroup);
1065                firstInvalidGroup.scrollIntoView({ behavior: 'smooth', block: 'center' });
1066                return;
1067            }
1068
1069            event.preventDefault();
1070            event.stopPropagation();
1071            event.stopImmediatePropagation();
1072            HTMLFormElement.prototype.submit.call(form);
1073        }, true);
1074
1075        form.addEventListener('submit', function(event) {
1076            var firstInvalidGroup = validateConfigurator(true);
1077            if (!firstInvalidGroup) {
1078                return;
1079            }
1080
1081            event.preventDefault();
1082            setOpenGroup(firstInvalidGroup);
1083            firstInvalidGroup.scrollIntoView({ behavior: 'smooth', block: 'center' });
1084
1085            var toggle = firstInvalidGroup.querySelector('.tobor-config-group-toggle');
1086            if (toggle) {
1087                toggle.focus();
1088            }
1089        });
1090    });
1091});
1092// PRODUCT CONFIGURATOR CODE END

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.