PageSourceSearch

https://sarahhattonartist.com/wp-content/plugins/wp-easycart/desig…/ec-option-defaults.js?ver=6_0_2

js sarahhattonartist.com collected 2026-10-04 06:01:51 UTC 12,181 bytes, 284 lines download raw bytes

1/**
2 * WP EasyCart - default option selections on the product detail page. @since 6.0.0
3 *
4 * The product templates already mark an option item as chosen in the markup when the merchant
5 * flagged it "selected by default", or when a ?<url_var>= / ?o<option_id>= URL variable matches
6 * one of its names. Nothing ever ran the selection routine for that pre-chosen item on load, so:
7 *
8 *   - a second level field ( the sizes for the chosen color ) stayed empty and disabled until
9 *     the shopper clicked a swatch that already looked selected,
10 *   - the hidden ec_option1 field stayed at 0, so add to cart reported a missing option,
11 *   - Your Price, the stock count and the option item image kept the product defaults,
12 *   - on a single level swatch field, ec_option1_init_swatches( ) in ec-store.js cleared the
13 *     ec_selected class the template had just written, so the default lost its highlight.
14 *
15 * This file snapshots what the server rendered before any other script touches it, then applies
16 * the selection once the rest of the store script has finished loading. Levels two and up are
17 * applied by firing the same click / change the shopper would, so there is exactly one request
18 * for the next level's stock - the same one a click makes - and no duplicate of it. A single
19 * level field needs no request at all and is applied directly from the variation data the
20 * template already printed.
21 *
22 * It lives in its own file, enqueued ahead of ec-store.js, so that stores running the minified
23 * build or a theme copy of ec-store.js in wp-easycart-data still receive the fix.
24 *
25 * 6.0.2: window.wpeasycart_option_defaults( container ) does the same for one part of the page, so
26 * product widgets drawn after the page loaded ( Elementor's editor, popups, Loop Grid "load more",
27 * quick views ) get their default selections too. ec-store.js calls it at the start of
28 * wpeasycart_init( container ), before that sets the swatches up. Every option block is looked at
29 * once ( a flag on the element ), so the page's own pass on ready and those later calls never
30 * apply a block twice; on a normal page the ready pass below runs first and nothing changes.
31 */
32( function() {
33	'use strict';
34
35	if ( 'undefined' === typeof jQuery ) {
36		return;
37	}
38
39	/* Levels two and up wait for their stock request to come back before being selected. */
40	var wpeasycart_option_defaults_retries  = 40;
41	var wpeasycart_option_defaults_interval = 125;
42
43	/**
44	 * Option item quantity tracking decides whether picking a level triggers a stock request.
45	 * Backorders turn tracking off, exactly as the handlers in ec-store.js read it.
46	 */
47	function wpeasycart_option_defaults_tracking( product_id, rand_id ) {
48		var backorders = jQuery( document.getElementById( 'ec_allow_backorders_' + product_id + '_' + rand_id ) );
49		if ( backorders.length && '1' == backorders.val( ) ) {
50			return false;
51		}
52		return '1' == jQuery( document.getElementById( 'use_optionitem_quantity_tracking_' + product_id + '_' + rand_id ) ).val( );
53	}
54
55	function wpeasycart_option_defaults_swatches( block, product_id, rand_id, level ) {
56		return block.find( 'li.ec_option' + level + '_' + product_id + '_' + rand_id );
57	}
58
59	function wpeasycart_option_defaults_combo( block, product_id, rand_id, level ) {
60		return block.find( 'select.ec_details_combo.ec_option' + level + '_' + product_id + '_' + rand_id ).first( );
61	}
62
63	/**
64	 * Record the option item each level was rendered with, before ec-store.js gets a chance to
65	 * rewrite the swatch classes. Blocks without a default on level one are skipped. 6.0.2: only
66	 * the blocks inside container ( and container itself ) not looked at before; returns their entries.
67	 */
68	function wpeasycart_option_defaults_snapshot( container ) {
69		var list  = [];
70		var scope = jQuery( container || document );
71		scope.find( '.ec_details_options_basic' ).add( scope.filter( '.ec_details_options_basic' ) ).each( function( ) {
72			if ( this.wpeasycartOptionDefaults ) {
73				return;
74			}
75			this.wpeasycartOptionDefaults = true;
76			var block      = jQuery( this );
77			var product_id = block.attr( 'data-product-id' );
78			var rand_id    = block.attr( 'data-rand-id' );
79			if ( ! product_id || ! rand_id ) {
80				return;
81			}
82
83			var levels  = [ false, false, false, false, false, false ];
84			var deepest = 0;
85			for ( var level = 1; level <= 5; level++ ) {
86				var swatches = wpeasycart_option_defaults_swatches( block, product_id, rand_id, level );
87				var combo    = wpeasycart_option_defaults_combo( block, product_id, rand_id, level );
88				if ( ! swatches.length && ! combo.length ) {
89					continue;
90				}
91				deepest = level;
92
93				var selected = swatches.filter( '.ec_selected' ).first( );
94				if ( selected.length && selected.attr( 'data-optionitem-id' ) ) {
95					levels[ level ] = { type: 'swatch', optionitem_id: String( selected.attr( 'data-optionitem-id' ) ) };
96				} else if ( combo.length && combo.val( ) && '0' !== String( combo.val( ) ) ) {
97					levels[ level ] = { type: 'combo', optionitem_id: String( combo.val( ) ) };
98				}
99			}
100
101			if ( levels[1] ) {
102				list.push( {
103					block: block,
104					product_id: product_id,
105					rand_id: rand_id,
106					levels: levels,
107					deepest: deepest,
108					cancelled: false
109				} );
110			}
111		} );
112		return list;
113	}
114
115	/**
116	 * The element for one level's default, or false while it is not selectable yet - a swatch the
117	 * store marked out of stock or hid, or a dropdown still disabled and waiting on its stock
118	 * request. Levels two and up are retried until their request lands.
119	 */
120	function wpeasycart_option_defaults_element( entry, level ) {
121		var target = entry.levels[ level ];
122		if ( 'swatch' === target.type ) {
123			var swatch = wpeasycart_option_defaults_swatches( entry.block, entry.product_id, entry.rand_id, level )
124				.filter( '[data-optionitem-id="' + target.optionitem_id + '"]' ).first( );
125			if ( ! swatch.length || ! swatch.hasClass( 'ec_active' ) || 'none' === swatch[0].style.display ) {
126				return false;
127			}
128			return swatch;
129		}
130
131		var combo = wpeasycart_option_defaults_combo( entry.block, entry.product_id, entry.rand_id, level );
132		if ( ! combo.length || combo.is( ':disabled' ) ) {
133			return false;
134		}
135		var option = combo.find( 'option' ).filter( '[value="' + target.optionitem_id + '"]' ).first( );
136		if ( ! option.length || option.is( ':disabled' ) || 'none' === option[0].style.display ) {
137			return false;
138		}
139		return combo;
140	}
141
142	/**
143	 * Level one of a product whose only option field is that level. Nothing further has to be
144	 * loaded, so this applies the selection straight from the variation data already on the page
145	 * rather than asking the server for option items that do not exist.
146	 */
147	function wpeasycart_option_defaults_apply_single( entry, element ) {
148		var product_id = entry.product_id;
149		var rand_id    = entry.rand_id;
150		var target     = entry.levels[1];
151		var quantity;
152
153		if ( 'swatch' === target.type ) {
154			element.addClass( 'ec_selected' );
155			jQuery( document.getElementById( 'ec_option1_' + product_id + '_' + rand_id ) ).val( target.optionitem_id );
156			quantity = element.attr( 'data-optionitem-quantity' );
157
158			var label = element.closest( '.ec_details_option_row' ).find( '.ec_details_option_label_selected' ).first( );
159			if ( label.length ) {
160				var image = element.find( 'img' ).first( );
161				if ( image.length ) {
162					if ( ! image.attr( 'title' ) && image.attr( 'pac_da_title' ) ) {
163						label.html( image.attr( 'pac_da_title' ) );
164					} else {
165						label.html( image.attr( 'title' ) );
166					}
167				} else {
168					label.html( element.attr( 'title' ) );
169				}
170			}
171		} else {
172			quantity = element.find( 'option:selected' ).attr( 'data-optionitem-quantity' );
173		}
174
175		if ( wpeasycart_option_defaults_tracking( product_id, rand_id ) ) {
176			var variations = window[ 'varitation_data_' + product_id + '_' + rand_id ];
177			var variation  = ( variations && variations[ target.optionitem_id + '0000' ] ) ? variations[ target.optionitem_id + '0000' ] : false;
178			if ( variation ) {
179				quantity = ( variation.tracking ) ? variation.quantity : 'inf';
180				quantity = ( variation.enabled ) ? quantity : 0;
181			}
182			if ( undefined !== quantity ) {
183				jQuery( document.getElementById( 'ec_details_stock_quantity_' + product_id + '_' + rand_id ) ).html( quantity );
184				if ( 'inf' != quantity ) {
185					jQuery( '.ec_details_stock_total' ).show( );
186				} else {
187					jQuery( '.ec_details_stock_total' ).hide( );
188				}
189				var quantity_input = jQuery( document.getElementById( 'ec_quantity_' + product_id + '_' + rand_id ) );
190				quantity_input.attr( 'max', quantity );
191				if ( Number( quantity_input.val( ) ) > Number( quantity ) ) {
192					quantity_input.val( quantity );
193				}
194			}
195		}
196
197		if ( 'function' === typeof ec_option1_image_change ) {
198			ec_option1_image_change( product_id, rand_id, target.optionitem_id, quantity );
199		}
200		jQuery( '.ec_option1_' + product_id + '_' + rand_id + '.ec_details_option_row_error' ).hide( );
201		if ( 'function' === typeof ec_details_base_adjust_price ) {
202			ec_details_base_adjust_price( product_id, rand_id );
203		}
204	}
205
206	/**
207	 * Select one level's default and move on to the next one. Levels are fired as the click or
208	 * change the shopper would make, so price, stock, images, the hidden field and the next
209	 * level's option items all update through the routines already in ec-store.js.
210	 */
211	function wpeasycart_option_defaults_apply( entry, level, tries ) {
212		if ( entry.cancelled || level > 5 || ! entry.levels[ level ] ) {
213			return;
214		}
215		/* 6.0.2: a block redrawn away meanwhile ( the Elementor editor draws a widget again ) is left alone. */
216		if ( ! jQuery.contains( document.documentElement, entry.block[0] ) ) {
217			entry.cancelled = true;
218			return;
219		}
220
221		var element = wpeasycart_option_defaults_element( entry, level );
222		if ( ! element ) {
223			/* Level one is settled by the time the page is ready; deeper levels wait on a request. */
224			if ( level > 1 && tries < wpeasycart_option_defaults_retries ) {
225				window.setTimeout( function( ) {
226					wpeasycart_option_defaults_apply( entry, level, tries + 1 );
227				}, wpeasycart_option_defaults_interval );
228			}
229			return;
230		}
231
232		if ( 1 === level && 1 === entry.deepest ) {
233			wpeasycart_option_defaults_apply_single( entry, element );
234			return;
235		}
236
237		if ( 'swatch' === entry.levels[ level ].type ) {
238			element.trigger( 'click' );
239		} else {
240			element.val( entry.levels[ level ].optionitem_id ).trigger( 'change' );
241		}
242
243		wpeasycart_option_defaults_apply( entry, level + 1, 0 );
244	}
245
246	/**
247	 * Anything the shopper does themselves wins: stop applying the remaining defaults so a
248	 * pending level is never forced on top of their own choice. Events raised by this file
249	 * carry isTrigger and are ignored here.
250	 */
251	function wpeasycart_option_defaults_watch( entry ) {
252		entry.block.on( 'click.wpeasycartOptionDefaults change.wpeasycartOptionDefaults', 'li, select', function( event ) {
253			if ( ! event.isTrigger ) {
254				entry.cancelled = true;
255			}
256		} );
257	}
258
259	/**
260	 * 6.0.2: snapshot the option blocks inside container now, and select their defaults once the
261	 * code running now ( the ready handlers, or the wpeasycart_init() call that asked ) has finished.
262	 *
263	 * @param {Element|jQuery|Document} container Where to look ( default: the page ).
264	 */
265	function wpeasycart_option_defaults( container ) {
266		var list = wpeasycart_option_defaults_snapshot( container );
267		if ( ! list.length ) {
268			return;
269		}
270
271		/* Runs once every other ready handler ( or the rest of wpeasycart_init() ), ec-store.js included, has finished. */
272		window.setTimeout( function( ) {
273			for ( var i = 0; i < list.length; i++ ) {
274				wpeasycart_option_defaults_watch( list[ i ] );
275				wpeasycart_option_defaults_apply( list[ i ], 1, 0 );
276			}
277		}, 0 );
278	}
279	window.wpeasycart_option_defaults = wpeasycart_option_defaults;
280
281	jQuery( document ).ready( function( ) {
282		wpeasycart_option_defaults( document );
283	} );
284}( ) );

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.