PageSourceSearch

https://napac.org.uk/wp-content/plugins/simple-cloudflare-turnstile/js/integrations/woocommerce.js?ver=2.0

js napac.org.uk collected 2026-09-24 09:39:33 UTC 9,798 bytes, 227 lines download raw bytes

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.