1/* eslint-disable @wordpress/no-unused-vars-before-return */ 2( function ( $ ) { 3 ( 'use strict' ); 4 5 const makeTemplatesVisible = () => { 6 const templates = document.querySelectorAll( '.revx-template' ); 7 templates.forEach( ( template ) => { 8 template.style.visibility = 'visible'; 9 template.style.opacity = '1'; 10 } ); 11 }; 12 13 // Run on first load 14 makeTemplatesVisible(); 15 16 // add events that does partial refresh of the DOM instead of full reload, to make the templates visible again 17 // Listen for cart / checkout partial refreshes 18 $( document.body ).on( 19 'updated_checkout updated_cart_totals woocommerce_update_order_review', 20 makeTemplatesVisible 21 ); 22 // Listen for mini-cart updates 23 $( document.body ).on( 24 'wc_fragments_loaded wc_fragments_refreshed', 25 makeTemplatesVisible 26 ); 27 28 // ----------------- 29 // Plugins 30 $.fn.revxSlider = function () { 31 return this.each( function () { 32 const $container = $( this ); 33 const $sliderIcons = $container.find( '.revx-slider-icons' ); 34 const $items = $container.find( '.revx-grid-item' ); 35 const itemCount = $items.length; 36 37 let currentPosition = 0; 38 39 function getBreakpoint( containerWidth ) { 40 if ( containerWidth <= 300 ) { 41 return 'sm'; 42 } 43 if ( containerWidth <= 500 ) { 44 return 'md'; 45 } 46 return 'lg'; 47 } 48 49 function getVisibleItems() { 50 const containerWidth = $container.width(); 51 const layout = $container.data( 'layout' ) || 'inpage'; 52 const sliderColumns = $container.data( 'slider-columns' ) || {}; 53 const breakpoint = getBreakpoint( containerWidth ); 54 55 if ( 56 sliderColumns[ layout ] && 57 sliderColumns[ layout ][ breakpoint ] 58 ) { 59 return parseInt( 60 sliderColumns[ layout ][ breakpoint ], 61 10 62 ); 63 } 64 65 return 1; 66 } 67 68 function getMaxStartIndex() { 69 return itemCount - getVisibleItems(); 70 } 71 72 function updateSliderPosition() { 73 $container.css( 74 '--revx-slider-translate', 75 `-${ currentPosition * 100 }%` 76 ); 77 } 78 79 function handleNext() { 80 const maxStartIndex = getMaxStartIndex(); 81 currentPosition = 82 currentPosition >= maxStartIndex ? 0 : currentPosition + 1; 83 updateSliderPosition(); 84 } 85 86 function handlePrev() { 87 const maxStartIndex = getMaxStartIndex(); 88 currentPosition = 89 currentPosition <= 0 ? maxStartIndex : currentPosition - 1; 90 updateSliderPosition(); 91 } 92 93 function updateSliderVisibility() { 94 const visibleItems = getVisibleItems(); 95 const isSliderNeeded = itemCount > visibleItems; 96 97 $container.toggleClass( 'has-slider', isSliderNeeded ); 98 99 return isSliderNeeded; 100 } 101 102 function initializeSlider() { 103 const visibleItems = getVisibleItems(); 104 const maxStartIndex = itemCount - visibleItems; 105 106 if ( currentPosition > maxStartIndex ) { 107 currentPosition = Math.max( 0, maxStartIndex ); 108 updateSliderPosition(); 109 } 110 111 updateSliderVisibility(); 112 } 113 114 // Attach events 115 $sliderIcons 116 .find( '.next' ) 117 .off( 'click' ) 118 .on( 'click', handleNext ); 119 $sliderIcons 120 .find( '.prev' ) 121 .off( 'click' ) 122 .on( 'click', handlePrev ); 123 124 // Resize event 125 $( window ).on( 'resize', initializeSlider ); 126 127 // Resize observer for wrapper 128 if ( typeof ResizeObserver !== 'undefined' ) { 129 const resizeObserver = new ResizeObserver( initializeSlider ); 130 const wrapper = $container 131 .find( '.revx-items-wrapper' ) 132 .get( 0 ); 133 if ( wrapper ) { 134 resizeObserver.observe( wrapper ); 135 } 136 } 137 138 // Initial setup 139 initializeSlider(); 140 } ); 141 }; 142 143 $.fn.revxCountdownTimer = function ( options ) { 144 const settings = $.extend( 145 { 146 endTimeAttr: 'data-end-time', 147 startTimeAttr: 'data-start-time', 148 formatTimeUnit( unit ) { 149 return unit < 10 ? '0' + unit : unit; 150 }, 151 onEnd( $el ) { 152 $el.html( '<span>00</span><span>:</span><span>00</span>' ); 153 }, 154 onBeforeStart( $el ) { 155 $el.html( '<span>00</span><span>:</span><span>00</span>' ); // You can change to "Coming Soon..." etc 156 }, 157 }, 158 options 159 ); 160 161 function initTimer( $timer ) { 162 if ( $timer.data( 'revxCountdownInitialized' ) ) { 163 return; 164 } 165 $timer.data( 'revxCountdownInitialized', true ); 166 167 const endTime = parseInt( $timer.attr( settings.endTimeAttr ), 10 ); 168 const startTime = 169 parseInt( $timer.attr( settings.startTimeAttr ), 10 ) || null; 170 171 if ( isNaN( endTime ) ) { 172 settings.onEnd( $timer ); 173 return; 174 } 175
176 function update() { 177 const now = Date.now(); 178 179 if ( startTime && now < startTime ) { 180 settings.onBeforeStart( $timer ); 181 return; 182 } 183 184 const diff = endTime - now; 185 186 if ( diff <= 0 ) { 187 clearInterval( interval ); 188 settings.onEnd( $timer ); 189 return; 190 } 191 192 const days = Math.floor( diff / ( 1000 * 60 * 60 * 24 ) ); 193 const hours = Math.floor( ( diff / ( 1000 * 60 * 60 ) ) % 24 ); 194 const minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 ); 195 const seconds = Math.floor( ( diff / 1000 ) % 60 ); 196 197 let html = ''; 198 199 if ( days > 0 ) { 200 html += 201 '<span>' + 202 settings.formatTimeUnit( days ) + 203 '</span><span>:</span>'; 204 } 205 if ( days > 0 || hours > 0 ) { 206 html += 207 '<span>' + 208 settings.formatTimeUnit( hours ) + 209 '</span><span>:</span>'; 210 } 211 html += 212 '<span>' + 213 settings.formatTimeUnit( minutes ) + 214 '</span><span>:</span>'; 215 html += 216 '<span>' + settings.formatTimeUnit( seconds ) + '</span>'; 217 218 $timer.html( html ); 219 } 220 221 update(); 222 var interval = setInterval( update, 1000 ); 223 $timer.data( 'revxCountdownInterval', interval ); 224 } 225 226 // Initialize on each element 227 this.each( function () { 228 const $this = $( this ); 229 initTimer( $this ); 230 } ); 231 232 return this; 233 }; 234 235 function calculateCurrentPrice( offerData, productId, quantity ) { 236 const product = offerData[ productId ]; 237 238 if ( ! product ) { 239 throw new Error( 'Product not found' ); 240 } 241 242 const regularPrice = parseFloat( product.regular_price ); 243 let currentPrice = regularPrice; 244 245 if ( product.offer && Array.isArray( product.offer ) ) { 246 for ( let i = 0; i < product.offer.length; i++ ) { 247 const offer = product.offer[ i ]; 248 const minQty = parseInt( offer.qty, 10 ); 249 250 if ( offer.type == 'free' ) { 251 if ( quantity >= minQty ) { 252 switch ( offer.type ) { 253 case 'free': 254 currentPrice = 0; 255 256 break; 257 258 default: 259 break; 260 } 261 // Add more offer types if needed (e.g., fixed discount) 262 } 263 } else if ( quantity >= minQty ) { 264 switch ( offer.type ) { 265 case 'percentage': 266 currentPrice = 267 regularPrice - 268 ( parseFloat( offer.value ) / 100 ) * 269 regularPrice; 270 break; 271 case 'amount': 272 case 'fixed_discount': 273 currentPrice = 274 regularPrice - parseFloat( offer.value ); 275 276 break; 277 case 'fixed_price': 278 currentPrice = parseFloat( offer.value ); 279 280 break; 281 case 'no_discount': 282 currentPrice = regularPrice; 283 284 break; 285 case 'free': 286 currentPrice = 0; 287 288 break; 289 290 default: 291 break; 292 } 293 // Add more offer types if needed (e.g., fixed discount) 294 } 295 } 296 } 297 298 return parseFloat( currentPrice * quantity ); 299 } 300 301 const formatPrice = ( price ) => { 302 const currencyFormat = revenue_campaign?.currency_format; 303 const currencySymbol = revenue_campaign?.currency_format_symbol; 304 const decimalSeparator = revenue_campaign?.currency_format_decimal_sep; 305 const thousandSeparator = 306 revenue_campaign?.currency_format_thousand_sep; 307 const numDecimals = revenue_campaign?.currency_format_num_decimals; 308 309 const fixedPrice = parseFloat( price ).toFixed( numDecimals || 2 ); 310 311 const parts = fixedPrice.split( '.' ); 312 let integerPart = parts[ 0 ]; 313 let decimalSepAndNum = ''; 314 if ( numDecimals > 0 ) { 315 decimalSepAndNum = decimalSeparator + ( parts[ 1 ] || '00' ); 316 } 317 318 integerPart = integerPart.replace( 319 /\B(?=(\d{3})+(?!\d))/g, 320 thousandSeparator 321 ); 322 323 const formattedPrice = integerPart + decimalSepAndNum; 324 325 return currencyFormat 326 .replace( '%1$s', currencySymbol ) 327 .replace( '%2$s', formattedPrice ); 328 }; 329 330 // Volume Discount 331 $( '.revx-volume-discount .revx-campaign-item' ).on( 332 'click', 333 function ( e ) { 334 // Remove selected style from all items 335 e.stopPropagation(); 336 const that = $( this ); 337 $( '.revx-volume-discount .revx-campaign-item' ).each( function () { 338 const item = $( this ).find( '.revx-volume-discount__tag' ); 339 const defaultStyle = item.data( 'default-style' ); 340 $( item ).attr( 'style', defaultStyle ); 341 $( this ).attr( 'data-revx-selected', false ); 342 } ); 343 const clickedItem = that.find( '.revx-volume-discount__tag' ); 344 345 // Apply selected style to the clicked item 346 const selectedStyle = clickedItem.data( 'selected-style' ); 347 clickedItem.attr( 'style', selectedStyle ); 348 that.attr( 'data-revx-selected', true ); 349 350 $( '.revx-ticket-type' ).trigger( 'change' ); 351 } 352 ); 353 354 $( 'select.revx-productAttr-wrapper__field' ).on( 'change', function () { 355 const attributeData = 356 $( '.variations_form' ).data( 'product_variations' ); 357 358 const attributeName = $( this ).data( 'attribute_name' ); 359 360 const parentWrapper = $( this ).closest( '.revx-productAttr-wrapper' ); 361 const fieldsInParent = parentWrapper.find( 362 '.revx-productAttr-wrapper__field' 363 ); 364 365 let allFieldsHaveValue = true; 366 const values = {}; 367 fieldsInParent.each( function () { 368 if ( $( this ).val() === '' ) { 369 allFieldsHaveValue = false;
370 return false; // Break the loop 371 } 372 values[ $( this ).data( 'attribute_name' ) ] = $( this ).val(); 373 } ); 374 375 let selectedVariation = false; 376 377 if ( allFieldsHaveValue ) { 378 attributeData.forEach( ( element ) => { 379 if ( 380 JSON.stringify( element.attributes ) == 381 JSON.stringify( values ) 382 ) { 383 selectedVariation = element; 384 } 385 } ); 386 } 387 388 $( '.revx-campaign-item' ).removeAttr( 'data-product-id' ); 389 if ( selectedVariation ) { 390 const parent = $( this ).closest( '.revx-campaign-item' ); 391 const campaignID = parent.data( 'campaignId' ); 392 const quantity = parent.data( 'quantity' ); 393 394 const offerData = JSON.parse( 395 $( `input[name="revx-offer-data-${ campaignID }"]` ).val() 396 ); 397 398 const variation_id = selectedVariation.variation_id; 399 400 const offer = offerData[ variation_id ]; 401 402 let nearestOffer = offer.offer[ 0 ]; 403 for ( let i = 1; i < offer.offer.length; i++ ) { 404 if ( offer.offer[ i ].qty <= quantity ) { 405 nearestOffer = offer.offer[ i ]; 406 } else { 407 break; 408 } 409 } 410 411 let regular_price = offer.regular_price; 412 let sale_price = ''; 413 414 switch ( nearestOffer.type ) { 415 case 'percentage': 416 sale_price = 417 parseFloat( offer.regular_price * nearestOffer.qty ) * 418 ( 1 - nearestOffer.value / 100 ); 419 420 break; 421 case 'amount': 422 case 'fixed_discount': 423 sale_price = 424 Math.max( 425 0, 426 parseFloat( offer.regular_price ) - 427 parseFloat( nearestOffer.value ) 428 ) * nearestOffer.qty; 429 430 break; 431 case 'fixed_price': 432 sale_price = 433 parseFloat( nearestOffer.value ) * nearestOffer.qty; 434 regular_price = false; 435 436 break; 437 case 'no_discount': 438 sale_price = 439 parseFloat( offer.regular_price ) * nearestOffer.qty; 440 regular_price = false; 441 442 break; 443 case 'free': 444 sale_price = 0; 445 446 break; 447 448 default: 449 break; 450 } 451 452 parent.attr( 'data-product-id', variation_id ); 453 454 parent 455 .parent() 456 .parent() 457 .find( '.revx-campaign-add-to-cart-btn' ) 458 .attr( 'data-product-id', variation_id ); 459 460 parent.find( 'input[data-name=revx_quantity]' ).trigger( 'change' ); 461 462 parent 463 .find( '.revx-campaign-item__regular-price' ) 464 .html( formatPrice( regular_price * quantity ) ); 465 parent 466 .find( '.revx-campaign-item__sale-price' ) 467 .html( formatPrice( sale_price ) ); 468 } else { 469 const parent = $( this ).closest( '.revx-campaign-item' ); 470 parent.find( '.revx-campaign-item__regular-price' ).html( '' ); 471 parent.find( '.revx-campaign-item__sale-price' ).html( '' ); 472 } 473 } ); 474 475 function updatePriceDisplay( parent, quantity, salePrice, regularPrice ) { 476 salePrice = parseFloat( salePrice ); 477 regularPrice = parseFloat( regularPrice ); 478 const salePriceElement = $( parent ).find( 479 '.revx-campaign-item__sale-price' 480 ); 481 const regularPriceElement = $( parent ).find( 482 '.revx-campaign-item__regular-price' 483 ); 484 const savingsTag = $( parent ).find( '.revx-builder-savings-tag' ); 485 486 if ( quantity == 0 ) { 487 salePriceElement.html( formatPrice( 0 ) ); 488 savingsTag.hide(); 489 return; 490 } 491 492 if ( salePrice !== regularPrice ) { 493 salePriceElement.html( 494 quantity > 1 495 ? `${ quantity } x ` + formatPrice( salePrice ) 496 : formatPrice( salePrice ) 497 ); 498 regularPriceElement.html( 499 quantity > 1 500 ? `${ quantity } x ` + formatPrice( regularPrice ) 501 : formatPrice( regularPrice ) 502 ); 503 savingsTag.show(); 504 } else { 505 salePriceElement.html( 506 quantity > 1 507 ? `${ quantity } x ` + formatPrice( regularPrice ) 508 : formatPrice( regularPrice ) 509 ); 510 regularPriceElement.empty(); 511 savingsTag.hide(); 512 } 513 } 514 515 $( 516 '.revx-volume-discount .revx-builder__quantity input[data-name=revx_quantity]' 517 ).on( 'change', function () { 518 const parent = $( this ).closest( '.revx-campaign-item' ); 519 const product_id = $( this ) 520 .closest( '.revx-campaign-item' ) 521 .data( 'product-id' ); 522 523 if ( ! product_id ) { 524 return; 525 } 526 527 const quantity = $( this ).val(); 528 529 parent 530 .parent() 531 .parent() 532 .find( '.revx-campaign-add-to-cart-btn' ) 533 .attr( 'data-quantity', quantity ); 534 535 const campaignId = $( this ).data( 'campaign-id' ); // Get data-campaign-id attribute value 536 537 let offerData = $( `input[name="revx-offer-data-${ campaignId }"]` ); 538 offerData = offerData[ 0 ].value; 539 const jsonData = JSON.parse( offerData ); 540 541 const regularPrice = ( 542 jsonData[ product_id ].regular_price * quantity 543 ).toFixed( 2 ); 544 const salePrice = calculateCurrentPrice( 545 jsonData, 546 product_id, 547 quantity 548 ).toFixed( 2 ); 549 550 if ( salePrice != regularPrice ) { 551 $( parent ) 552 .find( '.revx-campaign-item__sale-price' ) 553 .html( formatPrice( salePrice ) ); 554 $( parent ) 555 .find( '.revx-campaign-item__regular-price' ) 556 .html( formatPrice( regularPrice ) ); 557 } else { 558 $( parent ) 559 .find( '.revx-campaign-item__regular-price' ) 560 .html( formatPrice( salePrice ) ); 561 } 562 } ); 563 564 // Bundle Discount 565 $( 566 '.revx-bundle-discount .revx-builder__quantity input[data-name=revx_quantity]' 567 ).on( 'change', function () { 568 const parent = $( this ).closest( '.revx-campaign-container__wrapper' ); 569 const bundle_products = $( this ) 570 .closest( '.revx-campaign-container__wrapper' ) 571 .data( 'bundle_products' ); 572 573 if ( bundle_products.length == 0 ) { 574 return; 575 } 576 577 const quantity = $( this ).val(); 578 579 parent 580 .find( '.revenue-campaign-add-bundle-to-cart' ) 581 .attr( 'data-quantity', quantity ); 582 583 const campaignId = $( this ).data( 'campaign-id' ); // Get data-campaign-id attribute value 584 585 let offerData = $( `input[name="revx-offer-data-${ campaignId }"]` ); 586 offerData = offerData[ 0 ].value; 587 const jsonData = JSON.parse( offerData ); 588 589 let totalRegularPrice = 0; 590 let totalSalePrice = 0; 591
592 bundle_products.forEach( ( product ) => { 593 const productId = product.item_id; 594 // let quantity = product.quantity; 595 596 if ( jsonData[ productId ] ) { 597 totalRegularPrice += parseFloat( 598 ( 599 jsonData[ productId ].regular_price * 600 quantity * 601 product.quantity 602 ).toFixed( 2 ) 603 ); 604 totalSalePrice += parseFloat( 605 calculateCurrentPrice( 606 jsonData, 607 productId, 608 product.quantity * quantity 609 ).toFixed( 2 ) 610 ); 611 } 612 } ); 613 614 if ( totalRegularPrice != totalSalePrice ) { 615 $( parent ) 616 .find( 617 '.revx-total-price__offer-price .revx-campaign-item__sale-price' 618 ) 619 .html( formatPrice( totalSalePrice ) ); 620 $( parent ) 621 .find( 622 '.revx-total-price__offer-price .revx-campaign-item__regular-price' 623 ) 624 .html( formatPrice( totalRegularPrice ) ); 625 } else { 626 $( parent ) 627 .find( 628 '.revx-total-price__offer-price .revx-campaign-item__regular-price' 629 ) 630 .html( formatPrice( totalRegularPrice ) ); 631 } 632 } ); 633 634 // Normal Discount 635 $( 636 '.revx-normal-discount .revx-builder__quantity input[data-name=revx_quantity]' 637 ).on( 'change', function () { 638 const parent = $( this ).closest( '.revx-campaign-item' ); 639 const product_id = $( this ) 640 .closest( '.revx-campaign-item' ) 641 .data( 'product-id' ); 642 643 if ( ! product_id ) { 644 return; 645 } 646 647 const quantity = $( this ).val(); 648 649 parent 650 .find( '.revx-campaign-add-to-cart-btn' ) 651 .attr( 'data-quantity', quantity ); 652 const campaignId = $( this ).data( 'campaign-id' ); // Get data-campaign-id attribute value 653 654 let offerData = $( `input[name="revx-offer-data-${ campaignId }"]` ); 655 offerData = offerData[ 0 ].value; 656 const jsonData = JSON.parse( offerData ); 657 658 if ( ! jsonData[ product_id ] ) { 659 return; 660 } 661 662 const inRP = jsonData[ product_id ].regular_price; 663 664 const regularPrice = ( 665 jsonData[ product_id ].regular_price * quantity 666 ).toFixed( 2 ); 667 668 const salePrice = calculateCurrentPrice( 669 jsonData, 670 product_id, 671 quantity 672 ).toFixed( 2 ); 673 674 const inSP = ( salePrice / quantity ).toFixed( 2 ); 675 676 updatePriceDisplay( parent, quantity, inSP, inRP ); 677 678 // if (salePrice != regularPrice) { 679 // $(parent).find('.revx-campaign-item__sale-price').html(`${qty} x `+formatPrice(inSP)); 680 // $(parent).find('.revx-campaign-item__regular-price').html(`${qty} x `+formatPrice(inRP)); 681 // } else { 682 // $(parent).find('.revx-campaign-item__sale-price').html(`${qty} x `+formatPrice(inRP)); 683 // } 684 } ); 685 686 // Delegate click event for the 'minus' button 687 // $( document ).on( 'click', '.revx-quantity-minus', function ( e ) { 688 // e.preventDefault(); 689 // e.stopPropagation(); // Stop event propagation to parent elements 690 // const $input = $( this ).siblings( 'input[type="number"]' ); 691 692 // if ( $( this ).data( 'skip-global' ) ) { 693 // return; 694 // } 695 696 // if ( ! $input ) { 697 // return; 698 // } 699 // $input.focus(); // Focus on the input field after updating its value 700 701 // const currentValue = parseInt( $input.val(), 10 ); 702 // const min = $input.attr( 'min' ); 703 704 // if ( min && currentValue - 1 >= min ) { 705 // if ( ! isNaN( currentValue ) && currentValue > 0 ) { 706 // $input.val( currentValue - 1 ); 707 // } 708 // } 709 // // else if ( ! isNaN( currentValue ) && currentValue - 1 > 0 ) { 710 // // $input.val( currentValue - 1 ); 711 // // } 712 713 // $input.trigger( 'change' ); 714 // } ); 715 716 // // Delegate click event for the 'plus' button 717 // $( document ).on( 'click', '.revx-quantity-plus', function ( e ) { 718 // e.preventDefault(); 719 // e.stopPropagation(); // Stop event propagation to parent elements 720 721 // // Skip if it has data-skip-global attribute 722 // if ( $( this ).data( 'skip-global' ) ) { 723 // return; 724 // } 725 726 // const $input = $( this ).siblings( 'input[type="number"]' ); 727 // if ( ! $input.length ) { 728 // return; 729 // } 730 // $input.focus(); // Focus on the input field after updating its value 731 732 // const currentValue = parseInt( $input.val(), 10 ); 733 // const maxValue = parseInt( $input.attr( 'max' ), 10 ); // Get the max attribute value 734 735 // if ( ! isNaN( currentValue ) ) { 736 // // Check if current value is less than the max value 737 // if ( ! isNaN( maxValue ) && currentValue < maxValue ) { 738 // $input.val( currentValue + 1 ); 739 // } else if ( isNaN( maxValue ) ) { 740 // $input.val( currentValue + 1 ); // No max set, just increment 741 // } 742 // } else { 743 // $input.val( 1 ); // Set default value if current value is not a number 744 // } 745 746 // $input.trigger( 'change' ); 747 // } ); 748 749 // Delegate input event for the quantity input field 750 $( document ).on( 751 'input', 752 'input[data-name=revx_quantity]', 753 function ( e ) { 754 const minVal = parseInt( $( this ).attr( 'min' ) ) || 0; // Default to 0 if min is not set 755 const maxVal = parseInt( $( this ).attr( 'max' ) ); // Parse max value from the attribute 756 const val = parseInt( $( this ).val() ); // Get the current value of the input 757 758 // If the current value is less than min, set it to min 759 if ( val < minVal ) { 760 $( this ).val( minVal ); 761 } 762 763 // If the current value is greater than max, set it to max 764 if ( ! isNaN( maxVal ) && val > maxVal ) { 765 $( this ).val( maxVal ); 766 } 767 // Trigger change event after updating the value 768 $( this ).trigger( 'change' ); 769 } 770 ); 771 772 function getCookie( cname ) { 773 const name = 'revx_' + cname + '='; 774 const decodedCookie = decodeURIComponent( document.cookie ); 775 const ca = decodedCookie.split( ';' ); 776 for ( let i = 0; i < ca.length; i++ ) { 777 let c = ca[ i ]; 778 while ( c.charAt( 0 ) == ' ' ) { 779 c = c.substring( 1 ); 780 } 781 if ( c.indexOf( name ) == 0 ) { 782 return c.substring( name.length, c.length ); 783 } 784 } 785 return ''; 786 } 787 788 // Function to set the cookie 789 function setCookie( name, value, days ) { 790 const date = new Date(); 791 date.setTime( date.getTime() + days * 24 * 60 * 60 * 1000 ); 792 const expires = 'expires=' + date.toUTCString(); 793 document.cookie = 794 'revx_' + 795 name + 796 '=' + 797 encodeURIComponent( value ) + 798 ';' + 799 expires + 800 ';path=/'; 801 } 802 803 // Mix Match campaigns single product add to cart button. 804 $( '.revx-campaign-product-card .revx-mix-match-product-btn' ).on( 805 'click', 806 function ( e ) { 807 e.preventDefault(); 808 809 const campaignId = $( this ).data( 'campaign-id' ); 810 const item = $( this ).closest( '.revx-mix_match-add-to-cart' ); 811 let productId = item.data( 'product-id' ); 812 const productType = item.attr( 'product_type' ); 813 814 const quantity = 815 item.find( `input[data-name="revx_quantity"]` ).val() ?? 1; 816 817 const offerData = $( 818 `input[name="revx-offer-data-${ campaignId }"]` 819 ).val(); 820 const container = $( this ).closest( 821 '.revx-campaign-product-card' 822 ); 823 const jsonData = JSON.parse( offerData ); 824
825 const qtyData = $( 826 `input[name="revx-qty-data-${ campaignId }"]` 827 ).val(); 828 const jsonQtyData = JSON.parse( qtyData ); 829 let parentId = null; 830 let variationProductDetails = null; 831 let variationAttributes = null; 832 let selectedData = null; 833 834 if ( productType === 'variable' ) { 835 const variationMap = item 836 .find( '[data-variation-map]' ) 837 .data( 'variation-map' ); 838 selectedData = getSelectedAttributes( item ); 839 const odata = container.attr( 'data-variations' ); 840 parentId = container.data( 'product-id' ); 841 const a = JSON.parse( odata ); 842 843 if ( ! selectedData ) { 844 // throw new Error( 'Selected data is undefined or null.' ); 845 showToast( 846 revenue_campaign?.select_all_attributes || 847 'Please select all product attributes before adding to cart.', 848 'error', 849 3000 850 ); 851 return; 852 } 853 854 // Check for empty or undefined attribute values 855 for ( const [ , value ] of Object.entries( selectedData ) ) { 856 if ( 857 value === '' || 858 value === null || 859 value === undefined 860 ) { 861 showToast( 862 revenue_campaign?.select_all_attributes || 863 'Please select all required attributes', 864 'error', 865 3000 866 ); 867 return; 868 } 869 } 870 871 // now i need to match product id from variation map with selected data 872 let matchedVariationId = null; 873 874 const matchedVariation = variationMap.find( ( variation ) => { 875 if ( ! variation.attributes ) { 876 return false; 877 } 878 return Object.entries( selectedData ).every( 879 ( [ key, val ] ) => 880 ! val || 881 ! variation.attributes[ key ] || 882 variation.attributes[ key ] === val 883 ); 884 } ); 885 886 if ( matchedVariation ) { 887 matchedVariationId = matchedVariation.variation_id; 888 productId = matchedVariationId; 889 } 890 891 variationProductDetails = a.find( ( v ) => { 892 return parseInt( v.id ) === parseInt( matchedVariationId ); 893 } ); 894 895 variationAttributes = Object.entries( selectedData ) 896 .filter( ( [ key ] ) => key.startsWith( 'attribute_' ) ) // only attributes 897 .map( ( [ , value ] ) => value ) // just take the value 898 .join( ' - ' ); // join with dash 899 900 jsonData[ matchedVariationId ] = { 901 item_id: parseInt( matchedVariationId ), 902 item_name: `${ jsonData[ parentId ]?.item_name || '' }${ 903 variationAttributes ? ' - ' + variationAttributes : '' 904 }`, 905 thumbnail: variationProductDetails.image_url || '', 906 regular_price: variationProductDetails.regular_price || '', 907 sale_price: variationProductDetails.sale_price || '', 908 quantity: 1, 909 parent_id: parentId, 910 }; 911 } 912 913 const data = { 914 id: productId, 915 productName: 916 productType === 'variable' 917 ? `${ jsonData[ parentId ]?.item_name || '' }${ 918 variationAttributes 919 ? ' - ' + variationAttributes 920 : '' 921 }` 922 : jsonData[ productId ]?.item_name, 923 regularPrice: 924 productType === 'variable' 925 ? variationProductDetails.regular_price 926 : jsonData[ productId ]?.regular_price, 927 thumbnail: 928 productType === 'variable' 929 ? variationProductDetails.image_url 930 : jsonData[ productId ]?.thumbnail, 931 quantity, 932 }; 933 934 const cookieName = `mix_match_${ campaignId }`; 935 let prevData = getCookie( cookieName ); 936 937 let prevSelectedItems = $( 938 `input[name="revx-selected-items-${ campaignId }"]` 939 ).val(); 940 941 prevSelectedItems = prevSelectedItems 942 ? JSON.parse( prevSelectedItems ) 943 : {}; 944 945 prevData = prevData ? JSON.parse( prevData ) : {}; 946 947 // handles the case of initial render. when the required items are pre-selected and not in cookie, 948 // merge the cookie data with pre selected data and then update accordingly. 949 if ( Object.keys( prevSelectedItems ).length !== 0 ) { 950 for ( const itemId in prevSelectedItems ) { 951 if ( ! prevData[ itemId ] ) { 952 prevData[ itemId ] = prevSelectedItems[ itemId ]; 953 } 954 } 955 } 956 let clonedItem; 957 958 if ( prevData[ productId ] ) { 959 prevData[ productId ].quantity = 960 parseInt( prevData[ productId ].quantity ) + 961 parseInt( quantity ); 962 963 $( 964 `.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id=${ productId }] .revx-selected-item__product-price .woocommerce-Price-amount` 965 ).text( `${ formatPrice( data.regularPrice ) }` ); 966 $( 967 `.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id=${ productId }] .revx-selected-item__product-price .revx-qty` 968 ).text( `(x ${ prevData[ productId ].quantity })` ); 969 } else { 970 const selectedContainer = $( 971 `.revx-campaign-${ campaignId } .revx-selected-container` 972 ); 973 974 selectedContainer.removeClass( 'revx-d-none' ); 975 selectedContainer.removeClass( 'revx-empty-selected-items' ); 976 977 $( 978 `.revx-campaign-${ campaignId } .revx-empty-mix-match` 979 ).addClass( 'revx-d-none' ); 980 981 prevData[ productId ] = data; 982 983 const placeholderItem = $( 984 `.revx-selected-item.revx-d-none[data-campaign-id=${ campaignId }]` 985 ).first(); // ensure only one placeholder. 986 clonedItem = placeholderItem.clone(); 987 clonedItem 988 .find( '.revx-selected-title' ) 989 .html( data.productName ); 990 clonedItem 991 .find( '.revx-campaign-item__image img' ) 992 .attr( 'src', data.thumbnail ); 993 clonedItem 994 .find( '.revx-campaign-item__image img' ) 995 .attr( 'alt', data.productName ); 996 clonedItem 997 .find( 998 '.revx-selected-item__product-price .revx-price-placeholder' 999 ) 1000 .text( `${ formatPrice( data.regularPrice ) }` ); 1001 clonedItem 1002 .find( '.revx-selected-item__product-price .revx-qty' ) 1003 .text( `(x ${ quantity })` ); 1004 clonedItem.removeClass( 'revx-d-none' );
1005 clonedItem.attr( 'data-product-id', productId ); 1006 if ( productType === 'variable' ) { 1007 clonedItem.attr( 1008 'data-selected-attribute', 1009 JSON.stringify( selectedData ) 1010 ); 1011 } 1012 clonedItem.attr( 'data-parent-id', parentId ); 1013 clonedItem.attr( 'data-product-type', productType ); 1014 placeholderItem.before( clonedItem ); 1015 } 1016 1017 $( `input[name="revx-selected-items-${ campaignId }"]` ).val( 1018 JSON.stringify( prevData ) 1019 ); 1020 1021 setCookie( cookieName, JSON.stringify( prevData ), 7 ); 1022 1023 updateMixMatchHeaderAndPrices( campaignId, prevData, jsonQtyData ); 1024 1025 $( this ) 1026 .parent() 1027 .find( `input[data-name="revx_quantity"]` ) 1028 .val( 1 ); 1029 // make the reset button visible after adding any product 1030 $( this ) 1031 .closest( '.revx-template' ) 1032 .find( '[data-mix-match-reset-btn]' ) 1033 .removeClass( 'revx-d-none' ); 1034 } 1035 ); 1036 1037 function removeMixMatchSelectedItem() { 1038 const productId = $( this ) 1039 .closest( '[data-product-id]' ) 1040 .data( 'product-id' ); 1041 const campaignId = $( this ) 1042 .closest( '[data-campaign-id]' ) 1043 .data( 'campaign-id' ); 1044 const item = $( 1045 `.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id="${ productId }"]` 1046 ); 1047 1048 item.remove(); 1049 1050 const cookieName = `mix_match_${ campaignId }`; 1051 let prevData = getCookie( cookieName ); 1052 let prevSelectedItems = $( 1053 `input[name=revx-selected-items-${ campaignId }]` 1054 ).val(); 1055 prevSelectedItems = prevSelectedItems 1056 ? JSON.parse( prevSelectedItems ) 1057 : {}; 1058 1059 prevData = prevData ? JSON.parse( prevData ) : {}; 1060 1061 // handles the case of initial render, when the required items are pre-selected, 1062 // merge the cookie data with pre selected data and then update accordingly. 1063 if ( Object.keys( prevSelectedItems ).length !== 0 ) { 1064 for ( const itemId in prevSelectedItems ) { 1065 if ( ! prevData[ itemId ] ) { 1066 prevData[ itemId ] = prevSelectedItems[ itemId ]; 1067 } 1068 } 1069 } 1070 1071 delete prevData[ productId ]; 1072 setCookie( cookieName, JSON.stringify( prevData ), 7 ); 1073 $( `input[name=revx-selected-items-${ campaignId }]` ).val( 1074 JSON.stringify( prevData ) 1075 ); 1076 1077 const qtyData = $( `input[name=revx-qty-data-${ campaignId }]` ).val(); 1078 const jsonQtyData = JSON.parse( qtyData ); 1079 1080 if ( Object.keys( prevData ).length === 0 ) { 1081 $( 1082 `.revx-campaign-${ campaignId } .revx-empty-selected-products` 1083 ).removeClass( 'revx-d-none' ); 1084 $( 1085 `.revx-campaign-${ campaignId } .revx-selected-product-container` 1086 ).addClass( 'revx-empty-selected-items' ); 1087 $( 1088 `.revx-campaign-${ campaignId } .revx-empty-mix-match` 1089 ).removeClass( 'revx-d-none' ); 1090 1091 $( 1092 `.revx-campaign-${ campaignId } [data-mix-match-reset-btn]` 1093 ).addClass( 'revx-d-none' ); 1094 } 1095 1096 updateMixMatchHeaderAndPrices( campaignId, prevData, jsonQtyData ); 1097 } 1098 1099 $( '[data-container-level="mix_match_file"]' ).on( 1100 'click', 1101 '.revx-selected-remove', 1102 removeMixMatchSelectedItem 1103 ); 1104 1105 // refactor later to make DRY 1106 function resetMixMatchSelectedItem() { 1107 const campaignId = $( this ) 1108 .closest( '[data-campaign-id]' ) 1109 .data( 'campaign-id' ); 1110 1111 const cookieName = `mix_match_${ campaignId }`; 1112 1113 let prevSelectedItems = $( 1114 `input[name=revx-selected-items-${ campaignId }]` 1115 ).val(); 1116 prevSelectedItems = prevSelectedItems 1117 ? JSON.parse( prevSelectedItems ) 1118 : {}; 1119 1120 for ( const itemId in prevSelectedItems ) { 1121 const $item = $( this ) 1122 .closest( '[revx-campaign-id]' ) 1123 .find( 1124 `.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id="${ itemId }"]` 1125 ); 1126 if ( prevSelectedItems[ itemId ].is_required === 'yes' ) { 1127 prevSelectedItems[ itemId ].quantity = 1; 1128 $item.find( '.revx-qty' ).text( '(x 1)' ); 1129 } else { 1130 $item.remove(); 1131 delete prevSelectedItems[ itemId ]; 1132 } 1133 } 1134 setCookie( cookieName, JSON.stringify( prevSelectedItems ), 7 ); 1135 $( `input[name=revx-selected-items-${ campaignId }]` ).val( 1136 JSON.stringify( prevSelectedItems ) 1137 ); 1138 1139 const qtyData = $( `input[name=revx-qty-data-${ campaignId }]` ).val(); 1140 const jsonQtyData = JSON.parse( qtyData ); 1141 1142 if ( Object.keys( prevSelectedItems ).length === 0 ) { 1143 $( 1144 `.revx-campaign-${ campaignId } .revx-empty-selected-products` 1145 ).removeClass( 'revx-d-none' ); 1146 $( 1147 `.revx-campaign-${ campaignId } .revx-selected-product-container` 1148 ).addClass( 'revx-empty-selected-items' ); 1149 $( 1150 `.revx-campaign-${ campaignId } .revx-empty-mix-match` 1151 ).removeClass( 'revx-d-none' ); 1152 $( this ).addClass( 'revx-d-none' ); 1153 } 1154
1155 updateMixMatchHeaderAndPrices( 1156 campaignId, 1157 prevSelectedItems, 1158 jsonQtyData 1159 ); 1160 } 1161 $( '[data-container-level="mix_match_file"]' ).on( 1162 'click', 1163 '[data-mix-match-reset-btn]', 1164 resetMixMatchSelectedItem 1165 ); 1166 1167 function setTierState( $tier, tierClass, checkboxClass, isChecked ) { 1168 $tier.addClass( tierClass.add ).removeClass( tierClass.remove ); 1169 const $checkboxContainer = $tier.find( '.revx-checkbox-container' ); 1170 $checkboxContainer 1171 .addClass( checkboxClass.add ) 1172 .removeClass( checkboxClass.remove ) 1173 .attr( 'data-is-checked', isChecked ? 'yes' : 'no' ) 1174 .css( 'display', isChecked ? '' : 'none' ); // Ensure visibility based on state 1175 } 1176 1177 function updateMixMatchHeaderAndPrices( 1178 campaignId, 1179 prevData, 1180 jsonQtyData = {} 1181 ) { 1182 const header = $( 1183 `.revx-campaign-${ campaignId } .revx-price-container` 1184 ); 1185 const itemCounts = Object.keys( prevData ).length; 1186 let $selectedTier = null; 1187 $( '.revx-tier-button' ).each( function () { 1188 // Extract the item count from the mix-match-title text 1189 const titleText = $( this ) 1190 .find( '.revx-mix-match-title' ) 1191 .text() 1192 .trim(); 1193 const itemCount = parseInt( titleText.split( ' ' )[ 0 ], 10 ); // Extract number from "X item" 1194 1195 if ( itemCounts >= itemCount ) { 1196 $selectedTier = $( this ); 1197 } 1198 } ); 1199 const selectedClass = { 1200 tierClass: { 1201 add: 'revx-tier-selected', 1202 remove: 'revx-tier-regular', 1203 }, 1204 checkboxClass: { 1205 add: 'revx-active', 1206 remove: 'revx-inactive revx-d-none', 1207 }, 1208 }; 1209 const unselectedClass = { 1210 tierClass: { 1211 add: 'revx-tier-regular', 1212 remove: 'revx-tier-selected', 1213 }, 1214 checkboxClass: { 1215 add: 'revx-inactive revx-d-none', 1216 remove: 'revx-active', 1217 }, 1218 }; 1219 // Update tier button styles based on selection 1220 $( '.revx-tier-button' ).each( function () { 1221 if ( $selectedTier && $( this ).is( $selectedTier ) ) { 1222 setTierState( 1223 $( this ), 1224 selectedClass.tierClass, 1225 selectedClass.checkboxClass, 1226 true 1227 ); 1228 } else { 1229 setTierState( 1230 $( this ), 1231 unselectedClass.tierClass, 1232 unselectedClass.checkboxClass, 1233 false 1234 ); 1235 } 1236 } ); 1237 const qtyData = $( `input[name=revx-qty-data-${ campaignId }]` ).val(); 1238 jsonQtyData = qtyData ? JSON.parse( qtyData ) : []; 1239 1240 header.toggleClass( 'revx-d-none', ! itemCounts ); // remove none if more than 0, adds none when item count is 0 1241 1242 // const addToCart = $(`.revx-campaign-add-to-cart-btn[data-campaign-id=${campaign_id}]`); 1243 // if(item_counts==0 && !addToCart.hasClass('revx-d-none') ) { 1244 // $(`.revx-campaign-add-to-cart-btn[data-campaign-id=${campaign_id}]`).addClass('revx-d-none'); 1245 // } else if(item_counts>0) { 1246 // $(`.revx-campaign-add-to-cart-btn[data-campaign-id=${campaign_id}]`).removeClass('revx-d-none'); 1247 // } 1248 header.find( '.revx-selected-product-count' ).html( itemCounts ); 1249 1250 let totalRegularPrice = 0; 1251 let totalSalePrice = 0; 1252 let totalQuantity = 0; 1253 Object.values( prevData ).forEach( ( item ) => { 1254 totalRegularPrice += 1255 parseFloat( item.regularPrice ) * parseInt( item.quantity ); 1256 totalQuantity += parseInt( item.quantity ); 1257 } ); 1258 1259 let selectedIndex = -1; 1260 jsonQtyData.forEach( ( item, idx ) => { 1261 if ( itemCounts >= item.quantity ) { 1262 selectedIndex = idx; 1263 1264 switch ( item.type ) { 1265 case 'percentage': 1266 totalSalePrice = 1267 totalRegularPrice * ( 1 - item.value / 100 ); 1268 break; 1269 case 'fixed_discount': 1270 totalSalePrice = Math.max( 1271 0, 1272 parseFloat( totalRegularPrice ) - 1273 parseFloat( item.value * totalQuantity ) 1274 ); 1275 1276 break; 1277 case 'no_discount': 1278 totalSalePrice = totalRegularPrice; 1279 1280 break; 1281 case 'fixed_price': 1282 totalSalePrice = item.value * totalQuantity; 1283 break; 1284 default: 1285 break; 1286 } 1287 } 1288 } ); 1289 // make display none for no_discount, other cases, remove the class 1290 header 1291 .find( '.revx-product-old-price' ) 1292 .toggleClass( 1293 'revx-d-none', 1294 jsonQtyData[ selectedIndex ]?.type === 'no_discount' || 1295 totalSalePrice === 0 1296 ); 1297 const that = $( 1298 `.revx-campaign-${ campaignId } .revx-mixmatch-quantity` 1299 ); 1300 that.each( function () { 1301 const item = $( this ).find( '.revx-mixmatch-regular-quantity' ); 1302 1303 $( item ) 1304 .find( '.revx-checkbox-container' ) 1305 .addClass( 'revx-d-none' ); 1306 } ); 1307 1308 const clickedItem = that.find( `div[data-index=${ selectedIndex }]` ); 1309 $( clickedItem ) 1310 .find( '.revx-checkbox-container' ) 1311 .removeClass( 'revx-d-none' ); 1312 if ( totalSalePrice === 0 ) { 1313 header 1314 .find( '.revx-campaign-item__sale-price' ) 1315 .html( formatPrice( totalRegularPrice ) ); 1316 } else { 1317 if ( 1318 totalSalePrice && 1319 header 1320 .find( '.revx-campaign-item__sale-price' ) 1321 .hasClass( 'revx-d-none' ) 1322 ) { 1323 header 1324 .find( '.revx-campaign-item__sale-price' ) 1325 .removeClass( 'revx-d-none' ); 1326 } 1327 if ( 1328 totalRegularPrice && 1329 header 1330 .find( '.revx-campaign-item__regular-price' ) 1331 .hasClass( 'revx-d-none' ) 1332 ) { 1333 header 1334 .find( '.revx-campaign-item__regular-price' ) 1335 .removeClass( 'revx-d-none' ); 1336 } 1337 1338 header 1339 .find( '.revx-campaign-item__sale-price' ) 1340 .html( formatPrice( totalSalePrice ) ); 1341 header 1342 .find( '.revx-product-old-price' ) 1343 .html( formatPrice( totalRegularPrice ) ); 1344 } 1345 } 1346 1347 // old fbt - available for old campaigns in v1 1348 // Frequently Bought Together 1349 // Function to update the styles based on selection 1350 // function updateStyles( $checkbox, selected ) { 1351 // const selectedStyles = $checkbox.data( 'selected-style' ); 1352 // const defaultStyles = $checkbox.data( 'default-style' ); 1353 // if ( selected ) { 1354 // $checkbox.attr( 'style', selectedStyles ); 1355 // } else { 1356 // $checkbox.attr( 'style', defaultStyles ); 1357 // } 1358 // } 1359 // $( '.revx-frequently-bought-together' ).on( 1360 // 'click', 1361 // '.revx-item-options .revx-item-option', 1362 // function ( e ) { 1363 // e.preventDefault(); 1364 1365 // const $this = $( this ); 1366 // if ( $this.hasClass( 'revx-item-required' ) ) { 1367 // return; 1368 // } 1369 // const $checkbox = $this.find( '.revx-builder-checkbox' ); 1370 1371 // const parent = $this.closest( '.revx-campaign-container__wrapper' ); 1372 1373 // const campaign_id = parent.data( 'campaign-id' ); 1374 // const cookieName = `campaign_${ campaign_id }`; 1375 // let selectedProducts = getCookie( cookieName ); 1376 // let prevSelectedItems = $( 1377 // `input[name=revx-fbt-selected-items-${ campaign_id }]` 1378 // ).val(); 1379 // prevSelectedItems = prevSelectedItems 1380 // ? JSON.parse( prevSelectedItems ) 1381 // : {}; 1382 // selectedProducts = selectedProducts 1383 // ? JSON.parse( selectedProducts ) 1384 // : {}; 1385 // if ( Object.keys( selectedProducts ) == 0 ) { 1386 // selectedProducts = { ...prevSelectedItems }; 1387 // } 1388 1389 // const productId = $this.data( 'product-id' ); 1390 1391 // // Toggle the selected state 1392 // if ( selectedProducts[ productId ] ) { 1393 // // selectedProducts = selectedProducts.filter(id => id !== productId); 1394 // delete selectedProducts[ productId ]; 1395 // updateStyles( $checkbox, false ); 1396 // } else { 1397 // const quantityInput = 1398 // $( 1399 // `input[name="revx-quantity-${ campaign_id }-${ productId }"]` 1400 // ).val() ?? $this.data( 'min-quantity' ); 1401 1402 // selectedProducts[ productId ] = quantityInput; 1403 // updateStyles( $checkbox, true ); 1404 // } 1405 // $( `input[name="revx-fbt-selected-items-${ campaign_id }"]` ).val( 1406 // JSON.stringify( selectedProducts ) 1407 // ); 1408 1409 // // Update the cookie 1410 // setCookie( cookieName, JSON.stringify( selectedProducts ), 1 ); 1411 1412 // fbtCalculation( parent, campaign_id ); 1413 // } 1414 // ); 1415 1416 // const fbtCalculation = ( parent, campaign_id ) => { 1417 // const cookieName = `campaign_${ campaign_id }`; 1418 // let selectedProducts = getCookie( cookieName ); 1419 // let prevSelectedItems = $( 1420 // `input[name=revx-fbt-selected-items-${ campaign_id }]` 1421 // ).val(); 1422 // prevSelectedItems = prevSelectedItems 1423 // ? JSON.parse( prevSelectedItems ) 1424 // : {}; 1425 // selectedProducts = selectedProducts 1426 // ? JSON.parse( selectedProducts ) 1427 // : {}; 1428 // if ( Object.keys( selectedProducts ) == 0 ) { 1429 // selectedProducts = { ...prevSelectedItems }; 1430 // } 1431 1432 // const calculateSalePrice = ( data, qty = 1 ) => { 1433 // if ( ! data?.type ) { 1434 // return data.regular_price * qty; 1435 // } 1436 // let total = 0; 1437 // switch ( data.type ) { 1438 // case 'percentage': 1439 // total = 1440 // parseFloat( data.regular_price ) * 1441 // ( 1 - data.value / 100 ); 1442 1443 // break; 1444 // case 'amount': 1445 // case 'fixed_discount': 1446 // total = Math.max( 1447 // 0, 1448 // parseFloat( data.regular_price ) - 1449 // parseFloat( data.value ) 1450 // ); 1451 1452 // break; 1453 // case 'fixed_price': 1454 // total = parseFloat( data.value ); 1455 1456 // break; 1457 // case 'no_discount': 1458 // total = parseFloat( data.regular_price ); 1459 1460 // break; 1461 // case 'free': 1462 // total = 0; 1463 1464 // break; 1465 1466 // default: 1467 // break; 1468 // } 1469 1470 // return parseFloat( total ) * parseInt( qty ); 1471 // }; 1472 // let offerData = $( 1473 // `input[name=revx-offer-data-${ campaign_id }]` 1474 // ).val(); 1475 1476 // offerData = JSON.parse( offerData ); 1477 1478 // let totalRegularPrice = 0; 1479 // let totalSalePrice = 0; 1480 1481 // Object.keys( selectedProducts ).forEach( ( id ) => { 1482 // totalRegularPrice += 1483 // parseFloat( offerData[ id ]?.regular_price ) * 1484 // parseInt( selectedProducts[ id ] ); 1485 // totalSalePrice += parseFloat( 1486 // calculateSalePrice( 1487 // offerData[ id ], 1488 // parseInt( selectedProducts[ id ] ) 1489 // ) 1490 // ); 1491 // } ); 1492 1493 // if ( totalRegularPrice != totalSalePrice ) { 1494 // parent 1495 // .find( 1496 // `.revx-triggerProduct .revx-campaign-item__regular-price` 1497 // ) 1498 // .html( formatPrice( totalRegularPrice ) ); 1499 // parent 1500 // .find( `.revx-triggerProduct .revx-campaign-item__sale-price` ) 1501 // .html( formatPrice( totalSalePrice ) ); 1502 // } else { 1503 // parent 1504 // .find( `.revx-triggerProduct .revx-campaign-item__sale-price` ) 1505 // .html( formatPrice( totalSalePrice ) ); 1506 // parent 1507 // .find( 1508 // `.revx-triggerProduct .revx-campaign-item__regular-price` 1509 // ) 1510 // .html( '' ); 1511 // } 1512 // parent 1513 // .find( `.revx-triggerProduct .revx-selected-product-count` ) 1514 // .html( Object.keys( selectedProducts ).length ); 1515 // }; 1516 1517 // $( '.revx-frequently-bought-together' ).on( 1518 // 'change', 1519 // 'input[data-name=revx_quantity]', 1520 // function ( e ) { 1521 // e.preventDefault(); 1522 // const parent = $( this ).closest( 1523 // '.revx-campaign-container__wrapper' 1524 // ); 1525 1526 // const quantity = $( this ).val(); 1527 1528 // const campaign_id = parent.data( 'campaign-id' ); 1529 1530 // // addFbtRequiredProductsIfNotAdded(campaign_id,false); 1531 // const product_id = $( this ).data( 'product-id' ); 1532 // const cookieName = `campaign_${ campaign_id }`; 1533 1534 // let selectedProducts = getCookie( cookieName ); 1535 // let prevSelectedItems = $( 1536 // `input[name=revx-fbt-selected-items-${ campaign_id }]` 1537 // ).val(); 1538 // prevSelectedItems = prevSelectedItems 1539 // ? JSON.parse( prevSelectedItems ) 1540 // : {}; 1541 // selectedProducts = selectedProducts 1542 // ? JSON.parse( selectedProducts ) 1543 // : {}; 1544 // if ( Object.keys( selectedProducts ) == 0 ) { 1545 // selectedProducts = { ...prevSelectedItems }; 1546 // } 1547 1548 // if ( selectedProducts[ product_id ] ) { 1549 // selectedProducts[ product_id ] = quantity; 1550
1551 // setCookie( cookieName, JSON.stringify( selectedProducts ), 1 ); 1552 // fbtCalculation( parent, campaign_id ); 1553 // } 1554 1555 // $( `input[name=revx-fbt-selected-items-${ campaign_id }]` ).val( 1556 // JSON.stringify( selectedProducts ) 1557 // ); 1558 1559 // const calculateSalePrice = ( data, qty = 1 ) => { 1560 // if ( ! data?.type ) { 1561 // return data.regular_price * qty; 1562 // } 1563 // let total = 0; 1564 // switch ( data.type ) { 1565 // case 'percentage': 1566 // total = 1567 // parseFloat( data.regular_price ) * 1568 // ( 1 - data.value / 100 ); 1569 1570 // break; 1571 // case 'amount': 1572 // case 'fixed_discount': 1573 // total = Math.max( 1574 // 0, 1575 // parseFloat( data.regular_price ) - 1576 // parseFloat( data.value ) 1577 // ); 1578 1579 // break; 1580 // case 'fixed_price': 1581 // total = parseFloat( data.value ); 1582 1583 // break; 1584 // case 'no_discount': 1585 // total = parseFloat( data.regular_price ); 1586 1587 // break; 1588 // case 'free': 1589 // total = 0; 1590 1591 // break; 1592 1593 // default: 1594 // break; 1595 // } 1596 1597 // return parseFloat( total ) * parseInt( qty ); 1598 // }; 1599 // let offerData = $( `input[name=revx-offer-data-${ campaign_id }]` ); 1600 1601 // offerData = offerData[ 0 ].value; 1602 // const jsonData = JSON.parse( offerData ); 1603 1604 // const salePrice = calculateSalePrice( 1605 // jsonData[ product_id ], 1606 // quantity 1607 // ).toFixed( 2 ); 1608 1609 // const inRP = jsonData[ product_id ].regular_price; 1610 1611 // const inSP = ( salePrice / quantity ).toFixed( 2 ); 1612 1613 // const itemParent = $( this ).closest( '.revx-campaign-item' ); 1614 1615 // updatePriceDisplay( itemParent, quantity, inSP, inRP ); 1616 // } 1617 // ); 1618 1619 // Buy X Get Y 1620 $( '.revx-buyx-gety' ).on( 1621 'change', 1622 'input[data-name=revx_quantity]', 1623 function ( e ) { 1624 e.preventDefault(); 1625 const parent = $( this ).closest( '.revx-campaign-container' ); 1626 1627 const quantity = $( this ).val(); 1628 1629 const campaign_id = parent.data( 'campaign-id' ); 1630 1631 const product_id = $( this ).data( 'product-id' ); 1632 1633 let offerData = $( `input[name=revx-offer-data-${ campaign_id }]` ); 1634 1635 offerData = offerData[ 0 ].value; 1636 const jsonData = JSON.parse( offerData ); 1637 1638 const regularPrice = ( 1639 jsonData[ product_id ].regular_price * quantity 1640 ).toFixed( 2 ); 1641 const salePrice = calculateCurrentPrice( 1642 jsonData, 1643 product_id, 1644 quantity 1645 ).toFixed( 2 ); 1646 1647 const item = $( this ).closest( '.revx-campaign-item__content' ); 1648 1649 item.find( '.revx-campaign-item__regular-price' ).text( 1650 formatPrice( regularPrice ) 1651 ); 1652 item.find( '.revx-campaign-item__sale-price' ).text( 1653 formatPrice( salePrice ) 1654 ); 1655 1656 if ( regularPrice == salePrice ) { 1657 if ( 1658 ! item 1659 .find( '.revx-campaign-item__regular-price' ) 1660 .hasClass( 'revx-d-none' ) 1661 ) { 1662 item.find( '.revx-campaign-item__regular-price' ).addClass( 1663 'revx-d-none' 1664 ); 1665 } 1666 } else if ( 1667 item 1668 .find( '.revx-campaign-item__regular-price' ) 1669 .hasClass( 'revx-d-none' ) 1670 ) { 1671 item.find( '.revx-campaign-item__regular-price' ).removeClass( 1672 'revx-d-none' 1673 ); 1674 } 1675 1676 let totalRegularPrice = 0; 1677 let totalSalePrice = 0; 1678 1679 Object.keys( jsonData ).forEach( ( pid ) => { 1680 const qty = $( 1681 `input[name="revx-quantity-${ campaign_id }-${ pid }"]` 1682 ).val(); 1683 const rp = ( jsonData[ pid ].regular_price * qty ).toFixed( 2 ); 1684 const sp = parseFloat( 1685 calculateCurrentPrice( jsonData, pid, qty ).toFixed( 2 ) 1686 ); 1687 totalRegularPrice += parseFloat( rp ); 1688 totalSalePrice += parseFloat( sp ); 1689 } ); 1690 1691 parent 1692 .find( '.revx-total-price .revx-campaign-item__regular-price' ) 1693 .html( formatPrice( totalRegularPrice ) ); 1694 parent 1695 .find( '.revx-total-price .revx-campaign-item__sale-price' ) 1696 .html( formatPrice( totalSalePrice ) ); 1697 } 1698 ); 1699 1700 // Slider ----------------------- 1701 1702 function checkOverflow( container ) { 1703 $( container ).each( function () { 1704 const $this = $( this ); 1705 1706 const isOverflowing = 1707 $this[ 0 ].scrollWidth > $this[ 0 ].offsetWidth; 1708 1709 if ( isOverflowing ) { 1710 $( this ) 1711 .find( '.revx-builderSlider-icon' ) 1712 .addClass( 'revx-has-overflow' ); 1713 } else { 1714 $( this ) 1715 .find( '.revx-builderSlider-icon' ) 1716 .removeClass( 'revx-has-overflow' ); 1717 } 1718 } ); 1719 } 1720 1721 // function initializeSlider( 1722 // $sliderContainer, 1723 // $containerSelector = '.revx-inpage-container', 1724 // $campaign_type = '' 1725 // ) { 1726 // const $container = $sliderContainer.closest( $containerSelector ); 1727 // const containerElement = $container.get( 0 ); 1728 // const computedStyle = getComputedStyle( containerElement ); 1729 // const gridColumnValue = computedStyle 1730 // .getPropertyValue( '--revx-grid-column' ) 1731 // .trim(); 1732 // let itemGap = parseInt( 1733 // computedStyle.getPropertyValue( 'gap' ).trim() 1734 // ); 1735 1736 // if ( ! itemGap ) { 1737 // itemGap = 16; 1738 // } 1739 1740 // const $slides = $sliderContainer.find( '.revx-campaign-item' ); 1741 // const minSlideWidth = 100; // 12rem in pixels (assuming 1rem = 16px) 1742 1743 // let containerWidth = $sliderContainer.parent().width(); 1744 1745 // if ( $campaign_type == 'mix_match' ) { 1746 // containerWidth = $sliderContainer
1747 // .closest( '.revx-slider-items-wrapper' ) 1748 // .innerWidth(); 1749 // } 1750 // if ( $campaign_type == 'bundle_discount' ) { 1751 // containerWidth = $sliderContainer 1752 // .closest( '.revx-slider-items-wrapper' ) 1753 // .outerWidth(); 1754 // itemGap = 0; 1755 // } 1756 // if ( $campaign_type == 'fbt' ) { 1757 // containerWidth = $container 1758 // .find( '.revx-slider-items-wrapper' ) 1759 // .innerWidth(); 1760 // } 1761 // if ( $campaign_type == 'normal_discount' ) { 1762 // containerWidth = $container 1763 // .closest( '.revx-slider-items-wrapper' ) 1764 // .innerWidth(); 1765 // itemGap = 0; 1766 // } 1767 1768 // let slidesVisible = Math.min( 1769 // gridColumnValue, 1770 // Math.floor( containerWidth / minSlideWidth ) 1771 // ); // Calculate initial slides visible 1772 1773 // let slideWidth = containerWidth / slidesVisible; 1774 // slideWidth -= itemGap; 1775 1776 // if ( $campaign_type == 'bundle_discount' ) { 1777 // slideWidth -= $container 1778 // .find( '.revx-builder__middle_element' ) 1779 // .width(); 1780 // } 1781 1782 // const totalSlides = $slides.length; 1783 // let slideIndex = 0; 1784 1785 // function updateSlideWidth() { 1786 // containerWidth = $sliderContainer 1787 // .closest( '.revx-slider-items-wrapper' ) 1788 // .innerWidth(); 1789 1790 // slidesVisible = Math.min( 1791 // gridColumnValue, 1792 // Math.floor( containerWidth / minSlideWidth ) 1793 // ); // Recalculate slides visible 1794 // slideWidth = containerWidth / slidesVisible; 1795 // slideWidth -= itemGap; 1796 1797 // if ( $campaign_type == 'bundle_discount' ) { 1798 // slideWidth -= $sliderContainer 1799 // .find( '.revx-builder__middle_element' ) 1800 // .width(); 1801 // } 1802 1803 // $slides.css( 'width', slideWidth + 'px' ); 1804 1805 // moveToSlide( slideIndex ); 1806 // } 1807 1808 // setTimeout( () => { 1809 // updateSlideWidth(); 1810 // } ); 1811 1812 // function moveToSlide( index ) { 1813 // let tempWidth = slideWidth; 1814 // if ( $campaign_type == 'fbt' ) { 1815 // tempWidth += $sliderContainer 1816 // .find( '.revx-product-bundle' ) 1817 // .width(); 1818 // } 1819 // if ( $campaign_type == 'bundle_discount' ) { 1820 // tempWidth += $sliderContainer 1821 // .find( '.revx-builder__middle_element' ) 1822 // .width(); 1823 // } 1824 // if ( $campaign_type == 'mix_match' ) { 1825 // tempWidth += itemGap; 1826 // } 1827 // const offset = -tempWidth * index; 1828 1829 // $sliderContainer.css( { 1830 // transition: 'transform 0.5s ease-in-out', 1831 // transform: `translateX(${ offset }px)`, 1832 // } ); 1833 // } 1834 1835 // function moveToNextSlide() { 1836 // slideIndex++; 1837 1838 // if ( slideIndex > totalSlides - slidesVisible ) { 1839 // slideIndex = 0; 1840 // } 1841 1842 // moveToSlide( slideIndex ); 1843 // } 1844 1845 // function moveToPrevSlide() { 1846 // slideIndex--; 1847 1848 // if ( slideIndex < 0 ) { 1849 // slideIndex = totalSlides - slidesVisible; 1850 // } 1851 1852 // moveToSlide( slideIndex ); 1853 // } 1854 1855 // $sliderContainer 1856 // .siblings( '.revx-builderSlider-right' ) 1857 // .click( function () { 1858 // if ( ! $sliderContainer.is( ':animated' ) ) { 1859 // moveToNextSlide(); 1860 // } 1861 // } ); 1862 1863 // $sliderContainer 1864 // .siblings( '.revx-builderSlider-left' ) 1865 // .click( function () { 1866 // if ( ! $sliderContainer.is( ':animated' ) ) { 1867 // moveToPrevSlide(); 1868 // } 1869 // } ); 1870 1871 // setTimeout( () => { 1872 // // // const initialWidth = $sliderContainer.width(); 1873 // // $sliderContainer.width(containerWidth + 1); // Increase width by 1px 1874 // // $sliderContainer.width(containerWidth); // Reset to original width 1875 // $sliderContainer.parent().width( containerWidth ); // Reset to original width 1876 // $sliderContainer.parent().width( containerWidth + 1 ); // Reset to original width 1877 // $( window ).trigger( 'resize' ); // Trigger window resize 1878 // } ); 1879 1880 // $( window ).resize( function () { 1881 // updateSlideWidth(); 1882 // } ); 1883 1884 // $sliderContainer 1885 // .closest( '.revx-inpage-container' ) 1886 // .css( 'visibility', 'visible' ); 1887 // } 1888 1889 // function buxXGetYSlider() { 1890 // $( '.revx-inpage-container.revx-buyx-gety-grid' ).each( function () { 1891 // const $container = $( this ).find( 1892 // '.revx-campaign-container__wrapper' 1893 // ); 1894 // const containerElement = $container.get( 0 ); 1895 // const computedStyle = getComputedStyle( containerElement ); 1896 1897 // let gridColumnValue = parseInt( 1898 // computedStyle.getPropertyValue( '--revx-grid-column' ).trim() 1899 // ); 1900 // const minSlideWidth = 132; // 12rem in pixels (assuming 1rem = 16px) 1901 1902 // const $triggerItemContainer = $container.find( 1903 // '.revx-bxgy-trigger-items' 1904 // ); 1905 // const $offerItemContainer = $container.find( 1906 // '.revx-bxgy-offer-items' 1907 // ); 1908 1909 // let triggerItemColumn = parseInt( 1910 // getComputedStyle( $triggerItemContainer.get( 0 ) ) 1911 // .getPropertyValue( '--revx-grid-column' ) 1912 // .trim() 1913 // ); 1914 // let offerItemColumn = parseInt( 1915 // getComputedStyle( $offerItemContainer.get( 0 ) ) 1916 // .getPropertyValue( '--revx-grid-column' ) 1917 // .trim() 1918 // ); 1919 1920 // let containerWidth = $container.width(); 1921 1922 // const seperatorWidth = $container 1923 // .find( '.revx-product-bundle' ) 1924 // .width(); 1925 1926 // containerWidth -= seperatorWidth - 16; 1927 1928 // gridColumnValue = gridColumnValue ? gridColumnValue : 4; 1929 1930 // gridColumnValue = Math.min( 1931 // gridColumnValue, 1932 // Math.floor( containerWidth / minSlideWidth ) 1933 // ); 1934 // triggerItemColumn = Math.min( 1935 // $triggerItemContainer.find( '.revx-campaign-item' ).length, 1936 // triggerItemColumn 1937 // ); 1938 // offerItemColumn = Math.min( 1939 // $offerItemContainer.find( '.revx-campaign-item' ).length, 1940 // offerItemColumn 1941 // ); 1942 1943 // gridColumnValue = Math.min( 1944 // gridColumnValue, 1945 // triggerItemColumn + offerItemColumn 1946 // ); 1947 1948 // // gridColumnValue = gridColumnValue ? gridColumnValue : 4; 1949 1950 // // Ensure the total columns for trigger and offer items do not exceed the available grid columns 1951 // if ( triggerItemColumn + offerItemColumn > gridColumnValue ) { 1952 // const excessColumns = 1953 // triggerItemColumn + offerItemColumn - gridColumnValue; 1954 1955 // // Adjust columns proportionally to ensure total columns match gridColumnValue 1956 // const triggerAdjustment = Math.floor( 1957 // ( triggerItemColumn / 1958 // ( triggerItemColumn + offerItemColumn ) ) * 1959 // excessColumns 1960 // ); 1961 // const offerAdjustment = excessColumns - triggerAdjustment; 1962 1963 // triggerItemColumn -= triggerAdjustment; 1964 // offerItemColumn -= offerAdjustment; 1965 // } 1966 1967 // const slideWidth = containerWidth / gridColumnValue; 1968 1969 // initializeSubSlider( 1970 // $triggerItemContainer, 1971 // triggerItemColumn, 1972 // slideWidth, 1973 // 'trigger' 1974 // ); 1975 // initializeSubSlider( 1976 // $offerItemContainer, 1977 // offerItemColumn, 1978 // slideWidth, 1979 // 'offer' 1980 // ); 1981 1982 // $( this ).css( 'visibility', 'visible' ); 1983 // } ); 1984 // } 1985 1986 // function initializeSubSlider( 1987 // $sliderContainer, 1988 // itemColumn, 1989 // slideWidth, 1990 // type 1991 // ) {
1992 // const $container = $sliderContainer.find( '.revx-slider-container' ); 1993 // const itemGap = parseInt( 1994 // getComputedStyle( $container.get( 0 ) ) 1995 // .getPropertyValue( 'gap' ) 1996 // .trim() 1997 // ); 1998 1999 // // slideWidth -=itemGap; 2000 // slideWidth -= itemGap; 2001 // const containerWidth = itemColumn * slideWidth; 2002 // $sliderContainer.width( containerWidth ); 2003 // slideWidth -= 16; 2004 2005 // if ( type == 'offer' ) { 2006 // slideWidth += itemGap; 2007 // } 2008 2009 // $sliderContainer = $container; 2010 2011 // const $slides = $sliderContainer.find( '.revx-campaign-item' ); 2012 // $slides.css( { width: slideWidth + 'px' } ); 2013 2014 // const totalSlides = $slides.length; 2015 // let slideIndex = 0; // Start at the first slide 2016 2017 // function moveToSlide( index ) { 2018 // let tempWidth = slideWidth; 2019 // tempWidth += itemGap + 16; 2020 // tempWidth += index; 2021 2022 // if ( itemColumn == 1 ) { 2023 // tempWidth += itemGap; 2024 // } 2025 2026 // if ( type == 'offer' ) { 2027 // tempWidth -= 16; 2028 // } 2029 2030 // const offset = -tempWidth * index; 2031 2032 // $sliderContainer.css( { 2033 // transition: 'transform 0.5s ease-in-out', 2034 // transform: `translateX(${ offset }px)`, 2035 // } ); 2036 // } 2037 2038 // function moveToNextSlide() { 2039 // slideIndex++; 2040 // if ( slideIndex > totalSlides - itemColumn ) { 2041 // slideIndex = 0; 2042 // } 2043 // moveToSlide( slideIndex ); 2044 // } 2045 2046 // function moveToPrevSlide() { 2047 // slideIndex--; 2048 // if ( slideIndex < 0 ) { 2049 // slideIndex = totalSlides - itemColumn; 2050 // } 2051 // moveToSlide( slideIndex ); 2052 // } 2053 2054 // $sliderContainer 2055 // .siblings( '.revx-builderSlider-right' ) 2056 // .click( function () { 2057 // if ( ! $sliderContainer.is( ':animated' ) ) { 2058 // moveToNextSlide(); 2059 // } 2060 // } ); 2061 2062 // $sliderContainer 2063 // .siblings( '.revx-builderSlider-left' ) 2064 // .click( function () { 2065 // if ( ! $sliderContainer.is( ':animated' ) ) { 2066 // moveToPrevSlide(); 2067 // } 2068 // } ); 2069 2070 // $( window ).resize( function () { 2071 // moveToSlide( slideIndex ); 2072 // } ); 2073 2074 // moveToSlide( slideIndex ); 2075 // } 2076 2077 // buxXGetYSlider(); 2078 2079 // $( window ).resize( function () { 2080 // buxXGetYSlider(); 2081 // } ); 2082 2083 // $( 2084 // '.revx-inpage-container.revx-normal-discount-grid .revx-slider-container' 2085 // ).each( function () { 2086 // initializeSlider( 2087 // $( this ), 2088 // '.revx-campaign-view__items', 2089 // 'normal_discount' 2090 // ); 2091 // } ); 2092 // $( 2093 // '.revx-inpage-container.revx-mix-match-grid .revx-slider-container' 2094 // ).each( function () { 2095 // initializeSlider( 2096 // $( this ), 2097 // '.revx-campaign-view__items', 2098 // 'mix_match' 2099 // ); 2100 // } ); 2101 // $( 2102 // '.revx-inpage-container.revx-bundle-discount-grid .revx-slider-container' 2103 // ).each( function () { 2104 // initializeSlider( 2105 // $( this ), 2106 // '.revx-campaign-view__items', 2107 // 'bundle_discount' 2108 // ); 2109 // } ); 2110 // $( 2111 // '.revx-inpage-container.revx-frequently-bought-together-grid .revx-slider-container' 2112 // ).each( function () { 2113 // initializeSlider( $( this ), '.revx-inpage-container', 'fbt' ); 2114 // } ); 2115 2116 // $( window ).on( 'load resize', function () { 2117 // checkOverflow( '.revx-slider' ); 2118 // } ); 2119 2120 // ---------------- Slider 2121 2122 // $('.revx-ticket-type') 2123 2124 $( '.revx-ticket-type' ).change( function () { 2125 const selectedSlug = $( this ).val(); 2126 const campaignID = $( this ).data( 'campaign-id' ); 2127 const offerQty = $( this ).data( 'quantity' ); 2128 2129 const prices = $( this ).data( 'prices' ); 2130 2131 $( this ) 2132 .closest( '.revx-campaign-item' ) 2133 .find( '.revx-total-ticket-price' ) 2134 .text( 2135 `Total Price: ${ formatPrice( 2136 prices[ selectedSlug ] * offerQty 2137 ) }` 2138 ); 2139 2140 const selectedItem = $( this ) 2141 .closest( '.revx-volume-discount' ) 2142 .find( '.revx-campaign-item[data-revx-selected=true]' ); 2143 2144 if ( selectedItem.length ) { 2145 // Check if the selectedItem exists 2146 // selectedItem.data('data-selected-ticket', selectedSlug); 2147 selectedItem.attr( 'data-selected-ticket', selectedSlug ); 2148 selectedItem.attr( 2149 'data-selected-ticket-price', 2150 prices[ selectedSlug ] * offerQty 2151 ); 2152 } 2153 } ); 2154 2155 $( '.revx-ticket-type' ).trigger( 'change' ); 2156 2157 // Tooltip - Spending Goal 2158 function updateTooltipPosition( $container ) { 2159 const $tooltip = $container.find( '.revx-gift-tooltip' ); 2160 if ( ! $tooltip.length ) { 2161 return; 2162 } 2163 2164 const containerRect = $container[ 0 ].getBoundingClientRect(); 2165 const tooltipRect = $tooltip[ 0 ].getBoundingClientRect(); 2166 const windowHeight = $( window ).height(); 2167 const windowWidth = $( window ).width(); 2168 2169 // Calculate available space 2170 const spaceAbove = containerRect.top; 2171 const spaceBelow = windowHeight - containerRect.bottom; 2172 2173 // Determine position (top or bottom) 2174 const position = spaceAbove > spaceBelow ? 'top' : 'bottom';
2175 $tooltip.attr( 'data-position', position ); 2176 2177 // Handle horizontal overflow 2178 const tooltipWidth = $tooltip.outerWidth(); 2179 const containerCenterX = containerRect.left + containerRect.width / 2; 2180 const spaceLeft = containerCenterX; 2181 const spaceRight = windowWidth - containerCenterX; 2182 2183 if ( tooltipWidth / 2 > spaceLeft ) { 2184 // Not enough space on the left 2185 $tooltip.css( { 2186 left: '0', 2187 transform: 'translateX(0)', 2188 } ); 2189 } else if ( tooltipWidth / 2 > spaceRight ) { 2190 // Not enough space on the right 2191 $tooltip.css( { 2192 left: 'auto', 2193 right: '0', 2194 transform: 'translateX(0)', 2195 } ); 2196 } else { 2197 // Center aligned 2198 $tooltip.css( { 2199 // left: '50%', 2200 right: 'auto', 2201 transform: 'translateX(-50%)', 2202 } ); 2203 } 2204 } 2205 2206 // Handle mouse enter 2207 $( '.revx-step-icon-container' ).on( 'mouseenter', function () { 2208 updateTooltipPosition( $( this ) ); 2209 } ); 2210 2211 // Update positions on window resize 2212 let resizeTimer; 2213 $( window ).on( 'resize', function () { 2214 clearTimeout( resizeTimer ); 2215 resizeTimer = setTimeout( function () { 2216 $( '.revx-step-icon-container:hover' ).each( function () { 2217 updateTooltipPosition( $( this ) ); 2218 } ); 2219 }, 250 ); 2220 } ); 2221 2222 function updateGiftPosition( $container ) { 2223 const $gift = $container.find( '.revx-gift-container' ); 2224 const $giftWrapper = $container.find( '.revx-spending-gift' ); 2225 if ( ! $gift.length ) { 2226 return; 2227 } 2228 // remove the class before. otherwise top calculation is wrong. 2229 $gift.removeClass( 'revx-d-none' ); 2230 2231 const iconRect = $container[ 0 ].getBoundingClientRect(); // relative to viewport 2232 const giftRect = $gift[ 0 ].getBoundingClientRect(); 2233 2234 const giftWidth = giftRect?.width; 2235 const iconWidth = iconRect?.width; 2236 const windowWidth = window.innerWidth; 2237 const giftCenterToLeft = Math.abs( giftWidth - iconWidth ) / 2; 2238 2239 const isAbove = iconRect?.top > giftRect?.height + 10; 2240 const isRightSpace = iconRect?.left + giftCenterToLeft < windowWidth; 2241 const isLeftSpace = iconRect?.left - giftCenterToLeft >= 0; 2242 2243 const top = isAbove 2244 ? ( giftRect?.height + 8 ) * -1 2245 : iconRect?.height + 8; 2246 2247 let left = 0; 2248 if ( ! isLeftSpace && isRightSpace ) { 2249 left = -20; // small offset from left edge 2250 } else if ( isLeftSpace && ! isRightSpace ) { 2251 left = iconWidth - giftWidth; 2252 } else { 2253 // for space both left/right OR no space left/right keep center. 2254 left = giftCenterToLeft * -1; 2255 } 2256 2257 $gift.css( { 2258 position: 'fixed', 2259 top: `${ top }px`, 2260 left: `${ left }px`, 2261 zIndex: 999999, 2262 visibility: 'visible', 2263 opacity: 1, 2264 } ); 2265 $giftWrapper.css( { 2266 transformOrigin: `${ isAbove ? 'bottom' : 'top' } ${ 2267 ! isLeftSpace && isRightSpace 2268 ? 'left' 2269 : isLeftSpace && ! isRightSpace 2270 ? 'right' 2271 : 'center' 2272 }`, 2273 } ); 2274 } 2275 2276 // Stock Scarcity 2277 $( '.revx-flip-wrapper' ).each( function () { 2278 const $this = $( this ); // Define $this as the current .revx-flip-wrapper element 2279 const flipTextHeight = $this.find( '.revx-flip-text' ).outerHeight(); 2280 $this.css( 'min-height', flipTextHeight + 'px' ); 2281 } ); 2282 2283 let giftHideTimer; 2284 $( document ) 2285 .on( 'mouseenter', '.revx-progress-step-icon-container', function () { 2286 clearTimeout( giftHideTimer ); // prevent hiding 2287 updateGiftPosition( $( this ) ); 2288 } ) 2289 .on( 'mouseleave', '.revx-progress-step-icon-container', function () { 2290 const $this = $( this ); 2291 giftHideTimer = setTimeout( () => { 2292 $this 2293 .find( '.revx-gift-container' ) 2294 .addClass( 'revx-d-none' ) 2295 .css( { 2296 visibility: 'hidden', 2297 opacity: 0, 2298 left: '-250px', // to handle the overflow x case 2299 top: 'unset', 2300 } ); 2301 }, 200 ); // adjust delay as needed 2302 } ); 2303 2304 $( document ) 2305 .on( 'mouseenter', '.revx-gift-container', function () { 2306 clearTimeout( giftHideTimer ); // prevent hiding 2307 } ) 2308 .on( 'mouseleave', '.revx-gift-container', function () { 2309 $( this ).addClass( 'revx-d-none' ).css( { 2310 visibility: 'hidden', 2311 opacity: 0, 2312 left: 'unset', 2313 top: 'unset', 2314 } ); 2315 } ); 2316 2317 // Reposition gift on resize or scroll 2318 $( window ).on( 'resize scroll', function () { 2319 $( '.revx-progress-step-icon-container:hover' ).each( function () { 2320 updateGiftPosition( $( this ) ); 2321 } ); 2322 } ); 2323 2324 function adjustContentMarginTop() { 2325 // const helloBar = $( '.revx-spending-goal-top' ).length 2326 // ? $( '.revx-spending-goal-top' ) 2327 // : $( '.revx-campaign-fsb-top' ); 2328 const helloBar = $( '.revx-campaign-top' ).length 2329 ? $( '.revx-campaign-top' ) 2330 : $( '.revx-campaign-fsb-top' ); 2331 2332 // Check if hello bar exists 2333 if ( helloBar.length === 0 ) { 2334 return; 2335 } 2336 2337 const helloBarHeight = helloBar.outerHeight(); 2338 2339 // Only adjust if we have a valid height 2340 if ( helloBarHeight ) { 2341 $( 'body' ).css( 'margin-top', helloBarHeight ); 2342 2343 // Remove margin-bottom as it's causing double spacing 2344 // helloBar.css( 'margin-top', -helloBarHeight ); 2345 } 2346 } 2347 2348 adjustContentMarginTop(); 2349 function adjustContentMarginBottom() { 2350 const helloBar = $( '.revx-campaign-bottom' ).length 2351 ? $( '.revx-campaign-bottom' ) 2352 : $( '.revx-campaign-fsb-bottom' ); 2353 2354 // Check if hello bar exists 2355 if ( helloBar.length === 0 ) { 2356 return; 2357 } 2358 2359 const helloBarHeight = helloBar.outerHeight(); 2360 2361 // Only adjust if we have a valid height 2362 if ( helloBarHeight ) { 2363 $( 'body' ).css( 'margin-bottom', helloBarHeight ); 2364 2365 // Remove margin-bottom as it's causing double spacing 2366 // helloBar.css('margin-bottom', -helloBarHeight); 2367 } 2368 } 2369 2370 adjustContentMarginBottom(); 2371 2372 function showToast( message, type = 'success', duration = 3000 ) { 2373 // Check if toast container exists, otherwise create it 2374 let $toastContainer = $( '.revx-toaster-container' ); 2375 if ( $toastContainer.length === 0 ) { 2376 $toastContainer = $( '<div class="revx-toaster-container"></div>' ); 2377 $( 'body' ).append( $toastContainer ); 2378 } 2379 2380 // Determine toast class and icon based on type 2381 const toastClasses = { 2382 success: 'revx-toaster__success', 2383 warning: 'revx-toaster__warning', 2384 error: 'revx-toaster__error', 2385 }; 2386 2387 const icon = ` 2388 <svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" fill="none" viewBox="0 0 16 16" class="revx-toaster__close-icon revx-toaster__icon"> 2389 <path stroke="#fff" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2" d="m12 4-8 8M4 4l8 8"></path> 2390 </svg> 2391 `; 2392 2393 // Create a new toast element as a jQuery object 2394 const $toast = $( ` 2395 <div class="revx-toaster revx-justify-space revx-toaster-lg ${ toastClasses[ type ] }" style="display: flex;"> 2396 <div class="revx-paragraph--xs revx-align-center-xs"> 2397 ${ message }
2398 </div> 2399 <div class="revx-paragraph--xs revx-align-center"> 2400 ${ icon } 2401 </div> 2402 </div> 2403 ` ); 2404 2405 // Add close button functionality 2406 $toast.find( '.revx-toaster__close-icon' ).on( 'click', function () { 2407 $toast.fadeOut( 400, function () { 2408 $( this ).remove(); // Remove the toast from DOM 2409 } ); 2410 } ); 2411 2412 // Append the toast to the toast container 2413 $toastContainer.append( $toast ); 2414 2415 // Show the toast 2416 $toast.fadeIn( 400 ); 2417 2418 // Set timeout to hide the toast after the specified duration 2419 setTimeout( function () { 2420 if ( $toast.is( ':visible' ) ) { 2421 // Only remove if still visible 2422 $toast.fadeOut( 400, function () { 2423 $( this ).remove(); // Remove the toast from DOM 2424 } ); 2425 } 2426 }, duration ); 2427 } 2428 2429 const $drawerContainer = $( '.revx-drawer-container' ).first(); 2430 if ( $drawerContainer.length ) { 2431 // Clone the entire container including content 2432 // const $drawerContent = $drawerContainer.find( '.revx-drawer-content' ); 2433 // $drawerContent.each( function () { 2434 // this.style.setProperty( 'max-width', '0vh', 'important' ); 2435 // this.style.setProperty( 'padding', '0vh', 'important' ); 2436 // } ); 2437 2438 const $clone = $drawerContainer.clone( true, true ); 2439 const $cloneContent = $clone.find( '.revx-drawer-content' ); 2440 // Remove max-width and padding limits on drawer-content inside clone 2441 $cloneContent.each( function () { 2442 this.style.setProperty( 'max-width', 'none', 'important' ); 2443 this.style.setProperty( 2444 'padding-top', 2445 'var(--revx-drawer-padding-top)', 2446 'important' 2447 ); 2448 this.style.setProperty( 2449 'padding-right', 2450 'var(--revx-drawer-padding-right)', 2451 'important' 2452 ); 2453 this.style.setProperty( 2454 'padding-bottom', 2455 'var(--revx-drawer-padding-bottom)', 2456 'important' 2457 ); 2458 this.style.setProperty( 2459 'padding-left', 2460 'var(--revx-drawer-padding-left)', 2461 'important' 2462 ); 2463 // this.style.setProperty( 'position', 'static', 'important' ); 2464 } ); 2465 // $cloneContent.find( '*' ).each( function () { 2466 // this.style.setProperty( 'position', 'static', 'important' ); 2467 // } ); 2468 2469 $clone.css( { 2470 display: 'flex', 2471 position: 'absolute', 2472 top: '-9999px', 2473 left: '-9999px', 2474 visibility: 'hidden', 2475 bottom: 'unset', 2476 right: 'unset', 2477 } ); 2478 // Append clone to body so you can see it on top 2479 $( 'body' ).append( $clone ); 2480 const cloneHeight = $clone.innerHeight(); 2481 // const cloneHeight = $clone.outerHeight( true ); 2482 $drawerContainer.each( function () { 2483 this.style.setProperty( 'height', cloneHeight + 'px', 'important' ); 2484 this.style.setProperty( 'display', 'flex', 'important' ); 2485 } ); 2486 } 2487 2488 // Open campaign drawer 2489 $( document ).on( 'click', '.revx-drawer-opener', function () { 2490 const $container = $( this ).closest( '.revx-drawer-container' ); 2491 const $drawerContent = $container.find( '.revx-drawer-content' ); 2492 2493 // $drawerContent.each( function () { 2494 // this.style.setProperty( 'transition', 'all 0.2s', 'important' ); 2495 // } ); 2496 $drawerContent.addClass( 'revx-transition' ); 2497 2498 if ( $container.hasClass( 'revx-active' ) ) { 2499 $container.removeClass( 'revx-active' ); 2500 $container.css( { overflow: 'hidden' } ); 2501 // $drawerContent.each( function () { 2502 // this.style.setProperty( 'max-width', '0vh', 'important' ); 2503 // this.style.setProperty( 'padding', '0vh', 'important' ); 2504 // } ); 2505 } else { 2506 $container.addClass( 'revx-active' ); 2507 $container.css( { overflow: 'visible' } ); 2508 // $drawerContent.each( function () { 2509 // this.style.removeProperty( 'max-width' ); 2510 // this.style.removeProperty( 'padding' ); 2511 // } ); 2512 } 2513 } ); 2514 // Close campaign drawer 2515 $( document ).on( 'click', '.revx-drawer-closer', function () { 2516 const $container = $( this ).closest( '.revx-drawer-container' ); 2517 // const $drawerContent = $container.find( '.revx-drawer-content' ); 2518 $container.removeClass( 'revx-active' ); 2519 // $drawerContent.each( function () { 2520 // this.style.setProperty( 'max-width', '0vh', 'important' ); 2521 // this.style.setProperty( 'padding', '0vh', 'important' ); 2522 // } ); 2523 } ); 2524 2525 // Hide Campaign on Close Button Click 2526 $( document ).on( 'click', '.revx-campaign-close', function () { 2527 const campaignID = $( this ).data( 'campaign-id' ); 2528 $( `.revx-campaign-${ campaignID }` ).hide(); 2529 $( 'body' ).css( 'margin-bottom', 0 ); 2530 $( 'body' ).css( 'margin-top', 0 ); 2531 } ); 2532 2533 // Next Order Coupon Copy Clip-Borad 2534 $( '.revx-coupon-copy-btn' ).on( 'click', function () { 2535 const $btn = $( this ); 2536 2537 // // const $content = $btn.closest( '.revx-Coupon-button' ); 2538 // const text = $content 2539 // .clone() // Clone to avoid modifying original 2540 // .children() // Remove children (like .revx-coupon-copy-btn) 2541 // .remove() 2542 // .end() // Go back to cloned parent 2543 // .text() 2544 // .trim(); // Get just the raw text 2545 2546 const $content = $btn.siblings( '.revx-coupon-value' ); 2547 const text = $content.text().trim(); 2548 2549 // Fallback for browsers that do not support navigator.clipboard 2550 const tempInput = $( '<input>' ); 2551 $( 'body' ).append( tempInput ); 2552 tempInput.val( text ).select(); 2553 try { 2554 document.execCommand( 'copy' ); 2555 // $btn.text( 'Copied!' ); 2556 $( '.revx-coupon-value' ).css( 'background-color', '#008000' ); 2557 $( '.revx-coupon-value' ).css( { 2558 transition: 'background-color 0.4s', 2559 } ); 2560 2561 setTimeout( 2562 () => 2563 $( '.revx-coupon-value' ).css( 2564 'background-color', 2565 'unset' 2566 ), 2567 400 2568 ); 2569 } catch ( err ) { 2570 console.error( 'Fallback: Failed to copy:', err ); 2571 } 2572 tempInput.remove(); 2573 } ); 2574 2575 window.Revenue = { 2576 getCookie, 2577 setCookie, 2578 calculateCurrentPrice, 2579 formatPrice, 2580 updatePriceDisplay,
2581 updateMixMatchHeaderAndPrices, 2582 // fbtCalculation, 2583 // updateStyles, 2584 showToast, 2585 }; 2586 // eslint-disable-next-line no-undef 2587 2588 $( '.revx-slider-wrapper' ).each( function () { 2589 if ( ! $( this ).data( 'slider-initialized' ) ) { 2590 $( this ).revxSlider(); 2591 $( this ).data( 'slider-initialized', true ); 2592 } 2593 } ); 2594 2595 // Auto initialize on page load 2596 // function initTimers( $context ) { 2597 // $context.find( '.revx-countdown-timer' ).revxCountdownTimer(); 2598 // } 2599 2600 // $( document ).ready( function () { 2601 // initTimers( $( document ) ); 2602 2603 // // MutationObserver for dynamically added timers 2604 // const observer = new MutationObserver( function ( mutations ) { 2605 // mutations.forEach( function ( mutation ) { 2606 // $( mutation.addedNodes ).each( function () { 2607 // if ( this.nodeType === 1 ) { 2608 // const $el = $( this ); 2609 // if ( $el.is( '.revx-countdown-timer' ) ) { 2610 // $el.revxCountdownTimer(); 2611 // } else { 2612 // initTimers( $el ); 2613 // } 2614 // } 2615 // } ); 2616 // } ); 2617 // } ); 2618 2619 // observer.observe( document.body, { 2620 // childList: true, 2621 // subtree: true, 2622 // } ); 2623 // } ); 2624 2625 function getSelectedAttributes( $form ) { 2626 const selectedData = {}; 2627 $form.find( 'select[name^="attribute_"]' ).each( function () { 2628 const key = $( this ).attr( 'name' ); 2629 selectedData[ key ] = $( this ).val() || ''; 2630 } ); 2631 return selectedData; 2632 } 2633 2634 // Update data-selected-value when any attribute dropdown changes 2635 $( '.product' ).on( 'change', 'select[name^="attribute_"]', function () { 2636 const $form = $( this ).closest( '.product' ); 2637 const selectedData = getSelectedAttributes( $form ); 2638 // Update the data-selected-value attribute 2639 $form.attr( 'data-selected-value', JSON.stringify( selectedData ) ); 2640 } ); 2641} )( jQuery ); 2642 2643// move jquery code from the template1 volume discount selection. 2644jQuery( function ( $ ) { 2645 $( document ).on( 'click', '.revx-volume-discount-item', function () { 2646 const $radio = $( this ).find( '.revx-radio-wrapper' ); 2647 const $attribute = $( this ).find( '.revx-volume-attributes' ); 2648 2649 // deactivate every other option 2650 $( '.revx-radio-wrapper, .revx-volume-attributes' ) 2651 .removeClass( 'revx-active' ) 2652 .addClass( 'revx-inactive' ); 2653 2654 // activate the clicked one 2655 $radio 2656 .add( $attribute ) 2657 .addClass( 'revx-active' ) 2658 .removeClass( 'revx-inactive' ); 2659 } ); 2660} );
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.