: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param {jQuery} $el Element.
confirmationFieldsToggle( $el ) {
$block = $el.closest( '.wpforms-builder-settings-block-content' );
$block.find( '.wpforms-panel-field' )
.not( '.wpforms-conditionals-enable-toggle' )
$block.find( '.wpforms-panel-field-confirmations-' + type ).closest( '.wpforms-panel-field' ).show();
if ( type === 'message' ) {
$block.find( '.wpforms-panel-field-confirmations-message_scroll' ).closest( '.wpforms-panel-field' ).show();
$block.find( '.wpforms-panel-field-confirmations-message_entry_preview' ).trigger( 'change' ).closest( '.wpforms-panel-field' ).show();
$block.find( '.wpforms-panel-field-confirmations-message_order_summary' ).closest( '.wpforms-panel-field' ).toggle( $( '#wpforms-panel-fields .wpforms-field-payment-total' ).length !== 0 );
* Show/hide an entry preview message.
confirmationEntryPreviewToggle() {
$styleField = $this.closest( '.wpforms-builder-settings-block-content' ).find( '.wpforms-panel-field-confirmations-message_entry_preview_style' ).parent();
$this.is( ':checked' ) ? $styleField.show() : $styleField.hide();
* Toggle the displaying notification settings depending on if the
* notifications are enabled.
const $notification = $( '#wpforms-panel-field-settings-notification_enable' ),
$settingsBlock = $notification.closest( '.wpforms-panel-content-section' ).find( '.wpforms-builder-settings-block' ),
$enabled = $notification.is( ':checked' );
// Toggle Add new notification button.
$( '.wpforms-notifications-add' ).toggleClass( 'wpforms-hidden', ! $enabled );
$enabled ? $settingsBlock.show() : $settingsBlock.hide();
* Notifications by status alerts.
notificationsByStatusAlerts() {
$builder.on( 'change', '.wpforms-panel-content-section-notifications .wpforms-notification-by-status-alert', function( e ) { // eslint-disable-line no-unused-vars
const $input = $( this );
if ( ! $input.prop( 'checked' ) ) {
const $enabled = $( '.wpforms-radio-group-' + $input.attr( 'data-radio-group' ) + ':checked:not(#' + $input.attr( 'id' ) + ')' );
if ( $enabled.length === 0 ) {
alertText = wpforms_builder.notification_by_status_enable_alert;
alertText = alertText.replace( /%1\$s/g, $input.data( 'provider-title' ) );
alertText = wpforms_builder.notification_by_status_switch_alert;
alertText = alertText.replace( /%2\$s/g, $enabled.data( 'provider-title' ) );
alertText = alertText.replace( /%1\$s/g, $input.data( 'provider-title' ) );
title: wpforms_builder.heads_up,
icon: 'fa fa-exclamation-circle',
text: wpforms_builder.ok,
* Add new settings block.
* @since 1.6.1 Added processing for Field Map table.
* @since 1.6.1.2 Registered `wpformsSettingsBlockAdded` trigger.
* @param {jQuery} $el Settings Block jQuery object.
settingsBlockAdd( $el ) { // eslint-disable-line max-lines-per-function
const nextID = Number( $el.attr( 'data-next-id' ) ),
panelID = $el.closest( '.wpforms-panel-content-section' ).data( 'panel' ),
blockType = $el.data( 'block-type' ),
namePrompt = wpforms_builder[ blockType + '_prompt' ],
nameField = '<input autofocus="" type="text" id="settings-block-name" placeholder="' + wpforms_builder[ blockType + '_ph' ] + '">',
nameError = '<p class="error">' + wpforms_builder[ blockType + '_error' ] + '</p>',
modalContent = namePrompt + nameField + nameError;
const modal = $.confirm( {
icon: 'fa fa-info-circle',
text: wpforms_builder.ok,
action() { // eslint-disable-line complexity, max-lines-per-function
const settingsBlockName = this.$content.find( 'input#settings-block-name' ).val().toString().trim(),
error = this.$content.find( '.error' );
if ( settingsBlockName === '' ) {
const $firstSettingsBlock = $el.closest( '.wpforms-panel-content-section' ).find( '.wpforms-builder-settings-block' ).first();
// Restore tooltips before cloning.
wpf.restoreTooltips( $firstSettingsBlock );
const $newSettingsBlock = $firstSettingsBlock.clone(),
blockID = $firstSettingsBlock.data( 'block-id' );
$newSettingsBlock.attr( 'data-block-id', nextID );
$newSettingsBlock.find( '.wpforms-builder-settings-block-header span' ).text( settingsBlockName );
* Fires to reset settings block elements on adding new settings block.
* @param {jQuery} $element jQuery object of an element.
const resetFormElement = function( $element ) {
if ( $element.attr( 'name' ) ) {
$element.val( '' ).attr( 'name', $element.attr( 'name' ).replace( /\[(\d+)\]/, '[' + nextID + ']' ) );
if ( $element.is( 'select' ) ) {
$element.find( 'option' ).prop( 'selected', false ).attr( 'selected', false );
$element.find( 'option' ).first().prop( 'selected', true ).attr( 'selected', 'selected' );
} else if ( $element.attr( 'type' ) === 'checkbox' ) {
$element.prop( 'checked', false ).attr( 'checked', false ).val( '1' );
$element.val( '' ).attr( 'value', '' );
$newSettingsBlock.find( 'input, textarea, select' ).each( function() {
const $parent = $this.parent();
if ( $this.hasClass( 'wpforms-disabled' ) && ( $parent.hasClass( 'from-name' ) || $parent.hasClass( 'from-email' ) ) ) {
resetFormElement( $this );
const idPrefixPanel = 'wpforms-panel-field-' + panelID + '-',
idPrefixBlock = idPrefixPanel + blockID;
$newSettingsBlock.find( '[id^="' + idPrefixBlock + '"], [for^="' + idPrefixBlock + '"]' ).each( function( index, el ) { // eslint-disable-line no-unused-vars
attr = $el.prop( 'tagName' ) === 'LABEL' ? 'for' : 'id',
elID = $el.attr( attr ).replace( new RegExp( idPrefixBlock, 'g' ), idPrefixPanel + nextID );
// Update `notification by status` checkboxes.
const radioGroup = blockID + '-notification-by-status';
$newSettingsBlock.find( '[data-radio-group="' + radioGroup + '"]' ).each( function( index, el ) { // eslint-disable-line no-unused-vars
.removeClass( 'wpforms-radio-group-' + radioGroup )
.addClass( 'wpforms-radio-group-' + nextID + '-notification-by-status' )
.attr( 'data-radio-group', nextID + '-notification-by-status' );
$newSettingsBlock.find( '.wpforms-builder-settings-block-header input' ).val( settingsBlockName ).attr( 'value', settingsBlockName );
if ( blockType === 'notification' ) {
$newSettingsBlock.find( '.email-msg textarea' ).val( '{all_fields}' ).attr( 'value', '{all_fields}' );
$newSettingsBlock.find( '.email-recipient input' ).val( '{admin_email}' ).attr( 'value', '{admin_email}' );
$newSettingsBlock.removeClass( 'wpforms-builder-settings-block-default' );
if ( blockType === 'confirmation' ) {
$newSettingsBlock.find( '.wpforms-panel-field-tinymce' ).remove();
if ( typeof WPForms !== 'undefined' ) {
$newSettingsBlock.find( '.wpforms-panel-field-confirmations-type-wrap' )
.after( WPForms.Admin.Builder.Templates
.get( 'wpforms-builder-confirmations-message-field' )( {
// Conditional logic, if present
const $conditionalLogic = $newSettingsBlock.find( '.wpforms-conditional-block' );
if ( $conditionalLogic.length && typeof WPForms !== 'undefined' ) {
.html( WPForms.Admin.Builder.Templates
.get( 'wpforms-builder-conditional-logic-toggle-field' )( {
actions: JSON.stringify( $newSettingsBlock.find( '.wpforms-panel-field-conditional_logic-checkbox' ).data( 'actions' ) ),
actionDesc: $newSettingsBlock.find( '.wpforms-panel-field-conditional_logic-checkbox' ).data( 'action-desc' ),
// Fields Map Table, if present.
const $fieldsMapTable = $newSettingsBlock.find( '.wpforms-field-map-table' );
if ( $fieldsMapTable.length ) {
$fieldsMapTable.each( function( index, el ) {
$table.find( 'tr:not(:first-child)' ).remove();
const $input = $table.find( '.key input' ),
$select = $table.find( '.field select' ),
name = $select.data( 'name' );
$input.attr( 'value', '' );
.attr( 'data-name', name.replace( /\[(\d+)\]/, '[' + nextID + ']' ) );
newSettingsBlock = $newSettingsBlock.wrap( '<div>' ).parent().html();
newSettingsBlock = newSettingsBlock.replace( /\[conditionals\]\[(\d+)\]\[(\d+)\]/g, '[conditionals][0][0]' );
$firstSettingsBlock.before( newSettingsBlock );
const $addedSettingBlock = $firstSettingsBlock.prev();
// Reset the confirmation type to the 1st one.
if ( blockType === 'confirmation' ) {
app.prepareChoicesJSField( $addedSettingBlock, nextID );
app.confirmationFieldsToggle( $( '.wpforms-panel-field-confirmations-type' ).first() );
if ( typeof tinymce !== 'undefined' && typeof wp.editor !== 'undefined' && blockType === 'confirmation' ) {
wp.editor.initialize( 'wpforms-panel-field-confirmations-message-' + nextID, s.tinymceDefaults );
// Init tooltips for a new section.
$builder.trigger( 'wpformsSettingsBlockAdded', [ $addedSettingBlock ] );
$el.attr( 'data-next-id', nextID + 1 );
text: wpforms_builder.cancel,
// We need to process this event here, because we need a confirmation
// modal object defined, so we can intrude into it.
// Pressing Enter will click the Ok button.
$builder.on( 'keypress', '#settings-block-name', function( e ) {
if ( e.keyCode === 13 ) {
$( modal.buttons.confirm.el ).trigger( 'click' );
* Reset the 'Select Page' field to it's initial state then
* re-initialize ChoicesJS on it.
* @param {jQuery} $addedSettingBlock Newly added Settings Block jQuery object.
* @param {number} addedSettingBlockID Number ID used when `$addedSettingBlock` was created.
prepareChoicesJSField( $addedSettingBlock, addedSettingBlockID ) {
const $addedConfirmationWrap = $addedSettingBlock.find( `#wpforms-panel-field-confirmations-${ addedSettingBlockID }-page-wrap` );
if ( $addedConfirmationWrap.length <= 0 ) {
const $confirmationSelectPageField = $addedConfirmationWrap.find( `#wpforms-panel-field-confirmations-${ addedSettingBlockID }-page` );
if ( $confirmationSelectPageField.length <= 0 && ! $confirmationSelectPageField.hasClass( 'choicesjs-select' ) ) {
const $choicesWrapper = $addedConfirmationWrap.find( '.choices' );
if ( $choicesWrapper.length <= 0 ) {
// Remove ChoicesJS-related attr.
const $selectPageField = $confirmationSelectPageField.first();
$selectPageField.removeAttr( 'data-choice' );
$selectPageField.removeAttr( 'hidden' );
$selectPageField.removeClass( 'choices__input' );
// Move the select page field to it's initial location in the DOM.
$( $selectPageField ).appendTo( $addedConfirmationWrap.first() );
// Remove the `.choices` wrapper.
$choicesWrapper.first().remove();
app.dropdownField.events.choicesInit( $selectPageField );
* Show settings block editing interface.
* @param {jQuery} $el Element.
settingsBlockNameEditingShow( $el ) {
const headerHolder = $el.parents( '.wpforms-builder-settings-block-header' ),
nameHolder = headerHolder.find( '.wpforms-builder-settings-block-name' );
// Make the editing interface active and in focus
headerHolder.find( '.wpforms-builder-settings-block-name-edit' ).addClass( 'active' );
wpf.focusCaretToEnd( headerHolder.find( 'input' ) );
* Update settings block name and hide editing interface.
* @param {jQuery} $el Element.
settingsBlockNameEditingHide( $el ) {
const headerHolder = $el.parents( '.wpforms-builder-settings-block-header' ),
nameHolder = headerHolder.find( '.wpforms-builder-settings-block-name' ),
editHolder = headerHolder.find( '.wpforms-builder-settings-block-name-edit' );
let currentName = editHolder.find( 'input' ).val().trim();
const blockType = $el.closest( '.wpforms-builder-settings-block' ).data( 'block-type' );
// Provide a default value for empty settings block name.
if ( ! currentName.length ) {
currentName = wpforms_builder[ blockType + '_def_name' ];
// This is done for sanitizing.
editHolder.find( 'input' ).val( currentName );
nameHolder.text( currentName );
// Editing should be hidden, displaying - active.
.removeClass( 'editing' )
editHolder.removeClass( 'active' );
* Clone the Notification block with all of its content and events.
* Put the newly created clone above the target.
* @since 1.7.7 Registered `wpformsSettingsBlockCloned` trigger.
* @param {Object} $el Clone icon DOM element.
settingsBlockPanelClone( $el ) { // eslint-disable-line max-lines-per-function
const $panel = $el.closest( '.wpforms-panel-content-section' ),
$addNewSettingButton = $panel.find( '.wpforms-builder-settings-block-add' ),
$settingsBlock = $el.closest( '.wpforms-builder-settings-block' ),
$settingBlockContent = $settingsBlock.find( '.wpforms-builder-settings-block-content' ),
settingsBlockId = parseInt( $addNewSettingButton.attr( 'data-next-id' ), 10 ),
settingsBlockType = $settingsBlock.data( 'block-type' ),
settingsBlockName = $settingsBlock.find( '.wpforms-builder-settings-block-name' ).text().trim() + wpforms_builder[ settingsBlockType + '_clone' ],
isVisibleContent = $settingBlockContent.is( ':hidden' );
// Restore tooltips before cloning.
wpf.restoreTooltips( $settingsBlock );
const $clone = $settingsBlock.clone( false, true );
// Save open/close state while cloning.
app.settingsBlockUpdateState( isVisibleContent, settingsBlockId, settingsBlockType );
// Change the cloned setting block ID and name.
$clone.data( 'block-id', settingsBlockId );
$clone.find( '.wpforms-builder-settings-block-header span' ).text( settingsBlockName );
$clone.find( '.wpforms-builder-settings-block-header input' ).val( settingsBlockName );
$clone.removeClass( 'wpforms-builder-settings-block-default' );
// Change the Next Settings block ID for "Add new" button.
$addNewSettingButton.attr( 'data-next-id', settingsBlockId + 1 );
// Change the name attribute.
$clone.find( 'input, textarea, select' ).each( function() {
if ( $this.attr( 'name' ) ) {
$this.attr( 'name', $this.attr( 'name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ) );
if ( $this.data( 'name' ) ) {
$this.data( 'name', $this.data( 'name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ) );
if ( $this.attr( 'class' ) ) {
$this.attr( 'class', $this.attr( 'class' ).replace( /-(\d+)/, '-' + settingsBlockId ) );
if ( $this.attr( 'data-radio-group' ) ) {
$this.attr( 'data-radio-group', $this.attr( 'data-radio-group' ).replace( /(\d+)-/, settingsBlockId + '-' ) );
// Change IDs/data-attributes in DOM elements.
$clone.find( '*' ).each( function() {
if ( $this.attr( 'id' ) ) {
$this.attr( 'id', $this.attr( 'id' ).replace( /-(\d+)/, '-' + settingsBlockId ) );
if ( $this.attr( 'for' ) ) {
$this.attr( 'for', $this.attr( 'for' ).replace( /-(\d+)-/, '-' + settingsBlockId + '-' ) );
if ( $this.data( 'input-name' ) ) {
$this.data( 'input-name', $this.data( 'input-name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ) );
// Transfer selected values to copy elements since jQuery doesn't clone the current selected state.
$settingsBlock.find( 'select' ).each( function() {
const baseSelectName = $( this ).attr( 'name' ),
clonedSelectName = $( this ).attr( 'name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' );
$clone.find( 'select[name="' + clonedSelectName + '"]' ).val( $( this ).attr( 'name', baseSelectName ).val() );
// Insert before the target settings block.
.css( 'display', 'none' )
.insertBefore( $settingsBlock )
.show( 'fast', function() {
// Init tooltips for a new section.
$builder.trigger( 'wpformsSettingsBlockCloned', [ $clone, $settingsBlock.data( 'block-id' ) ] );
* Show or hide settings block panel content.
* @param {Object} $el Toggle icon DOM element.
settingsBlockPanelToggle( $el ) {
const $settingsBlock = $el.closest( '.wpforms-builder-settings-block' ),
settingsBlockId = $settingsBlock.data( 'block-id' ),
settingsBlockType = $settingsBlock.data( 'block-type' ),
$content = $settingsBlock.find( '.wpforms-builder-settings-block-content' ),
isVisible = $content.is( ':visible' );
$content.stop().slideToggle( {
// Send early to save fast.
// It's an animation start, so we should save the state for the animation end (reversed).
app.settingsBlockUpdateState( isVisible, settingsBlockId, settingsBlockType );
if ( $content.is( ':visible' ) ) {
$el.html( '<i class="fa fa-chevron-circle-up"></i>' );
$el.html( '<i class="fa fa-chevron-circle-down"></i>' );