jQuery( document ).ready( function( $ ) { let form = $( '.sailthru-contact-form' ); if( form.length ) { $( form ).on( 'submit', function ( e ) { e.preventDefault(); let form = $( e.target ), action = $( e.target ).find( 'input[name="action"]' ).val() || 'contact'; // Validate form on front-end if (!validateForm( e.target )) { return; } if( 'undefined' !== typeof amiSailthruReCaptcha ) { grecaptcha.enterprise.ready( async () => { let token = await grecaptcha.enterprise.execute( amiSailthruReCaptcha.site_key, { action: action } ); submit_form( form, token ); } ); } else { submit_form( form ); } } ); } /** * Submits a form via AJAX request * * @param {Object} form - The form element or selector * @param {boolean} [token=false] - The token to add to the form data * * @return {void} */ function submit_form( form, token = false ) { let submit_form = $( form ), submit = $( submit_form ).find( '.contact-submit' ), messaging = $( submit_form ).parent().find( '.contact__messaging' ), success = $( submit_form ).parent().find( '.contact__success' ), success_message = $( success ).find( '.contact-success' ); $( submit ).prop( 'disabled', true ); $( messaging ).text( amiSailthruContact.messages.process ).removeClass( 'error' ); $( submit_form ).find( 'input.error' ).removeClass( 'error' ).attr('aria-invalid', 'false'); let prepare_form_data = $( submit_form ).serializeArray(), form_data = {}; $.map( prepare_form_data, function( n, i ){ form_data[ n['name'] ] = n['value']; } ); // add token if( token ) { form_data['token'] = token; } $.ajax( { url: amiSailthruContact.endpoint, type: 'POST', data: form_data, dataType: 'json', success: function ( response ) { if( response.success ) { $( submit_form ).hide(); $( messaging ).hide(); $( success ).show() if ( success_message.length ) { success_message.scrollIntoView( { behavior: 'smooth', block: 'center' } ); } } else { $( submit ).prop( 'disabled', false ); $( messaging ).text( amiSailthruContact.messages.default_error ).addClass( 'error' ); } }, error: function( error ) { $( submit ).prop( 'disabled', false ); let errors_data = error.responseJSON; if( 'rest_invalid_param' === errors_data.code || 'rest_missing_callback_param' === errors_data.code ) { let invalid_param = 'rest_invalid_param' === errors_data.code ? Object.keys( errors_data.data.params )[0] : errors_data.data.params[0]; $( messaging ).text( amiSailthruContact.messages[ 'invalid_' + invalid_param ] ).addClass( 'error' ); $( submit_form ).find( 'input[name="' + invalid_param + '"]' ).addClass( 'error' ).attr('aria-invalid', 'true'); } else { $( messaging ).text( amiSailthruContact.messages.default_error ).addClass( 'error' ); } } } ); } /** * Form field validation * * @param field * @returns {boolean} */ function validateField(field) { let isValid = true; const fieldType = field.type; if ((fieldType === 'text') || (fieldType === 'textarea')) { if (field.value.trim() === '') { isValid = false; } } else if (fieldType === 'email') { const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; if (!emailPattern.test(field.value)) { isValid = false; } } return isValid; } /** * Front-end form validation */ function validateForm(form) { const messaging = form.parentNode.querySelector( '.contact__messaging' ); let formIsValid = true; let errorMessages = ''; messaging.classList.remove('error'); messaging.innerHTML = ''; form.querySelectorAll('[aria-required="true"]').forEach(field => { if (validateField(field)) { field.classList.remove('error'); field.setAttribute('aria-invalid', 'false'); } else { formIsValid = false; field.classList.add('error'); field.setAttribute('aria-invalid', 'true'); const nextEl = field.nextElementSibling; errorMessages += nextEl?.classList?.contains('error-message') ? nextEl.textContent + '
' : amiSailthruContact.messages['invalid_field'] + ` ${field?.name}.
`; } }); if (!formIsValid) { displayFormMessage().then(() => { messaging.classList.add('error'); messaging.innerHTML = errorMessages; }); } return formIsValid; } // Display form message with a delay (needed for screen readers) function displayFormMessage() { return new Promise(resolve => setTimeout(resolve, 50)); } } );