1/* Woo Checkout */ 2( function () { 3 4 var WIDGET_ID = 'cf-turnstile-woo-checkout'; 5 6 // Prefer the widget inside the checkout form, as Divi can leave stray copies 7 function cfturnstileWooWidget() { 8 return document.querySelector( 'form.checkout #' + WIDGET_ID ) || document.getElementById( WIDGET_ID ); 9 } 10 11 function cfturnstileWooOnReady( fn ) { 12 if ( document.readyState === 'loading' ) { 13 document.addEventListener( 'DOMContentLoaded', fn ); 14 } else { 15 fn(); 16 } 17 } 18 19 function cfturnstileWooClicked( e, selector ) { 20 return !!( e.target && e.target.closest && e.target.closest( selector ) ); 21 } 22 23 // Explicit render ignores data-*-callback, so pass the options in 24 function cfturnstileWooOpts( target ) { 25 return ( typeof window.cfturnstileOpts === 'function' ) ? window.cfturnstileOpts( target ) : {}; 26 } 27 28 // Rendered but no token yet. Checks every input, as turnstile.remove() can leave an empty one behind 29 function cfturnstileWooAwaitingToken( el ) { 30 var inputs = el.querySelectorAll( 'input[name="cf-turnstile-response"]' ); 31 for ( var i = 0; i < inputs.length; i++ ) { 32 if ( inputs[ i ].value ) { 33 return false; 34 } 35 } 36 return inputs.length > 0; 37 } 38 39 /* Give the classic checkout widget a fresh token */ 40 function turnstileWooCheckoutReset() { 41 if ( typeof turnstile === 'undefined' ) { 42 return; 43 } 44 45 var el = cfturnstileWooWidget(); 46 if ( !el ) { 47 return; 48 } 49 50 // Empty container, render a new widget 51 if ( !el.firstElementChild ) { 52 try { turnstile.render( el, cfturnstileWooOpts( el ) ); } catch ( e ) {} 53 return; 54 } 55 56 // No token yet, so nothing to clear 57 if ( cfturnstileWooAwaitingToken( el ) ) { 58 return; 59 } 60 61 // Otherwise reset in place 62 try { 63 turnstile.reset( el ); 64 return; 65 } catch ( e ) {} 66 67 // reset() failed, so rebuild it 68 try { turnstile.remove( el ); } catch ( e ) {} 69 try { turnstile.render( el, cfturnstileWooOpts( el ) ); } catch ( e ) {} 70 } 71 72 /* "Show login" toggle: rebuild the login widget once the panel is open */ 73 document.addEventListener( 'click', function ( e ) { 74 if ( !cfturnstileWooClicked( e, '.showlogin, .show-login, .e-show-login, .woocommerce-form-login-toggle a' ) ) { 75 return; 76 } 77 setTimeout( function () { 78 if ( typeof turnstile === 'undefined' ) { 79 return; 80 } 81 try { turnstile.remove( '.sct-woocommerce-login' ); } catch ( err ) {} 82 try { turnstile.render( '.sct-woocommerce-login', cfturnstileWooOpts( '.sct-woocommerce-login' ) ); } catch ( err ) {} 83 }, 250 ); 84 } ); 85 86 /* Classic checkout: keep the widget valid across fragment refreshes */ 87 function cfturnstileWooRun() { 88 89 var $ = window.jQuery; 90 91 $( document ).ready( function () { 92 93 var attempted = false; 94 95 function hasCheckoutError() { 96 return !!document.querySelector( '.woocommerce-error' ); 97 } 98 99 // Only reset after a real submission attempt, not on every checkout refresh. 100 $( document.body ).on( 'submit', 'form.checkout', function () { 101 attempted = true; 102 } ); 103 104 // Error already showing on page load 105 if ( hasCheckoutError() ) { 106 setTimeout( turnstileWooCheckoutReset, 50 ); 107 } 108 109 $( document.body ).on( 'update_checkout updated_checkout applied_coupon_in_checkout removed_coupon_in_checkout', function () { 110 var el = cfturnstileWooWidget(); 111 112 // Container replaced or emptied, re-render 113 if ( !el || !el.firstElementChild ) {
114 setTimeout( turnstileWooCheckoutReset, 300 ); 115 return; 116 } 117 118 // Failed submit, clear the used token 119 if ( attempted && hasCheckoutError() ) { 120 setTimeout( turnstileWooCheckoutReset, 300 ); 121 attempted = false; 122 } 123 } ); 124 125 // Fired when the AJAX checkout submission comes back with an error. 126 $( document.body ).on( 'checkout_error', function () { 127 setTimeout( turnstileWooCheckoutReset, 500 ); 128 attempted = false; 129 } ); 130 } ); 131 } 132 133 /* Woo Checkout Block */ 134 function cfturnstileWooBlockRun() { 135 136 function setExtensionData( token ) { 137 var dispatch = wp.data.dispatch( 'wc/store/checkout' ); 138 if ( !dispatch ) { 139 return; 140 } 141 if ( typeof dispatch.setExtensionData === 'function' ) { 142 dispatch.setExtensionData( 'simple-cloudflare-turnstile', { token: token } ); 143 } else if ( typeof dispatch.__internalSetExtensionData === 'function' ) { 144 dispatch.__internalSetExtensionData( 'simple-cloudflare-turnstile', { token: token } ); 145 } 146 } 147 148 var widgetId = null; 149 var renderedEl = null; 150 var submitted = false; 151 152 function renderBlockWidget() { 153 // API may still be loading, the subscription below retries 154 if ( typeof turnstile === 'undefined' ) { 155 return; 156 } 157 158 var el = cfturnstileWooWidget(); 159 if ( !el ) { 160 return; 161 } 162 163 // Already rendered into this element, reset in place 164 if ( el === renderedEl && el.firstElementChild ) { 165 // No token yet, so nothing to clear 166 if ( cfturnstileWooAwaitingToken( el ) ) { 167 return; 168 } 169 try { 170 turnstile.reset( el ); 171 setExtensionData( '' ); 172 return; 173 } catch ( e ) {} 174 } 175 176 // Woo can swap the container for a copy, so drop the old widget by id 177 if ( widgetId !== null ) { 178 try { turnstile.remove( widgetId ); } catch ( e ) {} 179 widgetId = null; 180 } 181 try { turnstile.remove( el ); } catch ( e ) {} 182 183 try { 184 widgetId = turnstile.render( el, { 185 sitekey: el.dataset.sitekey, 186 appearance: el.dataset.appearance || 'always', 187 callback: setExtensionData, 188 'expired-callback': function () { setExtensionData( '' ); } 189 } ); 190 renderedEl = el; 191 } catch ( e ) {} 192 } 193 194 // The order was sent but failed, possibly at the gateway after it (e.g. 3DS), so the token may be spent 195 function resetAfterFailedSubmit() { 196 var checkout = wp.data.select( 'wc/store/checkout' ); 197 if ( !checkout || typeof checkout.isProcessing !== 'function' || typeof checkout.hasError !== 'function' ) { 198 return; 199 } 200 if ( checkout.isProcessing() ) { 201 submitted = true; 202 return; 203 } 204 // Gateway still working, e.g. 3DS 205 if ( !submitted || ( checkout.isAfterProcessing() && !checkout.hasError() ) ) { 206 return; 207 } 208 submitted = false; 209 if ( checkout.hasError() || checkout.isIdle() ) { 210 renderBlockWidget(); 211 } 212 } 213 214 // Render when the widget is missing or Woo replaced its container 215 function onStoreChange() { 216 var el = cfturnstileWooWidget(); 217 if ( el && ( el !== renderedEl || !el.firstElementChild ) ) { 218 renderBlockWidget(); 219 } 220 resetAfterFailedSubmit(); 221 } 222 wp.data.subscribe( onStoreChange, 'wc/store/cart' ); 223 wp.data.subscribe( onStoreChange, 'wc/store/checkout' ); 224 225 renderBlockWidget(); 226 } 227 228 // Run fn once ready() passes, as a delay-JS optimizer only loads our dependencies on the first interaction 229 function cfturnstileWooWhen( ready, fn ) { 230 if ( ready() ) { 231 fn(); 232 return; 233 } 234 var tries = 0; 235 var wait = setInterval( function () { 236 if ( ready() ) { 237 clearInterval( wait ); 238 fn(); 239 } else if ( ++tries > 6000 ) { // ~10 min safety cap 240 clearInterval( wait ); 241 } 242 }, 100 ); 243 } 244 245 // Block checkout only, the classic checkout uses the same widget id 246 cfturnstileWooOnReady( function () { 247 if ( document.querySelector( '.wp-block-woocommerce-checkout, .wc-block-checkout' ) ) { 248 cfturnstileWooWhen( function () { 249 return typeof wp !== 'undefined' && !!wp.data && !!wp.data.select( 'wc/store/checkout' ); 250 }, cfturnstileWooBlockRun ); 251 } 252 } ); 253 254 // Classic checkout: queue the widget if nothing else did, so it does not depend on Woo's jQuery events 255 cfturnstileWooOnReady( function () { 256 var el = document.querySelector( 'form.checkout #' + WIDGET_ID ); 257 var queue = window.cfturnstileQueue = window.cfturnstileQueue || []; 258 if ( el && !el.firstElementChild && queue.indexOf( '-woo-checkout' ) === -1 ) { 259 queue.push( '-woo-checkout' ); 260 if ( typeof window.cfturnstileRender === 'function' ) { 261 window.cfturnstileRender(); 262 } 263 } 264 } ); 265 266 cfturnstileWooWhen( function () { return typeof window.jQuery !== 'undefined'; }, cfturnstileWooRun ); 267 268} )();
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.