1/* Woo Checkout */ 2( function () { 3 4 // perf.php excludes this file from "delay JavaScript" optimizers so the widget can appear 5 // without a user interaction. Those optimizers may therefore run it BEFORE jQuery, which is 6 // usually still delayed - and touching jQuery at eval time would throw, aborting this script 7 // and cascading into Woo's own delayed checkout scripts (breaking country/state switching and 8 // the order review refresh). So nothing here touches jQuery until it is known to exist, and 9 // everything that does not need jQuery runs independently of it. 10 11 var WIDGET_ID = 'cf-turnstile-woo-checkout'; 12 13 function cfturnstileWooWidget() { 14 return document.getElementById( WIDGET_ID ); 15 } 16 17 function cfturnstileWooOnReady( fn ) { 18 if ( document.readyState === 'loading' ) { 19 document.addEventListener( 'DOMContentLoaded', fn ); 20 } else { 21 fn(); 22 } 23 } 24 25 // Matches a delegated click without needing jQuery. 26 function cfturnstileWooClicked( e, selector ) { 27 return !!( e.target && e.target.closest && e.target.closest( selector ) ); 28 } 29 30 // Explicit rendering ignores data-*-callback, which would leave the submit button disabled. 31 function cfturnstileWooOpts( target ) { 32 return ( typeof window.cfturnstileOpts === 'function' ) ? window.cfturnstileOpts( target ) : {}; 33 } 34 35 /* Give the classic checkout widget a fresh token. Touches only the DOM and the global 36 `turnstile`, so it is safe to call at any point. */ 37 function turnstileWooCheckoutReset() { 38 if ( typeof turnstile === 'undefined' ) { 39 return; 40 } 41 42 var el = cfturnstileWooWidget(); 43 if ( !el ) { 44 return; 45 } 46 47 // Woo replaced the container and left it empty: render a new widget into it. 48 if ( !el.firstElementChild ) { 49 try { turnstile.render( el, cfturnstileWooOpts( el ) ); } catch ( e ) {} 50 return; 51 } 52 53 // Otherwise reset in place, which clears the used or expired token. 54 try { 55 turnstile.reset( el ); 56 return; 57 } catch ( e ) {} 58 59 // reset() only fails when Turnstile no longer recognises the element, so rebuild it. 60 try { turnstile.remove( el ); } catch ( e ) {} 61 try { turnstile.render( el, cfturnstileWooOpts( el ) ); } catch ( e ) {} 62 } 63 64 /* "Show login" toggle: rebuild that widget once the panel is open. Bound natively on document 65 so it survives fragment refreshes and works whether or not jQuery has loaded. */ 66 document.addEventListener( 'click', function ( e ) { 67 if ( !cfturnstileWooClicked( e, '.showlogin, .show-login, .e-show-login, .woocommerce-form-login-toggle a' ) ) { 68 return; 69 } 70 setTimeout( function () { 71 if ( typeof turnstile === 'undefined' ) { 72 return; 73 } 74 try { turnstile.remove( '.sct-woocommerce-login' ); } catch ( err ) {} 75 try { turnstile.render( '.sct-woocommerce-login', cfturnstileWooOpts( '.sct-woocommerce-login' ) ); } catch ( err ) {} 76 }, 250 ); 77 } ); 78 79 /* Classic checkout: keep the widget valid across Woo's fragment refreshes. Woo fires these as 80 jQuery custom events, so this is the one part that genuinely needs jQuery. */ 81 function cfturnstileWooRun() { 82 83 var $ = window.jQuery; 84 85 $( document ).ready( function () { 86 87 var attempted = false; 88 89 function hasCheckoutError() { 90 return !!document.querySelector( '.woocommerce-error' ); 91 } 92 93 // Only reset after a real submission attempt, not on every checkout refresh. 94 $( document.body ).on( 'submit', 'form.checkout', function () { 95 attempted = true; 96 } ); 97 98 // Page loaded with an error already showing: the next submit needs a fresh token. 99 if ( hasCheckoutError() ) { 100 setTimeout( turnstileWooCheckoutReset, 50 ); 101 } 102 103 $( document.body ).on( 'update_checkout updated_checkout applied_coupon_in_checkout removed_coupon_in_checkout', function () { 104 var el = cfturnstileWooWidget(); 105 106 // Container was replaced or emptied: re-render once the DOM has settled. 107 if ( !el || !el.firstElementChild ) {
108 setTimeout( turnstileWooCheckoutReset, 300 ); 109 return; 110 } 111 112 // Woo refreshes fragments after a failed submit; clear the used token. 113 if ( attempted && hasCheckoutError() ) { 114 setTimeout( turnstileWooCheckoutReset, 300 ); 115 attempted = false; 116 } 117 } ); 118 119 // Fired when the AJAX checkout submission comes back with an error. 120 $( document.body ).on( 'checkout_error', function () { 121 setTimeout( turnstileWooCheckoutReset, 500 ); 122 attempted = false; 123 } ); 124 } ); 125 } 126 127 /* Woo Checkout Block. React based and needs nothing from jQuery, so it must not wait on a 128 jQuery that a delay-JS optimizer may only load on the first interaction. PHP does not emit 129 a render script for this widget on a block checkout, so this is what renders it. */ 130 function cfturnstileWooBlockRun() { 131 132 // Guard against the classic (shortcode) checkout, which uses the same widget id - without 133 // this the block code would tear down and re-render the classic widget while wiring it to 134 // the block-only wc/store/checkout. wp.data is always defined (declared as a dependency). 135 if ( !document.querySelector( '.wp-block-woocommerce-checkout, .wc-block-checkout' ) || typeof wp === 'undefined' || !wp.data ) { 136 return; 137 } 138 139 function setExtensionData( token ) { 140 var dispatch = wp.data.dispatch( 'wc/store/checkout' ); 141 if ( !dispatch ) { 142 return; 143 } 144 if ( typeof dispatch.setExtensionData === 'function' ) { 145 dispatch.setExtensionData( 'simple-cloudflare-turnstile', { token: token } ); 146 } else if ( typeof dispatch.__internalSetExtensionData === 'function' ) { 147 dispatch.__internalSetExtensionData( 'simple-cloudflare-turnstile', { token: token } ); 148 } 149 } 150 151 function renderBlockWidget() { 152 // The API is loaded with render=explicit and may still be in flight. Bail quietly - 153 // the subscription below runs on every cart change and calls back once it lands. 154 if ( typeof turnstile === 'undefined' ) { 155 return; 156 } 157 158 var el = cfturnstileWooWidget(); 159 if ( !el ) { 160 return; 161 } 162 163 // Already up: reset in place, which clears the token but keeps the widget. 164 if ( el.getAttribute( 'data-sct-init' ) === 'true' && el.firstElementChild ) { 165 try { 166 turnstile.reset( el ); 167 setExtensionData( '' ); 168 return; 169 } catch ( e ) {} 170 } 171 172 try { turnstile.remove( el ); } catch ( e ) {} 173 174 try { 175 turnstile.render( el, { 176 sitekey: el.dataset.sitekey, 177 appearance: el.dataset.appearance || 'always', 178 callback: setExtensionData, 179 'expired-callback': function () { setExtensionData( '' ); } 180 } ); 181 el.setAttribute( 'data-sct-init', 'true' ); 182 } catch ( e ) {} 183 } 184 185 // Refresh the token shortly after Place order is clicked. 186 var clickTimer = null; 187 document.addEventListener( 'click', function ( e ) { 188 if ( !cfturnstileWooClicked( e, '.wc-block-components-checkout-place-order-button' ) ) { 189 return; 190 } 191 clearTimeout( clickTimer ); 192 clickTimer = setTimeout( renderBlockWidget, 2000 ); 193 } ); 194 195 // Render whenever the cart store changes and the widget is missing or uninitialised. This 196 // runs on every change, so test for a child element rather than serializing innerHTML. 197 wp.data.subscribe( function () { 198 var el = cfturnstileWooWidget(); 199 if ( el && ( !el.firstElementChild || el.getAttribute( 'data-sct-init' ) !== 'true' ) ) { 200 renderBlockWidget(); 201 } 202 }, 'wc/store/cart' ); 203 204 renderBlockWidget(); 205 } 206 207 // The block checkout needs no jQuery, so boot it as soon as the DOM is ready. 208 cfturnstileWooOnReady( cfturnstileWooBlockRun ); 209 210 // The classic checkout handlers are jQuery based. When a delay-JS optimizer runs this file 211 // ahead of jQuery, poll for it (jQuery loads on the first interaction) instead of throwing. 212 if ( typeof window.jQuery !== 'undefined' ) { 213 cfturnstileWooRun(); 214 } else { 215 var tries = 0; 216 var wait = setInterval( function () { 217 if ( typeof window.jQuery !== 'undefined' ) { 218 clearInterval( wait ); 219 cfturnstileWooRun(); 220 } else if ( ++tries > 1200 ) { // ~60s safety cap 221 clearInterval( wait ); 222 } 223 }, 50 ); 224 } 225 226} )();
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.