1(function () { 2 var HIDE_TIMEOUT = 4000; 3 var hideTimerByForm = new WeakMap(); 4 5 function t(key, fallback) { 6 var dict = window.NetworkFormsI18n || {}; 7 var v = dict[key]; 8 return (v != null && String(v) !== '') ? String(v) : fallback; 9 } 10 11 function getAjaxUrl() { 12 if (window.MyForms && window.MyForms.ajaxUrl) return window.MyForms.ajaxUrl; 13 14 if (window.ajaxurl) return window.ajaxurl; 15 16 return '/wp-admin/admin-ajax.php'; 17 } 18 19 function getMessageBox(form) { 20 var root = form.closest('[data-network-form-root]') || form.closest('section') || form.parentElement; 21 if (root) { 22 var box = root.querySelector('[data-form-message]'); 23 if (box) return box; 24 } 25 26 var next = form.nextElementSibling; 27 if (next && next.matches && next.matches('[data-form-message]')) return next; 28 return null; 29 } 30 31 function applyMessageStyles(box) { 32 box.style.marginTop = '10px'; 33 box.style.textAlign = 'center'; 34 box.style.fontSize = '14px'; 35 box.style.display = 'block'; 36 box.style.opacity = '1'; 37 box.style.transition = 'opacity 0.3s ease'; 38 } 39 40 function hideMessage(box) { 41 box.style.opacity = '0'; 42 setTimeout(function () { 43 box.textContent = ''; 44 box.removeAttribute('data-status'); 45 box.style.display = 'none'; 46 }, 300); 47 } 48 49 function setMessage(form, text, ok) { 50 var box = getMessageBox(form); 51 if (!box) return; 52 53 var prevTimer = hideTimerByForm.get(form); 54 if (prevTimer) { 55 clearTimeout(prevTimer); 56 hideTimerByForm.delete(form); 57 } 58 59 applyMessageStyles(box); 60 61 box.textContent = text || ''; 62 box.setAttribute('data-status', ok ? 'ok' : 'err'); 63 64 if (!text) { 65 box.style.display = 'none'; 66 return; 67 } 68 69 var timer = setTimeout(function () { 70 hideMessage(box); 71 hideTimerByForm.delete(form); 72 }, HIDE_TIMEOUT); 73 74 hideTimerByForm.set(form, timer); 75 } 76 77 function setLoading(form, loading) { 78 var btn = form.querySelector('button[type="submit"], input[type="submit"]'); 79 if (!btn) return; 80 81 btn.disabled = !!loading; 82 btn.setAttribute('aria-busy', loading ? 'true' : 'false'); 83 84 var label = btn.tagName === 'INPUT' ? btn.value : btn.textContent; 85 86 if (loading) { 87 btn.dataset._label = label; 88 if (btn.tagName === 'INPUT') btn.value = t('btnSending', 'Sending...'); 89 else btn.textContent = t('btnSending', 'Sending...'); 90 } else if (btn.dataset._label) { 91 if (btn.tagName === 'INPUT') btn.value = btn.dataset._label; 92 else btn.textContent = btn.dataset._label; 93 delete btn.dataset._label; 94 } 95 } 96 97 function getRequired(form) { 98 var formTypeEl = form.querySelector('input[name="form_type"]'); 99 var nonceEl = form.querySelector('input[name="network_forms_nonce"]'); 100 101 return { 102 formType: formTypeEl ? String(formTypeEl.value || '').trim() : '', 103 nonce: nonceEl ? String(nonceEl.value || '') : '' 104 }; 105 } 106 107 function buildFormData(form, formType, nonce) { 108 var fd = new FormData(form); 109 110 fd.set('action', 'network_forms_submit'); 111 fd.set('form_type', formType); 112 fd.set('network_forms_nonce', nonce); 113 return fd; 114 } 115 116 function showSuccessPopup(form) { 117 var popup = document.querySelector('[data-popup="success"]'); 118 if (!popup) return; 119 120 var titleEl = popup.querySelector('.popup__title'); 121 var descEl = popup.querySelector('.popup__description p'); 122 123 if (titleEl) titleEl.textContent = form.getAttribute('data-success-title') || ''; 124 if (descEl) descEl.textContent = form.getAttribute('data-success-text') || ''; 125 126 popup.classList.add('active'); 127 } 128 129 /** 130 * A page coming from the full-page cache carries the nonce of whoever filled 131 * the cache, so a logged-in visitor (or a day-old tab) submits one that does 132 * not verify. This asks the server for a fresh one. 133 */ 134 async function fetchNonce() { 135 try { 136 var res = await fetch(getAjaxUrl() + '?action=network_forms_nonce', { 137 credentials: 'same-origin', 138 cache: 'no-store' 139 }); 140 141 var data = await res.json().catch(function () { return null; }); 142 143 return (data && data.success && data.data && data.data.nonce) ? data.data.nonce : ''; 144 } catch (e) { 145 return ''; 146 } 147 } 148 149 async function post(form, formType, nonce) {
150 var res = await fetch(getAjaxUrl(), { 151 method: 'POST', 152 body: buildFormData(form, formType, nonce), 153 credentials: 'same-origin' 154 }); 155 156 var text = await res.text(); 157 var data = null; 158 159 try { data = JSON.parse(text); } catch (e) { 160 /* A cache, proxy or fatal error answered instead of WordPress. */ 161 console.warn('[network-forms] unexpected response', res.status, text.slice(0, 300)); 162 } 163 164 return { res: res, data: data }; 165 } 166 167 async function submitAjax(form) { 168 var req = getRequired(form); 169 170 if (!req.formType) { 171 setMessage(form, t('errMissingFormType', 'Form configuration error: missing form_type.'), false); 172 return; 173 } 174 if (!req.nonce) { 175 setMessage(form, t('errMissingNonce', 'Form configuration error: missing nonce.'), false); 176 return; 177 } 178 179 setLoading(form, true); 180 setMessage(form, '', true); 181 182 try { 183 var answer = await post(form, req.formType, req.nonce); 184 var data = answer.data; 185 186 /* Stale nonce: take a fresh one and send the application once more. */ 187 if (data && !data.success && data.data && data.data.code === 'invalid_nonce') { 188 var fresh = await fetchNonce(); 189 190 if (fresh) { 191 var field = form.querySelector('input[name="network_forms_nonce"]'); 192 if (field) field.value = fresh; 193 194 answer = await post(form, req.formType, fresh); 195 data = answer.data; 196 } 197 } 198 199 if (!data || !data.success) { 200 var msg = (data && data.data && data.data.message) ? data.data.message : t('errGeneric', 'Error. Please try again.'); 201 setMessage(form, msg, false); 202 return; 203 } 204 205 showSuccessPopup(form); 206 207 Array.prototype.forEach.call(form.elements, function (el) { 208 if (!el || !el.name) return; 209 if (el.type === 'hidden') return; 210 if (el.type === 'checkbox' || el.type === 'radio') el.checked = false; 211 else el.value = ''; 212 }); 213 214 } catch (e) { 215 setMessage(form, t('errNetwork', 'Network error. Please try again.'), false); 216 } finally { 217 setLoading(form, false); 218 } 219 } 220 221 document.addEventListener('submit', function (e) { 222 var form = e.target; 223 if (!(form instanceof HTMLFormElement)) return; 224 if (!form.hasAttribute('data-network-form')) return; 225 226 if (!window.fetch || !window.FormData) return; 227 228 e.preventDefault(); 229 submitAjax(form); 230 }, true); 231})();
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.