: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
const $captchaContainer = $form.find( '.wpforms-recaptcha-container' );
if ( $captchaContainer.hasClass( 'wpforms-is-hcaptcha' ) ) {
} else if ( $captchaContainer.hasClass( 'wpforms-is-turnstile' ) ) {
// Check for invisible recaptcha first.
recaptchaID = $form.find( '.wpforms-submit' ).get( 0 ).recaptchaID;
// Check for hcaptcha/recaptcha v2, if invisible recaptcha is not found.
if ( app.empty( recaptchaID ) && recaptchaID !== 0 ) {
recaptchaID = $form.find( '.g-recaptcha' ).data( 'recaptcha-id' );
if ( ! app.empty( recaptchaID ) || recaptchaID === 0 ) {
apiVar.reset( recaptchaID );
* Console log AJAX error.
* @param {string} error Error text (optional).
consoleLogAjaxError( error ) {
console.error( 'WPForms AJAX submit error:\n%s', error ); // eslint-disable-line no-console
console.error( 'WPForms AJAX submit error' ); // eslint-disable-line no-console
* Display form AJAX errors.
* @param {jQuery} $form Form element.
* @param {Object} errors Errors in format { general: { generalErrors }, field: { fieldErrors } }.
displayFormAjaxErrors( $form, errors ) { // eslint-disable-line complexity
if ( 'string' === typeof errors ) {
app.displayFormAjaxGeneralErrors( $form, errors );
errors = errors && ( 'errors' in errors ) ? errors.errors : null;
if ( app.empty( errors ) || ( app.empty( errors.general ) && app.empty( errors.field ) ) ) {
app.consoleLogAjaxError();
if ( ! app.empty( errors.general ) ) {
app.displayFormAjaxGeneralErrors( $form, errors.general );
if ( ! app.empty( errors.field ) ) {
app.displayFormAjaxFieldErrors( $form, errors.field );
* Display form AJAX general errors that cannot be displayed using jQuery Validation plugin.
* @param {jQuery} $form Form element.
* @param {Object} errors Errors in format { errorType: errorText }.
displayFormAjaxGeneralErrors( $form, errors ) { // eslint-disable-line complexity
if ( ! $form || ! $form.length ) {
if ( app.empty( errors ) ) {
if ( app.isModernMarkupEnabled() ) {
// Safety net for random errors thrown by a third-party code. Should never be used intentionally.
if ( 'string' === typeof errors ) {
const roleAttr = app.isModernMarkupEnabled() ? ' role="alert"' : '',
errPrefix = app.isModernMarkupEnabled() ? `<span class="wpforms-hidden">${ wpforms_settings.formErrorMessagePrefix }</span>` : '';
.find( '.wpforms-submit-container' )
.before( `<div class="wpforms-error-container"${ roleAttr }>${ errPrefix }${ errors }</div>` );
app.setCurrentPage( $form, {} );
const formId = $form.data( 'formid' );
app.printGeneralErrors( $form, errors, formId );
* @param {jQuery} $form Form element.
* @param {Object} errors Error Object.
* @param {string} formId Form ID.
printGeneralErrors( $form, errors, formId ) {
* @param {string} html Error HTML.
function handleHeaderError( html ) {
* @param {string} html Error HTML.
function handleFooterError( html ) {
if ( $form.find( '.wpforms-page-indicator' ).length === 0 ) {
$form.find( '.wpforms-submit-container' ).before( html );
// Check if it is a multipage form.
// If it is a multipage form, we need error only on the first page.
$form.find( '.wpforms-page-1' ).append( html );
* Handle reCAPTCHA error.
* @param {string} html Error HTML.
function handleRecaptchaError( html ) {
$form.find( '.wpforms-recaptcha-container' ).append( html );
$.each( errors, function( type, html ) {
handleHeaderError( html );
handleFooterError( html );
handleRecaptchaError( html );
if ( app.isModernMarkupEnabled() ) {
const errormessage = $form.attr( 'aria-errormessage' ) || '';
$form.attr( 'aria-errormessage', `${ errormessage } wpforms-${ formId }-${ type }-error` );
if ( $form.find( '.wpforms-error-container' ).length ) {
app.animateScrollTop( $form.find( '.wpforms-error-container' ).first().offset().top - 100 );
* Clear forms AJAX general errors that cannot be cleared using jQuery Validation plugin.
* @param {jQuery} $form Form element.
clearFormAjaxGeneralErrors( $form ) {
$form.find( '.wpforms-error-container' ).remove();
$form.find( '#wpforms-field_recaptcha-error' ).remove();
// Clear form accessibility attributes.
if ( app.isModernMarkupEnabled() ) {
* Display form AJAX field errors using jQuery Validation plugin.
* @param {jQuery} $form Form element.
* @param {Object} errors Errors in format { fieldName: errorText }.
displayFormAjaxFieldErrors( $form, errors ) {
if ( ! $form || ! $form.length ) {
if ( app.empty( errors ) ) {
const validator = $form.data( 'validator' );
errors = app.splitFieldErrors( errors );
// Set data attribute for each field with server error.
$.each( errors, function( field, message ) {
$( '[name="' + field + '"]', $form ).attr( 'data-server-error', message );
validator.showErrors( errors );
if ( ! app.formHasCaptcha( $form ) ) {
validator.focusInvalid();
* @param {Object} errors Errors.
* @return {Object} Errors.
splitFieldErrors: ( errors ) => {
$.each( errors, function( field, message ) {
if ( 'string' === typeof message ) {
// If errors an object consisting of { subfield: errorMessage }, then iterate each to display error.
$.each( message, function( subfield, errorMessage ) {
// Get the last part of the field (in []) and check if it is the same as subfield.
const lastPart = field.split( '[' ).pop().replace( ']', '' );
// Get from the `field` name all except what we caught in `lastPart`.
const fieldNameBase = field.replace( '[' + lastPart + ']', '' );
if ( lastPart === subfield ) {
errors[ field ] = errorMessage;
} else if ( 'string' === typeof subfield && isNaN( subfield ) ) {
errors[ fieldNameBase + '[' + subfield + ']' ] = errorMessage;
* Submit a form using AJAX.
* @since 1.7.6 Allow canceling Ajax submission.
* @param {jQuery} $form Form element.
* @return {JQueryXHR|JQueryDeferred} Promise like an object for async callbacks.
formSubmitAjax: ( $form ) => { // eslint-disable-line max-lines-per-function
return $.Deferred().reject(); // eslint-disable-line new-cap
const $container = $form.closest( '.wpforms-container' ),
$spinner = $form.find( '.wpforms-submit-spinner' );
$container.css( 'opacity', 0.6 );
app.clearFormAjaxGeneralErrors( $form );
const formData = new FormData( $form.get( 0 ) );
formData.append( 'action', 'wpforms_submit' );
formData.append( 'start_timestamp', $form.data( 'timestamp' ) );
formData.append( 'end_timestamp', Date.now() );
url : wpforms_settings.ajaxurl,
args.success = function( json ) { // eslint-disable-line complexity
app.consoleLogAjaxError();
if ( json.data && json.data.action_required ) {
$form.trigger( 'wpformsAjaxSubmitActionRequired', json );
app.resetFormRecaptcha( $form );
app.displayFormAjaxErrors( $form, json.data );
$form.trigger( 'wpformsAjaxSubmitFailed', json );
app.setCurrentPage( $form, json.data );
$form.trigger( 'wpformsAjaxSubmitSuccess', json );
if ( json.data.redirect_url ) {
$form.trigger( 'wpformsAjaxSubmitBeforeRedirect', json );
window.location = json.data.redirect_url;
if ( json.data.confirmation ) {
$container.html( json.data.confirmation );
$confirmationScroll = $container.find( 'div.wpforms-confirmation-scroll' );
$container.trigger( 'wpformsAjaxSubmitSuccessConfirmation', json );
if ( $confirmationScroll.length ) {
app.animateScrollTop( $confirmationScroll.offset().top - 100 );
args.error = function( jqHXR, textStatus, error ) {
app.consoleLogAjaxError( error );
$form.trigger( 'wpformsAjaxSubmitError', [ jqHXR, textStatus, error ] );
args.complete = function( jqHXR, textStatus ) {
* Do not make form active if the action is required, or
* if the ajax request was successful and the form has a redirect.
jqHXR.responseJSON.data &&
jqHXR.responseJSON.data.action_required ||
( textStatus === 'success' && jqHXR.responseJSON.data.redirect_url )
app.restoreSubmitButton( $form, $container );
$form.trigger( 'wpformsAjaxSubmitCompleted', [ jqHXR, textStatus ] );
const event = WPFormsUtils.triggerEvent( $form, 'wpformsAjaxBeforeSubmit', [ $form ] );
// Allow callbacks on `wpformsAjaxBeforeSubmit` to cancel Ajax form submission by triggering `event.preventDefault()`.
if ( event.isDefaultPrevented() ) {
app.restoreSubmitButton( $form, $container );
return $.Deferred().reject(); // eslint-disable-line new-cap
* Display page with error for multiple page form.
* @param {jQuery} $form Form element.
* @param {Object} $json Error json.
setCurrentPage( $form, $json ) { // eslint-disable-line complexity
// Return for one-page forms.
if ( $form.find( '.wpforms-page-indicator' ).length === 0 ) {
$form.find( '.wpforms-page' ).each( function( index, el ) {
if ( $( el ).find( '.wpforms-has-error' ).length >= 1 ) {
return $errorPages.push( $( el ) );
// Do not change the page if there is a captcha error and there are no other field or footer errors.
$errorPages.length === 0 &&
$json.errors !== undefined &&
$json.errors.general !== undefined &&
$json.errors.general.footer === undefined &&
$json.errors.general.recaptcha !== undefined
// Get the first page with error.
const $currentPage = $errorPages.length > 0 ? $errorPages[ 0 ] : $form.find( '.wpforms-page-1' );
const currentPage = $currentPage.data( 'page' );
// If error is on the first page, or we have general errors among others, go to the first page.
if ( currentPage === 1 || ( $json.errors !== undefined && $json.errors.general.footer !== undefined ) ) {
$page = $form.find( '.wpforms-page-1' ).next();
$page = $currentPage.next().length !== 0 ? $currentPage.next() : $currentPage.prev();
action = $currentPage.next().length !== 0 ? 'prev' : 'next';
// Take the page from which navigate to error.
const $nextBtn = $page.find( '.wpforms-page-next' ),
page = $page.data( 'page' );
// Imitate navigation to the page with error.
app.navigateToPage( $nextBtn, action, page, $form, $( '.wpforms-page-' + page ) );
* Scroll to position with animation.
* @param {number} position Position (in pixels) to scroll to,
* @param {number} duration Animation duration.
* @param {Function} complete Function to execute after animation is complete.
* @return {Promise} A promise object for async callbacks.
animateScrollTop( position, duration, complete ) {
duration = duration || 1000;
complete = typeof complete === 'function' ? complete : function() {};
return $( 'html, body' ).animate( { scrollTop: parseInt( position, 10 ) }, { duration, complete } ).promise();
if ( typeof tinyMCE !== 'undefined' ) {
* Check if an object is a function.
* @param {any} object Object to check if it is a function.
* @return {boolean} True if an object is a function.
return !! ( object && object.constructor && object.call && object.apply );