PageSourceSearch

https://stroy-svai.ru/wp-content/plugins/woocommerce-tm-extra-prod…/assets/js/tm-epo-product.js?ver=16.3

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