PageSourceSearch

https://www.thebts.co.uk/wp-content/plugins/wp-easycart/design/the…/ec-option-defaults.js?ver=6_0_1

js thebts.co.uk collected 2026-10-02 11:22:04 UTC 10,402 bytes, 255 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( function() {
26	'use strict';
27
28	if ( 'undefined' === typeof jQuery ) {
29		return;
30	}
31
32	/* Levels two and up wait for their stock request to come back before being selected. */
33	var wpeasycart_option_defaults_retries  = 40;
34	var wpeasycart_option_defaults_interval = 125;
35
36	var wpeasycart_option_defaults_list = [];
37
38	/**
39	 * Option item quantity tracking decides whether picking a level triggers a stock request.
40	 * Backorders turn tracking off, exactly as the handlers in ec-store.js read it.
41	 */
42	function wpeasycart_option_defaults_tracking( product_id, rand_id ) {
43		var backorders = jQuery( document.getElementById( 'ec_allow_backorders_' + product_id + '_' + rand_id ) );
44		if ( backorders.length && '1' == backorders.val( ) ) {
45			return false;
46		}
47		return '1' == jQuery( document.getElementById( 'use_optionitem_quantity_tracking_' + product_id + '_' + rand_id ) ).val( );
48	}
49
50	function wpeasycart_option_defaults_swatches( block, product_id, rand_id, level ) {
51		return block.find( 'li.ec_option' + level + '_' + product_id + '_' + rand_id );
52	}
53
54	function wpeasycart_option_defaults_combo( block, product_id, rand_id, level ) {
55		return block.find( 'select.ec_details_combo.ec_option' + level + '_' + product_id + '_' + rand_id ).first( );
56	}
57
58	/**
59	 * Record the option item each level was rendered with, before ec-store.js gets a chance to
60	 * rewrite the swatch classes. Blocks without a default on level one are skipped.
61	 */
62	function wpeasycart_option_defaults_snapshot( ) {
63		jQuery( '.ec_details_options_basic' ).each( function( ) {
64			var block      = jQuery( this );
65			var product_id = block.attr( 'data-product-id' );
66			var rand_id    = block.attr( 'data-rand-id' );
67			if ( ! product_id || ! rand_id ) {
68				return;
69			}
70
71			var levels  = [ false, false, false, false, false, false ];
72			var deepest = 0;
73			for ( var level = 1; level <= 5; level++ ) {
74				var swatches = wpeasycart_option_defaults_swatches( block, product_id, rand_id, level );
75				var combo    = wpeasycart_option_defaults_combo( block, product_id, rand_id, level );
76				if ( ! swatches.length && ! combo.length ) {
77					continue;
78				}
79				deepest = level;
80
81				var selected = swatches.filter( '.ec_selected' ).first( );
82				if ( selected.length && selected.attr( 'data-optionitem-id' ) ) {
83					levels[ level ] = { type: 'swatch', optionitem_id: String( selected.attr( 'data-optionitem-id' ) ) };
84				} else if ( combo.length && combo.val( ) && '0' !== String( combo.val( ) ) ) {
85					levels[ level ] = { type: 'combo', optionitem_id: String( combo.val( ) ) };
86				}
87			}
88
89			if ( levels[1] ) {
90				wpeasycart_option_defaults_list.push( {
91					block: block,
92					product_id: product_id,
93					rand_id: rand_id,
94					levels: levels,
95					deepest: deepest,
96					cancelled: false
97				} );
98			}
99		} );
100	}
101
102	/**
103	 * The element for one level's default, or false while it is not selectable yet - a swatch the
104	 * store marked out of stock or hid, or a dropdown still disabled and waiting on its stock
105	 * request. Levels two and up are retried until their request lands.
106	 */
107	function wpeasycart_option_defaults_element( entry, level ) {
108		var target = entry.levels[ level ];
109		if ( 'swatch' === target.type ) {
110			var swatch = wpeasycart_option_defaults_swatches( entry.block, entry.product_id, entry.rand_id, level )
111				.filter( '[data-optionitem-id="' + target.optionitem_id + '"]' ).first( );
112			if ( ! swatch.length || ! swatch.hasClass( 'ec_active' ) || 'none' === swatch[0].style.display ) {
113				return false;
114			}
115			return swatch;
116		}
117
118		var combo = wpeasycart_option_defaults_combo( entry.block, entry.product_id, entry.rand_id, level );
119		if ( ! combo.length || combo.is( ':disabled' ) ) {
120			return false;
121		}
122		var option = combo.find( 'option' ).filter( '[value="' + target.optionitem_id + '"]' ).first( );
123		if ( ! option.length || option.is( ':disabled' ) || 'none' === option[0].style.display ) {
124			return false;
125		}
126		return combo;
127	}
128
129	/**
130	 * Level one of a product whose only option field is that level. Nothing further has to be
131	 * loaded, so this applies the selection straight from the variation data already on the page
132	 * rather than asking the server for option items that do not exist.
133	 */
134	function wpeasycart_option_defaults_apply_single( entry, element ) {
135		var product_id = entry.product_id;
136		var rand_id    = entry.rand_id;
137		var target     = entry.levels[1];
138		var quantity;
139
140		if ( 'swatch' === target.type ) {
141			element.addClass( 'ec_selected' );
142			jQuery( document.getElementById( 'ec_option1_' + product_id + '_' + rand_id ) ).val( target.optionitem_id );
143			quantity = element.attr( 'data-optionitem-quantity' );
144
145			var label = element.closest( '.ec_details_option_row' ).find( '.ec_details_option_label_selected' ).first( );
146			if ( label.length ) {
147				var image = element.find( 'img' ).first( );
148				if ( image.length ) {
149					if ( ! image.attr( 'title' ) && image.attr( 'pac_da_title' ) ) {
150						label.html( image.attr( 'pac_da_title' ) );
151					} else {
152						label.html( image.attr( 'title' ) );
153					}
154				} else {
155					label.html( element.attr( 'title' ) );
156				}
157			}
158		} else {
159			quantity = element.find( 'option:selected' ).attr( 'data-optionitem-quantity' );
160		}
161
162		if ( wpeasycart_option_defaults_tracking( product_id, rand_id ) ) {
163			var variations = window[ 'varitation_data_' + product_id + '_' + rand_id ];
164			var variation  = ( variations && variations[ target.optionitem_id + '0000' ] ) ? variations[ target.optionitem_id + '0000' ] : false;
165			if ( variation ) {
166				quantity = ( variation.tracking ) ? variation.quantity : 'inf';
167				quantity = ( variation.enabled ) ? quantity : 0;
168			}
169			if ( undefined !== quantity ) {
170				jQuery( document.getElementById( 'ec_details_stock_quantity_' + product_id + '_' + rand_id ) ).html( quantity );
171				if ( 'inf' != quantity ) {
172					jQuery( '.ec_details_stock_total' ).show( );
173				} else {
174					jQuery( '.ec_details_stock_total' ).hide( );
175				}
176				var quantity_input = jQuery( document.getElementById( 'ec_quantity_' + product_id + '_' + rand_id ) );
177				quantity_input.attr( 'max', quantity );
178				if ( Number( quantity_input.val( ) ) > Number( quantity ) ) {
179					quantity_input.val( quantity );
180				}
181			}
182		}
183
184		if ( 'function' === typeof ec_option1_image_change ) {
185			ec_option1_image_change( product_id, rand_id, target.optionitem_id, quantity );
186		}
187		jQuery( '.ec_option1_' + product_id + '_' + rand_id + '.ec_details_option_row_error' ).hide( );
188		if ( 'function' === typeof ec_details_base_adjust_price ) {
189			ec_details_base_adjust_price( product_id, rand_id );
190		}
191	}
192
193	/**
194	 * Select one level's default and move on to the next one. Levels are fired as the click or
195	 * change the shopper would make, so price, stock, images, the hidden field and the next
196	 * level's option items all update through the routines already in ec-store.js.
197	 */
198	function wpeasycart_option_defaults_apply( entry, level, tries ) {
199		if ( entry.cancelled || level > 5 || ! entry.levels[ level ] ) {
200			return;
201		}
202
203		var element = wpeasycart_option_defaults_element( entry, level );
204		if ( ! element ) {
205			/* Level one is settled by the time the page is ready; deeper levels wait on a request. */
206			if ( level > 1 && tries < wpeasycart_option_defaults_retries ) {
207				window.setTimeout( function( ) {
208					wpeasycart_option_defaults_apply( entry, level, tries + 1 );
209				}, wpeasycart_option_defaults_interval );
210			}
211			return;
212		}
213
214		if ( 1 === level && 1 === entry.deepest ) {
215			wpeasycart_option_defaults_apply_single( entry, element );
216			return;
217		}
218
219		if ( 'swatch' === entry.levels[ level ].type ) {
220			element.trigger( 'click' );
221		} else {
222			element.val( entry.levels[ level ].optionitem_id ).trigger( 'change' );
223		}
224
225		wpeasycart_option_defaults_apply( entry, level + 1, 0 );
226	}
227
228	/**
229	 * Anything the shopper does themselves wins: stop applying the remaining defaults so a
230	 * pending level is never forced on top of their own choice. Events raised by this file
231	 * carry isTrigger and are ignored here.
232	 */
233	function wpeasycart_option_defaults_watch( entry ) {
234		entry.block.on( 'click.wpeasycartOptionDefaults change.wpeasycartOptionDefaults', 'li, select', function( event ) {
235			if ( ! event.isTrigger ) {
236				entry.cancelled = true;
237			}
238		} );
239	}
240
241	jQuery( document ).ready( function( ) {
242		wpeasycart_option_defaults_snapshot( );
243		if ( ! wpeasycart_option_defaults_list.length ) {
244			return;
245		}
246
247		/* Runs once every other ready handler, ec-store.js included, has finished. */
248		window.setTimeout( function( ) {
249			for ( var i = 0; i < wpeasycart_option_defaults_list.length; i++ ) {
250				wpeasycart_option_defaults_watch( wpeasycart_option_defaults_list[ i ] );
251				wpeasycart_option_defaults_apply( wpeasycart_option_defaults_list[ i ], 1, 0 );
252			}
253		}, 0 );
254	} );
255}( ) );

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.