1(function () { 2 'use strict'; 3 4 /* ââ Server-side data ââââââââââââââââââââââââââââââââââââââââââââââââââââ 5 * Injected by wp_add_inline_script() in the plugin PHP file. 6 * cf7ncspData = { embedded, endpoint, ttl, minTime, msgFail } 7 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 8 var embedded = cf7ncspData.embedded; 9 var ENDPOINT = cf7ncspData.endpoint; 10 var TTL = cf7ncspData.ttl; 11 var MIN_TIME = cf7ncspData.minTime; 12 var MSG_FAIL = cf7ncspData.msgFail; 13 14 /* ââ Stale-token detection âââââââââââââââââââââââââââââââââââââââââââââââ 15 * A token is considered valid when: 16 * - Its timestamp is not in the future (guards against clock skew). 17 * - It will not expire before the user has time to submit the form. 18 * We subtract MIN_TIME from TTL to leave a safe submission window. 19 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 20 function embeddedIsValid() { 21 var now = Math.floor( Date.now() / 1000 ); 22 var age = now - parseInt( embedded.ts, 10 ); 23 return age >= 0 && age < ( TTL - MIN_TIME ); 24 } 25 26 /* ââ Form field injection ââââââââââââââââââââââââââââââââââââââââââââââââ 27 * Writes token, timestamp, and nonce into the three hidden fields. 28 * Safe to call multiple times; each call overwrites the previous value. 29 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 30 function fillForm( form, payload ) { 31 var t = form.querySelector( '[name="form-token"]' ); 32 var s = form.querySelector( '[name="form-ts"]' ); 33 var n = form.querySelector( '[name="form-nonce"]' ); 34 if ( t ) t.value = payload.token; 35 if ( s ) s.value = payload.ts; 36 if ( n ) n.value = payload.nonce; 37 } 38 39 /* ââ REST fallback âââââââââââââââââââââââââââââââââââââââââââââââââââââââ 40 * Fetches a fresh token from the server. A 3-second timeout prevents 41 * the form from hanging indefinitely on slow connections. 42 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 43 function fetchFreshToken() { 44 var fetchPromise = fetch( ENDPOINT, { credentials: 'same-origin' } ) 45 .then( function ( r ) { 46 if ( ! r.ok ) throw new Error( 'cf7ncsp: token request failed (' + r.status + ')' ); 47 return r.json(); 48 } ); 49 50 var timeout = new Promise( function ( _, reject ) { 51 setTimeout( function () { 52 reject( new Error( 'cf7ncsp: token request timed out' ) ); 53 }, 3000 ); 54 } ); 55 56 return Promise.race( [ fetchPromise, timeout ] ); 57 } 58 59 /* ââ Per-form initialisation âââââââââââââââââââââââââââââââââââââââââââââ 60 * Each form on the page gets its own token promise and submit guard. 61 * Forms are independent: a slow fetch on one does not block another. 62 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 63 function initForm( form ) { 64 65 /* 66 * `ready` is a Promise that resolves to a valid payload object. 67 * 68 * - Embedded token is fresh â resolves immediately (no network call). 69 * - Embedded token is stale â resolves after the REST fetch completes. 70 * 71 * Either way the form fields are populated as soon as possible, 72 * before the user hits Submit. 73 */ 74 var ready = embeddedIsValid() 75 ? Promise.resolve( embedded ) 76 : fetchFreshToken(); 77 78 ready.then( function ( payload ) { fillForm( form, payload ); } ); 79 80 /* 81 * Submission guard. 82 * Prevents double-click races and ensures a token is always present 83 * before CF7 fires its AJAX request. 84 */ 85 var locking = false;
86 87 /* 88 * Listen in the CAPTURE phase so this handler runs before CF7's own 89 * submit listener. If the token field is already populated we do 90 * nothing and let CF7 proceed normally. 91 * 92 * If the field is empty (token fetch still in flight or failed): 93 * 1. Prevent the default form submission. 94 * 2. Wait for (or retry) the token fetch. 95 * 3. Re-trigger via btn.click() to hand control back to CF7. 96 * 97 * Using btn.click() rather than form.submit() is critical â it 98 * preserves CF7's internal AJAX flow and validation pipeline. 99 */ 100 form.addEventListener( 'submit', function ( e ) { 101 102 var tokenField = form.querySelector( '[name="form-token"]' ); 103 104 // Token is present â CF7 can proceed uninterrupted. 105 if ( tokenField && tokenField.value ) return; 106 107 // Guard against re-entrant calls (e.g. rapid double-click). 108 if ( locking ) return; 109 locking = true; 110 111 e.preventDefault(); 112 e.stopImmediatePropagation(); 113 114 var btn = form.querySelector( 'button, input[type="submit"]' ); 115 if ( btn ) btn.disabled = true; 116 117 // Chain onto the in-flight promise; retry once on failure. 118 ready = ready 119 .catch( function () { return fetchFreshToken(); } ) 120 .then( function ( payload ) { 121 fillForm( form, payload ); 122 locking = false; 123 if ( btn ) btn.disabled = false; 124 if ( btn ) btn.click(); // re-triggers CF7's submit handler 125 } ) 126 .catch( function () { 127 locking = false; 128 if ( btn ) btn.disabled = false; 129 alert( MSG_FAIL ); 130 } ); 131 132 }, true ); // true = capture phase â must run before CF7's listener 133 134 /* 135 * Re-arm the form after every submission outcome so the user can 136 * retry immediately without reloading the page. 137 * 138 * wpcf7mailsent â email delivered successfully 139 * wpcf7submit â any submission (success or server-side failure) 140 * wpcf7invalid â CF7 client-side validation failed 141 * wpcf7spam â server flagged the submission as spam 142 */ 143 function refreshToken() { 144 ready = fetchFreshToken().then( function ( payload ) { 145 fillForm( form, payload ); 146 return payload; 147 } ); 148 } 149 150 /* 151 * Re-arm only on genuine success or client-side validation failure. 152 * 153 * wpcf7submit and wpcf7spam are intentionally NOT listed here. 154 * Refreshing the token after a spam block would reset the server-side 155 * elapsed-time counter, allowing a bot to bypass CF7NCSP_MIN_TIME by 156 * re-submitting immediately after a rejection. 157 * 158 * wpcf7invalid fires when CF7's own field validation fails (e.g. a 159 * required field is empty). The anti-spam check never ran in that 160 * case, so issuing a fresh token is both safe and necessary so the 161 * user can correct and re-submit. 162 * 163 * wpcf7mailsent fires only when the email was actually delivered. 164 */ 165 form.addEventListener( 'wpcf7mailsent', refreshToken ); 166 form.addEventListener( 'wpcf7invalid', refreshToken ); 167 } 168 169 /* ââ Bootstrap âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 170 * Attach initForm to every CF7 form found in the document. 171 * Works whether this script loads before or after DOMContentLoaded. 172 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 173 function initAll() { 174 document.querySelectorAll( '.wpcf7 form, .wpcf7-form' ).forEach( initForm ); 175 } 176 177 if ( document.readyState === 'loading' ) { 178 document.addEventListener( 'DOMContentLoaded', initAll ); 179 } else { 180 initAll(); 181 } 182 183}());
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.