1/** 2 * 3 * Shoptimizer quantity custom script 4 * 5 * @package Shoptimizer 6 */ 7 8let changEv = new Event( 'change' ); 9let sidebarEl = []; 10let timerId; 11let miniItems; 12const shopWidget = document.querySelector( '.widget_shopping_cart' ); 13 14const shoptimizerDelayFunction = function( func, delay ) { 15 clearTimeout( timerId ); 16 timerId = setTimeout( func, delay ); 17}; 18function shoptimizerInitMiniCartQty() { 19 if ( ! shopWidget ) { 20 return; 21 } 22 sidebarEl = []; 23 miniItems = shopWidget.querySelectorAll( '.shoptimizer-custom-quantity-mini-cart' ); 24 miniItems.forEach( 25 function( item ) { 26 sidebarEl.push( 27 { 28 qtInput: item.querySelector( 'input' ), 29 qtButtons: Array.from( item.querySelectorAll( '.shoptimizer-custom-quantity-mini-cart_button' ) ) 30 } 31 ); 32 } 33 ); 34 sidebarEl.forEach( 35 function( item ) { 36 item.qtButtons.forEach( 37 function( btn ) { 38 btn.addEventListener( 'click', shoptimizerEachSideBtnListener ); 39 } 40 ); 41 item.qtInput.addEventListener( 'change', shoptimizerUpdateMiniCart ); 42 } 43 ); 44} 45/** 46 * Return the number of decimal places in a numeric string, used so we can 47 * round incremented values to the precision defined by the `step` attribute 48 * (important for plugins like ATUM that allow decimal stock quantities). 49 */ 50function shoptimizerDecimalPlaces( numStr ) { 51 if ( ! numStr ) { 52 return 0; 53 } 54 const parts = String( numStr ).split( '.' ); 55 return parts[1] ? parts[1].length : 0; 56} 57function shoptimizerEachSideBtnListener() { 58 const item = sidebarEl.find( ( el ) => el.qtButtons.includes( this ) ); 59 let value = parseFloat( item.qtInput.value ); 60 let stepAttr = item.qtInput.getAttribute( 'step' ); 61 let step = parseFloat( stepAttr ); 62 if ( isNaN( step ) || step <= 0 ) { 63 step = 1; 64 } 65 let minValue = parseFloat( item.qtInput.getAttribute( 'min' ) ); 66 let maxValue = parseFloat( item.qtInput.getAttribute( 'max' ) ); 67 let fallback = ! isNaN( minValue ) && minValue > 0 ? minValue : step; 68 value = isNaN( value ) ? fallback : value; 69 const decimals = Math.max( shoptimizerDecimalPlaces( stepAttr ), shoptimizerDecimalPlaces( item.qtInput.value ) ); 70 if ( this.classList.contains( 'quantity-up' ) ) { 71 value += step; 72 if ( ! isNaN( maxValue ) && value > maxValue ) { 73 value = maxValue; 74 } 75 } else { 76 value -= step; 77 if ( ! isNaN( minValue ) && value < minValue ) { 78 value = minValue; 79 } 80 // Never go below the step value (or min, if min is positive). 81 if ( value < fallback ) { 82 value = fallback; 83 } 84 } 85 // Round to the precision of the step to avoid floating-point artefacts 86 // like 0.1 + 0.2 = 0.30000000000000004. 87 if ( decimals > 0 ) { 88 value = parseFloat( value.toFixed( decimals ) ); 89 } 90 if ( parseFloat( item.qtInput.value ) !== value ) { 91 item.qtInput.value = value; 92 item.qtInput.dispatchEvent( changEv ); 93 } 94} 95function shoptimizerUpdateMiniCart() { 96 shoptimizerDelayFunction( 97 function() { 98 var formData = new FormData(); 99 miniItems = document.querySelectorAll( '.shoptimizer-custom-quantity-mini-cart' ); 100 miniItems.forEach( 101 function( item ) { 102 let input = item.querySelector( 'input' ); 103 // Use parseFloat so decimal quantities (e.g. ATUM) are preserved. 104 let qty = parseFloat( input.value ); 105 qty = isNaN( qty ) ? 1 : qty; 106 let itemKey = input.getAttribute( 'data-cart_item_key' ); 107 formData.append( 'data[' + itemKey + ']', qty ); 108 } 109 ); 110 if ( typeof shoptimizer_mini_cart !== 'undefined' && shoptimizer_mini_cart.nonce ) { 111 formData.append( 'nonce', shoptimizer_mini_cart.nonce ); 112 } 113 formData.append( 'action', 'cg_shoptimizer_update_mini_cart' ); 114 var ajax_loader = document.querySelector( '#ajax-loading' ); 115 if ( ajax_loader ) { 116 ajax_loader.style.display = 'block'; 117 } 118 fetch( 119 woocommerce_params.ajax_url, 120 { 121 method: 'POST', 122 body: formData 123 } 124 ).then( 125 function( response ) { 126 return response.json().then( 127 function( json ) { 128 if ( ! response.ok || ! json.success ) { 129 var error_text = json && json.data && json.data.message ? json.data.message : ''; 130 error_text = error_text || ( typeof shoptimizer_mini_cart !== 'undefined' && shoptimizer_mini_cart.error_text ? shoptimizer_mini_cart.error_text : 'Cart update failed.' ); 131 throw new Error( error_text ); 132 } 133 return json; 134 } 135 ); 136 } 137 ).then( 138 function() { 139 var wcfragment = new Event( 'wc_fragment_refresh' ); 140 document.body.dispatchEvent( wcfragment ); 141 var wccart = document.querySelectorAll( 'form.woocommerce-cart-form input.qty, form.woocommerce-checkout' ); 142 if ( 0 < wccart.length ) { 143 window.location.reload(); 144 } 145 }
146 ).catch( 147 function( error ) { 148 if ( ajax_loader ) { 149 ajax_loader.style.display = 'none'; 150 } 151 console.error( error ); 152 } 153 ); 154 }, 155 600 156 ); 157} 158jQuery( 'body' ).on( 'wc_fragments_refreshed wc_fragments_loaded', shoptimizerInitMiniCartQty ); 159shoptimizerInitMiniCartQty();
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.