: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$items = $wrap.find( '.wpforms-field-rating-item' );
$this.focus(); // Enable keyboard navigation.
$items.removeClass( 'hover selected' );
$this.parent().prevAll().addBack().addClass( 'selected' );
// Rating field: preselect the selected rating (from dynamic/fallback population).
$( '.wpforms-field-rating-item input:checked' ).trigger( 'change' );
// Checkbox/Radio/Payment checkbox: make labels keyboard-accessible.
$document.on( 'keydown', '.wpforms-image-choices-item label', function( event ) {
const $label = $( this ),
$field = $label.closest( '.wpforms-field' );
if ( $field.hasClass( 'wpforms-conditional-hide' ) ) {
// Cause the input to be clicked when pressing Space bar on the label.
if ( event.keyCode !== 32 ) {
$label.find( 'input' ).trigger( 'click' );
// IE: Click on the `image choice` image should trigger the click event on the input (checkbox or radio) field.
if ( window.document.documentMode ) {
$document.on( 'click', '.wpforms-image-choices-item img', function() {
$( this ).closest( 'label' ).find( 'input' ).trigger( 'click' );
$document.on( 'change', '.wpforms-field-checkbox input, .wpforms-field-radio input, .wpforms-field-payment-multiple input, .wpforms-field-payment-checkbox input, .wpforms-field-gdpr-checkbox input', function( event ) {
$field = $this.closest( '.wpforms-field' );
if ( $field.hasClass( 'wpforms-conditional-hide' ) ) {
switch ( $this.attr( 'type' ) ) {
$this.closest( 'ul' ).find( 'li' ).removeClass( 'wpforms-selected' ).find( 'input[type=radio]' ).removeProp( 'checked' );
.closest( 'li' ).addClass( 'wpforms-selected' );
if ( $this.is( ':checked' ) ) {
$this.closest( 'li' ).addClass( 'wpforms-selected' );
$this.prop( 'checked', true );
$this.closest( 'li' ).removeClass( 'wpforms-selected' );
$this.prop( 'checked', false );
// Upload fields: Check combined file size.
$document.on( 'input', '.wpforms-field-file-upload', function() {
$uploads = $this.closest( 'form.wpforms-form' ).find( '.wpforms-field-file-upload input:not(".dropzone-input")' );
postMaxSize = Number( wpforms_settings.post_max_size ),
errorMsg = '<div class="wpforms-error-container-post_max_size">' + wpforms_settings.val_post_max_size + '</div>';
const errorCntTpl = '<div class="wpforms-error-container">{errorMsg}</div>';
const $submitCnt = $this.closest( 'form.wpforms-form' ).find( '.wpforms-submit-container' );
let $submitBtn = $submitCnt.find( 'button.wpforms-submit' ),
$errorCnt = $submitCnt.prev();
const $form = $submitBtn.closest( 'form' ),
$btnNext = $form.find( '.wpforms-page-next:visible' );
// For multi-pages layout, use the "Next" button instead of the primary "Submit" button.
if ( $form.find( '.wpforms-page-indicator' ).length !== 0 && $btnNext.length !== 0 ) {
// Calculating totalSize.
$uploads.each( function() {
const $upload = $( this );
const len = $upload[ 0 ].files.length;
totalSize += $upload[ 0 ].files[ i ].size;
if ( totalSize < postMaxSize ) {
// Remove error and release submit button.
$errorCnt.find( '.wpforms-error-container-post_max_size' ).remove();
$submitBtn.prop( 'disabled', false );
WPFormsUtils.triggerEvent( $form, 'wpformsFormSubmitButtonRestore', [ $form, $submitBtn ] );
WPFormsUtils.triggerEvent( $form, 'wpformsCombinedUploadsSizeOk', [ $form, $errorCnt ] );
totalSize = Number( ( totalSize / 1048576 ).toFixed( 3 ) );
postMaxSize = Number( ( postMaxSize / 1048576 ).toFixed( 3 ) );
// Preparing error message.
errorMsg = errorMsg.replace( /{totalSize}/, totalSize ).replace( /{maxSize}/, postMaxSize );
if ( $errorCnt.hasClass( 'wpforms-error-container' ) ) {
$errorCnt.find( '.wpforms-error-container-post_max_size' ).remove();
$errorCnt.append( errorMsg );
$submitCnt.before( errorCntTpl.replace( /{errorMsg}/, errorMsg ) );
$errorCnt = $submitCnt.prev();
// Disable submit button.
$submitBtn.prop( 'disabled', true );
WPFormsUtils.triggerEvent( $form, 'wpformsFormSubmitButtonDisable', [ $form, $submitBtn ] );
WPFormsUtils.triggerEvent( $form, 'wpformsCombinedUploadsSizeError', [ $form, $errorCnt ] );
// Number Slider field: update hints.
$document.on( 'change input', '.wpforms-field-number-slider input[type=range]', function( event ) {
const hintEl = $( event.target ).siblings( '.wpforms-field-number-slider-hint' );
hintEl.html( hintEl.data( 'hint' ).replaceAll( '{value}', '<b>' + event.target.value + '</b>' ) );
$document.on( 'keydown', '.wpforms-form input', function( e ) {
if ( e.keyCode !== 13 ) {
$page = $t.closest( '.wpforms-page' );
if ( $page.length === 0 ) {
if ( [ 'text', 'tel', 'number', 'email', 'url', 'radio', 'checkbox' ].indexOf( $t.attr( 'type' ) ) < 0 ) {
if ( $t.hasClass( 'wpforms-datepicker' ) ) {
if ( $page.hasClass( 'last' ) ) {
$page.closest( '.wpforms-form' ).find( '.wpforms-submit' ).trigger( 'click' );
$page.find( '.wpforms-page-next' ).trigger( 'click' );
// Allow only numbers, minus and decimal point to be entered into the Numbers field.
$document.on( 'keypress', '.wpforms-field-number input', function( e ) {
return /^[-0-9.]+$/.test( String.fromCharCode( e.keyCode || e.which ) );
// Start anti-spam timer on interaction of the form fields.
.one( 'input', '.wpforms-field input, .wpforms-field textarea, .wpforms-field select', app.formChanged )
.one( 'change', '.wpforms-field-select-style-modern, .wpforms-timepicker', app.formChanged )
.one( 'focus', '.dropzone-input', app.formChanged )
.one( 'click touchstart', '.wpforms-signature-canvas', app.formChanged )
.one( 'wpformsRichTextContentChange', app.richTextContentChanged );
$( 'form.wpforms-form' ).on( 'wpformsBeforePageChange', app.skipEmptyPages );
* Skip empty pages (by CL, hidden fields etc.) inside multi-steps forms.
* @param {Event} event Event.
* @param {number} nextPage Next page.
* @param {jQuery} $form Current form.
* @param {string} action The navigation action.
skipEmptyPages( event, nextPage, $form, action ) {
const nextNonEmptyPage = app.findNonEmptyPage( nextPage, $form, action );
if ( nextNonEmptyPage === nextPage ) {
if ( nextNonEmptyPage === 1 && action === 'prev' ) {
const $secondPage = $form.find( '.wpforms-page-2' );
const $currentPage = $form.find( '.wpforms-page-' + nextPage );
// The previous button is optional. We pass the fallback to the original previous button
// in the case when the previous button on the second page does not exist.
const $prevButton = $secondPage.find( '.wpforms-page-prev' ).length
? $secondPage.find( '.wpforms-page-prev' )
: $currentPage.find( '.wpforms-page-prev' );
wpforms.navigateToPage( $prevButton, 'prev', 2, $form, $secondPage );
// The next page button is always visible.
// So we take the previous page before the next non-empty page
// and simulate a jump forward from the next page.
const prevPage = nextNonEmptyPage - 1;
const $previousPage = $form.find( '.wpforms-page-' + prevPage );
wpforms.navigateToPage( $previousPage.find( '.wpforms-page-next' ), 'next', prevPage, $form, $previousPage );
* Find the next non-empty page.
* @param {number} page Current page.
* @param {jQuery} $form Current form.
* @param {string} action The navigation action.
* @return {number} The next non-empty page number.
findNonEmptyPage( page, $form, action ) {
let nextNonEmptyPage = page;
while ( app.isEmptyPage( $form, nextNonEmptyPage ) ) {
if ( action === 'prev' ) {
* Check the target page is empty.
* @param {jQuery} $form Current form.
* @param {number} page Page number.
* @return {boolean} True if page is empty.
isEmptyPage( $form, page ) {
// The first page is always visible.
const $currentPage = $form.find( '.wpforms-page-' + page );
// The last page has the "Submit" button, so it's always non-empty.
if ( $currentPage.hasClass( 'last' ) ) {
const $fieldsOnPage = $currentPage.find( '.wpforms-field:not(.wpforms-field-pagebreak):not(.wpforms-field-hidden)' );
return $currentPage.find( '.wpforms-conditional-hide' ).length === $fieldsOnPage.length;
* @param {Object} event Event object.
const $form = $( this ).closest( '.wpforms-form' );
app.maybeSetStartTime( $form );
* Rich text content changed.
* @param {Object} event Event object.
* @param {Object} mutation Mutation object.
* @param {Object} editor Editor object.
richTextContentChanged( event, mutation, editor ) {
const container = editor.getContainer();
const $form = $( container ).closest( '.wpforms-form' );
app.maybeSetStartTime( $form );
* Maybe set start time for anti-spam timer.
* @param {jQuery} $form Form element.
maybeSetStartTime( $form ) {
if ( $form.data( 'timestamp' ) ) {
if ( $form.hasClass( 'wpforms-ajax-form' ) && typeof FormData !== 'undefined' ) {
$form.data( 'timestamp', Date.now() );
$form.append( '<input type="hidden" name="start_timestamp" value="' + Date.now() + '">' );
* Entry preview field callback for a page changing.
* @param {Event} event Event.
* @param {number} currentPage Current page.
* @param {jQuery} $form Current form.
entryPreviewFieldPageChange( event, currentPage, $form ) {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Obsolete function called. Function wpforms.entryPreviewFieldPageChange has been deprecated, please use the WPFormsEntryPreview.pageChange function instead!' );
WPFormsEntryPreview.pageChange( event, currentPage, $form );
* Update the entry preview fields on the page.
* @param {number} currentPage Current page.
* @param {jQuery} $form Current form.
entryPreviewFieldUpdate( currentPage, $form ) {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Obsolete function called. Function wpforms.entryPreviewFieldUpdate has been deprecated, please use the WPFormsEntryPreview.update function instead!' );
WPFormsEntryPreview.update( currentPage, $form );
* Scroll to and focus on the field with error.
* @param {jQuery} $el Form, container or input element jQuery object.
if ( $el.length === 0 ) {
// Look for a field with an error inside an $el.
let $field = $el.find( '.wpforms-field.wpforms-has-error' );
// Look outside in not found inside.
if ( $field.length === 0 ) {
$field = $el.closest( '.wpforms-field' );
if ( $field.length === 0 ) {
const offset = $field.offset();
if ( typeof offset === 'undefined' ) {
app.animateScrollTop( offset.top - 75, 750 ).done( function() {
const $error = $field.find( '.wpforms-error' ).first();
if ( typeof $error.focus === 'function' ) {
$error.trigger( 'focus' );
* Update Pagebreak navigation.
* @param {jQuery} el jQuery element object.
action = $this.data( 'action' ),
page = $this.data( 'page' ),
$form = $this.closest( '.wpforms-form' ),
$page = $form.find( '.wpforms-page-' + page );
if ( 'next' === action && ( typeof $.fn.validate !== 'undefined' ) ) {
app.checkForInvalidFields( $form, $page, function() {
app.navigateToPage( $this, action, page, $form, $page );
if ( 'prev' === action || 'next' === action ) {
app.navigateToPage( $this, action, page, $form, $page );
* Check the validity of all the fields in the current page.
* @param {jQuery} $form WPForms element object.
* @param {jQuery} $page Current page element object in page break context.
* @param {Function} callback Callback to run when all fields are valid.
checkForInvalidFields( $form, $page, callback ) {
const validator = $form.data( 'validator' );
if ( validator.pendingRequest > 0 ) {
app.checkForInvalidFields( $form, $page, callback );
$page.find( ':input' ).each( function( index, el ) {
// Skip input fields without `name` attribute, which could have fields.
// E.g. `Placeholder` input for Modern dropdown.
if ( ! $el.attr( 'name' ) ) {
// Skip validation for some fields.
// E.g., applied coupon hidden field.
if ( $el.hasClass( 'wpforms-field-skip-validation' ) ) {
if ( ! $( el ).valid() ) {
app.scrollToError( $page );
* Navigate through page break pages.
* @param {jQuery} $this jQuery element of the next / prev nav button.
* @param {string} action The navigation action.
* @param {number} page Current page number.
* @param {jQuery} $form WPForms element object.
* @param {jQuery} $page Current page element object in page break context.
navigateToPage( $this, action, page, $form, $page ) {
if ( $this.hasClass( 'wpforms-disabled' ) ) {
if ( 'next' === action ) {