1/** 2 * DSAR form submit handler â loaded via wp_enqueue_script so it runs even 3 * when a page builder (e.g. Bricks) injects the shortcode HTML dynamically. 4 * Inline scripts inside dynamically-injected HTML are silently ignored by 5 * browsers, so the handler must live in a separately-enqueued file. 6 * 7 * The listener is attached directly to each .faz-dsar-form element (not 8 * delegated from document) so that page-builder event interceptors that call 9 * stopPropagation() at the document level cannot suppress the submit handler. 10 * A MutationObserver covers forms added to the DOM after script execution. 11 * 12 * Data flow: 13 * ajaxUrl / error strings â window.fazDsarConfig (set by wp_localize_script) 14 * nonce + action â hidden inputs already in form HTML 15 * form data â FormData(form) captures all inputs including nonce 16 */ 17(function () { 18 /** 19 * Switch the notice container into "error" mode: role=alert + aria-live=assertive 20 * so assistive tech announces validation/submit failures immediately. Success 21 * messages keep the polite role=status (set in setNoticeSuccess()). 22 */ 23 function setNoticeError(notice) { 24 if (!notice) { return; } 25 notice.className = 'faz-dsar-notice error'; 26 notice.setAttribute('role', 'alert'); 27 notice.setAttribute('aria-live', 'assertive'); 28 } 29 30 function setNoticeSuccess(notice) { 31 if (!notice) { return; } 32 notice.className = 'faz-dsar-notice success'; 33 notice.setAttribute('role', 'status'); 34 notice.setAttribute('aria-live', 'polite'); 35 } 36 37 /** 38 * Remove any prior per-field error markup. Called at the start of every 39 * submit attempt so stale errors do not linger after the user corrects them. 40 */ 41 function clearFieldErrors(form) { 42 if (!form) { return; } 43 var inputs = form.querySelectorAll('[aria-invalid="true"]'); 44 for (var i = 0; i < inputs.length; i++) { 45 inputs[i].removeAttribute('aria-invalid'); 46 var describedBy = inputs[i].getAttribute('aria-describedby') || ''; 47 // Strip our error IDs (faz-dsar-err-*) but preserve any other describedby tokens. 48 var tokens = describedBy.split(/\s+/).filter(function (tok) { 49 return tok && tok.indexOf('faz-dsar-err-') !== 0; 50 }); 51 if (tokens.length) { 52 inputs[i].setAttribute('aria-describedby', tokens.join(' ')); 53 } else { 54 inputs[i].removeAttribute('aria-describedby'); 55 } 56 } 57 var spans = form.querySelectorAll('.faz-field-error'); 58 for (var j = 0; j < spans.length; j++) { 59 spans[j].parentNode.removeChild(spans[j]); 60 } 61 } 62 63 /** 64 * Mark `input` as invalid: set aria-invalid, render a per-field <span> with 65 * a stable id, and wire it up via aria-describedby so screen readers 66 * announce the message when focus enters the field. 67 */ 68 function markFieldInvalid(input, message) { 69 if (!input) { return; } 70 input.setAttribute('aria-invalid', 'true'); 71 var errId = input.id ? (input.id + '-err') : ('faz-dsar-err-' + Math.random().toString(36).slice(2, 9)); 72 // Reuse our own id namespace to make cleanup deterministic. 73 if (input.id) { 74 errId = 'faz-dsar-err-' + input.id; 75 } 76 var existing = document.getElementById(errId); 77 if (existing && existing.parentNode) { 78 existing.parentNode.removeChild(existing); 79 } 80 var span = document.createElement('span'); 81 span.className = 'faz-field-error'; 82 span.id = errId; 83 span.textContent = message; 84 // Insert after the input so it is visually adjacent to the field. 85 if (input.parentNode) { 86 input.parentNode.insertBefore(span, input.nextSibling); 87 } 88 var prior = input.getAttribute('aria-describedby'); 89 var tokens = prior ? prior.split(/\s+/).filter(Boolean) : []; 90 if (tokens.indexOf(errId) === -1) { 91 tokens.push(errId); 92 } 93 input.setAttribute('aria-describedby', tokens.join(' ')); 94 } 95 96 /** 97 * Move keyboard focus and viewport to the first invalid field so the user 98 * can correct it immediately without hunting through the form. 99 */ 100 function focusFirstInvalid(form) { 101 if (!form) { return; } 102 var first = form.querySelector('[aria-invalid="true"]'); 103 if (!first) { return; } 104 try { 105 first.focus({ preventScroll: true }); 106 } catch (err) { 107 first.focus(); 108 } 109 if (typeof first.scrollIntoView === 'function') { 110 try { 111 first.scrollIntoView({ behavior: 'smooth', block: 'center' }); 112 } catch (err) { 113 first.scrollIntoView(); 114 } 115 } 116 } 117 118 function handleSubmit(e) { 119 e.preventDefault(); 120 121 var form = e.currentTarget; 122 var wrap = form.parentElement; 123 var notice = wrap ? wrap.querySelector('.faz-dsar-notice') : null; 124 var config = window.fazDsarConfig || {}; 125 var ajaxUrl = config.ajaxUrl || ''; 126 var errMsg = config.errMsg || 'An error occurred. Please try again.'; 127 var reqMsg = config.reqMsg || 'Please fill in all required fields.'; 128 129 var nameEl = form.querySelector('[name="dsar_name"]'); 130 var emailEl = form.querySelector('[name="dsar_email"]'); 131 var typeEl = form.querySelector('[name="dsar_type"]'); 132 var name = nameEl ? nameEl.value.trim() : ''; 133 var email = emailEl ? emailEl.value.trim() : ''; 134 var type = typeEl ? typeEl.value : ''; 135 136 // Reset any prior validation markup before re-evaluating. 137 clearFieldErrors(form); 138 139 var missing = []; 140 var nameLabel = config.nameLabel || 'Name'; 141 var emailLabel = config.emailLabel || 'Email'; 142 var typeLabel = config.typeLabel || 'Request type'; 143 if (!name) { 144 missing.push(nameLabel); 145 markFieldInvalid(nameEl, reqMsg); 146 } 147 if (!email) { 148 missing.push(emailLabel); 149 markFieldInvalid(emailEl, reqMsg); 150 } 151 if (!type) { 152 missing.push(typeLabel); 153 markFieldInvalid(typeEl, reqMsg); 154 } 155 if (missing.length) { 156 if (notice) { 157 setNoticeError(notice); 158 notice.textContent = reqMsg + ' Missing: ' + missing.join(', ') + '.'; 159 notice.style.display = 'block'; 160 } 161 focusFirstInvalid(form); 162 return; 163 } 164 165 var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 166 if (!emailPattern.test(email)) { 167 var emailErr = config.emailMsg || 'Please enter a valid email address.'; 168 markFieldInvalid(emailEl, emailErr); 169 if (notice) { 170 setNoticeError(notice); 171 notice.textContent = emailErr; 172 notice.style.display = 'block'; 173 } 174 focusFirstInvalid(form); 175 return; 176 } 177 178 var btn = form.querySelector('button'); 179 if (btn) { btn.disabled = true; } 180 if (notice) { notice.style.display = 'none'; } 181 182 fetch(ajaxUrl, { 183 method: 'POST', 184 credentials: 'same-origin', 185 body: new FormData(form), 186 }) 187 .then(function (r) { return r.json(); }) 188 .then(function (res) { 189 var payload = (res && typeof res.data !== 'undefined') ? res.data : null; 190 var payloadMessage = ''; 191 if (payload && typeof payload === 'object') { 192 payloadMessage = payload.message ? String(payload.message) : JSON.stringify(payload); 193 } else if (typeof payload === 'string') { 194 payloadMessage = payload; 195 } 196 if (res.success) { 197 form.style.display = 'none'; 198 if (notice) { 199 setNoticeSuccess(notice); 200 notice.textContent = payloadMessage || config.successMsg || 'Request sent successfully.'; 201 } 202 } else { 203 if (notice) { 204 setNoticeError(notice); 205 notice.textContent = payloadMessage || errMsg; 206 } 207 if (btn) { btn.disabled = false; } 208 } 209 if (notice) { 210 notice.style.display = 'block'; 211 notice.focus(); 212 } 213 }) 214 .catch(function () { 215 if (notice) { 216 setNoticeError(notice); 217 notice.textContent = errMsg; 218 notice.style.display = 'block'; 219 notice.focus(); 220 } 221 if (btn) { btn.disabled = false; } 222 }); 223 } 224 225 function attachToForm(form) { 226 if (form._fazDsarAttached) { return; } 227 form._fazDsarAttached = true; 228 form.addEventListener('submit', handleSubmit); 229 } 230 231 // Attach to any .faz-dsar-form already in the DOM. 232 var existing = document.querySelectorAll('.faz-dsar-form'); 233 for (var i = 0; i < existing.length; i++) { 234 attachToForm(existing[i]); 235 } 236 237 // Watch for forms injected after script execution (page-builder lazy render). 238 if (typeof MutationObserver !== 'undefined') { 239 var observer = new MutationObserver(function (mutations) { 240 for (var m = 0; m < mutations.length; m++) { 241 var added = mutations[m].addedNodes; 242 for (var n = 0; n < added.length; n++) { 243 var node = added[n]; 244 if (node.nodeType !== 1) { continue; } 245 if (node.classList && node.classList.contains('faz-dsar-form')) { 246 attachToForm(node); 247 } 248 var nested = node.querySelectorAll ? node.querySelectorAll('.faz-dsar-form') : []; 249 for (var k = 0; k < nested.length; k++) { 250 attachToForm(nested[k]); 251 } 252 } 253 } 254 }); 255 observer.observe(document.body || document.documentElement, { 256 childList: true, 257 subtree: true, 258 }); 259 } 260}());
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.