: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
} else if ( 'prev' === action ) {
const event = WPFormsUtils.triggerEvent( $this, 'wpformsBeforePageChange', [ nextPage, $form, action ] );
// Allow callbacks on `wpformsBeforePageChange` to cancel page changing by triggering `event.preventDefault()`.
if ( event.isDefaultPrevented() ) {
$form.find( '.wpforms-page' ).hide();
const $destinationPage = $form.find( '.wpforms-page-' + nextPage );
app.toggleReCaptchaAndSubmitDisplay( $form, action, $destinationPage );
app.checkTurnstileVisibility( $form );
const pageScroll = app.getPageScroll( $form );
app.animateScrollTop( $form.offset().top - pageScroll, 750, null );
$this.trigger( 'wpformsPageChange', [ nextPage, $form, action ] );
app.manipulateIndicator( nextPage, $form );
* Toggle the reCaptcha and submit container display.
* @param {jQuery} $form WPForms element object.
* @param {string} action The navigation action.
* @param {jQuery} $destinationPage Destination Page element object.
toggleReCaptchaAndSubmitDisplay( $form, action, $destinationPage ) {
const $submit = $form.find( '.wpforms-submit-container' ),
$reCAPTCHA = $form.find( '.wpforms-recaptcha-container' );
if ( 'next' === action && $destinationPage.hasClass( 'last' ) ) {
} else if ( 'prev' === action ) {
* Update Turnstile container class if invisible mode is chosen.
* @param {jQuery} $form WPForms element object.
checkTurnstileVisibility( $form ) {
const $turnstile = $form.find( '.wpforms-recaptcha-container' );
// Check if Turnstile captcha is enabled.
if ( ! $turnstile.hasClass( 'wpforms-is-turnstile' ) ) {
const iframeWrapperHeight = $turnstile.find( '.g-recaptcha' ).height();
parseInt( iframeWrapperHeight, 10 ) === 0
? $turnstile.addClass( 'wpforms-is-turnstile-invisible' )
: $turnstile.removeClass( 'wpforms-is-turnstile-invisible' );
* Get the page scroll position.
* @param {jQuery} $form WPForms element object.
* @return {number|boolean} Returns a number if position to page scroll is found.
* Otherwise, return `false` if position isn't found.
if ( false === window.wpforms_pageScroll ) {
if ( ! app.empty( window.wpform_pageScroll ) ) {
return window.wpform_pageScroll;
return $form.find( '.wpforms-page-indicator' ).data( 'scroll' ) !== 0 ? 75 : false;
* Manipulate the indicator.
* @param {number} nextPage The next's / destination's page number.
* @param {jQuery} $form WPForms element object.
manipulateIndicator( nextPage, $form ) {
const $indicator = $form.find( '.wpforms-page-indicator' );
const theme = $indicator.data( 'indicator' );
if ( 'connector' === theme || 'circles' === theme ) {
app.manipulateConnectorAndCirclesIndicator( $indicator, theme, nextPage );
if ( 'progress' === theme ) {
app.manipulateProgressIndicator( $indicator, $form, nextPage );
* Manipulate 'circles' or 'connector' theme indicator.
* @param {jQuery} $indicator The indicator jQuery element object.
* @param {string} theme Indicator theme.
* @param {number} nextPage The next's / destination's page number.
manipulateConnectorAndCirclesIndicator( $indicator, theme, nextPage ) {
const color = $indicator.data( 'indicator-color' );
$indicator.find( '.wpforms-page-indicator-page' ).removeClass( 'active' );
$indicator.find( '.wpforms-page-indicator-page-' + nextPage ).addClass( 'active' );
$indicator.find( '.wpforms-page-indicator-page-number' ).removeAttr( 'style' );
$indicator.find( '.active .wpforms-page-indicator-page-number' ).css( 'background-color', color );
if ( 'connector' === theme ) {
$indicator.find( '.wpforms-page-indicator-page-triangle' ).removeAttr( 'style' );
$indicator.find( '.active .wpforms-page-indicator-page-triangle' ).css( 'border-top-color', color );
* Manipulate 'progress' theme indicator.
* @param {jQuery} $indicator The indicator jQuery element object.
* @param {jQuery} $form WPForms element object.
* @param {number} nextPage The next's / destination's page number.
manipulateProgressIndicator( $indicator, $form, nextPage ) {
const $pageTitle = $indicator.find( '.wpforms-page-indicator-page-title' ),
$pageSep = $indicator.find( '.wpforms-page-indicator-page-title-sep' ),
totalPages = $form.find( '.wpforms-page' ).length,
width = ( nextPage / totalPages ) * 100;
$indicator.find( '.wpforms-page-indicator-page-progress' ).css( 'width', width + '%' );
$indicator.find( '.wpforms-page-indicator-steps-current' ).text( nextPage );
if ( $pageTitle.data( 'page-' + nextPage + '-title' ) ) {
$pageTitle.css( 'display', 'inline' ).text( $pageTitle.data( 'page-' + nextPage + '-title' ) );
$pageSep.css( 'display', 'inline' );
$pageTitle.css( 'display', 'none' );
$pageSep.css( 'display', 'none' );
* OptinMonster compatibility.
* Re-initialize after OptinMonster loads to accommodate changes that
* have occurred to the DOM.
document.addEventListener( 'om.Campaign.load', function( event ) {
app.optinMonsterRecaptchaReset( event.detail.Campaign.data.id );
$( document ).on( 'OptinMonsterOnShow', function( event, data, object ) {
app.optinMonsterRecaptchaReset( data.optin );
* Reset/recreate hCaptcha/reCAPTCHA v2 inside OptinMonster.
* @since 1.6.4 Added hCaptcha support.
* @param {string} optinId OptinMonster ID.
optinMonsterRecaptchaReset( optinId ) {
const $form = $( '#om-' + optinId ).find( '.wpforms-form' ),
$captchaContainer = $form.find( '.wpforms-recaptcha-container' ),
$captcha = $form.find( '.g-recaptcha' );
if ( $form.length && $captcha.length ) {
const captchaSiteKey = $captcha.attr( 'data-sitekey' ),
captchaID = 'recaptcha-' + Date.now(),
apiVar = $captchaContainer.hasClass( 'wpforms-is-hcaptcha' ) ? hcaptcha : grecaptcha;
$captchaContainer.prepend( '<div class="g-recaptcha" id="' + captchaID + '" data-sitekey="' + captchaSiteKey + '"></div>' );
wpformsRecaptchaCallback( $( '#' + captchaID ) );
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//
* Payments: Run amount calculation and update the Total field value.
* @since 1.5.1 Added support for payment-checkbox field.
* @param {Object} el jQuery DOM object.
* @param {boolean} validate Whether to validate or not.
amountTotal( el, validate ) {
validate = validate || false;
const $form = $( el ).closest( '.wpforms-form' ),
total = app.amountTotalCalc( $form ),
totalFormattedSymbol = app.amountFormatSymbol( total );
$form.find( '.wpforms-payment-total' ).each( function() {
if ( 'hidden' === $( this ).attr( 'type' ) || 'text' === $( this ).attr( 'type' ) ) {
$( this ).val( totalFormattedSymbol );
if ( 'text' === $( this ).attr( 'type' ) && validate && $form.data( 'validator' ) ) {
$( this ).text( totalFormattedSymbol );
app.updateOrderSummaryItems( $form, $( el ), totalFormattedSymbol );
* Update summary table items visibility and total amount.
* @param {jQuery} $form Form object.
* @param {jQuery} $paymentField Payment field object.
* @param {string} total Formatted form total.
updateOrderSummaryItems( $form, $paymentField, total ) {
$form.find( '.wpforms-order-summary-preview' ).each( function() {
const $summary = $( this );
$summary.find( '.wpforms-order-summary-preview-total .wpforms-order-summary-item-price' ).text( total );
if ( $paymentField.hasClass( 'wpforms-payment-total' ) ) {
// Update each payment field price in case it was changed while total calculation.
$form.find( '.wpforms-payment-price' ).each( function() {
app.updateOrderSummaryItem( $( this ), $summary );
app.updateOrderSummaryItem( $paymentField, $summary );
* Update summary table item visibility and amount.
* @param {jQuery} $paymentField Payment field object.
* @param {jQuery} $summary Summary object.
// eslint-disable-next-line complexity
updateOrderSummaryItem( $paymentField, $summary ) {
if ( ! $paymentField.hasClass( 'wpforms-payment-price' ) ) {
const $field = $paymentField.closest( '.wpforms-field' ),
fieldId = $field.data( 'field-id' ),
type = $paymentField.prop( 'type' );
if ( type === 'checkbox' ) {
$summary.find( `tr[data-field="${ fieldId }"][data-choice="${ $paymentField.val() }"]` ).toggle( $paymentField.is( ':checked' ) );
} else if ( type === 'radio' || type === 'select-one' ) {
// Show only selected items.
$summary.find( `tr[data-field="${ fieldId }"]` ).each( function() {
const choiceID = $( this ).data( 'choice' );
if ( type === 'radio' ) {
$( this ).toggle( $field.find( `input[value="${ choiceID }"]` ).is( ':checked' ) );
$( this ).toggle( choiceID === parseInt( $field.find( 'select' ).val(), 10 ) );
const $item = $summary.find( `tr[data-field="${ fieldId }"]` ),
amount = $paymentField.val();
$item.find( '.wpforms-order-summary-item-price' ).text( app.amountFormatSymbol( amount ) );
$item.toggle( $field.css( 'display' ) === 'block' );
if ( ! $field.hasClass( 'wpforms-payment-quantities-enabled' ) ) {
app.updateSummaryPriceWidth( $summary );
app.toggleSummaryPlaceholder( $summary );
app.updateOrderSummaryItemQuantity( $paymentField );
* Update summary table item quantity and price.
* @param {jQuery} $input Payment input object.
updateOrderSummaryItemQuantity( $input ) {
const $field = $input.closest( '.wpforms-field' ),
$paymentField = $field.find( 'input.wpforms-payment-price, select.wpforms-payment-price' ),
$form = $input.closest( '.wpforms-form' ),
fieldId = $field.data( 'field-id' ),
quantity = app.getPaymentFieldQuantity( $paymentField ),
amount = app.getPaymentFieldAmount( $paymentField ),
type = $paymentField.prop( 'type' );
$form.find( '.wpforms-order-summary-preview' ).each( function() {
const $summary = $( this );
if ( type === 'checkbox' || type === 'radio' || type === 'select-one' ) {
const choiceId = $paymentField.val();
$item = $summary.find( `tr[data-field="${ fieldId }"][data-choice="${ choiceId }"]` );
$item = $summary.find( `tr[data-field="${ fieldId }"]` );
$item.toggle( quantity > 0 );
// Update field quantity and amount.
$item.find( '.wpforms-order-summary-item-quantity' ).text( quantity );
$item.find( '.wpforms-order-summary-item-price' ).text( app.amountFormatSymbol( amount * quantity ) );
app.updateSummaryPriceWidth( $summary );
app.toggleSummaryPlaceholder( $summary );
* Update summary price column width.
* @param {jQuery} $summary Summary table object.
updateSummaryPriceWidth( $summary ) {
const priceColumnWidth = Math.max( $summary.find( '.wpforms-order-summary-preview-coupon-total .wpforms-order-summary-item-price' ).text().length, $summary.find( '.wpforms-order-summary-preview-total .wpforms-order-summary-item-price' ).text().length + 3 );
$summary.find( '.wpforms-order-summary-item-price' ).css( 'width', `${ priceColumnWidth }ch` );
* Update summary placeholder visibility.
* @param {jQuery} $summary Summary table object.
toggleSummaryPlaceholder( $summary ) {
const $placeholder = $summary.find( '.wpforms-order-summary-placeholder' );
let showPlaceholder = true;
$summary.find( '.wpforms-order-summary-field' ).each( function() {
if ( $( this ).css( 'display' ) !== 'none' ) {
$placeholder.toggle( showPlaceholder );
* Payments: Calculate a total amount without formatting.
* @param {jQuery} $form Form element.
* @return {number} Total amount.
amountTotalCalc( $form ) {
$( '.wpforms-payment-price', $form ).each( function() {
if ( $this.closest( '.wpforms-field-payment-single' ).hasClass( 'wpforms-conditional-hide' ) ) {
const amount = app.getPaymentFieldAmount( $this );
total = Number( total ) + ( amount * app.getPaymentFieldQuantity( $this ) );
const $document = $( document );
* Trigger whe the total amount has been calculated.
* Allow addons to modify the total amount.
* @param {Object} data Form element and total.
const event = WPFormsUtils.triggerEvent( $document, 'wpformsAmountTotalCalculate', [ $form, total ] );
total = event.result !== undefined && event.result >= 0 ? event.result : total;
* Trigger on the end of the process of calculating the total amount.
* @param {Object} data Form element and total.
WPFormsUtils.triggerEvent( $document, 'wpformsAmountTotalCalculated', [ $form, total ] );
* Get payment field sanitized amount.
* @param {jQuery} $field Field element.
* @return {number} Sanitized amount.
// eslint-disable-next-line complexity
getPaymentFieldAmount( $field ) {
const type = $field.attr( 'type' );
if ( type === 'text' || type === 'hidden' ) {
return Number( app.amountSanitize( $field.val() ) );
if ( ( type === 'radio' || type === 'checkbox' ) && $field.is( ':checked' ) ) {
return Number( app.amountSanitize( $field.data( 'amount' ) ) );
if ( $field.is( 'select' ) && $field.find( 'option:selected' ).length > 0 && $field.find( 'option:selected' ).data( 'amount' ) ) {
return Number( app.amountSanitize( $field.find( 'option:selected' ).data( 'amount' ) ) );
* Get payment field quantity.