PageSourceSearch

https://clarijs-fietstassen.nl/wp-content/plugins/revenue/assets/js/frontend/campaign.js?ver=2.2.14

js clarijs-fietstassen.nl collected 2026-10-01 14:03:03 UTC 76,756 bytes, 2,714 lines download raw bytes

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			let 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			// Addon: split single-attribute variation card. The card already carries the fixed
835			// variation context, so add it directly without resolving a dropdown selection.
836			const splitRaw = item.attr( 'data-split-variation' );
837			const isSplitVariation = !! splitRaw;
838			if ( isSplitVariation ) {
839				const split = JSON.parse( splitRaw );
840				productId = split.variation_id;
841				parentId = split.parent_id;
842				selectedData = split.attributes || {};
843				variationAttributes = Object.values( selectedData ).join( ' - ' );
844				variationProductDetails = {
845					regular_price: split.regular_price,
846					sale_price: split.sale_price,
847					image_url: split.thumbnail,
848				};
849				// Downstream selected-item clone + add-to-cart expect the 'variable' branch.
850				productType = 'variable';
851				// Ensure the parent name prefix resolves even though offer data omits variations.
852				if ( ! jsonData[ parentId ] ) {
853					jsonData[ parentId ] = { item_name: split.item_name };
854				}
855				jsonData[ productId ] = {
856					item_name: split.item_name,
857					regular_price: split.regular_price,
858					sale_price: split.sale_price,
859					thumbnail: split.thumbnail,
860				};
861			}
862
863			if ( productType === 'variable' && ! isSplitVariation ) {
864				const variationMap = item
865					.find( '[data-variation-map]' )
866					.data( 'variation-map' );
867				selectedData = getSelectedAttributes( item );
868				const odata = container.attr( 'data-variations' );
869				parentId = container.data( 'product-id' );
870				const a = JSON.parse( odata );
871
872				if ( ! selectedData ) {
873					// throw new Error( 'Selected data is undefined or null.' );
874					showToast(
875						revenue_campaign?.select_all_attributes ||
876							'Please select all product attributes before adding to cart.',
877						'error',
878						3000
879					);
880					return;
881				}
882
883				// Check for empty or undefined attribute values
884				for ( const [ , value ] of Object.entries( selectedData ) ) {
885					if (
886						value === '' ||
887						value === null ||
888						value === undefined
889					) {
890						showToast(
891							revenue_campaign?.select_all_attributes ||
892								'Please select all required attributes',
893							'error',
894							3000
895						);
896						return;
897					}
898				}
899
900				// now i need to match product id from variation map with selected data
901				let matchedVariationId = null;
902
903				const matchedVariation = variationMap.find( ( variation ) => {
904					if ( ! variation.attributes ) {
905						return false;
906					}
907					return Object.entries( selectedData ).every(
908						( [ key, val ] ) =>
909							! val ||
910							! variation.attributes[ key ] ||
911							variation.attributes[ key ] === val
912					);
913				} );
914
915				if ( matchedVariation ) {
916					matchedVariationId = matchedVariation.variation_id;
917					productId = matchedVariationId;
918				}
919
920				variationProductDetails = a.find( ( v ) => {
921					return parseInt( v.id ) === parseInt( matchedVariationId );
922				} );
923
924				variationAttributes = Object.entries( selectedData )
925					.filter( ( [ key ] ) => key.startsWith( 'attribute_' ) ) // only attributes
926					.map( ( [ , value ] ) => value ) // just take the value
927					.join( ' - ' ); // join with dash
928
929				jsonData[ matchedVariationId ] = {
930					item_id: parseInt( matchedVariationId ),
931					item_name: `${ jsonData[ parentId ]?.item_name || '' }${
932						variationAttributes ? ' - ' + variationAttributes : ''
933					}`,
934					thumbnail: variationProductDetails.image_url || '',
935					regular_price: variationProductDetails.regular_price || '',
936					sale_price: variationProductDetails.sale_price || '',
937					quantity: 1,
938					parent_id: parentId,
939				};
940			}
941
942			const data = {
943				id: productId,
944				productName: isSplitVariation
945					? jsonData[ productId ]?.item_name
946					: productType === 'variable'
947					? `${ jsonData[ parentId ]?.item_name || '' }${
948							variationAttributes
949								? ' - ' + variationAttributes
950								: ''
951					  }`
952					: jsonData[ productId ]?.item_name,
953				regularPrice:
954					productType === 'variable'
955						? variationProductDetails.regular_price
956						: jsonData[ productId ]?.regular_price,
957				thumbnail:
958					productType === 'variable'
959						? variationProductDetails.image_url
960						: jsonData[ productId ]?.thumbnail,
961				quantity,
962			};
963
964			const cookieName = `mix_match_${ campaignId }`;
965			let prevData = getCookie( cookieName );
966
967			let prevSelectedItems = $(
968				`input[name="revx-selected-items-${ campaignId }"]`
969			).val();
970
971			prevSelectedItems = prevSelectedItems
972				? JSON.parse( prevSelectedItems )
973				: {};
974
975			prevData = prevData ? JSON.parse( prevData ) : {};
976
977			// handles the case of initial render. when the required items are pre-selected and not in cookie,
978			// merge the cookie data with pre selected data and then update accordingly.
979			if ( Object.keys( prevSelectedItems ).length !== 0 ) {
980				for ( const itemId in prevSelectedItems ) {
981					if ( ! prevData[ itemId ] ) {
982						prevData[ itemId ] = prevSelectedItems[ itemId ];
983					}
984				}
985			}
986			let clonedItem;
987
988			if ( prevData[ productId ] ) {
989				prevData[ productId ].quantity =
990					parseInt( prevData[ productId ].quantity ) +
991					parseInt( quantity );
992
993				$(
994					`.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id=${ productId }] .revx-selected-item__product-price .woocommerce-Price-amount`
995				).text( `${ formatPrice( data.regularPrice ) }` );
996				$(
997					`.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id=${ productId }] .revx-selected-item__product-price .revx-qty`
998				).text( `(x ${ prevData[ productId ].quantity })` );
999			} else {
1000				const selectedContainer = $(
1001					`.revx-campaign-${ campaignId } .revx-selected-container`
1002				);
1003
1004				selectedContainer.removeClass( 'revx-d-none' );
1005				selectedContainer.removeClass( 'revx-empty-selected-items' );
1006
1007				$(
1008					`.revx-campaign-${ campaignId } .revx-empty-mix-match`
1009				).addClass( 'revx-d-none' );
1010
1011				prevData[ productId ] = data;
1012
1013				const placeholderItem = $(
1014					`.revx-selected-item.revx-d-none[data-campaign-id=${ campaignId }]`
1015				).first(); // ensure only one placeholder.
1016				clonedItem = placeholderItem.clone();
1017				clonedItem
1018					.find( '.revx-selected-title' )
1019					.html( data.productName );
1020				clonedItem
1021					.find( '.revx-campaign-item__image img' )
1022					.attr( 'src', data.thumbnail );
1023				clonedItem
1024					.find( '.revx-campaign-item__image img' )
1025					.attr( 'alt', data.productName );
1026				clonedItem
1027					.find(
1028						'.revx-selected-item__product-price .revx-price-placeholder'
1029					)
1030					.text( `${ formatPrice( data.regularPrice ) }` );
1031				clonedItem
1032					.find( '.revx-selected-item__product-price .revx-qty' )
1033					.text( `(x ${ quantity })` );
1034				clonedItem.removeClass( 'revx-d-none' );
1035				clonedItem.attr( 'data-product-id', productId );
1036				if ( productType === 'variable' ) {
1037					clonedItem.attr(
1038						'data-selected-attribute',
1039						JSON.stringify( selectedData )
1040					);
1041				}
1042				clonedItem.attr( 'data-parent-id', parentId );
1043				clonedItem.attr( 'data-product-type', productType );
1044				placeholderItem.before( clonedItem );
1045			}
1046
1047			$( `input[name="revx-selected-items-${ campaignId }"]` ).val(
1048				JSON.stringify( prevData )
1049			);
1050
1051			setCookie( cookieName, JSON.stringify( prevData ), 7 );
1052
1053			updateMixMatchHeaderAndPrices( campaignId, prevData, jsonQtyData );
1054
1055			$( this )
1056				.parent()
1057				.find( `input[data-name="revx_quantity"]` )
1058				.val( 1 );
1059			// make the reset button visible after adding any product
1060			$( this )
1061				.closest( '.revx-template' )
1062				.find( '[data-mix-match-reset-btn]' )
1063				.removeClass( 'revx-d-none' );
1064		}
1065	);
1066
1067	function removeMixMatchSelectedItem() {
1068		const productId = $( this )
1069			.closest( '[data-product-id]' )
1070			.data( 'product-id' );
1071		const campaignId = $( this )
1072			.closest( '[data-campaign-id]' )
1073			.data( 'campaign-id' );
1074		const item = $(
1075			`.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id="${ productId }"]`
1076		);
1077
1078		item.remove();
1079
1080		const cookieName = `mix_match_${ campaignId }`;
1081		let prevData = getCookie( cookieName );
1082		let prevSelectedItems = $(
1083			`input[name=revx-selected-items-${ campaignId }]`
1084		).val();
1085		prevSelectedItems = prevSelectedItems
1086			? JSON.parse( prevSelectedItems )
1087			: {};
1088
1089		prevData = prevData ? JSON.parse( prevData ) : {};
1090
1091		// handles the case of initial render, when the required items are pre-selected,
1092		// merge the cookie data with pre selected data and then update accordingly.
1093		if ( Object.keys( prevSelectedItems ).length !== 0 ) {
1094			for ( const itemId in prevSelectedItems ) {
1095				if ( ! prevData[ itemId ] ) {
1096					prevData[ itemId ] = prevSelectedItems[ itemId ];
1097				}
1098			}
1099		}
1100
1101		delete prevData[ productId ];
1102		setCookie( cookieName, JSON.stringify( prevData ), 7 );
1103		$( `input[name=revx-selected-items-${ campaignId }]` ).val(
1104			JSON.stringify( prevData )
1105		);
1106
1107		const qtyData = $( `input[name=revx-qty-data-${ campaignId }]` ).val();
1108		const jsonQtyData = JSON.parse( qtyData );
1109
1110		if ( Object.keys( prevData ).length === 0 ) {
1111			$(
1112				`.revx-campaign-${ campaignId } .revx-empty-selected-products`
1113			).removeClass( 'revx-d-none' );
1114			$(
1115				`.revx-campaign-${ campaignId } .revx-selected-product-container`
1116			).addClass( 'revx-empty-selected-items' );
1117			$(
1118				`.revx-campaign-${ campaignId } .revx-empty-mix-match`
1119			).removeClass( 'revx-d-none' );
1120
1121			$(
1122				`.revx-campaign-${ campaignId } [data-mix-match-reset-btn]`
1123			).addClass( 'revx-d-none' );
1124		}
1125
1126		updateMixMatchHeaderAndPrices( campaignId, prevData, jsonQtyData );
1127	}
1128
1129	$( '[data-container-level="mix_match_file"]' ).on(
1130		'click',
1131		'.revx-selected-remove',
1132		removeMixMatchSelectedItem
1133	);
1134
1135	// refactor later to make DRY
1136	function resetMixMatchSelectedItem() {
1137		const campaignId = $( this )
1138			.closest( '[data-campaign-id]' )
1139			.data( 'campaign-id' );
1140
1141		const cookieName = `mix_match_${ campaignId }`;
1142
1143		let prevSelectedItems = $(
1144			`input[name=revx-selected-items-${ campaignId }]`
1145		).val();
1146		prevSelectedItems = prevSelectedItems
1147			? JSON.parse( prevSelectedItems )
1148			: {};
1149
1150		for ( const itemId in prevSelectedItems ) {
1151			const $item = $( this )
1152				.closest( '[revx-campaign-id]' )
1153				.find(
1154					`.revx-selected-item[data-campaign-id=${ campaignId }][data-product-id="${ itemId }"]`
1155				);
1156			if ( prevSelectedItems[ itemId ].is_required === 'yes' ) {
1157				prevSelectedItems[ itemId ].quantity = 1;
1158				$item.find( '.revx-qty' ).text( '(x 1)' );
1159			} else {
1160				$item.remove();
1161				delete prevSelectedItems[ itemId ];
1162			}
1163		}
1164		setCookie( cookieName, JSON.stringify( prevSelectedItems ), 7 );
1165		$( `input[name=revx-selected-items-${ campaignId }]` ).val(
1166			JSON.stringify( prevSelectedItems )
1167		);
1168
1169		const qtyData = $( `input[name=revx-qty-data-${ campaignId }]` ).val();
1170		const jsonQtyData = JSON.parse( qtyData );
1171
1172		if ( Object.keys( prevSelectedItems ).length === 0 ) {
1173			$(
1174				`.revx-campaign-${ campaignId } .revx-empty-selected-products`
1175			).removeClass( 'revx-d-none' );
1176			$(
1177				`.revx-campaign-${ campaignId } .revx-selected-product-container`
1178			).addClass( 'revx-empty-selected-items' );
1179			$(
1180				`.revx-campaign-${ campaignId } .revx-empty-mix-match`
1181			).removeClass( 'revx-d-none' );
1182			$( this ).addClass( 'revx-d-none' );
1183		}
1184
1185		updateMixMatchHeaderAndPrices(
1186			campaignId,
1187			prevSelectedItems,
1188			jsonQtyData
1189		);
1190	}
1191	$( '[data-container-level="mix_match_file"]' ).on(
1192		'click',
1193		'[data-mix-match-reset-btn]',
1194		resetMixMatchSelectedItem
1195	);
1196
1197	function setTierState( $tier, tierClass, checkboxClass, isChecked ) {
1198		$tier.addClass( tierClass.add ).removeClass( tierClass.remove );
1199		const $checkboxContainer = $tier.find( '.revx-checkbox-container' );
1200		$checkboxContainer
1201			.addClass( checkboxClass.add )
1202			.removeClass( checkboxClass.remove )
1203			.attr( 'data-is-checked', isChecked ? 'yes' : 'no' )
1204			.css( 'display', isChecked ? '' : 'none' ); // Ensure visibility based on state
1205	}
1206
1207	function updateMixMatchHeaderAndPrices(
1208		campaignId,
1209		prevData,
1210		jsonQtyData = {}
1211	) {
1212		const header = $(
1213			`.revx-campaign-${ campaignId } .revx-price-container`
1214		);
1215		const itemCounts = Object.keys( prevData ).length;
1216		// Eligibility basis: 'total' sums offered-product quantities, otherwise count distinct products.
1217		const countMode =
1218			typeof revenue_campaign !== 'undefined' &&
1219			revenue_campaign.mix_match_count_mode === 'total'
1220				? 'total'
1221				: 'unique';
1222		const eligibleQty =
1223			countMode === 'total'
1224				? Object.values( prevData ).reduce(
1225						( sum, i ) => sum + parseInt( i.quantity ),
1226						0
1227				  )
1228				: itemCounts;
1229		let $selectedTier = null;
1230		$( '.revx-tier-button' ).each( function () {
1231			// Extract the item count from the mix-match-title text
1232			const titleText = $( this )
1233				.find( '.revx-mix-match-title' )
1234				.text()
1235				.trim();
1236			const itemCount = parseInt( titleText.split( ' ' )[ 0 ], 10 ); // Extract number from "X item"
1237
1238			if ( eligibleQty >= itemCount ) {
1239				$selectedTier = $( this );
1240			}
1241		} );
1242		const selectedClass = {
1243			tierClass: {
1244				add: 'revx-tier-selected',
1245				remove: 'revx-tier-regular',
1246			},
1247			checkboxClass: {
1248				add: 'revx-active',
1249				remove: 'revx-inactive revx-d-none',
1250			},
1251		};
1252		const unselectedClass = {
1253			tierClass: {
1254				add: 'revx-tier-regular',
1255				remove: 'revx-tier-selected',
1256			},
1257			checkboxClass: {
1258				add: 'revx-inactive revx-d-none',
1259				remove: 'revx-active',
1260			},
1261		};
1262		// Update tier button styles based on selection
1263		$( '.revx-tier-button' ).each( function () {
1264			if ( $selectedTier && $( this ).is( $selectedTier ) ) {
1265				setTierState(
1266					$( this ),
1267					selectedClass.tierClass,
1268					selectedClass.checkboxClass,
1269					true
1270				);
1271			} else {
1272				setTierState(
1273					$( this ),
1274					unselectedClass.tierClass,
1275					unselectedClass.checkboxClass,
1276					false
1277				);
1278			}
1279		} );
1280		const qtyData = $( `input[name=revx-qty-data-${ campaignId }]` ).val();
1281		jsonQtyData = qtyData ? JSON.parse( qtyData ) : [];
1282
1283		header.toggleClass( 'revx-d-none', ! itemCounts ); // remove none if more than 0, adds none when item count is 0
1284
1285		// Addon: hide the whole footer (selected list + total + add to cart) until a product is added.
1286		if (
1287			typeof revenue_campaign !== 'undefined' &&
1288			revenue_campaign.mix_match_hide_footer_until_selected
1289		) {
1290			$( `.revx-mixmatch-footer[revx-campaign-id="${ campaignId }"]` ).toggleClass(
1291				'revx-d-none',
1292				! itemCounts
1293			);
1294		}
1295
1296		// const addToCart = $(`.revx-campaign-add-to-cart-btn[data-campaign-id=${campaign_id}]`);
1297		// if(item_counts==0 && !addToCart.hasClass('revx-d-none') ) {
1298		//     $(`.revx-campaign-add-to-cart-btn[data-campaign-id=${campaign_id}]`).addClass('revx-d-none');
1299		// } else if(item_counts>0) {
1300		//     $(`.revx-campaign-add-to-cart-btn[data-campaign-id=${campaign_id}]`).removeClass('revx-d-none');
1301		// }
1302		header.find( '.revx-selected-product-count' ).html( eligibleQty );
1303
1304		let totalRegularPrice = 0;
1305		let totalSalePrice = 0;
1306		let totalQuantity = 0;
1307		Object.values( prevData ).forEach( ( item ) => {
1308			totalRegularPrice +=
1309				parseFloat( item.regularPrice ) * parseInt( item.quantity );
1310			totalQuantity += parseInt( item.quantity );
1311		} );
1312
1313		let selectedIndex = -1;
1314		jsonQtyData.forEach( ( item, idx ) => {
1315			if ( eligibleQty >= item.quantity ) {
1316				selectedIndex = idx;
1317
1318				switch ( item.type ) {
1319					case 'percentage':
1320						totalSalePrice =
1321							totalRegularPrice * ( 1 - item.value / 100 );
1322						break;
1323					case 'fixed_discount':
1324						totalSalePrice = Math.max(
1325							0,
1326							parseFloat( totalRegularPrice ) -
1327								parseFloat( item.value * totalQuantity )
1328						);
1329
1330						break;
1331					case 'no_discount':
1332						totalSalePrice = totalRegularPrice;
1333
1334						break;
1335					case 'fixed_price':
1336						totalSalePrice = item.value * totalQuantity;
1337						break;
1338					default:
1339						break;
1340				}
1341			}
1342		} );
1343		// make display none for no_discount, other cases, remove the class
1344		header
1345			.find( '.revx-product-old-price' )
1346			.toggleClass(
1347				'revx-d-none',
1348				jsonQtyData[ selectedIndex ]?.type === 'no_discount' ||
1349					totalSalePrice === 0
1350			);
1351		const that = $(
1352			`.revx-campaign-${ campaignId } .revx-mixmatch-quantity`
1353		);
1354		that.each( function () {
1355			const item = $( this ).find( '.revx-mixmatch-regular-quantity' );
1356
1357			$( item )
1358				.find( '.revx-checkbox-container' )
1359				.addClass( 'revx-d-none' );
1360		} );
1361
1362		const clickedItem = that.find( `div[data-index=${ selectedIndex }]` );
1363		$( clickedItem )
1364			.find( '.revx-checkbox-container' )
1365			.removeClass( 'revx-d-none' );
1366		if ( totalSalePrice === 0 ) {
1367			header
1368				.find( '.revx-campaign-item__sale-price' )
1369				.html( formatPrice( totalRegularPrice ) );
1370		} else {
1371			if (
1372				totalSalePrice &&
1373				header
1374					.find( '.revx-campaign-item__sale-price' )
1375					.hasClass( 'revx-d-none' )
1376			) {
1377				header
1378					.find( '.revx-campaign-item__sale-price' )
1379					.removeClass( 'revx-d-none' );
1380			}
1381			if (
1382				totalRegularPrice &&
1383				header
1384					.find( '.revx-campaign-item__regular-price' )
1385					.hasClass( 'revx-d-none' )
1386			) {
1387				header
1388					.find( '.revx-campaign-item__regular-price' )
1389					.removeClass( 'revx-d-none' );
1390			}
1391
1392			header
1393				.find( '.revx-campaign-item__sale-price' )
1394				.html( formatPrice( totalSalePrice ) );
1395			header
1396				.find( '.revx-product-old-price' )
1397				.html( formatPrice( totalRegularPrice ) );
1398		}
1399	}
1400
1401	// old fbt - available for old campaigns in v1
1402	// Frequently Bought Together
1403	// Function to update the styles based on selection
1404	// function updateStyles( $checkbox, selected ) {
1405	// 	const selectedStyles = $checkbox.data( 'selected-style' );
1406	// 	const defaultStyles = $checkbox.data( 'default-style' );
1407	// 	if ( selected ) {
1408	// 		$checkbox.attr( 'style', selectedStyles );
1409	// 	} else {
1410	// 		$checkbox.attr( 'style', defaultStyles );
1411	// 	}
1412	// }
1413	// $( '.revx-frequently-bought-together' ).on(
1414	// 	'click',
1415	// 	'.revx-item-options .revx-item-option',
1416	// 	function ( e ) {
1417	// 		e.preventDefault();
1418
1419	// 		const $this = $( this );
1420	// 		if ( $this.hasClass( 'revx-item-required' ) ) {
1421	// 			return;
1422	// 		}
1423	// 		const $checkbox = $this.find( '.revx-builder-checkbox' );
1424
1425	// 		const parent = $this.closest( '.revx-campaign-container__wrapper' );
1426
1427	// 		const campaign_id = parent.data( 'campaign-id' );
1428	// 		const cookieName = `campaign_${ campaign_id }`;
1429	// 		let selectedProducts = getCookie( cookieName );
1430	// 		let prevSelectedItems = $(
1431	// 			`input[name=revx-fbt-selected-items-${ campaign_id }]`
1432	// 		).val();
1433	// 		prevSelectedItems = prevSelectedItems
1434	// 			? JSON.parse( prevSelectedItems )
1435	// 			: {};
1436	// 		selectedProducts = selectedProducts
1437	// 			? JSON.parse( selectedProducts )
1438	// 			: {};
1439	// 		if ( Object.keys( selectedProducts ) == 0 ) {
1440	// 			selectedProducts = { ...prevSelectedItems };
1441	// 		}
1442
1443	// 		const productId = $this.data( 'product-id' );
1444
1445	// 		// Toggle the selected state
1446	// 		if ( selectedProducts[ productId ] ) {
1447	// 			// selectedProducts = selectedProducts.filter(id => id !== productId);
1448	// 			delete selectedProducts[ productId ];
1449	// 			updateStyles( $checkbox, false );
1450	// 		} else {
1451	// 			const quantityInput =
1452	// 				$(
1453	// 					`input[name="revx-quantity-${ campaign_id }-${ productId }"]`
1454	// 				).val() ?? $this.data( 'min-quantity' );
1455
1456	// 			selectedProducts[ productId ] = quantityInput;
1457	// 			updateStyles( $checkbox, true );
1458	// 		}
1459	// 		$( `input[name="revx-fbt-selected-items-${ campaign_id }"]` ).val(
1460	// 			JSON.stringify( selectedProducts )
1461	// 		);
1462
1463	// 		// Update the cookie
1464	// 		setCookie( cookieName, JSON.stringify( selectedProducts ), 1 );
1465
1466	// 		fbtCalculation( parent, campaign_id );
1467	// 	}
1468	// );
1469
1470	// const fbtCalculation = ( parent, campaign_id ) => {
1471	// 	const cookieName = `campaign_${ campaign_id }`;
1472	// 	let selectedProducts = getCookie( cookieName );
1473	// 	let prevSelectedItems = $(
1474	// 		`input[name=revx-fbt-selected-items-${ campaign_id }]`
1475	// 	).val();
1476	// 	prevSelectedItems = prevSelectedItems
1477	// 		? JSON.parse( prevSelectedItems )
1478	// 		: {};
1479	// 	selectedProducts = selectedProducts
1480	// 		? JSON.parse( selectedProducts )
1481	// 		: {};
1482	// 	if ( Object.keys( selectedProducts ) == 0 ) {
1483	// 		selectedProducts = { ...prevSelectedItems };
1484	// 	}
1485
1486	// 	const calculateSalePrice = ( data, qty = 1 ) => {
1487	// 		if ( ! data?.type ) {
1488	// 			return data.regular_price * qty;
1489	// 		}
1490	// 		let total = 0;
1491	// 		switch ( data.type ) {
1492	// 			case 'percentage':
1493	// 				total =
1494	// 					parseFloat( data.regular_price ) *
1495	// 					( 1 - data.value / 100 );
1496
1497	// 				break;
1498	// 			case 'amount':
1499	// 			case 'fixed_discount':
1500	// 				total = Math.max(
1501	// 					0,
1502	// 					parseFloat( data.regular_price ) -
1503	// 						parseFloat( data.value )
1504	// 				);
1505
1506	// 				break;
1507	// 			case 'fixed_price':
1508	// 				total = parseFloat( data.value );
1509
1510	// 				break;
1511	// 			case 'no_discount':
1512	// 				total = parseFloat( data.regular_price );
1513
1514	// 				break;
1515	// 			case 'free':
1516	// 				total = 0;
1517
1518	// 				break;
1519
1520	// 			default:
1521	// 				break;
1522	// 		}
1523
1524	// 		return parseFloat( total ) * parseInt( qty );
1525	// 	};
1526	// 	let offerData = $(
1527	// 		`input[name=revx-offer-data-${ campaign_id }]`
1528	// 	).val();
1529
1530	// 	offerData = JSON.parse( offerData );
1531
1532	// 	let totalRegularPrice = 0;
1533	// 	let totalSalePrice = 0;
1534
1535	// 	Object.keys( selectedProducts ).forEach( ( id ) => {
1536	// 		totalRegularPrice +=
1537	// 			parseFloat( offerData[ id ]?.regular_price ) *
1538	// 			parseInt( selectedProducts[ id ] );
1539	// 		totalSalePrice += parseFloat(
1540	// 			calculateSalePrice(
1541	// 				offerData[ id ],
1542	// 				parseInt( selectedProducts[ id ] )
1543	// 			)
1544	// 		);
1545	// 	} );
1546
1547	// 	if ( totalRegularPrice != totalSalePrice ) {
1548	// 		parent
1549	// 			.find(
1550	// 				`.revx-triggerProduct .revx-campaign-item__regular-price`
1551	// 			)
1552	// 			.html( formatPrice( totalRegularPrice ) );
1553	// 		parent
1554	// 			.find( `.revx-triggerProduct .revx-campaign-item__sale-price` )
1555	// 			.html( formatPrice( totalSalePrice ) );
1556	// 	} else {
1557	// 		parent
1558	// 			.find( `.revx-triggerProduct .revx-campaign-item__sale-price` )
1559	// 			.html( formatPrice( totalSalePrice ) );
1560	// 		parent
1561	// 			.find(
1562	// 				`.revx-triggerProduct .revx-campaign-item__regular-price`
1563	// 			)
1564	// 			.html( '' );
1565	// 	}
1566	// 	parent
1567	// 		.find( `.revx-triggerProduct .revx-selected-product-count` )
1568	// 		.html( Object.keys( selectedProducts ).length );
1569	// };
1570
1571	// $( '.revx-frequently-bought-together' ).on(
1572	// 	'change',
1573	// 	'input[data-name=revx_quantity]',
1574	// 	function ( e ) {
1575	// 		e.preventDefault();
1576	// 		const parent = $( this ).closest(
1577	// 			'.revx-campaign-container__wrapper'
1578	// 		);
1579
1580	// 		const quantity = $( this ).val();
1581
1582	// 		const campaign_id = parent.data( 'campaign-id' );
1583
1584	// 		// addFbtRequiredProductsIfNotAdded(campaign_id,false);
1585	// 		const product_id = $( this ).data( 'product-id' );
1586	// 		const cookieName = `campaign_${ campaign_id }`;
1587
1588	// 		let selectedProducts = getCookie( cookieName );
1589	// 		let prevSelectedItems = $(
1590	// 			`input[name=revx-fbt-selected-items-${ campaign_id }]`
1591	// 		).val();
1592	// 		prevSelectedItems = prevSelectedItems
1593	// 			? JSON.parse( prevSelectedItems )
1594	// 			: {};
1595	// 		selectedProducts = selectedProducts
1596	// 			? JSON.parse( selectedProducts )
1597	// 			: {};
1598	// 		if ( Object.keys( selectedProducts ) == 0 ) {
1599	// 			selectedProducts = { ...prevSelectedItems };
1600	// 		}
1601
1602	// 		if ( selectedProducts[ product_id ] ) {
1603	// 			selectedProducts[ product_id ] = quantity;
1604
1605	// 			setCookie( cookieName, JSON.stringify( selectedProducts ), 1 );
1606	// 			fbtCalculation( parent, campaign_id );
1607	// 		}
1608
1609	// 		$( `input[name=revx-fbt-selected-items-${ campaign_id }]` ).val(
1610	// 			JSON.stringify( selectedProducts )
1611	// 		);
1612
1613	// 		const calculateSalePrice = ( data, qty = 1 ) => {
1614	// 			if ( ! data?.type ) {
1615	// 				return data.regular_price * qty;
1616	// 			}
1617	// 			let total = 0;
1618	// 			switch ( data.type ) {
1619	// 				case 'percentage':
1620	// 					total =
1621	// 						parseFloat( data.regular_price ) *
1622	// 						( 1 - data.value / 100 );
1623
1624	// 					break;
1625	// 				case 'amount':
1626	// 				case 'fixed_discount':
1627	// 					total = Math.max(
1628	// 						0,
1629	// 						parseFloat( data.regular_price ) -
1630	// 							parseFloat( data.value )
1631	// 					);
1632
1633	// 					break;
1634	// 				case 'fixed_price':
1635	// 					total = parseFloat( data.value );
1636
1637	// 					break;
1638	// 				case 'no_discount':
1639	// 					total = parseFloat( data.regular_price );
1640
1641	// 					break;
1642	// 				case 'free':
1643	// 					total = 0;
1644
1645	// 					break;
1646
1647	// 				default:
1648	// 					break;
1649	// 			}
1650
1651	// 			return parseFloat( total ) * parseInt( qty );
1652	// 		};
1653	// 		let offerData = $( `input[name=revx-offer-data-${ campaign_id }]` );
1654
1655	// 		offerData = offerData[ 0 ].value;
1656	// 		const jsonData = JSON.parse( offerData );
1657
1658	// 		const salePrice = calculateSalePrice(
1659	// 			jsonData[ product_id ],
1660	// 			quantity
1661	// 		).toFixed( 2 );
1662
1663	// 		const inRP = jsonData[ product_id ].regular_price;
1664
1665	// 		const inSP = ( salePrice / quantity ).toFixed( 2 );
1666
1667	// 		const itemParent = $( this ).closest( '.revx-campaign-item' );
1668
1669	// 		updatePriceDisplay( itemParent, quantity, inSP, inRP );
1670	// 	}
1671	// );
1672
1673	// Buy X Get Y
1674	$( '.revx-buyx-gety' ).on(
1675		'change',
1676		'input[data-name=revx_quantity]',
1677		function ( e ) {
1678			e.preventDefault();
1679			const parent = $( this ).closest( '.revx-campaign-container' );
1680
1681			const quantity = $( this ).val();
1682
1683			const campaign_id = parent.data( 'campaign-id' );
1684
1685			const product_id = $( this ).data( 'product-id' );
1686
1687			let offerData = $( `input[name=revx-offer-data-${ campaign_id }]` );
1688
1689			offerData = offerData[ 0 ].value;
1690			const jsonData = JSON.parse( offerData );
1691
1692			const regularPrice = (
1693				jsonData[ product_id ].regular_price * quantity
1694			).toFixed( 2 );
1695			const salePrice = calculateCurrentPrice(
1696				jsonData,
1697				product_id,
1698				quantity
1699			).toFixed( 2 );
1700
1701			const item = $( this ).closest( '.revx-campaign-item__content' );
1702
1703			item.find( '.revx-campaign-item__regular-price' ).text(
1704				formatPrice( regularPrice )
1705			);
1706			item.find( '.revx-campaign-item__sale-price' ).text(
1707				formatPrice( salePrice )
1708			);
1709
1710			if ( regularPrice == salePrice ) {
1711				if (
1712					! item
1713						.find( '.revx-campaign-item__regular-price' )
1714						.hasClass( 'revx-d-none' )
1715				) {
1716					item.find( '.revx-campaign-item__regular-price' ).addClass(
1717						'revx-d-none'
1718					);
1719				}
1720			} else if (
1721				item
1722					.find( '.revx-campaign-item__regular-price' )
1723					.hasClass( 'revx-d-none' )
1724			) {
1725				item.find( '.revx-campaign-item__regular-price' ).removeClass(
1726					'revx-d-none'
1727				);
1728			}
1729
1730			let totalRegularPrice = 0;
1731			let totalSalePrice = 0;
1732
1733			Object.keys( jsonData ).forEach( ( pid ) => {
1734				const qty = $(
1735					`input[name="revx-quantity-${ campaign_id }-${ pid }"]`
1736				).val();
1737				const rp = ( jsonData[ pid ].regular_price * qty ).toFixed( 2 );
1738				const sp = parseFloat(
1739					calculateCurrentPrice( jsonData, pid, qty ).toFixed( 2 )
1740				);
1741				totalRegularPrice += parseFloat( rp );
1742				totalSalePrice += parseFloat( sp );
1743			} );
1744
1745			parent
1746				.find( '.revx-total-price .revx-campaign-item__regular-price' )
1747				.html( formatPrice( totalRegularPrice ) );
1748			parent
1749				.find( '.revx-total-price .revx-campaign-item__sale-price' )
1750				.html( formatPrice( totalSalePrice ) );
1751		}
1752	);
1753
1754	// Slider -----------------------
1755
1756	function checkOverflow( container ) {
1757		$( container ).each( function () {
1758			const $this = $( this );
1759
1760			const isOverflowing =
1761				$this[ 0 ].scrollWidth > $this[ 0 ].offsetWidth;
1762
1763			if ( isOverflowing ) {
1764				$( this )
1765					.find( '.revx-builderSlider-icon' )
1766					.addClass( 'revx-has-overflow' );
1767			} else {
1768				$( this )
1769					.find( '.revx-builderSlider-icon' )
1770					.removeClass( 'revx-has-overflow' );
1771			}
1772		} );
1773	}
1774
1775	// function initializeSlider(
1776	// 	$sliderContainer,
1777	// 	$containerSelector = '.revx-inpage-container',
1778	// 	$campaign_type = ''
1779	// ) {
1780	// 	const $container = $sliderContainer.closest( $containerSelector );
1781	// 	const containerElement = $container.get( 0 );
1782	// 	const computedStyle = getComputedStyle( containerElement );
1783	// 	const gridColumnValue = computedStyle
1784	// 		.getPropertyValue( '--revx-grid-column' )
1785	// 		.trim();
1786	// 	let itemGap = parseInt(
1787	// 		computedStyle.getPropertyValue( 'gap' ).trim()
1788	// 	);
1789
1790	// 	if ( ! itemGap ) {
1791	// 		itemGap = 16;
1792	// 	}
1793
1794	// 	const $slides = $sliderContainer.find( '.revx-campaign-item' );
1795	// 	const minSlideWidth = 100; // 12rem in pixels (assuming 1rem = 16px)
1796
1797	// 	let containerWidth = $sliderContainer.parent().width();
1798
1799	// 	if ( $campaign_type == 'mix_match' ) {
1800	// 		containerWidth = $sliderContainer
1801	// 			.closest( '.revx-slider-items-wrapper' )
1802	// 			.innerWidth();
1803	// 	}
1804	// 	if ( $campaign_type == 'bundle_discount' ) {
1805	// 		containerWidth = $sliderContainer
1806	// 			.closest( '.revx-slider-items-wrapper' )
1807	// 			.outerWidth();
1808	// 		itemGap = 0;
1809	// 	}
1810	// 	if ( $campaign_type == 'fbt' ) {
1811	// 		containerWidth = $container
1812	// 			.find( '.revx-slider-items-wrapper' )
1813	// 			.innerWidth();
1814	// 	}
1815	// 	if ( $campaign_type == 'normal_discount' ) {
1816	// 		containerWidth = $container
1817	// 			.closest( '.revx-slider-items-wrapper' )
1818	// 			.innerWidth();
1819	// 		itemGap = 0;
1820	// 	}
1821
1822	// 	let slidesVisible = Math.min(
1823	// 		gridColumnValue,
1824	// 		Math.floor( containerWidth / minSlideWidth )
1825	// 	); // Calculate initial slides visible
1826
1827	// 	let slideWidth = containerWidth / slidesVisible;
1828	// 	slideWidth -= itemGap;
1829
1830	// 	if ( $campaign_type == 'bundle_discount' ) {
1831	// 		slideWidth -= $container
1832	// 			.find( '.revx-builder__middle_element' )
1833	// 			.width();
1834	// 	}
1835
1836	// 	const totalSlides = $slides.length;
1837	// 	let slideIndex = 0;
1838
1839	// 	function updateSlideWidth() {
1840	// 		containerWidth = $sliderContainer
1841	// 			.closest( '.revx-slider-items-wrapper' )
1842	// 			.innerWidth();
1843
1844	// 		slidesVisible = Math.min(
1845	// 			gridColumnValue,
1846	// 			Math.floor( containerWidth / minSlideWidth )
1847	// 		); // Recalculate slides visible
1848	// 		slideWidth = containerWidth / slidesVisible;
1849	// 		slideWidth -= itemGap;
1850
1851	// 		if ( $campaign_type == 'bundle_discount' ) {
1852	// 			slideWidth -= $sliderContainer
1853	// 				.find( '.revx-builder__middle_element' )
1854	// 				.width();
1855	// 		}
1856
1857	// 		$slides.css( 'width', slideWidth + 'px' );
1858
1859	// 		moveToSlide( slideIndex );
1860	// 	}
1861
1862	// 	setTimeout( () => {
1863	// 		updateSlideWidth();
1864	// 	} );
1865
1866	// 	function moveToSlide( index ) {
1867	// 		let tempWidth = slideWidth;
1868	// 		if ( $campaign_type == 'fbt' ) {
1869	// 			tempWidth += $sliderContainer
1870	// 				.find( '.revx-product-bundle' )
1871	// 				.width();
1872	// 		}
1873	// 		if ( $campaign_type == 'bundle_discount' ) {
1874	// 			tempWidth += $sliderContainer
1875	// 				.find( '.revx-builder__middle_element' )
1876	// 				.width();
1877	// 		}
1878	// 		if ( $campaign_type == 'mix_match' ) {
1879	// 			tempWidth += itemGap;
1880	// 		}
1881	// 		const offset = -tempWidth * index;
1882
1883	// 		$sliderContainer.css( {
1884	// 			transition: 'transform 0.5s ease-in-out',
1885	// 			transform: `translateX(${ offset }px)`,
1886	// 		} );
1887	// 	}
1888
1889	// 	function moveToNextSlide() {
1890	// 		slideIndex++;
1891
1892	// 		if ( slideIndex > totalSlides - slidesVisible ) {
1893	// 			slideIndex = 0;
1894	// 		}
1895
1896	// 		moveToSlide( slideIndex );
1897	// 	}
1898
1899	// 	function moveToPrevSlide() {
1900	// 		slideIndex--;
1901
1902	// 		if ( slideIndex < 0 ) {
1903	// 			slideIndex = totalSlides - slidesVisible;
1904	// 		}
1905
1906	// 		moveToSlide( slideIndex );
1907	// 	}
1908
1909	// 	$sliderContainer
1910	// 		.siblings( '.revx-builderSlider-right' )
1911	// 		.click( function () {
1912	// 			if ( ! $sliderContainer.is( ':animated' ) ) {
1913	// 				moveToNextSlide();
1914	// 			}
1915	// 		} );
1916
1917	// 	$sliderContainer
1918	// 		.siblings( '.revx-builderSlider-left' )
1919	// 		.click( function () {
1920	// 			if ( ! $sliderContainer.is( ':animated' ) ) {
1921	// 				moveToPrevSlide();
1922	// 			}
1923	// 		} );
1924
1925	// 	setTimeout( () => {
1926	// 		// // const initialWidth = $sliderContainer.width();
1927	// 		// $sliderContainer.width(containerWidth + 1); // Increase width by 1px
1928	// 		// $sliderContainer.width(containerWidth); // Reset to original width
1929	// 		$sliderContainer.parent().width( containerWidth ); // Reset to original width
1930	// 		$sliderContainer.parent().width( containerWidth + 1 ); // Reset to original width
1931	// 		$( window ).trigger( 'resize' ); // Trigger window resize
1932	// 	} );
1933
1934	// 	$( window ).resize( function () {
1935	// 		updateSlideWidth();
1936	// 	} );
1937
1938	// 	$sliderContainer
1939	// 		.closest( '.revx-inpage-container' )
1940	// 		.css( 'visibility', 'visible' );
1941	// }
1942
1943	// function buxXGetYSlider() {
1944	// 	$( '.revx-inpage-container.revx-buyx-gety-grid' ).each( function () {
1945	// 		const $container = $( this ).find(
1946	// 			'.revx-campaign-container__wrapper'
1947	// 		);
1948	// 		const containerElement = $container.get( 0 );
1949	// 		const computedStyle = getComputedStyle( containerElement );
1950
1951	// 		let gridColumnValue = parseInt(
1952	// 			computedStyle.getPropertyValue( '--revx-grid-column' ).trim()
1953	// 		);
1954	// 		const minSlideWidth = 132; // 12rem in pixels (assuming 1rem = 16px)
1955
1956	// 		const $triggerItemContainer = $container.find(
1957	// 			'.revx-bxgy-trigger-items'
1958	// 		);
1959	// 		const $offerItemContainer = $container.find(
1960	// 			'.revx-bxgy-offer-items'
1961	// 		);
1962
1963	// 		let triggerItemColumn = parseInt(
1964	// 			getComputedStyle( $triggerItemContainer.get( 0 ) )
1965	// 				.getPropertyValue( '--revx-grid-column' )
1966	// 				.trim()
1967	// 		);
1968	// 		let offerItemColumn = parseInt(
1969	// 			getComputedStyle( $offerItemContainer.get( 0 ) )
1970	// 				.getPropertyValue( '--revx-grid-column' )
1971	// 				.trim()
1972	// 		);
1973
1974	// 		let containerWidth = $container.width();
1975
1976	// 		const seperatorWidth = $container
1977	// 			.find( '.revx-product-bundle' )
1978	// 			.width();
1979
1980	// 		containerWidth -= seperatorWidth - 16;
1981
1982	// 		gridColumnValue = gridColumnValue ? gridColumnValue : 4;
1983
1984	// 		gridColumnValue = Math.min(
1985	// 			gridColumnValue,
1986	// 			Math.floor( containerWidth / minSlideWidth )
1987	// 		);
1988	// 		triggerItemColumn = Math.min(
1989	// 			$triggerItemContainer.find( '.revx-campaign-item' ).length,
1990	// 			triggerItemColumn
1991	// 		);
1992	// 		offerItemColumn = Math.min(
1993	// 			$offerItemContainer.find( '.revx-campaign-item' ).length,
1994	// 			offerItemColumn
1995	// 		);
1996
1997	// 		gridColumnValue = Math.min(
1998	// 			gridColumnValue,
1999	// 			triggerItemColumn + offerItemColumn
2000	// 		);
2001
2002	// 		// gridColumnValue = gridColumnValue ? gridColumnValue : 4;
2003
2004	// 		// Ensure the total columns for trigger and offer items do not exceed the available grid columns
2005	// 		if ( triggerItemColumn + offerItemColumn > gridColumnValue ) {
2006	// 			const excessColumns =
2007	// 				triggerItemColumn + offerItemColumn - gridColumnValue;
2008
2009	// 			// Adjust columns proportionally to ensure total columns match gridColumnValue
2010	// 			const triggerAdjustment = Math.floor(
2011	// 				( triggerItemColumn /
2012	// 					( triggerItemColumn + offerItemColumn ) ) *
2013	// 					excessColumns
2014	// 			);
2015	// 			const offerAdjustment = excessColumns - triggerAdjustment;
2016
2017	// 			triggerItemColumn -= triggerAdjustment;
2018	// 			offerItemColumn -= offerAdjustment;
2019	// 		}
2020
2021	// 		const slideWidth = containerWidth / gridColumnValue;
2022
2023	// 		initializeSubSlider(
2024	// 			$triggerItemContainer,
2025	// 			triggerItemColumn,
2026	// 			slideWidth,
2027	// 			'trigger'
2028	// 		);
2029	// 		initializeSubSlider(
2030	// 			$offerItemContainer,
2031	// 			offerItemColumn,
2032	// 			slideWidth,
2033	// 			'offer'
2034	// 		);
2035
2036	// 		$( this ).css( 'visibility', 'visible' );
2037	// 	} );
2038	// }
2039
2040	// function initializeSubSlider(
2041	// 	$sliderContainer,
2042	// 	itemColumn,
2043	// 	slideWidth,
2044	// 	type
2045	// ) {
2046	// 	const $container = $sliderContainer.find( '.revx-slider-container' );
2047	// 	const itemGap = parseInt(
2048	// 		getComputedStyle( $container.get( 0 ) )
2049	// 			.getPropertyValue( 'gap' )
2050	// 			.trim()
2051	// 	);
2052
2053	// 	// slideWidth -=itemGap;
2054	// 	slideWidth -= itemGap;
2055	// 	const containerWidth = itemColumn * slideWidth;
2056	// 	$sliderContainer.width( containerWidth );
2057	// 	slideWidth -= 16;
2058
2059	// 	if ( type == 'offer' ) {
2060	// 		slideWidth += itemGap;
2061	// 	}
2062
2063	// 	$sliderContainer = $container;
2064
2065	// 	const $slides = $sliderContainer.find( '.revx-campaign-item' );
2066	// 	$slides.css( { width: slideWidth + 'px' } );
2067
2068	// 	const totalSlides = $slides.length;
2069	// 	let slideIndex = 0; // Start at the first slide
2070
2071	// 	function moveToSlide( index ) {
2072	// 		let tempWidth = slideWidth;
2073	// 		tempWidth += itemGap + 16;
2074	// 		tempWidth += index;
2075
2076	// 		if ( itemColumn == 1 ) {
2077	// 			tempWidth += itemGap;
2078	// 		}
2079
2080	// 		if ( type == 'offer' ) {
2081	// 			tempWidth -= 16;
2082	// 		}
2083
2084	// 		const offset = -tempWidth * index;
2085
2086	// 		$sliderContainer.css( {
2087	// 			transition: 'transform 0.5s ease-in-out',
2088	// 			transform: `translateX(${ offset }px)`,
2089	// 		} );
2090	// 	}
2091
2092	// 	function moveToNextSlide() {
2093	// 		slideIndex++;
2094	// 		if ( slideIndex > totalSlides - itemColumn ) {
2095	// 			slideIndex = 0;
2096	// 		}
2097	// 		moveToSlide( slideIndex );
2098	// 	}
2099
2100	// 	function moveToPrevSlide() {
2101	// 		slideIndex--;
2102	// 		if ( slideIndex < 0 ) {
2103	// 			slideIndex = totalSlides - itemColumn;
2104	// 		}
2105	// 		moveToSlide( slideIndex );
2106	// 	}
2107
2108	// 	$sliderContainer
2109	// 		.siblings( '.revx-builderSlider-right' )
2110	// 		.click( function () {
2111	// 			if ( ! $sliderContainer.is( ':animated' ) ) {
2112	// 				moveToNextSlide();
2113	// 			}
2114	// 		} );
2115
2116	// 	$sliderContainer
2117	// 		.siblings( '.revx-builderSlider-left' )
2118	// 		.click( function () {
2119	// 			if ( ! $sliderContainer.is( ':animated' ) ) {
2120	// 				moveToPrevSlide();
2121	// 			}
2122	// 		} );
2123
2124	// 	$( window ).resize( function () {
2125	// 		moveToSlide( slideIndex );
2126	// 	} );
2127
2128	// 	moveToSlide( slideIndex );
2129	// }
2130
2131	// buxXGetYSlider();
2132
2133	// $( window ).resize( function () {
2134	// 	buxXGetYSlider();
2135	// } );
2136
2137	// $(
2138	// 	'.revx-inpage-container.revx-normal-discount-grid .revx-slider-container'
2139	// ).each( function () {
2140	// 	initializeSlider(
2141	// 		$( this ),
2142	// 		'.revx-campaign-view__items',
2143	// 		'normal_discount'
2144	// 	);
2145	// } );
2146	// $(
2147	// 	'.revx-inpage-container.revx-mix-match-grid .revx-slider-container'
2148	// ).each( function () {
2149	// 	initializeSlider(
2150	// 		$( this ),
2151	// 		'.revx-campaign-view__items',
2152	// 		'mix_match'
2153	// 	);
2154	// } );
2155	// $(
2156	// 	'.revx-inpage-container.revx-bundle-discount-grid .revx-slider-container'
2157	// ).each( function () {
2158	// 	initializeSlider(
2159	// 		$( this ),
2160	// 		'.revx-campaign-view__items',
2161	// 		'bundle_discount'
2162	// 	);
2163	// } );
2164	// $(
2165	// 	'.revx-inpage-container.revx-frequently-bought-together-grid .revx-slider-container'
2166	// ).each( function () {
2167	// 	initializeSlider( $( this ), '.revx-inpage-container', 'fbt' );
2168	// } );
2169
2170	// $( window ).on( 'load resize', function () {
2171	// 	checkOverflow( '.revx-slider' );
2172	// } );
2173
2174	// ---------------- Slider
2175
2176	// $('.revx-ticket-type')
2177
2178	$( '.revx-ticket-type' ).change( function () {
2179		const selectedSlug = $( this ).val();
2180		const campaignID = $( this ).data( 'campaign-id' );
2181		const offerQty = $( this ).data( 'quantity' );
2182
2183		const prices = $( this ).data( 'prices' );
2184
2185		$( this )
2186			.closest( '.revx-campaign-item' )
2187			.find( '.revx-total-ticket-price' )
2188			.text(
2189				`Total Price: ${ formatPrice(
2190					prices[ selectedSlug ] * offerQty
2191				) }`
2192			);
2193
2194		const selectedItem = $( this )
2195			.closest( '.revx-volume-discount' )
2196			.find( '.revx-campaign-item[data-revx-selected=true]' );
2197
2198		if ( selectedItem.length ) {
2199			// Check if the selectedItem exists
2200			// selectedItem.data('data-selected-ticket', selectedSlug);
2201			selectedItem.attr( 'data-selected-ticket', selectedSlug );
2202			selectedItem.attr(
2203				'data-selected-ticket-price',
2204				prices[ selectedSlug ] * offerQty
2205			);
2206		}
2207	} );
2208
2209	$( '.revx-ticket-type' ).trigger( 'change' );
2210
2211	// Tooltip - Spending Goal
2212	function updateTooltipPosition( $container ) {
2213		const $tooltip = $container.find( '.revx-gift-tooltip' );
2214		if ( ! $tooltip.length ) {
2215			return;
2216		}
2217
2218		const containerRect = $container[ 0 ].getBoundingClientRect();
2219		const tooltipRect = $tooltip[ 0 ].getBoundingClientRect();
2220		const windowHeight = $( window ).height();
2221		const windowWidth = $( window ).width();
2222
2223		// Calculate available space
2224		const spaceAbove = containerRect.top;
2225		const spaceBelow = windowHeight - containerRect.bottom;
2226
2227		// Determine position (top or bottom)
2228		const position = spaceAbove > spaceBelow ? 'top' : 'bottom';
2229		$tooltip.attr( 'data-position', position );
2230
2231		// Handle horizontal overflow
2232		const tooltipWidth = $tooltip.outerWidth();
2233		const containerCenterX = containerRect.left + containerRect.width / 2;
2234		const spaceLeft = containerCenterX;
2235		const spaceRight = windowWidth - containerCenterX;
2236
2237		if ( tooltipWidth / 2 > spaceLeft ) {
2238			// Not enough space on the left
2239			$tooltip.css( {
2240				left: '0',
2241				transform: 'translateX(0)',
2242			} );
2243		} else if ( tooltipWidth / 2 > spaceRight ) {
2244			// Not enough space on the right
2245			$tooltip.css( {
2246				left: 'auto',
2247				right: '0',
2248				transform: 'translateX(0)',
2249			} );
2250		} else {
2251			// Center aligned
2252			$tooltip.css( {
2253				// left: '50%',
2254				right: 'auto',
2255				transform: 'translateX(-50%)',
2256			} );
2257		}
2258	}
2259
2260	// Handle mouse enter
2261	$( '.revx-step-icon-container' ).on( 'mouseenter', function () {
2262		updateTooltipPosition( $( this ) );
2263	} );
2264
2265	// Update positions on window resize
2266	let resizeTimer;
2267	$( window ).on( 'resize', function () {
2268		clearTimeout( resizeTimer );
2269		resizeTimer = setTimeout( function () {
2270			$( '.revx-step-icon-container:hover' ).each( function () {
2271				updateTooltipPosition( $( this ) );
2272			} );
2273		}, 250 );
2274	} );
2275
2276	function updateGiftPosition( $container ) {
2277		const $gift = $container.find( '.revx-gift-container' );
2278		const $giftWrapper = $container.find( '.revx-spending-gift' );
2279		if ( ! $gift.length ) {
2280			return;
2281		}
2282		// remove the class before. otherwise top calculation is wrong.
2283		$gift.removeClass( 'revx-d-none' );
2284
2285		const iconRect = $container[ 0 ].getBoundingClientRect(); // relative to viewport
2286		const giftRect = $gift[ 0 ].getBoundingClientRect();
2287
2288		const giftWidth = giftRect?.width;
2289		const iconWidth = iconRect?.width;
2290		const windowWidth = window.innerWidth;
2291		const giftCenterToLeft = Math.abs( giftWidth - iconWidth ) / 2;
2292
2293		const isAbove = iconRect?.top > giftRect?.height + 10;
2294		const isRightSpace = iconRect?.left + giftCenterToLeft < windowWidth;
2295		const isLeftSpace = iconRect?.left - giftCenterToLeft >= 0;
2296
2297		const top = isAbove
2298			? ( giftRect?.height + 8 ) * -1
2299			: iconRect?.height + 8;
2300
2301		let left = 0;
2302		if ( ! isLeftSpace && isRightSpace ) {
2303			left = -20; // small offset from left edge
2304		} else if ( isLeftSpace && ! isRightSpace ) {
2305			left = iconWidth - giftWidth;
2306		} else {
2307			// for space both left/right OR no space left/right keep center.
2308			left = giftCenterToLeft * -1;
2309		}
2310
2311		$gift.css( {
2312			position: 'fixed',
2313			top: `${ top }px`,
2314			left: `${ left }px`,
2315			zIndex: 999999,
2316			visibility: 'visible',
2317			opacity: 1,
2318		} );
2319		$giftWrapper.css( {
2320			transformOrigin: `${ isAbove ? 'bottom' : 'top' } ${
2321				! isLeftSpace && isRightSpace
2322					? 'left'
2323					: isLeftSpace && ! isRightSpace
2324					? 'right'
2325					: 'center'
2326			}`,
2327		} );
2328	}
2329
2330	// Stock Scarcity
2331	$( '.revx-flip-wrapper' ).each( function () {
2332		const $this = $( this ); // Define $this as the current .revx-flip-wrapper element
2333		const flipTextHeight = $this.find( '.revx-flip-text' ).outerHeight();
2334		$this.css( 'min-height', flipTextHeight + 'px' );
2335	} );
2336
2337	let giftHideTimer;
2338	$( document )
2339		.on( 'mouseenter', '.revx-progress-step-icon-container', function () {
2340			clearTimeout( giftHideTimer ); // prevent hiding
2341			updateGiftPosition( $( this ) );
2342		} )
2343		.on( 'mouseleave', '.revx-progress-step-icon-container', function () {
2344			const $this = $( this );
2345			giftHideTimer = setTimeout( () => {
2346				$this
2347					.find( '.revx-gift-container' )
2348					.addClass( 'revx-d-none' )
2349					.css( {
2350						visibility: 'hidden',
2351						opacity: 0,
2352						left: '-250px', // to handle the overflow x case
2353						top: 'unset',
2354					} );
2355			}, 200 ); // adjust delay as needed
2356		} );
2357
2358	$( document )
2359		.on( 'mouseenter', '.revx-gift-container', function () {
2360			clearTimeout( giftHideTimer ); // prevent hiding
2361		} )
2362		.on( 'mouseleave', '.revx-gift-container', function () {
2363			$( this ).addClass( 'revx-d-none' ).css( {
2364				visibility: 'hidden',
2365				opacity: 0,
2366				left: 'unset',
2367				top: 'unset',
2368			} );
2369		} );
2370
2371	// Reposition gift on resize or scroll
2372	$( window ).on( 'resize scroll', function () {
2373		$( '.revx-progress-step-icon-container:hover' ).each( function () {
2374			updateGiftPosition( $( this ) );
2375		} );
2376	} );
2377
2378	function adjustContentMarginTop() {
2379		// const helloBar = $( '.revx-spending-goal-top' ).length
2380		// 	? $( '.revx-spending-goal-top' )
2381		// 	: $( '.revx-campaign-fsb-top' );
2382		const helloBar = $( '.revx-campaign-top' ).length
2383			? $( '.revx-campaign-top' )
2384			: $( '.revx-campaign-fsb-top' );
2385
2386		// Check if hello bar exists
2387		if ( helloBar.length === 0 ) {
2388			return;
2389		}
2390
2391		const helloBarHeight = helloBar.outerHeight();
2392
2393		// Only adjust if we have a valid height
2394		if ( helloBarHeight ) {
2395			$( 'body' ).css( 'margin-top', helloBarHeight );
2396
2397			// Remove margin-bottom as it's causing double spacing
2398			// helloBar.css( 'margin-top', -helloBarHeight );
2399		}
2400	}
2401
2402	adjustContentMarginTop();
2403	function adjustContentMarginBottom() {
2404		const helloBar = $( '.revx-campaign-bottom' ).length
2405			? $( '.revx-campaign-bottom' )
2406			: $( '.revx-campaign-fsb-bottom' );
2407
2408		// Check if hello bar exists
2409		if ( helloBar.length === 0 ) {
2410			return;
2411		}
2412
2413		const helloBarHeight = helloBar.outerHeight();
2414
2415		// Only adjust if we have a valid height
2416		if ( helloBarHeight ) {
2417			$( 'body' ).css( 'margin-bottom', helloBarHeight );
2418
2419			// Remove margin-bottom as it's causing double spacing
2420			// helloBar.css('margin-bottom', -helloBarHeight);
2421		}
2422	}
2423
2424	adjustContentMarginBottom();
2425
2426	function showToast( message, type = 'success', duration = 3000 ) {
2427		// Check if toast container exists, otherwise create it
2428		let $toastContainer = $( '.revx-toaster-container' );
2429		if ( $toastContainer.length === 0 ) {
2430			$toastContainer = $( '<div class="revx-toaster-container"></div>' );
2431			$( 'body' ).append( $toastContainer );
2432		}
2433
2434		// Determine toast class and icon based on type
2435		const toastClasses = {
2436			success: 'revx-toaster__success',
2437			warning: 'revx-toaster__warning',
2438			error: 'revx-toaster__error',
2439		};
2440
2441		const icon = `
2442				<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">
2443					<path stroke="#fff" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2" d="m12 4-8 8M4 4l8 8"></path>
2444				</svg>
2445			`;
2446
2447		// Create a new toast element as a jQuery object
2448		const $toast = $( `
2449			<div class="revx-toaster revx-justify-space revx-toaster-lg ${ toastClasses[ type ] }" style="display: flex;">
2450				<div class="revx-paragraph--xs revx-align-center-xs">
2451					${ message }
2452				</div>
2453				<div class="revx-paragraph--xs revx-align-center">
2454					${ icon }
2455				</div>
2456			</div>
2457		` );
2458
2459		// Add close button functionality
2460		$toast.find( '.revx-toaster__close-icon' ).on( 'click', function () {
2461			$toast.fadeOut( 400, function () {
2462				$( this ).remove(); // Remove the toast from DOM
2463			} );
2464		} );
2465
2466		// Append the toast to the toast container
2467		$toastContainer.append( $toast );
2468
2469		// Show the toast
2470		$toast.fadeIn( 400 );
2471
2472		// Set timeout to hide the toast after the specified duration
2473		setTimeout( function () {
2474			if ( $toast.is( ':visible' ) ) {
2475				// Only remove if still visible
2476				$toast.fadeOut( 400, function () {
2477					$( this ).remove(); // Remove the toast from DOM
2478				} );
2479			}
2480		}, duration );
2481	}
2482
2483	const $drawerContainer = $( '.revx-drawer-container' ).first();
2484	if ( $drawerContainer.length ) {
2485		// Clone the entire container including content
2486		// const $drawerContent = $drawerContainer.find( '.revx-drawer-content' );
2487		// $drawerContent.each( function () {
2488		// 	this.style.setProperty( 'max-width', '0vh', 'important' );
2489		// 	this.style.setProperty( 'padding', '0vh', 'important' );
2490		// } );
2491
2492		const $clone = $drawerContainer.clone( true, true );
2493		const $cloneContent = $clone.find( '.revx-drawer-content' );
2494		// Remove max-width and padding limits on drawer-content inside clone
2495		$cloneContent.each( function () {
2496			this.style.setProperty( 'max-width', 'none', 'important' );
2497			this.style.setProperty(
2498				'padding-top',
2499				'var(--revx-drawer-padding-top)',
2500				'important'
2501			);
2502			this.style.setProperty(
2503				'padding-right',
2504				'var(--revx-drawer-padding-right)',
2505				'important'
2506			);
2507			this.style.setProperty(
2508				'padding-bottom',
2509				'var(--revx-drawer-padding-bottom)',
2510				'important'
2511			);
2512			this.style.setProperty(
2513				'padding-left',
2514				'var(--revx-drawer-padding-left)',
2515				'important'
2516			);
2517			// this.style.setProperty( 'position', 'static', 'important' );
2518		} );
2519		// $cloneContent.find( '*' ).each( function () {
2520		// 	this.style.setProperty( 'position', 'static', 'important' );
2521		// } );
2522
2523		$clone.css( {
2524			display: 'flex',
2525			position: 'absolute',
2526			top: '-9999px',
2527			left: '-9999px',
2528			visibility: 'hidden',
2529			bottom: 'unset',
2530			right: 'unset',
2531		} );
2532		// Append clone to body so you can see it on top
2533		$( 'body' ).append( $clone );
2534		const cloneHeight = $clone.innerHeight();
2535		// const cloneHeight = $clone.outerHeight( true );
2536		$drawerContainer.each( function () {
2537			this.style.setProperty( 'height', cloneHeight + 'px', 'important' );
2538			this.style.setProperty( 'display', 'flex', 'important' );
2539		} );
2540	}
2541
2542	// Open campaign drawer
2543	$( document ).on( 'click', '.revx-drawer-opener', function () {
2544		const $container = $( this ).closest( '.revx-drawer-container' );
2545		const $drawerContent = $container.find( '.revx-drawer-content' );
2546
2547		// $drawerContent.each( function () {
2548		// 	this.style.setProperty( 'transition', 'all 0.2s', 'important' );
2549		// } );
2550		$drawerContent.addClass( 'revx-transition' );
2551
2552		if ( $container.hasClass( 'revx-active' ) ) {
2553			$container.removeClass( 'revx-active' );
2554			$container.css( { overflow: 'hidden' } );
2555			// $drawerContent.each( function () {
2556			// 	this.style.setProperty( 'max-width', '0vh', 'important' );
2557			// 	this.style.setProperty( 'padding', '0vh', 'important' );
2558			// } );
2559		} else {
2560			$container.addClass( 'revx-active' );
2561			$container.css( { overflow: 'visible' } );
2562			// $drawerContent.each( function () {
2563			// 	this.style.removeProperty( 'max-width' );
2564			// 	this.style.removeProperty( 'padding' );
2565			// } );
2566		}
2567	} );
2568	// Close campaign drawer
2569	$( document ).on( 'click', '.revx-drawer-closer', function () {
2570		const $container = $( this ).closest( '.revx-drawer-container' );
2571		// const $drawerContent = $container.find( '.revx-drawer-content' );
2572		$container.removeClass( 'revx-active' );
2573		// $drawerContent.each( function () {
2574		// 	this.style.setProperty( 'max-width', '0vh', 'important' );
2575		// 	this.style.setProperty( 'padding', '0vh', 'important' );
2576		// } );
2577	} );
2578
2579	// Hide Campaign on Close Button Click
2580	$( document ).on( 'click', '.revx-campaign-close', function () {
2581		const campaignID = $( this ).data( 'campaign-id' );
2582		$( `.revx-campaign-${ campaignID }` ).hide();
2583		$( 'body' ).css( 'margin-bottom', 0 );
2584		$( 'body' ).css( 'margin-top', 0 );
2585	} );
2586
2587	// Next Order Coupon Copy Clip-Borad
2588	$( '.revx-coupon-copy-btn' ).on( 'click', function () {
2589		const $btn = $( this );
2590
2591		// // const $content = $btn.closest( '.revx-Coupon-button' );
2592		// const text = $content
2593		// 	.clone() // Clone to avoid modifying original
2594		// 	.children() // Remove children (like .revx-coupon-copy-btn)
2595		// 	.remove()
2596		// 	.end() // Go back to cloned parent
2597		// 	.text()
2598		// 	.trim(); // Get just the raw text
2599
2600		const $content = $btn.siblings( '.revx-coupon-value' );
2601		const text = $content.text().trim();
2602
2603		// Fallback for browsers that do not support navigator.clipboard
2604		const tempInput = $( '<input>' );
2605		$( 'body' ).append( tempInput );
2606		tempInput.val( text ).select();
2607		try {
2608			document.execCommand( 'copy' );
2609			// $btn.text( 'Copied!' );
2610			$( '.revx-coupon-value' ).css( 'background-color', '#008000' );
2611			$( '.revx-coupon-value' ).css( {
2612				transition: 'background-color 0.4s',
2613			} );
2614
2615			setTimeout(
2616				() =>
2617					$( '.revx-coupon-value' ).css(
2618						'background-color',
2619						'unset'
2620					),
2621				400
2622			);
2623		} catch ( err ) {
2624			console.error( 'Fallback: Failed to copy:', err );
2625		}
2626		tempInput.remove();
2627	} );
2628
2629	window.Revenue = {
2630		getCookie,
2631		setCookie,
2632		calculateCurrentPrice,
2633		formatPrice,
2634		updatePriceDisplay,
2635		updateMixMatchHeaderAndPrices,
2636		// fbtCalculation,
2637		// updateStyles,
2638		showToast,
2639	};
2640	// eslint-disable-next-line no-undef
2641
2642	$( '.revx-slider-wrapper' ).each( function () {
2643		if ( ! $( this ).data( 'slider-initialized' ) ) {
2644			$( this ).revxSlider();
2645			$( this ).data( 'slider-initialized', true );
2646		}
2647	} );
2648
2649	// Auto initialize on page load
2650	// function initTimers( $context ) {
2651	// 	$context.find( '.revx-countdown-timer' ).revxCountdownTimer();
2652	// }
2653
2654	// $( document ).ready( function () {
2655	// 	initTimers( $( document ) );
2656
2657	// 	// MutationObserver for dynamically added timers
2658	// 	const observer = new MutationObserver( function ( mutations ) {
2659	// 		mutations.forEach( function ( mutation ) {
2660	// 			$( mutation.addedNodes ).each( function () {
2661	// 				if ( this.nodeType === 1 ) {
2662	// 					const $el = $( this );
2663	// 					if ( $el.is( '.revx-countdown-timer' ) ) {
2664	// 						$el.revxCountdownTimer();
2665	// 					} else {
2666	// 						initTimers( $el );
2667	// 					}
2668	// 				}
2669	// 			} );
2670	// 		} );
2671	// 	} );
2672
2673	// 	observer.observe( document.body, {
2674	// 		childList: true,
2675	// 		subtree: true,
2676	// 	} );
2677	// } );
2678
2679	function getSelectedAttributes( $form ) {
2680		const selectedData = {};
2681		$form.find( 'select[name^="attribute_"]' ).each( function () {
2682			const key = $( this ).attr( 'name' );
2683			selectedData[ key ] = $( this ).val() || '';
2684		} );
2685		return selectedData;
2686	}
2687
2688	// Update data-selected-value when any attribute dropdown changes
2689	$( '.product' ).on( 'change', 'select[name^="attribute_"]', function () {
2690		const $form = $( this ).closest( '.product' );
2691		const selectedData = getSelectedAttributes( $form );
2692		// Update the data-selected-value attribute
2693		$form.attr( 'data-selected-value', JSON.stringify( selectedData ) );
2694	} );
2695} )( jQuery );
2696
2697// move jquery code from the template1 volume discount selection.
2698jQuery( function ( $ ) {
2699	$( document ).on( 'click', '.revx-volume-discount-item', function () {
2700		const $radio = $( this ).find( '.revx-radio-wrapper' );
2701		const $attribute = $( this ).find( '.revx-volume-attributes' );
2702
2703		// deactivate every other option
2704		$( '.revx-radio-wrapper, .revx-volume-attributes' )
2705			.removeClass( 'revx-active' )
2706			.addClass( 'revx-inactive' );
2707
2708		// activate the clicked one
2709		$radio
2710			.add( $attribute )
2711			.addClass( 'revx-active' )
2712			.removeClass( 'revx-inactive' );
2713	} );
2714} );

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.