PageSourceSearch

https://simplyecologist.com/wp-content/plugins/revenue/assets/js/f…/variation-product-selection.js?ver=2.3.1

js simplyecologist.com collected 2026-10-06 20:39:13 UTC 13,074 bytes, 419 lines download raw bytes

1/* global revenue_campaign Revenue jQuery */
2// the below line ignores revenue_campaign not camel case warning
3/* eslint-disable camelcase */
4jQuery( function ( $ ) {
5	if ( typeof revenue_campaign === 'undefined' ) {
6		console.error( 'Revenue campaign script not loaded.' );
7		return;
8	}
9
10	function setSaveCapsuleText( amount ) {
11		const $amountPriceContianer = $( this ).find(
12			'.woocommerce-Price-amount'
13		);
14
15		if ( $amountPriceContianer.length ) {
16			$amountPriceContianer.text( Revenue.formatPrice( amount ) );
17		}
18	}
19	function setPriceText( price ) {
20		const $priceContainer = $( this ).find( '.woocommerce-Price-amount' );
21		$priceContainer.text( Revenue.formatPrice( price ) );
22	}
23	// to handle case when there is default attributes or variation of a product
24	// trigger change event on initial load.
25	$( function () {
26		// for all variation products
27		$( '[data-variations] .revx-product-Attr-wrapper' ).trigger( 'change' );
28		// for volume discount variation products
29		$(
30			'[data-campaign-type="volume_discount"] .revx-product-Attr-wrapper'
31		).trigger( 'change' );
32	} );
33	// for all other campaigns other than volume discount with variation product
34	$( document ).on(
35		'change',
36		'[data-variations] .revx-product-Attr-wrapper',
37		function () {
38			const $container = $( this ).closest( '[data-variations]' );
39			const variationMap = JSON.parse(
40				$container.attr( 'data-variations' ) || '[]'
41			);
42
43			// Collect selected attributes
44			const selectedAttrs = {};
45			$container.find( '.revx-product-Attr-wrapper' ).each( function () {
46				selectedAttrs[ $( this ).attr( 'name' ) ] = $( this ).val();
47			} );
48
49			// Try to find a matching variation (compare with variation.attributes)
50			const matchedVariation = variationMap.find( ( variation ) => {
51				if ( ! variation.attributes ) return false;
52				return Object.entries( selectedAttrs ).every(
53					( [ key, val ] ) =>
54						! val ||
55						! variation.attributes[
56							key.replace( /attribute_/, '' )
57						] ||
58						variation.attributes[
59							key.replace( /attribute_/, '' )
60						] === val
61				);
62			} );
63
64			// Update price if we found a match
65			if ( matchedVariation ) {
66				const $offerItemContainer = $container;
67				const $saveAmountContainer = $offerItemContainer.find(
68					'[data-smart-tag="saveBadgeWrapper"]'
69				);
70				const $regularPriceContainer = $offerItemContainer.find(
71					'.revx-product-regular-price'
72				);
73				const $oldPriceContainer = $offerItemContainer.find(
74					'.revx-product-old-price'
75				);
76				const $offeredPriceContainer = $offerItemContainer.find(
77					'.revx-product-offered-price'
78				);
79
80				setSaveCapsuleText.call(
81					$saveAmountContainer,
82					matchedVariation.saved_amount
83				);
84				setPriceText.call(
85					$regularPriceContainer,
86					matchedVariation.regular_price
87				);
88				setPriceText.call(
89					$oldPriceContainer,
90					matchedVariation.regular_price
91				);
92				$container.attr(
93					'data-regular-price',
94					matchedVariation?.regular_price
95				);
96				// Trigger products and buy-x-get-y products use their regular price.
97				const isRequired =
98					$container.data( 'is-x-product' ) === 'yes' ||
99					$container.data( 'is-trigger' ) === 'yes';
100				// if the product is trigger product or x product, it will get regular price
101				setPriceText.call(
102					$offeredPriceContainer,
103					matchedVariation?.offered_price ||
104						( isRequired ? matchedVariation?.regular_price : 0 )
105				);
106				$container.attr(
107					'data-offered-price',
108					matchedVariation?.offered_price ||
109						( isRequired ? matchedVariation?.regular_price : 0 )
110				);
111				// since both have some common classes, this can be done with same function
112				// make seperate funciton if situation changes, and use else if ladder or if.
113				if (
114					$container.attr( 'campaign_type' ) === 'bundle_discount' ||
115					$container.attr( 'campaign_type' ) === 'buy_x_get_y'
116				) {
117					const $bundleTopContainer =
118						$container.closest( '.revx-template' );
119					$bundleTopContainer.trigger( 'revx-variation-changed' );
120					// updateBundleTotal( $bundleTopContainer );
121				}
122				$( document ).trigger(
123					'revx-campaign-variation-updated',
124					[ $container, matchedVariation, isRequired ]
125				);
126			}
127
128			// === Filter other attributes ===
129			$container.find( '.revx-product-Attr-wrapper' ).each( function () {
130				const $select = $( this );
131				const attrName = $select.attr( 'name' );
132				const attrNameFiltered = attrName.replace( /attribute_/, '' );
133
134				// Build list of allowed options for this attribute given others
135				const allowedValues = new Set(); // use Set to avoid duplicates
136				allowedValues.add( '' );
137				for ( const variation of variationMap ) {
138					const fits = Object.entries( selectedAttrs ).every(
139						( [ key, val ] ) =>
140							key === attrName ||
141							! val ||
142							! variation.attributes[
143								key.replace( /attribute_/, '' )
144							] ||
145							variation.attributes[
146								key.replace( /^attribute_/, '' )
147							] === val
148					);
149					if ( fits ) {
150						if ( variation.attributes[ attrNameFiltered ] ) {
151							allowedValues.add(
152								variation.attributes[ attrNameFiltered ]
153							);
154						} else {
155							const options = JSON.parse(
156								$( this ).attr( 'data-options' ) || '[]'
157							);
158							options?.forEach( ( option ) =>
159								allowedValues.add( option )
160							);
161						}
162					}
163				}
164
165				// Update select options (enable/disable)
166				$select.find( 'option' ).each( function () {
167					const $opt = $( this );
168
169					// keep "choose" option
170					if ( allowedValues.has( $opt.val() ) ) {
171						$opt.prop( 'disabled', false ).show();
172					} else {
173						$opt.prop( 'disabled', true ).hide();
174					}
175				} );
176			} );
177
178			// Store matched variation + selected attributes
179			$container.attr(
180				'data-selected-value',
181				JSON.stringify( selectedAttrs || {} )
182			);
183			$container.attr(
184				'data-matched-variation',
185				JSON.stringify( matchedVariation || {} )
186			);
187			// setting variation id for payload usage
188			$container.attr(
189				'data-variation-id',
190				JSON.stringify( matchedVariation?.id ?? 0 )
191			);
192			// update image source
193			if ( matchedVariation?.image_url ) {
194				$container
195					.find( 'img' )
196					.attr( 'src', matchedVariation?.image_url );
197			}
198		}
199	);
200
201	// for volume discount only
202	$( '[data-campaign-type="volume_discount"]' ).on(
203		'change',
204		'.revx-product-Attr-wrapper',
205		function () {
206			const $container = $( this ).closest( '[data-product-id]' );
207			const variationMap = JSON.parse(
208				$container.attr( 'data-variation-map' ) || '[]'
209			);
210
211			// Collect selected attributes
212			const selectedAttrs = {};
213			$container.find( '.revx-product-Attr-wrapper' ).each( function () {
214				selectedAttrs[ $( this ).attr( 'name' ) ] = $( this ).val();
215			} );
216
217			// Try to find a matching variation (compare with variation.attributes)
218			const matchedVariation = variationMap.find( ( variation ) => {
219				if ( ! variation.attributes ) return false;
220				return Object.entries( selectedAttrs ).every(
221					( [ key, val ] ) =>
222						! val ||
223						! variation.attributes[ key ] ||
224						variation.attributes[ key ] === val
225				);
226			} );
227
228			// Store matched variation + selected attributes
229			$container.attr(
230				'data-selected-value',
231				JSON.stringify( selectedAttrs || {} )
232			);
233			$container.attr(
234				'data-matched-variation',
235				JSON.stringify( matchedVariation || {} )
236			);
237
238			// Update price if we found a match
239			if ( matchedVariation ) {
240				const $offerItemContainer =
241					$container.closest( '[data-offer-item]' );
242				const $saveAmountContainer = $offerItemContainer.find(
243					'[data-smart-tag="saveBadgeWrapper"]'
244				);
245				const $oldPriceContainer = $offerItemContainer.find(
246					'.revx-product-old-price'
247				);
248				const $offeredPriceContainer = $offerItemContainer.find(
249					'.revx-product-offered-price'
250				);
251				const $radioButton = $container
252					.closest( '[data-campaign-type="volume_discount"]' )
253					.find( '[data-saved-amount]' );
254
255				// Check if multiple variation selection is enabled
256				const $volumeContainer = $container.closest(
257					'.revx-volume-discount-item'
258				);
259				const $allVariationWrappers = $volumeContainer.find(
260					'.revx-volume-attributes [data-variation-map]'
261				);
262
263				if ( $allVariationWrappers.length > 1 ) {
264					// Multiple variations - calculate total price
265					let totalRegularPrice = 0;
266					let totalOfferedPrice = 0;
267					let totalSavedAmount = 0;
268
269					$allVariationWrappers.each( function () {
270						const currMatchedVariation = JSON.parse(
271							$( this ).attr( 'data-matched-variation' ) ?? '{}'
272						);
273
274						if ( currMatchedVariation ) {
275							totalRegularPrice += parseFloat(
276								currMatchedVariation.regular_price || 0
277							);
278							totalOfferedPrice += parseFloat(
279								currMatchedVariation.offered_price || 0
280							);
281							totalSavedAmount += parseFloat(
282								currMatchedVariation.saved_amount || 0
283							);
284						}
285					} );
286
287					$radioButton.data( 'saved-amount', totalSavedAmount );
288					setSaveCapsuleText.call(
289						$saveAmountContainer,
290						totalSavedAmount
291					);
292					setPriceText.call( $oldPriceContainer, totalRegularPrice );
293					setPriceText.call(
294						$offeredPriceContainer,
295						totalOfferedPrice
296					);
297				} else {
298					// Single variation - use matched variation price
299					$radioButton.data(
300						'saved-amount',
301						matchedVariation.saved_amount
302					);
303					setSaveCapsuleText.call(
304						$saveAmountContainer,
305						matchedVariation.saved_amount
306					);
307					setPriceText.call(
308						$oldPriceContainer,
309						matchedVariation.regular_price
310					);
311					setPriceText.call(
312						$offeredPriceContainer,
313						matchedVariation.offered_price
314					);
315				}
316
317				// trigger the `handleRadioClick` function of `add-to-cart.js` file with the correct container to update the atc button text .
318				$radioButton.trigger( '.revx-volume-discount-item' );
319			}
320
321			// === Filter other attributes ===
322			$container.find( '.revx-product-Attr-wrapper' ).each( function () {
323				const $select = $( this );
324				const attrName = $select.attr( 'name' );
325
326				// Build list of allowed options for this attribute given others
327				const allowedValues = new Set(); // use Set to avoid duplicates
328				allowedValues.add( '' );
329				for ( const variation of variationMap ) {
330					const fits = Object.entries( selectedAttrs ).every(
331						( [ key, val ] ) =>
332							key === attrName ||
333							! val ||
334							! variation.attributes[ key ] ||
335							variation.attributes[ key ] === val
336					);
337					if ( fits ) {
338						if ( variation.attributes[ attrName ] ) {
339							allowedValues.add(
340								variation.attributes[ attrName ]
341							);
342						} else {
343							const options = JSON.parse(
344								$( this ).attr( 'data-options' ) || '[]'
345							);
346							options?.forEach( ( option ) =>
347								allowedValues.add( option )
348							);
349						}
350					}
351				}
352
353				// Update select options (enable/disable)
354				$select.find( 'option' ).each( function () {
355					const $opt = $( this );
356
357					// keep "choose" option
358					if ( allowedValues.has( $opt.val() ) ) {
359						$opt.prop( 'disabled', false ).show();
360					} else {
361						$opt.prop( 'disabled', true ).hide();
362					}
363				} );
364			} );
365		}
366	);
367
368	// TODO: remove this funciton, handled in campaign-total.js now
369	// can handle bundle and buy x get y
370	function updateBundleTotal( $container ) {
371		let totalRegular = 0;
372		let totalOffer = 0;
373		// data-product-id is common in buy x get y and bungle.
374		$container.find( '[data-product-id]' ).each( function () {
375			const qty = parseInt( $( this ).attr( 'data-product-qty' ) ) || 1; // if quantity is not set, then its atleast 1
376			const regularPrice =
377				parseFloat( $( this ).attr( 'data-regular-price' ) ) || 0; // should always get regular price, still a fallback is set.
378			const offeredPrice = parseFloat(
379				$( this ).attr( 'data-offered-price' ) ?? regularPrice // need to decide if user will get the sale price or the regular price
380			);
381
382			totalRegular += regularPrice * qty;
383			totalOffer += offeredPrice * qty;
384		} );
385		const $totalContainer = $container
386			.find( '[data-smart-tag="totalText"]' )
387			.parent();
388		const $oldPriceContainer = $totalContainer.find(
389			'.revx-product-old-price'
390		);
391		const $offeredPriceContainer = $totalContainer.find(
392			'.revx-product-offered-price'
393		);
394		setPriceText.call( $oldPriceContainer, totalRegular );
395		setPriceText.call( $offeredPriceContainer, totalOffer );
396	}
397} );
398
399/**
400 * Volume discount (template2): highlight the selected option row.
401 * Moved out of inline <script> so campaign markup can be escaped.
402 */
403jQuery( function ( $ ) {
404	$( document ).on( 'click', '.revx-volume-discount-item', function () {
405		const $radio = $( this ).find( '.revx-radio-wrapper' );
406		const $attribute = $( this ).find( '.revx-volume-attributes' );
407
408		// Deactivate every other option.
409		$( '.revx-radio-wrapper, .revx-volume-attributes' )
410			.removeClass( 'revx-active' )
411			.addClass( 'revx-inactive' );
412
413		// Activate the clicked one.
414		$radio
415			.add( $attribute )
416			.addClass( 'revx-active' )
417			.removeClass( 'revx-inactive' );
418	} );
419} );

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.