PageSourceSearch

https://twoseedsinapod.com/wp-content/plugins/revenue/assets/js/frontend/campaign.js?ver=2.3.0

js twoseedsinapod.com collected 2026-09-24 12:11:00 UTC 35,214 bytes, 1,277 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	// 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.