PageSourceSearch

https://www.backsplash.com/wp-content/plugins/woocommerce-tm-extra…/assets/js/tm-epo-product.js?ver=7.6

js backsplash.com collected 2026-09-25 23:41:05 UTC 26,384 bytes, 776 lines download raw bytes

1/* global wc_add_to_cart_variation_params */
2
3( function( $, window, document ) {
4	'use strict';
5	/**
6	 * Handles variation forms and attributes.
7	 *
8	 * @param {Object} $form                     The product form
9	 * @param {Object} element                   The product element
10	 * @param {Object} currentCart               The current cart form
11	 * @param {Object} variableProductContainers The container for the variable product
12	 * @param {Object} epoObject                 The EPO Object
13	 */
14	var VariationForm = function( $form, element, currentCart, variableProductContainers, epoObject ) {
15		var self = this;
16
17		self.field = element;
18		self.currentCart = currentCart;
19		self.variableProductContainers = variableProductContainers;
20		self.epoObject = epoObject;
21		self.$form = $form;
22		self.$wrap = $form.closest( '.tc-epo-element-product-container-wrap' );
23		self.$attributeFields = $form.find( '.tc-epo-variable-product-selector' );
24		self.$singleVariation = $form.find( '.tc-epo-element-single-variation' );
25		self.$resetVariations = $form.find( '.tc-epo-element-variable-reset-variations' );
26		self.variationData = $form.data( 'product_variations' );
27
28		self.useAjax = false === self.variationData;
29		self.xhr = false;
30		self.loading = true;
31		self.variationId = $form.closest( '.tc-epo-element-product-li-container' ).find( '.tc-epo-element-product-container-variation-id input.product-variation-id' );
32
33		// Methods.
34		self.getChosenAttributes = self.getChosenAttributes.bind( self );
35		self.findMatchingVariations = self.findMatchingVariations.bind( self );
36		self.isMatch = self.isMatch.bind( self );
37		self.toggleResetLink = self.toggleResetLink.bind( self );
38
39		// Events.
40		$form.on( 'click.tc-variation-form', '.tc-epo-element-variable-reset-variations', { variationForm: self }, self.onReset );
41		$form.on( 'tc_hide_variation', { variationForm: self }, self.onHide );
42		$form.on( 'tc_show_variation', { variationForm: self }, self.onShow );
43		$form.on( 'tc_reset_data', { variationForm: self }, self.onResetDisplayedVariation );
44		$form.on( 'tc_reset_image', { variationForm: self }, self.onResetImage );
45		$form.on( 'change.tc-variation-form', '.tc-epo-variable-product-selector', { variationForm: self }, self.onChange );
46		$form.on( 'tc_found_variation.tc-variation-form', { variationForm: self }, self.onFoundVariation );
47		$form.on( 'tc_check_variations.tc-variation-form', { variationForm: self }, self.onFindVariation );
48		$form.on( 'tc_update_variation_values.tc-variation-form', { variationForm: self }, self.onUpdateAttributes );
49		$form.on( 'refresh.tc-variation-form', { variationForm: self }, self.onRefreshContainer );
50		$form.on( 'update_field.tc-variation-form', { variationForm: self }, self.onUpdateField );
51
52		self.variationId.val( '' );
53
54		// Init after gallery.
55		setTimeout( function() {
56			$form.trigger( 'refresh' );
57			$form.trigger( 'tc_variation_form' );
58			self.loading = false;
59		}, 100 );
60	};
61
62	/**
63	 * Refresh variations container
64	 *
65	 * @param {Event} event The event Object.
66	 */
67	VariationForm.prototype.onRefreshContainer = function( event ) {
68		var form = event.data.variationForm;
69
70		form.variableProductContainers.find( '.tc-epo-element-product-container' ).removeClass( 'variations_form' );
71
72		form.$form.addClass( 'variations_form' );
73
74		form.$form.trigger( 'tc_check_variations.tc-variation-form' );
75	};
76
77	/**
78	 * Reset all fields.
79	 *
80	 * @param {Event}  event     The event Object.
81	 * @param {Object} variation The variation Object.
82	 */
83	VariationForm.prototype.onUpdateField = function( event, variation ) {
84		var form = event.data.variationForm;
85		var field = form.field;
86		var associatedSetter;
87		var associatedPrice;
88		var associatedRawPrice;
89		var associatedOriginalPrice;
90		var associatedRawOriginalPrice;
91
92		event.preventDefault();
93
94		if ( ! variation ) {
95			associatedSetter = field;
96			if ( field.is( 'select' ) ) {
97				associatedSetter = field.find( 'option:selected' );
98			}
99
100			associatedPrice = 0;
101			associatedRawPrice = 0;
102			associatedOriginalPrice = 0;
103			associatedRawOriginalPrice = 0;
104
105			field.data( 'price_set', 1 );
106			associatedSetter.data( 'associated_price_set', 1 );
107			associatedSetter.data( 'price_set', 1 );
108			associatedSetter.data( 'raw_price', associatedRawPrice );
109			associatedSetter.data( 'raw_original_price', associatedRawOriginalPrice );
110			associatedSetter.data( 'price', associatedPrice );
111			associatedSetter.data( 'original_price', associatedOriginalPrice );
112
113			field.data( 'price-changed', 1 );
114
115			form.$form.trigger( {
116				type: 'tm-epo-update',
117				norules: 2
118			} );
119			form.currentCart.trigger( {
120				type: 'tm-epo-update',
121				norules: 2
122			} );
123		}
124	};
125
126	/**
127	 * Reset all fields.
128	 *
129	 * @param {Event} event The event Object.
130	 */
131	VariationForm.prototype.onReset = function( event ) {
132		event.preventDefault();
133		event.data.variationForm.$attributeFields.val( '' ).trigger( 'change' );
134		event.data.variationForm.$form.trigger( 'tc_reset_data' );
135	};
136
137	/**
138	 * When a variation is hidden.
139	 *
140	 * @param {Event} event The event Object.
141	 */
142	VariationForm.prototype.onHide = function( event ) {
143		event.preventDefault();
144	};
145	/**
146	 * When a variation is shown.
147	 *
148	 * @param {Event} event The event Object.
149	 */
150	VariationForm.prototype.onShow = function( event ) {
151		event.preventDefault();
152	};
153	/**
154	 * When displayed variation data is reset.
155	 *
156	 * @param {Event} event The event Object.
157	 */
158	VariationForm.prototype.onResetDisplayedVariation = function( event ) {
159		var form = event.data.variationForm;
160		form.$form.find( '.product-meta' ).find( '.tc-product-sku' ).tc_reset_content();
161		form.$form.trigger( 'tc_reset_image' );
162		form.$singleVariation.slideUp( 200 ).trigger( 'tc_hide_variation' );
163	};
164
165	/**
166	 * When the product image is reset.
167	 *
168	 * @param {Event} event The event Object.
169	 */
170	VariationForm.prototype.onResetImage = function( event ) {
171		event.data.variationForm.$form.tc_variations_image_update( false );
172	};
173
174	/**
175	 * Looks for matching variations for current selected attributes.
176	 *
177	 * @param {Event} event The event Object.
178	 */
179	VariationForm.prototype.onFindVariation = function( event ) {
180		var form = event.data.variationForm;
181		var attributes = form.getChosenAttributes();
182		var currentAttributes = attributes.data;
183		var cpfElement;
184		var matching_variations;
185		var variation;
186
187		if ( attributes.count === attributes.chosenCount ) {
188			if ( form.useAjax ) {
189				if ( typeof wc_add_to_cart_variation_params === 'undefined' ) {
190					return;
191				}
192				if ( form.xhr ) {
193					form.xhr.abort();
194				}
195				cpfElement = form.$form.closest( '.cpf-type-product' );
196				form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
197				currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 );
198				currentAttributes.discount = cpfElement.attr( 'data-discount' );
199				currentAttributes.discount_type = cpfElement.attr( 'data-discount-type' );
200				currentAttributes.discount_exclude_addons = cpfElement.attr( 'data-discount-exclude-addons' );
201
202				form.xhr = $.ajax( {
203					url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
204					type: 'POST',
205					data: currentAttributes,
206					success: function( svariation ) {
207						if ( svariation ) {
208							form.$form.trigger( 'tc_found_variation', [ svariation ] );
209						} else {
210							form.$form.trigger( 'tc_reset_data' );
211							attributes.chosenCount = 0;
212
213							if ( ! form.loading ) {
214								form.$form.find( '.tc-epo-element-single-variation' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' );
215								form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
216								form.$form.trigger( 'update_field', [] );
217							}
218						}
219					},
220					complete: function() {
221						form.$form.unblock();
222					}
223				} );
224			} else {
225				form.$form.trigger( 'tc_update_variation_values' );
226
227				matching_variations = form.findMatchingVariations( form.variationData, currentAttributes );
228				variation = matching_variations.shift();
229
230				if ( variation ) {
231					form.$form.trigger( 'tc_found_variation', [ variation ] );
232				} else {
233					form.$form.trigger( 'tc_reset_data' );
234					attributes.chosenCount = 0;
235
236					if ( ! form.loading ) {
237						form.$form.find( '.tc-epo-element-single-variation' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' );
238						form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
239					}
240				}
241			}
242		} else {
243			form.variationId.val( '' ).trigger( 'change' );
244			form.$form.trigger( 'tc_update_variation_values' );
245			form.$form.trigger( 'tc_reset_data' );
246			form.$form.trigger( 'update_field', [] );
247		}
248
249		// Show reset link.
250		form.toggleResetLink( attributes.chosenCount > 0 );
251	};
252
253	/**
254	 * Triggered when a variation has been found which matches all attributes.
255	 *
256	 * @param {Event}  event     The event Object.
257	 * @param {Object} variation The variation Object.
258	 */
259	VariationForm.prototype.onFoundVariation = function( event, variation ) {
260		var form = event.data.variationForm,
261			$sku = form.$form.find( '.product-meta' ).find( '.tc-product-sku' ),
262			$qtyWrap = form.$wrap.find( '.tm-quantity-alt' ),
263			$qty = $qtyWrap.find( 'input.tm-qty-alt' ),
264			qtyMin = $.epoAPI.math.toInt( $qty.attr( 'data-min' ) ),
265			qtyMax = $.epoAPI.math.toInt( $qty.attr( 'data-max' ) ),
266			dataQtyMax = $qty.attr( 'data-max' ),
267			purchasable = true,
268			template = false,
269			$template_html = '';
270
271		if ( variation.sku ) {
272			$sku.tc_set_content( variation.sku );
273		} else {
274			$sku.tc_reset_content();
275		}
276
277		form.$form.tc_variations_image_update( variation );
278		if ( ! variation.variation_is_visible ) {
279			template = wpTemplate( 'unavailable-variation-template' );
280		} else {
281			template = wpTemplate( 'variation-template' );
282		}
283
284		if ( form.field.attr( 'data-no-price' ) ) {
285			variation.display_price = '';
286			variation.display_regular_price = '';
287			variation.price_html = '';
288		}
289
290		form.$form.find( '.tc-epo-element-product-container-cart' ).find( '.stock' ).remove();
291
292		$template_html = template( {
293			variation: variation
294		} );
295		$template_html = $template_html.replace( '/*<![CDATA[*/', '' );
296		$template_html = $template_html.replace( '/*]]>*/', '' );
297		form.$singleVariation.html( $template_html );
298		form.variationId.val( variation.variation_id ).trigger( 'change' );
299
300		// Hide or show qty input
301		if ( variation.is_sold_individually === 'yes' ) {
302			$qty.val( '1' ).attr( 'min', '1' ).attr( 'max', '1' );
303			$qtyWrap.hide();
304		} else {
305			if ( variation.min_qty !== '' && variation.min_qty > qtyMin && qtyMin > 0 ) {
306				qtyMin = variation.min_qty;
307			}
308
309			if ( variation.max_qty !== '' && variation.max_qty < qtyMax ) {
310				qtyMax = variation.max_qty;
311			}
312
313			if ( qtyMin !== '' ) {
314				$qty.attr( 'min', qtyMin );
315			} else {
316				$qty.removeAttr( 'min' );
317			}
318			if ( dataQtyMax !== '' ) {
319				$qty.attr( 'max', qtyMax );
320			} else {
321				$qty.removeAttr( 'max' );
322			}
323
324			if ( qtyMax && $.epoAPI.math.toInt( $qty.val() ) > qtyMax ) {
325				$qty.val( qtyMax );
326			}
327			if ( qtyMin > $.epoAPI.math.toInt( $qty.val() ) ) {
328				$qty.val( qtyMin );
329			}
330			$qtyWrap.show();
331		}
332
333		if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) {
334			purchasable = false;
335			$qty.val( '0' ).attr( 'min', '0' ).attr( 'max', '0' );
336			$qtyWrap.hide();
337		}
338
339		$qty.trigger( 'change' );
340
341		// Reveal
342		if ( $.epoAPI.util.trim( form.$singleVariation.text() ) ) {
343			form.$singleVariation.slideDown( 200 ).trigger( 'tc_show_variation', [ variation, purchasable ] );
344		} else {
345			form.$singleVariation.show().trigger( 'tc_show_variation', [ variation, purchasable ] );
346		}
347		form.$form.trigger( 'update_field', [ variation ] );
348	};
349
350	/**
351	 * Triggered when an attribute field changes.
352	 *
353	 * @param {Event} event The event Object.
354	 */
355	VariationForm.prototype.onChange = function( event ) {
356		var form = event.data.variationForm;
357		var field = form.field;
358
359		form.variationId.val( '' ).trigger( 'change' );
360		form.$form.find( '.wc-no-matching-variations' ).remove();
361
362		if ( form.useAjax ) {
363			form.$form.trigger( 'tc_check_variations' );
364		} else {
365			form.$form.trigger( 'woocommerce_variation_select_change' );
366			form.$form.trigger( 'tc_check_variations' );
367		}
368
369		// Custom event for when variation selection has been changed
370		form.$form.trigger( 'woocommerce_variation_has_changed' );
371
372		setTimeout( function() {
373			field.closest( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' );
374		}, 50 );
375	};
376
377	/**
378	 * Updates attributes in the DOM to show valid values.
379	 *
380	 * @param {Event} event The event Object.
381	 */
382	VariationForm.prototype.onUpdateAttributes = function( event ) {
383		var form = event.data.variationForm,
384			attributes = form.getChosenAttributes(),
385			currentAttributes = attributes.data;
386
387		if ( form.useAjax ) {
388			return;
389		}
390
391		// Loop through selects and disable/enable options based on selections.
392		form.$attributeFields.toArray().forEach( function( el ) {
393			var current_attr_select = $( el );
394			var current_attr_name = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' );
395			var show_option_none = $( el ).data( 'show_option_none' );
396			var option_gt_filter = ':gt(0)';
397			var attached_options_count = 0;
398			var new_attr_select = $( '<select/>' );
399			var selected_attr_val = current_attr_select.val() || '';
400			var selected_attr_val_valid = true;
401			var refSelect;
402			var checkAttributes;
403			var variations;
404			var num;
405			var i;
406			var len;
407			var variationAttributes;
408			var attr_name;
409			var attr_val;
410			var variation_active;
411			var $option_elements;
412			var $option_element;
413			var option_value;
414
415			// Reference options set at first.
416			if ( ! current_attr_select.data( 'attribute_html' ) ) {
417				refSelect = current_attr_select.clone();
418
419				refSelect.find( 'option' ).prop( 'disabled', false ).prop( 'selected', false ).removeAttr( 'attached' );
420
421				current_attr_select.data( 'attribute_options', refSelect.find( 'option' + option_gt_filter ).get() ); // Legacy data attribute.
422				current_attr_select.data( 'attribute_html', refSelect.html() );
423			}
424
425			new_attr_select.html( current_attr_select.data( 'attribute_html' ) );
426
427			// The attribute of this select field should not be taken into account when calculating its matching variations:
428			// The constraints of this attribute are shaped by the values of the other attributes.
429			checkAttributes = $.extend( true, {}, currentAttributes );
430
431			checkAttributes[ current_attr_name ] = '';
432
433			variations = form.findMatchingVariations( form.variationData, checkAttributes );
434
435			// Loop through variations.
436			for ( num in variations ) {
437				if ( typeof variations[ num ] !== 'undefined' ) {
438					variationAttributes = variations[ num ].attributes;
439
440					for ( attr_name in variationAttributes ) {
441						if ( Object.prototype.hasOwnProperty.call( variationAttributes, attr_name ) ) {
442							attr_val = variationAttributes[ attr_name ];
443							variation_active = '';
444
445							if ( attr_name === current_attr_name ) {
446								if ( variations[ num ].variation_is_active ) {
447									variation_active = 'enabled';
448								}
449
450								if ( attr_val ) {
451									// Decode entities.
452									attr_val = $( '<div/>' ).html( attr_val ).text();
453
454									// Attach to matching options by value. This is done to compare
455									// TEXT values rather than any HTML entities.
456									$option_elements = new_attr_select.find( 'option' );
457									if ( $option_elements.length ) {
458										for ( i = 0, len = $option_elements.length; i < len; i++ ) {
459											$option_element = $( $option_elements[ i ] );
460											option_value = $option_element.val();
461
462											if ( attr_val === option_value ) {
463												$option_element.addClass( 'attached ' + variation_active );
464												break;
465											}
466										}
467									}
468								} else {
469									// Attach all apart from placeholder.
470									new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active );
471								}
472							}
473						}
474					}
475				}
476			}
477
478			// Count available options.
479			attached_options_count = new_attr_select.find( 'option.attached' ).length;
480
481			// Check if current selection is in attached options.
482			if ( selected_attr_val ) {
483				selected_attr_val_valid = false;
484
485				if ( 0 !== attached_options_count ) {
486					new_attr_select.find( 'option.attached.enabled' ).toArray().forEach( function( o ) {
487						var thisVal = $( o ).val();
488
489						if ( selected_attr_val === thisVal ) {
490							selected_attr_val_valid = true;
491							return false; // break.
492						}
493					} );
494				}
495			}
496
497			/**
498			 * Detach the placeholder if:
499			 * - Valid options exist.
500			 * - The current selection is non-empty.
501			 * - The current selection is valid.
502			 * - Placeholders are not set to be permanently visible.
503			 */
504			if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && 'no' === show_option_none ) {
505				new_attr_select.find( 'option:first' ).remove();
506				option_gt_filter = '';
507			}
508
509			// Detach unattached.
510			new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove();
511
512			// Finally, copy to DOM and set value.
513			current_attr_select.html( new_attr_select.html() );
514			current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true );
515
516			// Choose selected value.
517			if ( selected_attr_val ) {
518				// If the previously selected value is no longer available, fall back to the placeholder (it's going to be there).
519				if ( selected_attr_val_valid ) {
520					current_attr_select.val( selected_attr_val );
521				} else {
522					current_attr_select.val( '' ).trigger( 'change' );
523				}
524			} else {
525				current_attr_select.val( '' ); // No change event to prevent infinite loop.
526			}
527		} );
528
529		// Custom event for when variations have been updated.
530		form.$form.trigger( 'woocommerce_update_variation_values' );
531	};
532
533	/**
534	 * Get chosen attributes from form.
535	 */
536	VariationForm.prototype.getChosenAttributes = function() {
537		var data = {};
538		var count = 0;
539		var chosen = 0;
540
541		this.$attributeFields.toArray().forEach( function( f ) {
542			var field = $( f );
543			var attribute_name = field.data( 'attribute_name' ) || field.attr( 'name' );
544			var value = field.val() || '';
545
546			if ( value.length > 0 ) {
547				chosen++;
548			}
549
550			count++;
551			data[ attribute_name ] = value;
552		} );
553
554		return {
555			count: count,
556			chosenCount: chosen,
557			data: data
558		};
559	};
560
561	/**
562	 * Find matching variations for attributes.
563	 *
564	 * @param {Array} variations Variations.
565	 * @param {Array} attributes attributes.
566	 */
567	VariationForm.prototype.findMatchingVariations = function( variations, attributes ) {
568		var matching = [];
569		var i;
570		var variation;
571		for ( i = 0; i < variations.length; i++ ) {
572			variation = variations[ i ];
573
574			if ( this.isMatch( variation.attributes, attributes ) ) {
575				matching.push( variation );
576			}
577		}
578		return matching;
579	};
580
581	/**
582	 * See if attributes match.
583	 *
584	 * @param {Array} variation_attributes Variation attributes.
585	 * @param {Array} attributes           attributes.
586	 */
587	VariationForm.prototype.isMatch = function( variation_attributes, attributes ) {
588		var match = true;
589		var val1;
590		var val2;
591		var attr_name;
592		for ( attr_name in variation_attributes ) {
593			if ( Object.prototype.hasOwnProperty.call( variation_attributes, attr_name ) ) {
594				val1 = variation_attributes[ attr_name ];
595				val2 = attributes[ attr_name ];
596				if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
597					match = false;
598				}
599			}
600		}
601		return match;
602	};
603
604	/**
605	 * Show or hide the reset link.
606	 *
607	 * @param {bool} on true or false.
608	 */
609	VariationForm.prototype.toggleResetLink = function( on ) {
610		if ( on ) {
611			if ( this.$resetVariations.css( 'visibility' ) === 'hidden' ) {
612				this.$resetVariations.css( 'visibility', 'visible' ).hide().fadeIn();
613			}
614		} else {
615			this.$resetVariations.css( 'visibility', 'hidden' );
616		}
617	};
618
619	/**
620	 * Function to call tc_variation_form on jquery selector.
621	 *
622	 * @param {Object} element                   The product element
623	 * @param {Object} currentCart               The current cart form
624	 * @param {Object} variableProductContainers The container for the variable product
625	 * @param {Object} thisEpoObject             The EPO Object
626	 */
627	$.fn.tc_product_variation_form = function( element, currentCart, variableProductContainers, thisEpoObject ) {
628		new VariationForm( this, element, currentCart, variableProductContainers, thisEpoObject );
629		this.trigger( 'tc_product_variation_form' );
630		return this;
631	};
632
633	/**
634	 * Stores the default text for an element so it can be reset later
635	 *
636	 * @param {string} content Content to set.
637	 */
638	$.fn.tc_set_content = function( content ) {
639		if ( undefined === this.attr( 'data-o_content' ) ) {
640			this.attr( 'data-o_content', this.text() );
641		}
642		this.text( content );
643	};
644	/**
645	 * Stores the default text for an element so it can be reset later
646	 */
647	$.fn.tc_reset_content = function() {
648		if ( undefined !== this.attr( 'data-o_content' ) ) {
649			this.text( this.attr( 'data-o_content' ) );
650		}
651	};
652
653	/**
654	 * Stores a default attribute for an element so it can be reset later
655	 *
656	 * @param {string} attr  Attribute name.
657	 * @param {string} value Value name.
658	 */
659	$.fn.tc_set_variation_attr = function( attr, value ) {
660		if ( undefined === this.attr( 'data-o_' + attr ) ) {
661			this.attr( 'data-o_' + attr, ! this.attr( attr ) ? '' : this.attr( attr ) );
662		}
663		if ( false === value ) {
664			this.removeAttr( attr );
665		} else {
666			this.attr( attr, value );
667		}
668	};
669
670	/**
671	 * Reset a default attribute for an element so it can be reset later
672	 *
673	 * @param {string} attr Attribute name.
674	 */
675	$.fn.tc_reset_variation_attr = function( attr ) {
676		if ( undefined !== this.attr( 'data-o_' + attr ) ) {
677			this.attr( attr, this.attr( 'data-o_' + attr ) );
678		}
679	};
680	/**
681	 * Sets product images for the chosen variation
682	 *
683	 * @param {Object} variation variation object.
684	 */
685	$.fn.tc_variations_image_update = function( variation ) {
686		var $form = this,
687			$product_img_wrap = $form.find( '.tc-product-image, .woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 ),
688			$product_img = $product_img_wrap.find( '.wp-post-image' ),
689			$product_link = $product_img_wrap.find( 'a' ).eq( 0 );
690
691		if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) {
692			$product_img.tc_set_variation_attr( 'src', variation.image.src );
693			$product_img.tc_set_variation_attr( 'height', variation.image.src_h );
694			$product_img.tc_set_variation_attr( 'width', variation.image.src_w );
695			$product_img.tc_set_variation_attr( 'srcset', variation.image.srcset );
696			$product_img.tc_set_variation_attr( 'sizes', variation.image.sizes );
697			$product_img.tc_set_variation_attr( 'title', variation.image.title );
698			$product_img.tc_set_variation_attr( 'data-caption', variation.image.caption );
699			$product_img.tc_set_variation_attr( 'alt', variation.image.alt );
700			$product_img.tc_set_variation_attr( 'data-src', variation.image.full_src );
701			$product_img.tc_set_variation_attr( 'data-large_image', variation.image.full_src );
702			$product_img.tc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w );
703			$product_img.tc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h );
704			$product_img_wrap.tc_set_variation_attr( 'data-thumb', variation.image.src );
705			$product_link.tc_set_variation_attr( 'href', variation.image.full_src );
706		} else {
707			$form.tc_variations_image_reset();
708		}
709	};
710
711	/**
712	 * Reset main image to defaults.
713	 */
714	$.fn.tc_variations_image_reset = function() {
715		var $form = this,
716			$product_img_wrap = $form.find( '.tc-product-image, .woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 ),
717			$product_img = $product_img_wrap.find( '.wp-post-image' ),
718			$product_link = $product_img_wrap.find( 'a' ).eq( 0 );
719
720		$product_img.tc_reset_variation_attr( 'src' );
721		$product_img.tc_reset_variation_attr( 'width' );
722		$product_img.tc_reset_variation_attr( 'height' );
723		$product_img.tc_reset_variation_attr( 'srcset' );
724		$product_img.tc_reset_variation_attr( 'sizes' );
725		$product_img.tc_reset_variation_attr( 'title' );
726		$product_img.tc_reset_variation_attr( 'data-caption' );
727		$product_img.tc_reset_variation_attr( 'alt' );
728		$product_img.tc_reset_variation_attr( 'data-src' );
729		$product_img.tc_reset_variation_attr( 'data-large_image' );
730		$product_img.tc_reset_variation_attr( 'data-large_image_width' );
731		$product_img.tc_reset_variation_attr( 'data-large_image_height' );
732		$product_img_wrap.tc_reset_variation_attr( 'data-thumb' );
733		$product_link.tc_reset_variation_attr( 'href' );
734	};
735
736	/**
737	 * Avoids using wp.template where possible in order to be CSP compliant.
738	 * wp.template uses internally eval().
739	 *
740	 * @param {string} templateId The id of the template.
741	 */
742	function wpTemplate( templateId ) {
743		var html = document.getElementById( 'tmpl-' + templateId ).textContent;
744		var hard = false;
745		// any <# #> interpolate (evaluate).
746		hard = hard || /<#\s?data\./.test( html );
747		// any data that is NOT data.variation.
748		hard = hard || /{{{?\s?data\.(?!variation\.).+}}}?/.test( html );
749		// any data access deeper than 1 level e.g.
750		// data.variation.object.item
751		// data.variation.object['item']
752		// data.variation.array[0]
753		hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test( html );
754		if ( hard ) {
755			return window.wp.template( templateId );
756		}
757		return function template( data ) {
758			var variation = data.variation || {};
759			var replacement;
760			return html.replace( /({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g, function( _, open, key, close ) {
761				// Error in the format, ignore.
762				if ( open.length !== close.length ) {
763					return '';
764				}
765				replacement = variation[ key ] || '';
766				// {{{ }}} => interpolate (unescaped).
767				// {{  }}  => interpolate (escaped).
768				// https://codex.wordpress.org/Javascript_Reference/wp.template
769				if ( open.length === 2 ) {
770					return window.escape( replacement );
771				}
772				return replacement;
773			} );
774		};
775	}
776}( window.jQuery, window, document ) );

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.