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.