' ).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() );
}
// Init the WP Editor.
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.
wpf.initTooltips();
$builder.trigger( 'wpformsSettingsBlockAdded', [ $addedSettingBlock ] );
$el.attr( 'data-next-id', nextID + 1 );
},
},
cancel: {
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.
*
* @since 1.7.9
*
* @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 ) {
return;
}
const $confirmationSelectPageField = $addedConfirmationWrap.find( `#wpforms-panel-field-confirmations-${ addedSettingBlockID }-page` );
if ( $confirmationSelectPageField.length <= 0 && ! $confirmationSelectPageField.hasClass( 'choicesjs-select' ) ) {
return;
}
const $choicesWrapper = $addedConfirmationWrap.find( '.choices' );
if ( $choicesWrapper.length <= 0 ) {
return;
}
// 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();
// Re-init ChoicesJS.
app.dropdownField.events.choicesInit( $selectPageField );
},
/**
* Show settings block editing interface.
*
* @since 1.4.8
*
* @param {jQuery} $el Element.
*/
settingsBlockNameEditingShow( $el ) {
const headerHolder = $el.parents( '.wpforms-builder-settings-block-header' ),
nameHolder = headerHolder.find( '.wpforms-builder-settings-block-name' );
nameHolder
.addClass( 'editing' )
.hide();
// 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.
*
* @since 1.4.8
*
* @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.
nameHolder
.removeClass( 'editing' )
.show();
editHolder.removeClass( 'active' );
},
/**
* Clone the Notification block with all of its content and events.
* Put the newly created clone above the target.
*
* @since 1.6.5
* @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() {
const $this = $( this );
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() {
const $this = $( this );
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.
$clone
.css( 'display', 'none' )
.insertBefore( $settingsBlock )
.show( 'fast', function() {
// Init tooltips for a new section.
wpf.initTooltips();
} );
$builder.trigger( 'wpformsSettingsBlockCloned', [ $clone, $settingsBlock.data( 'block-id' ) ] );
},
/**
* Show or hide settings block panel content.
*
* @since 1.4.8
*
* @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( {
duration: 400,
start() {
// Send it 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 );
},
always() {
if ( $content.is( ':visible' ) ) {
$el.html( '
' );
} else {
$el.html( '
' );
}
},
} );
},
/**
* Delete settings block.
*
* @since 1.4.8
* @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 ) {
return;
}
const $currentBlock = $el.closest( '.wpforms-builder-settings-block' ),
blockType = $currentBlock.data( 'block-type' );
$.confirm( {
title: false,
content: wpforms_builder[ blockType + '_delete' ],
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action() {
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,
form_id: s.formID,
} );
/* eslint-enable */
$currentBlock.remove();
$builder.trigger( 'wpformsSettingsBlockDeleted', [ blockType, settingsBlockId ] );
},
},
cancel: {
text: wpforms_builder.cancel,
},
},
} );
},
/**
* Change open/close state for setting block.
*
* @since 1.6.5
*
* @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',
form_id: s.formID,
block_id: settingsBlockId,
block_type: settingsBlockType,
nonce: wpforms_builder.nonce,
} );
},
/**
* Change visibility for notification elements, e.g.,
* the Enable This Notification toggle and notification status.
* The elements invisible when form has only one notification
* and customers can turn off all notifications instead.
*
* @since 1.9.2
*/
notificationsUpdateElementsVisibility() {
const $notifications = $( '.wpforms-panel-content-section-notifications .wpforms-notification' );
const $statuses = $notifications.find( '.wpforms-builder-settings-block-status' );
const isVisible = $notifications.length > 1;
const $fieldWrappers = $notifications.find( '.js-wpforms-enabled-notification' );
if ( isVisible ) {
$fieldWrappers.removeClass( 'wpforms-hidden' );
$statuses.removeClass( 'wpforms-hidden' );
return;
}
const $inputs = $fieldWrappers.find( 'input' );
$statuses.addClass( 'wpforms-hidden' );
$fieldWrappers.addClass( 'wpforms-hidden' );
$inputs.prop( 'checked', true );
},
/**
* Update notification status to display if the notification is active or inactive.
*
* @since 1.9.2
*
* @since 1.9.2
*
* @param {jQuery} $notification Notification element.
*/
notificationUpdateStatus( $notification ) {
const isNotificationsEnabled = $( '#wpforms-panel-field-settings-notification_enable' ).is( ':checked' );
const isEnabledNotification = $notification.find( '.js-wpforms-enabled-notification input' ).is( ':checked' );
const $status = $notification.find( '.wpforms-builder-settings-block-status' );
if ( isNotificationsEnabled && isEnabledNotification ) {
$status.removeClass( 'wpforms-badge-silver' );
$status.addClass( 'wpforms-badge-green' );
$status.text( $status.data( 'active' ) );
return;
}
$status.removeClass( 'wpforms-badge-green' );
$status.addClass( 'wpforms-badge-silver' );
$status.text( $status.data( 'inactive' ) );
},
//--------------------------------------------------------------------//
// Revisions Panel
//--------------------------------------------------------------------//
/**
* Element bindings for Revisions panel.
*
* @since 1.7.3
*/
bindUIActionsRevisions() {
// Update a revisions panel when it becomes active.
$builder.on( 'wpformsPanelSwitched', function( event, panel ) {
if ( panel !== 'revisions' ) {
return;
}
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' ) {
return;
}
app.updateRevisionsList();
} );
},
/**
* Fetch and update a list of form revisions.
*
* @since 1.7.3
*/
updateRevisionsList() {
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' ) ) {
return;
}
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.
*
* @since 1.7.3
*/
updateRevisionPreview() {
// Clone preview DOM from a Fields panel.
const $preview = elements.$formPreview.clone();
// Clean up the cloned preview, remove unnecessary elements, set states etc.
$preview
.find( '.wpforms-field-duplicate, .wpforms-field-delete, .wpforms-field-helper, .wpforms-debug' )
.remove()
.end();
$preview
.find( '.wpforms-field-wrap' )
.removeClass( 'ui-sortable' )
.addClass( 'ui-sortable-disabled' );
$preview
.find( '.wpforms-field' )
.removeClass( 'ui-sortable-handle ui-draggable ui-draggable-handle active' )
.removeAttr( 'id data-field-id data-field-type' )
.removeData();
$preview
.find( '.wpforms-field-submit-button' )
.prop( 'disabled', true );
// Put the cleaned-up clone into a Preview panel.
if ( elements.$revisionPreview.hasClass( 'has-preview' ) ) {
elements
.$revisionPreview
.find( '.wpforms-preview-wrap' )
.replaceWith( $preview );
} else {
elements
.$revisionPreview
.append( $preview )
.addClass( 'has-preview' );
}
},
/**
* Inform the user about making this version the default if revision is currently loaded, and it was modified.
*
* @since 1.7.3
*/
confirmSaveRevision() {
$.confirm( {
title: wpforms_builder.heads_up,
content: wpforms_builder.revision_update_confirm,
icon: 'fa fa-exclamation-circle',
type: 'orange',
closeIcon: false,
buttons: {
confirm: {
text: wpforms_builder.save,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action() {
// 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 );
},
},
cancel: {
text: wpforms_builder.cancel,
action() {
WPFormsBuilder.setCloseConfirmation( true );
},
},
},
} );
},
/**
* When a modified revision was saved as a current version, reload the Form Builder with the current tab active.
*
* @since 1.7.3
*/
revisionSavedReload() {
wpf.updateQueryString( 'view', wpf.getQueryString( 'view' ) );
wpf.removeQueryParam( 'revision_id' );
window.location.reload();
},
//--------------------------------------------------------------------//
// Save and Exit
//--------------------------------------------------------------------//
/**
* Element bindings for Embed and Save/Exit items.
*
* @since 1.0.0
* @since 1.5.8 Added trigger on `wpformsSaved` event to remove a `newform` URL-parameter.
*/
bindUIActionsSaveExit() {
// Embed form.
$builder.on( 'click', '#wpforms-embed', function( e ) {
e.preventDefault();
if ( $( this ).hasClass( 'wpforms-disabled' ) || $( this ).hasClass( 'wpforms-btn-light-grey-disabled' ) ) {
return;
}
WPFormsFormEmbedWizard.openPopup();
} );
// Save form.
$builder.on( 'click', '#wpforms-save', function( e ) {
e.preventDefault();
app.formSave( false );
} );
// Exit builder.
$builder.on( 'click', '#wpforms-exit', function( e ) {
e.preventDefault();
app.formExit();
} );
// After form save.
$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
/**
* Save form.
*
* @since 1.0.0
* @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();
return;
}
if ( typeof tinyMCE !== 'undefined' ) {
tinyMCE.triggerSave();
}
const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeSave' );
// Allow callbacks on `wpformsBeforeSave` to cancel form submission by triggering `event.preventDefault()`.
if ( event.isDefaultPrevented() ) {
return;
}
const $saveBtn = elements.$saveButton,
$icon = $saveBtn.find( 'i.fa-check' ),
$spinner = $saveBtn.find( 'i.wpforms-loading-spinner' ),
$label = $saveBtn.find( 'span' ),
text = $label.text();
$label.text( wpforms_builder.saving );
$saveBtn.prop( 'disabled', true );
$icon.addClass( 'wpforms-hidden' );
$spinner.removeClass( 'wpforms-hidden' );
const data = {
action: 'wpforms_save_form',
data: JSON.stringify( app.serializeAllData( $( '#wpforms-builder-form' ) ) ),
id: s.formID,
nonce: wpforms_builder.nonce,
};
return $.post( wpforms_builder.ajax_url, data, function( response ) {
if ( response.success ) {
wpf.savedState = wpf.getFormState( '#wpforms-builder-form' );
wpf.initialSave = false;
$builder.trigger( 'wpformsSaved', response.data );
if ( true === redirect && app.isBuilderInPopup() ) {
app.builderInPopupClose( 'saved' );
return;
}
if ( true === redirect ) {
window.location.href = wpforms_builder.exit_url;
}
} else {
wpf.debug( response );
app.formSaveError( response.data );
}
} ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars
wpf.debug( xhr );
app.formSaveError();
} ).always( function() {
$label.text( text );
$saveBtn.prop( 'disabled', false );
$spinner.addClass( 'wpforms-hidden' );
$icon.removeClass( 'wpforms-hidden' );
} );
},
/**
* Serialize all form data including checkboxes that are not checked.
*
* @since 1.9.0
*
* @param {Object} $form Form jQuery object.
*
* @return {Array} Form data.
*/
serializeAllData( $form ) {
const formData = $form.serializeArray();
$form.find( '.wpforms-field-option-layout .wpforms-field-option-row-label_hide input[type=checkbox]' ).each( function() {
const $checkbox = $( this );
const name = $checkbox.attr( 'name' );
const value = $checkbox.is( ':checked' ) ? '1' : '';
if ( ! value ) {
formData.push( { name, value } );
}
} );
return formData;
},
/**
* Form save error.
*
* @since 1.6.3
*
* @param {string} error Error message.
*/
formSaveError( error = '' ) {
// Default error message.
if ( wpf.empty( error ) ) {
error = wpforms_builder.error_save_form;
}
// Display error in a modal window.
$.confirm( {
title: wpforms_builder.heads_up,
content: '
' + error + '
' + wpforms_builder.error_contact_support + '
',
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
},
/**
* Exit form builder.
*
* @since 1.0.0
*/
formExit() {
if ( app.isBuilderInPopup() && app.formIsSaved() ) {
app.builderInPopupClose( 'saved' );
return;
}
if ( app.formIsSaved() ) {
window.location.href = wpforms_builder.exit_url;
} else {
$.confirm( {
title: false,
content: wpforms_builder.exit_confirm,
icon: 'fa fa-exclamation-circle',
type: 'orange',
closeIcon: true,
buttons: {
confirm: {
text: wpforms_builder.save_exit,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action() {
app.formSave( true );
},
},
cancel: {
text: wpforms_builder.exit,
action() {
closeConfirmation = false;
if ( app.isBuilderInPopup() ) {
app.builderInPopupClose( 'canceled' );
return;
}
window.location.href = wpforms_builder.exit_url;
},
},
},
} );
}
},
/**
* Close confirmation setter.
*
* @since 1.6.2
*
* @param {boolean} confirm Close confirmation flag value.
*/
setCloseConfirmation( confirm ) {
closeConfirmation = !! confirm;
},
/**
* Check the current form state.
*
* @since 1.0.0
*
* @return {boolean} True if the form is saved.
*/
formIsSaved() {
return wpf.savedState === wpf.getFormState( '#wpforms-builder-form' );
},
/**
* Check if the builder opened in the popup (iframe).
*
* @since 1.6.2
*
* @return {boolean} True if builder opened in the popup.
*/
isBuilderInPopup() {
return window.self !== window.parent && window.self.frameElement.id === 'wpforms-builder-iframe';
},
/**
* Close popup with the form builder.
*
* @since 1.6.2
*
* @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.fadeOut();
$popup.trigger( 'wpformsBuilderInPopupClose', [ action, s.formID, $title ] );
},
//--------------------------------------------------------------------//
// General / global
//--------------------------------------------------------------------//
/**
* Element bindings for general and global items.
*
* @since 1.2.0
*/
bindUIActionsGeneral() { // eslint-disable-line max-lines-per-function
// Toggle Smart Tags
$builder.on( 'click', '.toggle-smart-tag-display', app.smartTagToggle );
$builder.on( 'click', '.smart-tags-list-display a', app.smartTagInsert );
// Toggle unfoldable group of fields
$builder.on( 'click', '.wpforms-panel-fields-group.unfoldable .wpforms-panel-fields-group-title', app.toggleUnfoldableGroup );
// Hide field preview helper box.
$builder.on( 'click', '.wpforms-field-helper-hide ', app.hideFieldHelper );
// Field map table, update key source
$builder.on( 'input', '.wpforms-field-map-table .key-source', function() {
const value = $( this ).val(),
$dest = $( this ).parent().parent().find( '.key-destination' ),
name = $dest.data( 'name' );
if ( value ) {
$dest.attr( 'name', name.replace( '{source}', value.replace( /[^0-9a-zA-Z_-]/gi, '' ) ) );
}
} );
// Field map table, delete row
$builder.on( 'click', '.wpforms-field-map-table .remove', function( e ) {
e.preventDefault();
app.fieldMapTableDeleteRow( e, $( this ) );
} );
// Field map table, Add row
$builder.on( 'click', '.wpforms-field-map-table .add', function( e ) {
e.preventDefault();
app.fieldMapTableAddRow( e, $( this ) );
} );
// Global select field mapping
$( document ).on( 'wpformsFieldUpdate', app.fieldMapSelect );
// Restrict user money input fields
$builder.on( 'input', '.wpforms-money-input', function( event ) { // eslint-disable-line no-unused-vars
const $this = $( this ),
amount = $this.val(),
start = $this[ 0 ].selectionStart,
end = $this[ 0 ].selectionEnd;
$this.val( amount.replace( /[^0-9.,]/g, '' ) );
$this[ 0 ].setSelectionRange( start, end );
} );
// Format user money input fields
$builder.on( 'focusout', '.wpforms-money-input', function( event ) { // eslint-disable-line no-unused-vars
const $this = $( this ),
amount = $this.val();
if ( ! amount ) {
return amount;
}
const sanitized = wpf.amountSanitize( amount ),
formatted = wpf.amountFormat( sanitized );
$this.val( formatted );
} );
// Show/hide a group of options.
$builder.on( 'change', '.wpforms-panel-field-toggle', function() {
const $input = $( this );
if ( $input.prop( 'disabled' ) ) {
return;
}
$input.prop( 'disabled', true );
app.toggleOptionsGroup( $input );
} );
// Don't allow users to enable payments if storing entries has
// been disabled in the General settings.
$builder.on( 'change', app.getPaymentsTogglesSelector(), function( event ) { // eslint-disable-line no-unused-vars
const $this = $( this ),
gateway = $this.attr( 'id' ).replace( /wpforms-panel-field-|-enable|_one_time|_recurring/gi, '' ),
$notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-' + gateway + '-wrap"]' ),
gatewayEnabled = $this.prop( 'checked' ) || $( '#wpforms-panel-field-' + gateway + '-enable_one_time' ).prop( 'checked' ) || $( '#wpforms-panel-field-' + gateway + '-enable_recurring' ).prop( 'checked' );
if ( gatewayEnabled ) {
const disabled = $( '#wpforms-panel-field-settings-disable_entries' ).prop( 'checked' );
if ( disabled ) {
$.confirm( {
title: wpforms_builder.heads_up,
content: wpforms_builder.payments_entries_off,
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
$this.prop( 'checked', false );
} else {
$notificationWrap.removeClass( 'wpforms-hidden' );
}
} else {
$notificationWrap.addClass( 'wpforms-hidden' );
$notificationWrap.find( 'input[id*="-' + gateway + '"]' ).prop( 'checked', false );
}
} );
// Disable entries toggle.
$builder.on( 'change', '#wpforms-panel-field-settings-disable_entries', function( event ) { // eslint-disable-line no-unused-vars
const $this = $( this );
// Toggle the store spam entries toggle.
$( '#wpforms-panel-field-settings-store_spam_entries-wrap' ).toggleClass( 'wpforms-hidden', $this.prop( 'checked' ) );
// Don't allow users to disable entries if payments have been enabled.
if ( $this.prop( 'checked' ) ) {
if ( app.isPaymentsEnabled() ) {
$.confirm( {
title: wpforms_builder.heads_up,
content: wpforms_builder.payments_on_entries_off,
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
$this.prop( 'checked', false );
} else {
$.alert( {
title: wpforms_builder.heads_up,
content: wpforms_builder.disable_entries,
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
}
}
} );
// Upload or add an image.
$builder.on( 'click', '.wpforms-image-upload-add', function( event ) {
event.preventDefault();
const $this = $( this );
const $container = $this.parent();
const mediaFrame = wpf.initMediaLibrary( {
title: wpforms_builder.upload_image_title,
extensions: wpforms_builder.upload_image_extensions,
extensionsError: wpforms_builder.upload_image_extensions_error,
buttonText: wpforms_builder.upload_image_button,
} );
mediaFrame.on( 'select', function() {
const mediaAttachment = mediaFrame.state().get( 'selection' ).first().toJSON();
const $preview = $container.find( '.preview' );
$container.find( '.source' ).val( mediaAttachment.url );
$preview.empty();
$preview.prepend( '

' );
if ( $this.data( 'after-upload' ) === 'hide' ) {
$this.hide();
}
$builder.trigger( 'wpformsImageUploadAdd', [ $this, $container ] );
} ).on( 'close', function() {
mediaFrame.off( 'library:selection:add' );
} );
// Now that everything has been set, let's open up the frame.
mediaFrame.open();
} );
// Remove and uploaded image.
$builder.on( 'click', '.wpforms-image-upload-remove', function( event ) {
event.preventDefault();
const $container = $( this ).parent().parent();
$container.find( '.preview' ).empty();
$container.find( '.wpforms-image-upload-add' ).show();
$container.find( '.source' ).val( '' );
$builder.trigger( 'wpformsImageUploadRemove', [ $( this ), $container ] );
} );
// Validate email smart tags in Notifications fields.
$builder.on( 'blur', '.wpforms-notification .wpforms-panel-field-text input', function() {
app.validateEmailSmartTags( $( this ) );
} );
$builder.on( 'blur', '.wpforms-notification .wpforms-panel-field-textarea textarea', function() {
app.validateEmailSmartTags( $( this ) );
} );
// Validate From Email in Notification settings.
$builder.on( 'focusout', '.wpforms-notification .wpforms-panel-field.js-wpforms-from-email-validation input', app.validateFromEmail );
$builder.on( 'wpformsPanelSectionSwitch', app.notificationsPanelSectionSwitch );
// Mobile notice primary button / close icon click.
$builder.on( 'click', '#wpforms-builder-mobile-notice .wpforms-fullscreen-notice-button-primary, #wpforms-builder-mobile-notice .close', function() {
window.location.href = wpforms_builder.exit_url;
} );
// Mobile notice secondary button click.
$builder.on( 'click', '#wpforms-builder-mobile-notice .wpforms-fullscreen-notice-button-secondary', function() {
window.location.href = wpf.updateQueryString( 'force_desktop_view', 1, window.location.href );
} );
// License Alert close button click.
$( '#wpforms-builder-license-alert .close' ).on( 'click', function() {
window.location.href = wpforms_builder.exit_url;
} );
// License Alert dismiss button click.
$( '#wpforms-builder-license-alert .dismiss' ).on( 'click', function( event ) {
event.preventDefault();
$( '#wpforms-builder-license-alert' ).remove();
wpCookies.set( 'wpforms-builder-license-alert', 'true', 3600 );
} );
// Don't allow the Akismet setting to be enabled if the Akismet plugin isn't available.
$builder.on( 'change', '#wpforms-panel-field-settings-akismet.wpforms-akismet-disabled', function( event ) { // eslint-disable-line no-unused-vars
const $this = $( this ),
akismetStatus = $this.data( 'akismet-status' );
if ( $this.prop( 'checked' ) ) {
$.alert( {
title: wpforms_builder.heads_up,
content: wpforms_builder[ akismetStatus ],
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
onClose() {
$this.prop( 'checked', false );
},
} );
}
} );
// Re-init Show More button for multiselect instances when it's visible.
$builder.on( 'wpformsPanelSectionSwitch wpformsPanelSwitched', function() {
wpf.reInitShowMoreChoices( $( '#wpforms-panel-providers, #wpforms-panel-settings' ) );
} );
},
/**
* Notification section switch event handler.
*
* @since 1.8.2.3
*
* @param {Object} e Event object.
* @param {string} panel Panel name.
*/
notificationsPanelSectionSwitch( e, panel ) {
if ( panel !== 'notifications' ) {
return;
}
$( '.wpforms-notification .wpforms-panel-field.js-wpforms-from-email-validation input' ).trigger( 'focusout' );
},
/**
* Check if one of the payment addons payments enabled.
*
* @since 1.7.5
*
* @return {boolean} True if one of the payment addons payment enabled.
*/
isPaymentsEnabled() {
let paymentEnabled = false;
$( app.getPaymentsTogglesSelector() ).each( function() {
if ( $( this ).prop( 'checked' ) ) {
paymentEnabled = true;
return false;
}
} );
return paymentEnabled;
},
/**
* Get Payments toggles selector.
*
* @since 1.7.5
*
* @return {string} List of selectors.
*/
getPaymentsTogglesSelector() {
return `.wpforms-panel-content-section-payment-toggle-one-time input,
.wpforms-panel-content-section-payment-toggle-recurring input,
#wpforms-panel-field-stripe-enable,
#wpforms-panel-field-paypal_standard-enable,
#wpforms-panel-field-authorize_net-enable,
#wpforms-panel-field-square-enable`;
},
/**
* Toggle an options group.
*
* @since 1.6.3
*
* @param {Object} $input Toggled field.
*/
toggleOptionsGroup( $input ) {
const name = $input.attr( 'name' );
let value = '';
const $body = $( '.wpforms-panel-field-toggle-body[data-toggle="' + name + '"]' ),
enableInput = function() {
$input.prop( 'disabled', false );
};
app.toggleProviderActiveIcon( $input );
if ( $body.length === 0 ) {
enableInput();
return;
}
const type = $input.attr( 'type' );
if ( 'checkbox' === type || 'radio' === type ) {
value = $input.prop( 'checked' ) ? $input.val() : '0';
} else {
value = $input.val();
}
$body.each( function() {
const $this = $( this );
// eslint-disable-next-line no-unused-expressions
$this.attr( 'data-toggle-value' ).toString() === value.toString()
? $this.slideDown( '', enableInput )
: $this.slideUp( '', enableInput );
} );
},
/**
* Toggle Provider Active icon.
*
* @since 1.9.3
*
* @param {Object} $input Toggled field.
*/
toggleProviderActiveIcon( $input ) {
const provider = $input.closest( '.wpforms-panel-content-section' ).data( 'provider' );
const wrappers = [
'wpforms-panel-field-' + provider + '-enable-wrap',
'wpforms-panel-field-' + provider + '-enable_one_time-wrap',
'wpforms-panel-field-' + provider + '-enable_recurring-wrap',
];
if ( ! provider || ! wrappers.includes( $input.attr( 'id' ) ) ) {
return;
}
let isActive = false;
wrappers.forEach( ( wrapper ) => {
const $wrapper = $( '#' + wrapper );
if ( $wrapper.length && $wrapper.find( 'input' ).is( ':checked' ) ) {
isActive = true;
}
} );
const $sidebar = $( `.wpforms-panel-sidebar-section[data-section=${ provider }]` ),
$check_icon = $sidebar.find( '.fa-check-circle-o' );
$check_icon.toggleClass( 'wpforms-hidden', ! isActive );
},
/**
* Toggle all option groups.
*
* @since 1.6.3
*
* @param {jQuery} $context Context container jQuery object.
*/
toggleAllOptionGroups( $context ) {
$context = $context || $builder || $( '#wpforms-builder' ) || $( 'body' );
if ( ! $context ) {
return;
}
// Show a toggled bodies.
$context.find( '.wpforms-panel-field-toggle' ).each( function() {
const $input = $( this );
$input.prop( 'disabled', true );
app.toggleOptionsGroup( $input );
} );
},
/**
* Toggle unfoldable group of fields.
*
* @since 1.6.8
*
* @param {Object} e Event object.
*/
toggleUnfoldableGroup( e ) {
e.preventDefault();
const $title = $( e.target ),
$group = $title.closest( '.wpforms-panel-fields-group' ),
$inner = $group.find( '.wpforms-panel-fields-group-inner' ),
cookieName = 'wpforms_fields_group_' + $group.data( 'group' );
if ( $group.hasClass( 'opened' ) ) {
wpCookies.remove( cookieName );
$inner.stop().slideUp( 150, function() {
$group.removeClass( 'opened' );
} );
} else {
wpCookies.set( cookieName, 'true', 2592000 ); // 1 month.
$group.addClass( 'opened' );
$inner.stop().slideDown( 150 );
}
},
/**
* Hide field preview helper box.
*
* @since 1.7.1
*
* @param {Object} e Event object.
*/
hideFieldHelper( e ) {
e.preventDefault();
e.stopPropagation();
const $helpers = $( '.wpforms-field-helper' ),
cookieName = 'wpforms_field_helper_hide';
wpCookies.set( cookieName, 'true', 30 * 24 * 60 * 60 ); // 1 month.
$helpers.hide();
},
/**
* Smart Tag toggling.
*
* @since 1.0.1
* @since 1.6.9 Simplify method.
*
* @param {Event} e Event.
*/
smartTagToggle( e ) {
e.preventDefault();
// Prevent ajax to validate the default email queued on focusout event.
elements.$focusOutTarget = null;
const $this = $( this ),
$wrapper = $this.closest( '.wpforms-panel-field,.wpforms-field-option-row' );
if ( $wrapper.hasClass( 'smart-tags-toggling' ) ) {
return;
}
$wrapper.addClass( 'smart-tags-toggling' );
if ( $this.hasClass( 'smart-tag-showing' ) ) {
app.removeSmartTagsList( $this );
return;
}
app.insertSmartTagsList( $this );
},
/**
* Remove Smart Tag list.
*
* @since 1.6.9
*
* @param {jQuery} $el Toggle element.
*/
removeSmartTagsList( $el ) {
const $wrapper = $el.closest( '.wpforms-panel-field,.wpforms-field-option-row' ),
$list = $wrapper.find( '.smart-tags-list-display' );
$el.find( 'span' ).text( wpforms_builder.smart_tags_show );
$list.slideUp( '', function() {
$list.remove();
$el.removeClass( 'smart-tag-showing' );
$wrapper.removeClass( 'smart-tags-toggling' );
} );
},
/**
* Insert Smart Tag list.
*
* @since 1.6.9
*
* @param {jQuery} $el Toggle element.
*/
insertSmartTagsList( $el ) {
const $wrapper = $el.closest( '.wpforms-panel-field,.wpforms-field-option-row' );
let $label = $el.closest( 'label' ),
insideLabel = true;
if ( ! $label.length ) {
$label = $wrapper.find( 'label' );
insideLabel = false;
}
const smartTagList = app.getSmartTagsList( $el, $label.attr( 'for' ).indexOf( 'wpforms-field-option-' ) !== -1 );
// eslint-disable-next-line no-unused-expressions
insideLabel
? $label.after( smartTagList )
: $el.after( smartTagList );
$el.find( 'span' ).text( wpforms_builder.smart_tags_hide );
$wrapper.find( '.smart-tags-list-display' ).slideDown( '', function() {
$el.addClass( 'smart-tag-showing' );
$wrapper.removeClass( 'smart-tags-toggling' );
} );
},
/**
* Get Smart Tag list markup.
*
* @since 1.6.9
*
* @param {jQuery} $el Toggle element.
* @param {boolean} isFieldOption Is a field option.
*
* @return {string} Smart Tags list markup.
*/
getSmartTagsList( $el, isFieldOption ) {
let smartTagList;
smartTagList = '
';
return smartTagList;
},
/**
* Get Smart Tag fields elements markup.
*
* @since 1.6.9
*
* @param {jQuery} $el Toggle element.
*
* @return {string} Smart Tags list elements markup.
*/
getSmartTagsListFieldsElements( $el ) {
const type = $el.data( 'type' );
if ( ! [ 'fields', 'all' ].includes( type ) ) {
return '';
}
const fields = app.getSmartTagsFields( $el );
if ( ! fields ) {
return '
' + wpforms_builder.fields_unavailable + '';
}
let smartTagListElements = '';
smartTagListElements += '
' + wpforms_builder.fields_available + '';
for ( const fieldKey in fields ) {
smartTagListElements += app.getSmartTagsListFieldsElement( fields[ fieldKey ] );
}
return smartTagListElements;
},
/**
* Get fields that possible to create smart tag.
*
* @since 1.6.9
*
* @param {jQuery} $el Toggle element.
*
* @return {Array} Fields for smart tags.
*/
getSmartTagsFields( $el ) {
const allowed = $el.data( 'fields' );
const isAllowedRepeater = $el.data( 'allow-repeated-fields' );
const allowedFields = allowed ? allowed.split( ',' ) : undefined;
return wpf.getFields( allowedFields, true, isAllowedRepeater );
},
/**
* Get field markup for the Smart Tags list.
*
* @since 1.6.9
*
* @param {Object} field A field.
*
* @return {string} Smart Tags field markup.
*/
getSmartTagsListFieldsElement( field ) {
const label = field.label
? wpf.encodeHTMLEntities( wpf.sanitizeHTML( field.label ) )
: wpforms_builder.field + ' #' + field.id;
let html = `
${ label }`;
const additionalTags = field.additional || [];
// Add additional tags for `name`, `date/time` and `address` fields.
if ( additionalTags.length > 1 ) {
additionalTags.forEach( ( additionalTag ) => {
// Capitalize the first letter and add space before numbers.
const additionalTagLabel = additionalTag.charAt( 0 ).toUpperCase() + additionalTag.slice( 1 ).replace( /(\D)(\d)/g, '$1 $2' );
html += `
${ label } – ${ additionalTagLabel }`;
} );
}
return html;
},
/**
* Get Smart Tag other elements markup.
*
* @since 1.6.9
*
* @param {jQuery} $el Toggle element.
* @param {boolean} isFieldOption Is a field option.
*
* @return {string} Smart Tags list elements markup.
*/
getSmartTagsListOtherElements( $el, isFieldOption ) {
const type = $el.data( 'type' );
let smartTagListElements;
if ( type !== 'other' && type !== 'all' ) {
return '';
}
smartTagListElements = '
' + wpforms_builder.other + '';
for ( const smartTagKey in wpforms_builder.smart_tags ) {
if (
( isFieldOption && wpforms_builder.smart_tags_disabled_for_fields.includes( smartTagKey ) ) ||
(
$el.data( 'location' ) === 'confirmations' &&
wpforms_builder.smart_tags_disabled_for_confirmations.includes( smartTagKey )
)
) {
continue;
}
smartTagListElements += '
' + wpforms_builder.smart_tags[ smartTagKey ] + '';
}
return smartTagListElements;
},
/**
* Smart Tag insert.
*
* @since 1.0.1
* @since 1.6.9 TinyMCE compatibility.
*
* @param {Event} e Event.
*/
smartTagInsert( e ) { // eslint-disable-line complexity
e.preventDefault();
const $this = $( this ),
$list = $this.closest( '.smart-tags-list-display' ),
$wrapper = $list.closest( '.wpforms-panel-field,.wpforms-field-option-row' ),
$toggle = $wrapper.find( '.toggle-smart-tag-display' ),
$input = $wrapper.find( 'input[type=text], textarea' ),
meta = $this.data( 'meta' ),
additional = $this.data( 'additional' ) ? '|' + $this.data( 'additional' ) : '',
type = $this.data( 'type' );
let smartTag = type === 'field' ? '{field_id="' + meta + additional + '"}' : '{' + meta + '}',
editor;
if ( typeof tinyMCE !== 'undefined' ) {
editor = tinyMCE.get( $input.prop( 'id' ) );
if ( editor && ! editor.hasFocus() ) {
editor.focus( true );
}
}
if ( editor && ! editor.isHidden() ) {
editor.insertContent( smartTag );
} else {
smartTag = ' ' + smartTag + ' ';
$input.insertAtCaret( smartTag );
// Remove redundant spaces after wrapping smartTag into spaces.
$input.val( $input.val().trim().replace( ' ', ' ' ) );
$input.trigger( 'focus' ).trigger( 'input' );
}
// Remove the list, all done!
$list.slideUp( '', function() {
$list.remove();
} );
$toggle.find( 'span' ).text( wpforms_builder.smart_tags_show );
$wrapper.find( '.toggle-smart-tag-display' ).removeClass( 'smart-tag-showing' );
},
/**
* Field map table - Delete row.
*
* @since 1.2.0
* @since 1.6.1.2 Registered `wpformsFieldMapTableDeletedRow` trigger.
*
* @param {Event} e Event.
* @param {Element} el Element.
*/
fieldMapTableDeleteRow( e, el ) {
const $this = $( el ),
$row = $this.closest( 'tr' ),
$table = $this.closest( 'table' ),
$block = $row.closest( '.wpforms-builder-settings-block' ),
total = $table.find( 'tr' ).length;
if ( total > '1' ) {
$row.remove();
$builder.trigger( 'wpformsFieldMapTableDeletedRow', [ $block ] );
}
},
/**
* Field map table - Add row.
*
* @since 1.2.0
* @since 1.6.1.2 Registered `wpformsFieldMapTableAddedRow` trigger.
*
* @param {Event} e Event.
* @param {Element} el Element.
*/
fieldMapTableAddRow( e, el ) {
const $this = $( el ),
$row = $this.closest( 'tr' ),
$block = $row.closest( '.wpforms-builder-settings-block' ),
choice = $row.clone().insertAfter( $row );
choice.find( 'input' ).val( '' );
choice.find( 'select :selected' ).prop( 'selected', false );
choice.find( '.key-destination' ).attr( 'name', '' );
$builder.trigger( 'wpformsFieldMapTableAddedRow', [ $block, choice ] );
},
/**
* Update field mapped select items on form updates.
*
* @since 1.2.0
* @since 1.6.1.2 Registered `wpformsFieldSelectMapped` trigger.
*
* @param {Event} e Event.
* @param {Object} fields Fields.
*/
fieldMapSelect( e, fields ) { // eslint-disable-line max-lines-per-function
const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeFieldMapSelectUpdate' );
// Allow callbacks on `wpformsBeforeFieldMapSelectUpdate` to cancel adding field
// by triggering `event.preventDefault()`.
if ( event.isDefaultPrevented() ) {
return;
}
$( '.wpforms-field-map-select' ).each( function( index, el ) { // eslint-disable-line complexity, no-unused-vars
const $this = $( this );
let allowedFields = $this.data( 'field-map-allowed' ),
placeholder = $this.data( 'field-map-placeholder' );
// Check if custom placeholder was provided.
if ( typeof placeholder === 'undefined' || ! placeholder ) {
placeholder = wpforms_builder.select_field;
}
// If allowed, fields are not defined, bail.
if ( typeof allowedFields !== 'undefined' && allowedFields ) {
allowedFields = allowedFields.split( ' ' );
} else {
return;
}
const selected = $this.find( 'option:selected' ).val();
// Reset select and add a placeholder option.
$this.empty().append( $( '