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.