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