1(function($) { 2 'use strict'; 3 4 $(document).ready(function() { 5 $('form[data-gf-ajax]').each(function() { 6 initAjaxForm($(this)); 7 console.log('Initialized AJAX form:', this); 8 }); 9 }); 10 11 function hasGravityFormsRecaptcha($form) { 12 if ($form.find('.ginput_recaptcha, .gfield_recaptcha').length > 0) { 13 return true; 14 } 15 if (typeof gform !== 'undefined' && gform.recaptcha) { 16 return true; 17 } 18 if ($('script[src*="recaptcha"]').length > 0 && typeof gform !== 'undefined') { 19 return true; 20 } 21 return false; 22 } 23 24 function initAjaxForm($form) { 25 const formId = $form.data('gf-ajax'); 26 27 $form.on('submit', function(e) { 28 e.preventDefault(); 29 30 if (hasGravityFormsRecaptcha($form)) { 31 submitForm($form, formId, ''); 32 return; 33 } 34 35 if (typeof gfAjaxBridge !== 'undefined' && gfAjaxBridge.recaptchaSiteKey && typeof grecaptcha !== 'undefined') { 36 grecaptcha.ready(function() { 37 grecaptcha.execute(gfAjaxBridge.recaptchaSiteKey, { 38 action: 'form_submit' 39 }).then(function(token) { 40 submitForm($form, formId, token); 41 }).catch(function(error) { 42 console.error('reCAPTCHA execution failed:', error); 43 submitForm($form, formId, ''); 44 }); 45 }); 46 } else { 47 submitForm($form, formId, ''); 48 } 49 }); 50 } 51 52 function submitForm($form, formId, recaptchaToken = '') { 53 const formDataPayload = new FormData(); 54 55 // 1. Append Core Action Meta 56 formDataPayload.append('action', 'gf_ajax_submit'); 57 formDataPayload.append('nonce', typeof gfAjaxBridge !== 'undefined' ? gfAjaxBridge.nonce : ''); 58 formDataPayload.append('form_id', formId); 59 formDataPayload.append('recaptcha_token', recaptchaToken); 60 61 // 2. Loop through all fields and append to FormData 62 $form.find('input, select, textarea').each(function() { 63 const $field = $(this); 64 const name = $field.attr('name'); 65 const type = $field.attr('type'); 66 67 if (!name) return; 68 69 if (type === 'checkbox') { 70 if ($field.is(':checked')) { 71 formDataPayload.append(`form_data[${name}][]`, $field.val()); 72 } 73 } else if (type === 'radio') { 74 if ($field.is(':checked')) { 75 formDataPayload.append(`form_data[${name}]`, $field.val()); 76 } 77 } else if (type === 'file') { 78 if ($field[0].files && $field[0].files.length > 0) { 79 $.each($field[0].files, function(i, file) { 80 // Clean bracket wrappers if present (e.g. form_data[input_13] -> input_13) 81 let cleanName = name; 82 if (cleanName.includes('[')) { 83 const match = cleanName.match(/\[(.*?)\]/); 84 if (match && match[1]) { 85 cleanName = match[1]; 86 } 87 } 88 // Append strictly ONCE using the direct field key to avoid duplicate temporary files 89 formDataPayload.append(cleanName, file); 90 }); 91 } 92 } else { 93 const value = $field.val(); 94 if (value !== '' && value !== null) { 95 formDataPayload.append(`form_data[${name}]`, value); 96 } 97 } 98 }); 99 100 // Toggle Loading State 101 const $submitBtn = $form.find('[type="submit"]'); 102 const originalText = $submitBtn.text(); 103 $submitBtn.prop('disabled', true).text('Submitting...'); 104 105 // 3. Submit Payload via jQuery AJAX 106 $.ajax({ 107 url: typeof gfAjaxBridge !== 'undefined' ? gfAjaxBridge.ajaxUrl : '', 108 type: 'POST', 109 data: formDataPayload, 110 processData: false, // Required for binary file uploads 111 contentType: false, // Required for multipart boundaries 112 success: function(response) { 113 if (response.success) { 114 if (typeof showMessage === 'function') { 115 showMessage($form, 'success', response.data.message || 'Form submitted successfully!'); 116 } else { 117 alert(response.data.message || 'Form submitted successfully!'); 118 } 119 $form[0].reset(); 120 121 // Reset UI dropzone preview state if present 122 const uploadPrompt = document.getElementById('uploadPrompt'); 123 const filePreview = document.getElementById('filePreview'); 124 if (uploadPrompt && filePreview) { 125 uploadPrompt.classList.remove('hidden'); 126 filePreview.classList.add('hidden'); 127 } 128 129 $form.trigger('gfAjaxSuccess', [response.data]); 130 } else { 131 if (typeof showMessage === 'function') { 132 showMessage($form, 'error', response.data.message || 'An error occurred.'); 133 } else { 134 alert(response.data.message || 'An error occurred.'); 135 } 136 $form.trigger('gfAjaxError', [response.data]); 137 } 138 }, 139 error: function(xhr, status, error) { 140 console.error('AJAX Error:', xhr.responseText); 141 if (typeof showMessage === 'function') { 142 showMessage($form, 'error', 'Network error. Please try again.'); 143 } 144 $form.trigger('gfAjaxError', [{ message: 'Network error', xhr: xhr }]); 145 }, 146 complete: function() { 147 $submitBtn.prop('disabled', false).text(originalText); 148 } 149 }); 150 } 151})(jQuery);
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.