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 // Buy X Get Y 804 $( '.revx-buyx-gety' ).on( 805 'change', 806 'input[data-name=revx_quantity]', 807 function ( e ) { 808 e.preventDefault(); 809 const parent = $( this ).closest( '.revx-campaign-container' ); 810 811 const quantity = $( this ).val(); 812 813 const campaign_id = parent.data( 'campaign-id' ); 814 815 const product_id = $( this ).data( 'product-id' ); 816 817 let offerData = $( `input[name=revx-offer-data-${ campaign_id }]` ); 818 819 offerData = offerData[ 0 ].value; 820 const jsonData = JSON.parse( offerData ); 821 822 const regularPrice = ( 823 jsonData[ product_id ].regular_price * quantity 824 ).toFixed( 2 ); 825 const salePrice = calculateCurrentPrice( 826 jsonData, 827 product_id, 828 quantity 829 ).toFixed( 2 ); 830 831 const item = $( this ).closest( '.revx-campaign-item__content' ); 832 833 item.find( '.revx-campaign-item__regular-price' ).text( 834 formatPrice( regularPrice ) 835 ); 836 item.find( '.revx-campaign-item__sale-price' ).text( 837 formatPrice( salePrice ) 838 ); 839 840 if ( regularPrice == salePrice ) { 841 if ( 842 ! item 843 .find( '.revx-campaign-item__regular-price' ) 844 .hasClass( 'revx-d-none' ) 845 ) { 846 item.find( '.revx-campaign-item__regular-price' ).addClass( 847 'revx-d-none' 848 ); 849 } 850 } else if ( 851 item 852 .find( '.revx-campaign-item__regular-price' ) 853 .hasClass( 'revx-d-none' ) 854 ) { 855 item.find( '.revx-campaign-item__regular-price' ).removeClass( 856 'revx-d-none' 857 ); 858 } 859 860 let totalRegularPrice = 0; 861 let totalSalePrice = 0; 862 863 Object.keys( jsonData ).forEach( ( pid ) => {
864 const qty = $( 865 `input[name="revx-quantity-${ campaign_id }-${ pid }"]` 866 ).val(); 867 const rp = ( jsonData[ pid ].regular_price * qty ).toFixed( 2 ); 868 const sp = parseFloat( 869 calculateCurrentPrice( jsonData, pid, qty ).toFixed( 2 ) 870 ); 871 totalRegularPrice += parseFloat( rp ); 872 totalSalePrice += parseFloat( sp ); 873 } ); 874 875 parent 876 .find( '.revx-total-price .revx-campaign-item__regular-price' ) 877 .html( formatPrice( totalRegularPrice ) ); 878 parent 879 .find( '.revx-total-price .revx-campaign-item__sale-price' ) 880 .html( formatPrice( totalSalePrice ) ); 881 } 882 ); 883 884 // Slider ----------------------- 885 886 function checkOverflow( container ) { 887 $( container ).each( function () { 888 const $this = $( this ); 889 890 const isOverflowing = 891 $this[ 0 ].scrollWidth > $this[ 0 ].offsetWidth; 892 893 if ( isOverflowing ) { 894 $( this ) 895 .find( '.revx-builderSlider-icon' ) 896 .addClass( 'revx-has-overflow' ); 897 } else { 898 $( this ) 899 .find( '.revx-builderSlider-icon' ) 900 .removeClass( 'revx-has-overflow' ); 901 } 902 } ); 903 } 904 905 $( '.revx-ticket-type' ).change( function () { 906 const selectedSlug = $( this ).val(); 907 const campaignID = $( this ).data( 'campaign-id' ); 908 const offerQty = $( this ).data( 'quantity' ); 909 910 const prices = $( this ).data( 'prices' ); 911 912 $( this ) 913 .closest( '.revx-campaign-item' ) 914 .find( '.revx-total-ticket-price' ) 915 .text( 916 `Total Price: ${ formatPrice( 917 prices[ selectedSlug ] * offerQty 918 ) }` 919 ); 920 921 const selectedItem = $( this ) 922 .closest( '.revx-volume-discount' ) 923 .find( '.revx-campaign-item[data-revx-selected=true]' ); 924 925 if ( selectedItem.length ) { 926 // Check if the selectedItem exists 927 // selectedItem.data('data-selected-ticket', selectedSlug); 928 selectedItem.attr( 'data-selected-ticket', selectedSlug ); 929 selectedItem.attr( 930 'data-selected-ticket-price', 931 prices[ selectedSlug ] * offerQty 932 ); 933 } 934 } ); 935 936 $( '.revx-ticket-type' ).trigger( 'change' ); 937 938 // Stock Scarcity 939 $( '.revx-flip-wrapper' ).each( function () { 940 const $this = $( this ); // Define $this as the current .revx-flip-wrapper element 941 const flipTextHeight = $this.find( '.revx-flip-text' ).outerHeight(); 942 $this.css( 'min-height', flipTextHeight + 'px' ); 943 } ); 944 945 function adjustContentMarginTop() { 946 const helloBar = $( '.revx-campaign-top' ).length 947 ? $( '.revx-campaign-top' ) 948 : $( '.revx-campaign-fsb-top' ); 949 950 // Check if hello bar exists 951 if ( helloBar.length === 0 ) { 952 return; 953 } 954 955 const helloBarHeight = helloBar.outerHeight(); 956 957 // Only adjust if we have a valid height 958 if ( helloBarHeight ) { 959 $( 'body' ).css( 'margin-top', helloBarHeight ); 960 961 // Remove margin-bottom as it's causing double spacing 962 // helloBar.css( 'margin-top', -helloBarHeight ); 963 } 964 } 965 966 adjustContentMarginTop(); 967 function adjustContentMarginBottom() { 968 const helloBar = $( '.revx-campaign-bottom' ).length 969 ? $( '.revx-campaign-bottom' ) 970 : $( '.revx-campaign-fsb-bottom' ); 971 972 // Check if hello bar exists 973 if ( helloBar.length === 0 ) { 974 return; 975 } 976 977 const helloBarHeight = helloBar.outerHeight(); 978 979 // Only adjust if we have a valid height 980 if ( helloBarHeight ) { 981 $( 'body' ).css( 'margin-bottom', helloBarHeight ); 982 983 // Remove margin-bottom as it's causing double spacing 984 // helloBar.css('margin-bottom', -helloBarHeight); 985 } 986 } 987 988 adjustContentMarginBottom(); 989 990 function showToast( message, type = 'success', duration = 3000 ) { 991 // Check if toast container exists, otherwise create it 992 let $toastContainer = $( '.revx-toaster-container' ); 993 if ( $toastContainer.length === 0 ) { 994 $toastContainer = $( '<div class="revx-toaster-container"></div>' ); 995 $( 'body' ).append( $toastContainer ); 996 } 997 998 // Determine toast class and icon based on type 999 const toastClasses = { 1000 success: 'revx-toaster__success', 1001 warning: 'revx-toaster__warning', 1002 error: 'revx-toaster__error', 1003 }; 1004 1005 const icon = ` 1006 <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"> 1007 <path stroke="#fff" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2" d="m12 4-8 8M4 4l8 8"></path> 1008 </svg> 1009 `; 1010 1011 // Create a new toast element as a jQuery object 1012 const $toast = $( ` 1013 <div class="revx-toaster revx-justify-space revx-toaster-lg ${ toastClasses[ type ] }" style="display: flex;"> 1014 <div class="revx-paragraph--xs revx-align-center-xs"> 1015 ${ message }
1016 </div> 1017 <div class="revx-paragraph--xs revx-align-center"> 1018 ${ icon } 1019 </div> 1020 </div> 1021 ` ); 1022 1023 // Add close button functionality 1024 $toast.find( '.revx-toaster__close-icon' ).on( 'click', function () { 1025 $toast.fadeOut( 400, function () { 1026 $( this ).remove(); // Remove the toast from DOM 1027 } ); 1028 } ); 1029 1030 // Append the toast to the toast container 1031 $toastContainer.append( $toast ); 1032 1033 // Show the toast 1034 $toast.fadeIn( 400 ); 1035 1036 // Set timeout to hide the toast after the specified duration 1037 setTimeout( function () { 1038 if ( $toast.is( ':visible' ) ) { 1039 // Only remove if still visible 1040 $toast.fadeOut( 400, function () { 1041 $( this ).remove(); // Remove the toast from DOM 1042 } ); 1043 } 1044 }, duration ); 1045 } 1046 1047 const $drawerContainer = $( '.revx-drawer-container' ).first(); 1048 if ( $drawerContainer.length ) { 1049 // Clone the entire container including content 1050 // const $drawerContent = $drawerContainer.find( '.revx-drawer-content' ); 1051 // $drawerContent.each( function () { 1052 // this.style.setProperty( 'max-width', '0vh', 'important' ); 1053 // this.style.setProperty( 'padding', '0vh', 'important' ); 1054 // } ); 1055 1056 const $clone = $drawerContainer.clone( true, true ); 1057 const $cloneContent = $clone.find( '.revx-drawer-content' ); 1058 // Remove max-width and padding limits on drawer-content inside clone 1059 $cloneContent.each( function () { 1060 this.style.setProperty( 'max-width', 'none', 'important' ); 1061 this.style.setProperty( 1062 'padding-top', 1063 'var(--revx-drawer-padding-top)', 1064 'important' 1065 ); 1066 this.style.setProperty( 1067 'padding-right', 1068 'var(--revx-drawer-padding-right)', 1069 'important' 1070 ); 1071 this.style.setProperty( 1072 'padding-bottom', 1073 'var(--revx-drawer-padding-bottom)', 1074 'important' 1075 ); 1076 this.style.setProperty( 1077 'padding-left', 1078 'var(--revx-drawer-padding-left)', 1079 'important' 1080 ); 1081 // this.style.setProperty( 'position', 'static', 'important' ); 1082 } ); 1083 // $cloneContent.find( '*' ).each( function () { 1084 // this.style.setProperty( 'position', 'static', 'important' ); 1085 // } ); 1086 1087 $clone.css( { 1088 display: 'flex', 1089 position: 'absolute', 1090 top: '-9999px', 1091 left: '-9999px', 1092 visibility: 'hidden', 1093 bottom: 'unset', 1094 right: 'unset', 1095 } ); 1096 // Append clone to body so you can see it on top 1097 $( 'body' ).append( $clone ); 1098 const cloneHeight = $clone.innerHeight(); 1099 // const cloneHeight = $clone.outerHeight( true ); 1100 $drawerContainer.each( function () { 1101 this.style.setProperty( 'height', cloneHeight + 'px', 'important' ); 1102 this.style.setProperty( 'display', 'flex', 'important' ); 1103 } ); 1104 } 1105 1106 // Open campaign drawer 1107 $( document ).on( 'click', '.revx-drawer-opener', function () { 1108 const $container = $( this ).closest( '.revx-drawer-container' ); 1109 const $drawerContent = $container.find( '.revx-drawer-content' ); 1110 1111 // $drawerContent.each( function () { 1112 // this.style.setProperty( 'transition', 'all 0.2s', 'important' ); 1113 // } ); 1114 $drawerContent.addClass( 'revx-transition' ); 1115 1116 if ( $container.hasClass( 'revx-active' ) ) { 1117 $container.removeClass( 'revx-active' ); 1118 $container.css( { overflow: 'hidden' } ); 1119 // $drawerContent.each( function () { 1120 // this.style.setProperty( 'max-width', '0vh', 'important' ); 1121 // this.style.setProperty( 'padding', '0vh', 'important' ); 1122 // } ); 1123 } else { 1124 $container.addClass( 'revx-active' ); 1125 $container.css( { overflow: 'visible' } ); 1126 // $drawerContent.each( function () { 1127 // this.style.removeProperty( 'max-width' ); 1128 // this.style.removeProperty( 'padding' ); 1129 // } ); 1130 } 1131 } ); 1132 // Close campaign drawer 1133 $( document ).on( 'click', '.revx-drawer-closer', function () { 1134 const $container = $( this ).closest( '.revx-drawer-container' ); 1135 // const $drawerContent = $container.find( '.revx-drawer-content' ); 1136 $container.removeClass( 'revx-active' ); 1137 // $drawerContent.each( function () { 1138 // this.style.setProperty( 'max-width', '0vh', 'important' ); 1139 // this.style.setProperty( 'padding', '0vh', 'important' ); 1140 // } ); 1141 } ); 1142 1143 // Hide Campaign on Close Button Click 1144 $( document ).on( 'click', '.revx-campaign-close', function () { 1145 const campaignID = $( this ).data( 'campaign-id' ); 1146 $( `.revx-campaign-${ campaignID }` ).hide(); 1147 $( 'body' ).css( 'margin-bottom', 0 ); 1148 $( 'body' ).css( 'margin-top', 0 ); 1149 } ); 1150 1151 // Next Order Coupon Copy Clip-Borad 1152 $( '.revx-coupon-copy-btn' ).on( 'click', function () { 1153 const $btn = $( this ); 1154 1155 // // const $content = $btn.closest( '.revx-Coupon-button' ); 1156 // const text = $content 1157 // .clone() // Clone to avoid modifying original 1158 // .children() // Remove children (like .revx-coupon-copy-btn) 1159 // .remove() 1160 // .end() // Go back to cloned parent 1161 // .text() 1162 // .trim(); // Get just the raw text 1163 1164 const $content = $btn.siblings( '.revx-coupon-value' ); 1165 const text = $content.text().trim(); 1166 1167 // Fallback for browsers that do not support navigator.clipboard 1168 const tempInput = $( '<input>' ); 1169 $( 'body' ).append( tempInput ); 1170 tempInput.val( text ).select(); 1171 try { 1172 document.execCommand( 'copy' ); 1173 // $btn.text( 'Copied!' ); 1174 $( '.revx-coupon-value' ).css( 'background-color', '#008000' ); 1175 $( '.revx-coupon-value' ).css( { 1176 transition: 'background-color 0.4s', 1177 } ); 1178 1179 setTimeout( 1180 () => 1181 $( '.revx-coupon-value' ).css( 1182 'background-color', 1183 'unset' 1184 ), 1185 400 1186 ); 1187 } catch ( err ) { 1188 console.error( 'Fallback: Failed to copy:', err ); 1189 } 1190 tempInput.remove(); 1191 } ); 1192 1193 window.Revenue = { 1194 getCookie, 1195 setCookie, 1196 calculateCurrentPrice, 1197 formatPrice, 1198 updatePriceDisplay, 1199 // fbtCalculation, 1200 // updateStyles, 1201 showToast, 1202 }; 1203 // eslint-disable-next-line no-undef 1204
1205 $( '.revx-slider-wrapper' ).each( function () { 1206 if ( ! $( this ).data( 'slider-initialized' ) ) { 1207 $( this ).revxSlider(); 1208 $( this ).data( 'slider-initialized', true ); 1209 } 1210 } ); 1211 1212 // Auto initialize on page load 1213 // function initTimers( $context ) { 1214 // $context.find( '.revx-countdown-timer' ).revxCountdownTimer(); 1215 // } 1216 1217 // $( document ).ready( function () { 1218 // initTimers( $( document ) ); 1219 1220 // // MutationObserver for dynamically added timers 1221 // const observer = new MutationObserver( function ( mutations ) { 1222 // mutations.forEach( function ( mutation ) { 1223 // $( mutation.addedNodes ).each( function () { 1224 // if ( this.nodeType === 1 ) { 1225 // const $el = $( this ); 1226 // if ( $el.is( '.revx-countdown-timer' ) ) { 1227 // $el.revxCountdownTimer(); 1228 // } else { 1229 // initTimers( $el ); 1230 // } 1231 // } 1232 // } ); 1233 // } ); 1234 // } ); 1235 1236 // observer.observe( document.body, { 1237 // childList: true, 1238 // subtree: true, 1239 // } ); 1240 // } ); 1241 1242 function getSelectedAttributes( $form ) { 1243 const selectedData = {}; 1244 $form.find( 'select[name^="attribute_"]' ).each( function () { 1245 const key = $( this ).attr( 'name' ); 1246 selectedData[ key ] = $( this ).val() || ''; 1247 } ); 1248 return selectedData; 1249 } 1250 1251 // Update data-selected-value when any attribute dropdown changes 1252 $( '.product' ).on( 'change', 'select[name^="attribute_"]', function () { 1253 const $form = $( this ).closest( '.product' ); 1254 const selectedData = getSelectedAttributes( $form ); 1255 // Update the data-selected-value attribute 1256 $form.attr( 'data-selected-value', JSON.stringify( selectedData ) ); 1257 } ); 1258} )( jQuery ); 1259 1260// move jquery code from the template1 volume discount selection. 1261jQuery( function ( $ ) { 1262 $( document ).on( 'click', '.revx-volume-discount-item', function () { 1263 const $radio = $( this ).find( '.revx-radio-wrapper' ); 1264 const $attribute = $( this ).find( '.revx-volume-attributes' ); 1265 1266 // deactivate every other option 1267 $( '.revx-radio-wrapper, .revx-volume-attributes' ) 1268 .removeClass( 'revx-active' ) 1269 .addClass( 'revx-inactive' ); 1270 1271 // activate the clicked one 1272 $radio 1273 .add( $attribute ) 1274 .addClass( 'revx-active' ) 1275 .removeClass( 'revx-inactive' ); 1276 } ); 1277} );
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.