: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @since 1.6.1.2 Registered `wpformsSettingsBlockDeleted` trigger.
* @param {jQuery} $el Delete button element.
settingsBlockDelete( $el ) {
const $contentSection = $el.closest( '.wpforms-panel-content-section' );
// Skip if only one block persist.
// This condition should not execute in normal circumstances.
if ( $contentSection.find( '.wpforms-builder-settings-block' ).length < 2 ) {
const $currentBlock = $el.closest( '.wpforms-builder-settings-block' ),
blockType = $currentBlock.data( 'block-type' );
content: wpforms_builder[ blockType + '_delete' ],
icon: 'fa fa-exclamation-circle',
text: wpforms_builder.ok,
const settingsBlockId = $currentBlock.data( 'block-id' ),
settingsBlockType = $currentBlock.data( 'block-type' );
/* eslint-disable camelcase */
$.post( wpforms_builder.ajax_url, {
action: 'wpforms_builder_settings_block_state_remove',
nonce: wpforms_builder.nonce,
block_id: settingsBlockId,
block_type: settingsBlockType,
$builder.trigger( 'wpformsSettingsBlockDeleted', [ blockType, settingsBlockId ] );
text: wpforms_builder.cancel,
* Change open/close state for setting block.
* @param {boolean} isVisible State status.
* @param {number} settingsBlockId Block ID.
* @param {string} settingsBlockType Block type.
settingsBlockUpdateState( isVisible, settingsBlockId, settingsBlockType ) {
/* eslint-disable camelcase */
$.post( wpforms_builder.ajax_url, {
action: 'wpforms_builder_settings_block_state_save',
state: isVisible ? 'closed' : 'opened',
block_id: settingsBlockId,
block_type: settingsBlockType,
nonce: wpforms_builder.nonce,
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//
* Element bindings for Revisions panel.
bindUIActionsRevisions() {
// Update a revisions panel when it becomes active.
$builder.on( 'wpformsPanelSwitched', function( event, panel ) {
if ( panel !== 'revisions' ) {
app.updateRevisionsList();
app.updateRevisionPreview();
// Update revisions list when the form was saved with a revisions panel being active.
$builder.on( 'wpformsSaved', function( event ) { // eslint-disable-line no-unused-vars
if ( wpf.getQueryString( 'view' ) !== 'revisions' ) {
app.updateRevisionsList();
* Fetch and update a list of form revisions.
const $revisionsButtonBadge = $( '.wpforms-panel-revisions-button .badge-exclamation' );
// Revisions' badge exists, send a request and remove the badge on successful response.
if ( $revisionsButtonBadge.length ) {
$.post( wpforms_builder.ajax_url, {
action: 'wpforms_mark_panel_viewed',
form_id: s.formID, // eslint-disable-line camelcase
nonce: wpforms_builder.nonce,
.done( function( response ) {
// eslint-disable-next-line no-unused-expressions
response.success ? $revisionsButtonBadge.remove() : wpf.debug( response );
.fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars
wpf.debug( xhr.responseText || textStatus || '' );
// Revisions are disabled, no need to fetch a list of revisions.
if ( ! $builder.hasClass( 'wpforms-revisions-enabled' ) ) {
const $revisionsList = $( '#wpforms-panel-revisions .wpforms-revisions-content' );
// Dim the list, send a request and replace the list on successful response.
$revisionsList.fadeTo( 250, 0.25, function() {
$.post( wpforms_builder.ajax_url, {
action: 'wpforms_get_form_revisions',
form_id: s.formID, // eslint-disable-line camelcase
revision_id: wpf.getQueryString( 'revision_id' ), // eslint-disable-line camelcase
nonce: wpforms_builder.nonce,
.done( function( response ) {
// eslint-disable-next-line no-unused-expressions
response.success ? $revisionsList.replaceWith( response.data.html ) : wpf.debug( response );
.fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars
wpf.debug( xhr.responseText || textStatus || '' );
// Un-dim the list to reset the UI.
$revisionsList.fadeTo( 250, 1 );
* Clone form preview from Fields panel.
updateRevisionPreview() {
// Clone preview DOM from a Fields panel.
const $preview = elements.$formPreview.clone();
// Clean up the cloned preview, remove unnecessary elements, set states etc.
.find( '.wpforms-field-duplicate, .wpforms-field-delete, .wpforms-field-helper, .wpforms-debug' )
.find( '.wpforms-field-wrap' )
.removeClass( 'ui-sortable' )
.addClass( 'ui-sortable-disabled' );
.find( '.wpforms-field' )
.removeClass( 'ui-sortable-handle ui-draggable ui-draggable-handle active' )
.removeAttr( 'id data-field-id data-field-type' )
.find( '.wpforms-field-submit-button' )
.prop( 'disabled', true );
// Put the cleaned-up clone into a Preview panel.
if ( elements.$revisionPreview.hasClass( 'has-preview' ) ) {
.find( '.wpforms-preview-wrap' )
.replaceWith( $preview );
.addClass( 'has-preview' );
* Inform the user about making this version the default if revision is currently loaded, and it was modified.
title: wpforms_builder.heads_up,
content: wpforms_builder.revision_update_confirm,
icon: 'fa fa-exclamation-circle',
text: wpforms_builder.save,
// Put the Form Builder into "saving state".
$builder.addClass( 'wpforms-revision-is-saving' );
// Save the revision as current version and reload the Form Builder.
WPFormsBuilder.formSave( false ).done( app.revisionSavedReload );
text: wpforms_builder.cancel,
WPFormsBuilder.setCloseConfirmation( true );
* When a modified revision was saved as a current version, reload the Form Builder with the current tab active.
wpf.updateQueryString( 'view', wpf.getQueryString( 'view' ) );
wpf.removeQueryParam( 'revision_id' );
window.location.reload();
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//
* Element bindings for Embed and Save/Exit items.
* @since 1.5.8 Added trigger on `wpformsSaved` event to remove a `newform` URL-parameter.
bindUIActionsSaveExit() {
$builder.on( 'click', '#wpforms-embed', function( e ) {
if ( $( this ).hasClass( 'wpforms-disabled' ) || $( this ).hasClass( 'wpforms-btn-light-grey-disabled' ) ) {
WPFormsFormEmbedWizard.openPopup();
$builder.on( 'click', '#wpforms-save', function( e ) {
$builder.on( 'click', '#wpforms-exit', function( e ) {
$builder.on( 'wpformsSaved', function( e, data ) { // eslint-disable-line no-unused-vars
* Remove `newform` parameter if it's in URL, otherwise we can to get a "race condition".
* E.g., form settings will be updated before some provider connection is loaded.
wpf.removeQueryParam( 'newform' );
// eslint-disable-next-line jsdoc/require-returns
* @since 1.7.5 Added `wpformsBeforeSave` trigger.
* @param {boolean} redirect Whether to redirect after save.
formSave( redirect ) { // eslint-disable-line max-lines-per-function
// Saving a revision directly is not allowed. We need to notify the user that it will overwrite the current version.
if ( $builder.hasClass( 'wpforms-is-revision' ) && ! $builder.hasClass( 'wpforms-revision-is-saving' ) ) {
app.confirmSaveRevision();
if ( typeof tinyMCE !== 'undefined' ) {
const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeSave' );
// Allow callbacks on `wpformsBeforeSave` to cancel form submission by triggering `event.preventDefault()`.
if ( event.isDefaultPrevented() ) {
const $saveBtn = elements.$saveButton,
$icon = $saveBtn.find( 'i.fa-check' ),
$spinner = $saveBtn.find( 'i.wpforms-loading-spinner' ),
$label = $saveBtn.find( 'span' ),
$label.text( wpforms_builder.saving );
$saveBtn.prop( 'disabled', true );
$icon.addClass( 'wpforms-hidden' );
$spinner.removeClass( 'wpforms-hidden' );
action: 'wpforms_save_form',
data: JSON.stringify( $( '#wpforms-builder-form' ).serializeArray() ),
nonce: wpforms_builder.nonce,
return $.post( wpforms_builder.ajax_url, data, function( response ) {
if ( response.success ) {
wpf.savedState = wpf.getFormState( '#wpforms-builder-form' );
$builder.trigger( 'wpformsSaved', response.data );
if ( true === redirect && app.isBuilderInPopup() ) {
app.builderInPopupClose( 'saved' );
if ( true === redirect ) {
window.location.href = wpforms_builder.exit_url;
app.formSaveError( response.data );
} ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars
$saveBtn.prop( 'disabled', false );
$spinner.addClass( 'wpforms-hidden' );
$icon.removeClass( 'wpforms-hidden' );
* @param {string} error Error message.
// Default error message.
if ( wpf.empty( error ) ) {
error = wpforms_builder.error_save_form;
// Display error in a modal window.
title: wpforms_builder.heads_up,
content: '<p>' + error + '</p><p>' + wpforms_builder.error_contact_support + '</p>',
icon: 'fa fa-exclamation-circle',
text: wpforms_builder.ok,
if ( app.isBuilderInPopup() && app.formIsSaved() ) {
app.builderInPopupClose( 'saved' );
if ( app.formIsSaved() ) {
window.location.href = wpforms_builder.exit_url;
content: wpforms_builder.exit_confirm,
icon: 'fa fa-exclamation-circle',
text: wpforms_builder.save_exit,
text: wpforms_builder.exit,
closeConfirmation = false;
if ( app.isBuilderInPopup() ) {
app.builderInPopupClose( 'canceled' );
window.location.href = wpforms_builder.exit_url;
* Close confirmation setter.
* @param {boolean} confirm Close confirmation flag value.
setCloseConfirmation( confirm ) {
closeConfirmation = !! confirm;
* Check the current form state.
* @return {boolean} True if the form is saved.
return wpf.savedState === wpf.getFormState( '#wpforms-builder-form' );
* Check if the builder opened in the popup (iframe).
* @return {boolean} True if builder opened in the popup.
return window.self !== window.parent && window.self.frameElement.id === 'wpforms-builder-iframe';
* Close popup with the form builder.
* @param {string} action Performed action: saved or canceled.
builderInPopupClose( action ) {
const $popup = window.parent.jQuery( '.wpforms-builder-popup' );
const $title = $( '.wpforms-center-form-name' ).text();
$popup.find( '#wpforms-builder-iframe' ).attr( 'src', 'about:blank' );
$popup.trigger( 'wpformsBuilderInPopupClose', [ action, s.formID, $title ] );
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//