/* global jQuery, wpforms_builder, wpf, jconfirm, wpforms_panel_switch, Choices, WPForms, WPFormsFormEmbedWizard, wpCookies, tinyMCE, WPFormsUtils, List, wpforms_preset_choices */
/**
* @param wpforms_builder.smart_tags_disabled_for_confirmations
* @param wpforms_builder.allow_only_email_fields
* @param wpforms_builder.allow_only_one_email
* @param wpforms_builder.empty_email_address
* @param wpforms_builder.smart_tags_dropdown_mce_icon
*/
/* noinspection JSUnusedLocalSymbols */
/* eslint-disable no-unused-expressions, no-shadow */
// noinspection ES6ConvertVarToLetConst
var WPFormsBuilder = window.WPFormsBuilder || ( function( document, window, $ ) { // eslint-disable-line no-var
let s,
$builder;
const elements = {},
browser = {};
/**
* Whether to show the close confirmation dialog or not.
*
* @since 1.6.0
*
* @type {boolean}
*/
let closeConfirmation = true;
/**
* A field is adding.
*
* @since 1.7.1
*
* @type {boolean}
*/
let adding = false;
/**
* Preview tab.
*
* @since 1.9.4
*
* @type {object|null}
*/
let previewTab = null;
// noinspection JSUnusedGlobalSymbols
const app = {
/* eslint-disable camelcase */
settings: {
spinner: '',
spinnerInline: '',
tinymceDefaults: {
tinymce: { toolbar1: 'bold,italic,underline,blockquote,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,undo,redo,link' },
quicktags: true,
},
pagebreakTop: false,
pagebreakBottom: false,
upload_img_modal: false,
choicesLimit: 20, // Choices limit for fields different from Dropdown.
choicesLimitLong: 250, // Choices limit for Dropdown field.
},
/**
* Start the engine.
*
* @since 1.0.0
*/
init() {
const that = this;
wpforms_panel_switch = true;
s = this.settings;
// Document ready.
$( app.ready );
// Page load.
$( window ).on( 'load', function() {
// In the case of jQuery 3.+, we need to wait for a ready event first.
if ( typeof $.ready.then === 'function' ) {
$.ready.then( app.load );
} else {
app.load();
}
} );
$( window ).on( 'beforeunload', function() {
if ( ! that.formIsSaved() && closeConfirmation ) {
return wpforms_builder.are_you_sure_to_close;
}
} );
},
/**
* Page load.
*
* @since 1.0.0
* @since 1.7.9 Added `wpformsBuilderReady` hook.
*
* @return {false|void} False if default event is prevented.
*/
load() {
// Trigger initial save for new forms.
if ( wpf.getQueryString( 'newform' ) ) {
app.formSave( false );
}
const panel = $( '#wpforms-panels-toggle .active' ).data( 'panel' );
// Render form preview on the Revisions panel if the panel is active.
if ( panel === 'revisions' ) {
app.updateRevisionPreview();
}
// Allow callbacks to prevent making Form Builder ready...
const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBuilderReady' );
// ...by triggering `event.preventDefault()`.
if ( event.isDefaultPrevented() ) {
return false;
}
// Hide loading overlay and make the Form Builder ready to use.
app.hideLoadingOverlay();
// Maybe display informational modal.
// noinspection JSUnresolvedReference, EqualityComparisonWithCoercionJS
if ( wpforms_builder.template_modal_display == '1' && 'fields' === wpf.getQueryString( 'view' ) ) { // eslint-disable-line
$.alert( {
title: wpforms_builder.template_modal_title,
content: wpforms_builder.template_modal_msg,
icon: 'fa fa-info-circle',
type: 'blue',
buttons: {
confirm: {
text: wpforms_builder.close,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
}
},
/**
* Init elements cache.
*
* @since 1.9.2
*/
initElementsCache() {
// Cache builder element.
$builder = $( '#wpforms-builder' );
browser.isWindows = /Win/.test( navigator.userAgent );
browser.isLinux = /Linux/.test( navigator.userAgent );
browser.isMac = /Mac/.test( navigator.userAgent );
// Action buttons.
elements.$helpButton = $( '#wpforms-help' );
elements.$previewButton = $( '#wpforms-preview-btn' );
elements.$embedButton = $( '#wpforms-embed' );
elements.$saveButton = $( '#wpforms-save' );
elements.$exitButton = $( '#wpforms-exit' );
// Cache other elements.
elements.$noFieldsOptions = $( '#wpforms-panel-fields .wpforms-no-fields-holder .no-fields' );
elements.$noFieldsPreview = $( '#wpforms-panel-fields .wpforms-no-fields-holder .no-fields-preview' );
elements.$formPreview = $( '#wpforms-panel-fields .wpforms-preview-wrap' );
elements.$revisionPreview = $( '#wpforms-panel-revisions .wpforms-panel-content' );
elements.defaultEmailSelector = '.wpforms-field-option-email .wpforms-field-option-row-default_value input';
elements.$defaultEmail = $( elements.defaultEmailSelector );
elements.$focusOutTarget = null;
elements.$nextFieldId = $( '#wpforms-field-id' );
elements.$addFieldsTab = $( '#add-fields a' );
elements.$fieldOptions = $( '#wpforms-field-options' );
elements.$fieldsPreviewWrap = $( '#wpforms-panel-fields .wpforms-panel-content-wrap' );
elements.$sortableFieldsWrap = $( '#wpforms-panel-fields .wpforms-field-wrap' );
elements.$addFieldsButtons = $( '.wpforms-add-fields-button' ).not( '.not-draggable' ).not( '.warning-modal' ).not( '.education-modal' );
elements.$fieldsSidebar = $( '#wpforms-panel-fields .wpforms-add-fields' );
elements.$searchInput = $( '#wpforms-search-fields-input' );
elements.$sidebarToggle = $( '.wpforms-panels .wpforms-panel-sidebar-content .wpforms-panel-sidebar-toggle' );
},
/**
* Document ready.
*
* @since 1.0.0
*/
ready() { // eslint-disable-line max-lines-per-function
if ( app.isVisitedViaBackButton() ) {
location.reload();
return;
}
app.initElementsCache();
// Add `_wp_http_referer` to the data of every AJAX request.
$.ajaxSetup( {
data: {
// eslint-disable-next-line camelcase
_wp_http_referer: wpf.updateQueryString( '_wp_http_referer', null ),
},
} );
// Remove Embed button if builder opened in the popup.
if ( app.isBuilderInPopup() ) {
elements.$embedButton.remove();
elements.$previewButton.addClass( 'wpforms-alone' );
}
app.loadMsWinCSS();
// Bind all actions.
app.bindUIActions();
// Setup/cache some vars not available before
s.formID = $( '#wpforms-builder-form' ).data( 'id' );
s.pagebreakTop = $( '.wpforms-pagebreak-top' ).length;
s.pagebreakBottom = $( '.wpforms-pagebreak-bottom' ).length;
// Disable implicit submission for every form inside the builder.
// All form values are managed by JS and should not be submitted by pressing Enter.
$builder.on( 'keypress', '#wpforms-builder-form :input:not(textarea)', function( e ) {
if ( e.keyCode === 13 ) {
e.preventDefault();
}
} );
app.determineActiveSections();
app.loadEntryPreviewFields();
// Drag and drop sortable elements.
app.fieldChoiceSortable( 'select' );
app.fieldChoiceSortable( 'radio' );
app.fieldChoiceSortable( 'checkbox' );
app.fieldChoiceSortable( 'payment-multiple' );
app.fieldChoiceSortable( 'payment-checkbox' );
app.fieldChoiceSortable( 'payment-select' );
// Set field group visibility.
$( '.wpforms-add-fields-group' ).each( function( index, el ) { // eslint-disable-line no-unused-vars
app.fieldGroupToggle( $( this ), 'load' );
} );
app.registerTemplates();
// Trim long form titles.
app.trimFormTitle();
// Load Tooltips.
wpf.initTooltips();
// Load Color Pickers.
app.loadColorPickers();
// Hide/Show CAPTCHA in form.
app.captchaToggle();
// Confirmations' initial setup.
app.confirmationsSetup();
// Notification settings.
app.notificationToggle();
app.notificationsByStatusAlerts();
// Secret builder hotkeys.
app.builderHotkeys();
// jquery-confirm defaults.
jconfirm.defaults = {
closeIcon: false,
backgroundDismiss: false,
escapeKey: true,
animationBounce: 1,
useBootstrap: false,
theme: 'modern',
boxWidth: '400px',
animateFromElement: false,
content: wpforms_builder.something_went_wrong,
};
app.dropdownField.init();
app.iconChoices.init();
app.disabledFields.init();
app.checkEmptyDynamicChoices();
app.initSomeFieldOptions();
app.dismissNotice();
wpf.initializeChoicesEventHandlers();
},
checkEmptyDynamicChoices() {
const choices = wpf.orders.choices || {};
if ( ! Object.keys( choices ).length ) {
return;
}
wpf.orders.fields.forEach( function( fieldId ) { // eslint-disable-line complexity
const isDynamic = app.dropdownField.helpers.isDynamicChoices( fieldId );
if ( ! isDynamic ) {
return;
}
const $fieldPreview = $( '#wpforms-field-' + fieldId );
const type = app.dropdownField.helpers.getDynamicChoicesOptionType( fieldId );
const source = app.dropdownField.helpers.getDynamicChoicesOptionSource( fieldId );
const isModern = app.dropdownField.helpers.isDynamicChoicesOptionModern( fieldId );
let isEmpty = isModern
? $fieldPreview.find( '.has-no-choices' ).length
: $fieldPreview.find( '.primary-input option:not(.placeholder), .primary-input li' ).length === 0;
if ( isModern && ! isEmpty ) {
const placeholder = $( '#wpforms-field-option-' + fieldId + '-placeholder' ).val();
const choices = app.dropdownField.helpers.getInitialChoices( fieldId );
isEmpty = choices.length === 1 && choices[ 0 ].label === placeholder && choices[ 0 ].placeholder === true;
}
if ( isEmpty ) {
app.emptyChoicesNotice( fieldId, source, type );
}
} );
},
/**
* Load Microsoft Windows specific stylesheet.
*
* @since 1.6.8
*/
loadMsWinCSS() {
// Detect OS & browsers.
if ( browser.isMac ) {
return;
}
$( '' )
.appendTo( 'head' )
.attr( {
type: 'text/css',
rel: 'stylesheet',
href: wpforms_builder.scrollbars_css_url,
} );
},
/**
* Builder was visited via back button in the browser.
*
* @since 1.6.5
*
* @return {boolean} True if the builder was visited via back button in browser.
*/
isVisitedViaBackButton() {
if ( ! performance ) {
return false;
}
let isVisitedViaBackButton = false;
performance.getEntriesByType( 'navigation' ).forEach( function( nav ) {
if ( nav.type === 'back_forward' ) {
isVisitedViaBackButton = true;
}
} );
return isVisitedViaBackButton;
},
/**
* Remove loading overlay.
*
* @since 1.6.8
*/
hideLoadingOverlay() {
const $overlay = $( '#wpforms-builder-overlay' );
$overlay.addClass( 'fade-out' );
setTimeout( function() {
$overlay.hide();
}, 250 );
},
/**
* Show loading overlay.
*
* @since 1.6.8
*/
showLoadingOverlay() {
const $overlay = $( '#wpforms-builder-overlay' );
$overlay.removeClass( 'fade-out' );
$overlay.show();
},
/**
* Initialize some fields options controls.
*
* @since 1.6.3
*/
initSomeFieldOptions() {
// Show a toggled options groups.
app.toggleAllOptionGroups( $builder );
// Date/Time field Date type option.
$builder.find( '.wpforms-field-option-row-date .type select' ).trigger( 'change' );
},
/**
* Dropdown field component.
*
* @since 1.6.1
*/
dropdownField: {
/**
* Field configuration.
*
* @since 1.6.1
*/
config: {
modernClass: 'choicesjs-select',
args: {
searchEnabled: false,
searchChoices: false,
renderChoiceLimit: 1,
shouldSort: false,
callbackOnInit() {
const $element = $( this.containerOuter.element ),
$previewSelect = $element.closest( '.wpforms-field' ).find( 'select' );
// Turn off disabled styles.
if ( $element.hasClass( 'is-disabled' ) ) {
$element.removeClass( 'is-disabled' );
}
// Disable instances on the preview panel.
if ( $previewSelect.is( '[readonly]' ) ) {
this.disable();
$previewSelect.prop( 'disabled', false );
}
if ( this.passedElement.element.multiple ) {
// Hide a placeholder if field has selected choices.
if ( this.getValue( true ).length ) {
$( this.input.element ).addClass( 'choices__input--hidden' );
}
}
// Decode allowed HTML entities for choices.
$element.find( '.choices__item--selectable' ).each( function() {
const $choice = $( this );
const text = wpf.decodeAllowedHTMLEntities( $choice.text() );
$choice.text( text );
} );
},
},
},
/**
* Initialization for field component.
*
* @since 1.6.1
*/
init() {
// Choices.js init.
$builder.find( '.' + app.dropdownField.config.modernClass ).each( function() {
app.dropdownField.events.choicesInit( $( this ) );
} );
// Multiple option.
$builder.on(
'change',
'.wpforms-field-option-select .wpforms-field-option-row-multiple input',
app.dropdownField.events.multiple
);
// Style option.
$builder.on(
'change',
'.wpforms-field-option-select .wpforms-field-option-row-style select, .wpforms-field-option-payment-select .wpforms-field-option-row-style select',
app.dropdownField.events.applyStyle
);
// Add the ability to close the drop-down menu.
$builder.on( 'click', '.choices', function( e ) {
const $choices = $( this ),
choicesObj = $choices.find( 'select' ).data( 'choicesjs' );
if (
choicesObj &&
$choices.hasClass( 'is-open' ) &&
e.target.classList.contains( 'choices__inner' )
) {
choicesObj.hideDropdown();
}
} );
},
/**
* Field events.
*
* @since 1.6.1
*/
events: {
/**
* Load Choices.js library.
*
* @since 1.6.1
*
* @param {Object} $element jQuery element selector.
*/
choicesInit( $element ) {
const useAjax = $element.data( 'choicesjs-use-ajax' ) === 1;
let instance;
if ( $element.data( 'choicesjs-callback-fn' ) === 'select_pages' ) {
instance = WPForms.Admin.Builder.WPFormsChoicesJS.setup(
$element[ 0 ],
app.dropdownField.config.args,
{
action: 'wpforms_ajax_search_pages_for_dropdown',
nonce: useAjax ? wpforms_builder.nonce : null,
}
);
} else {
instance = new Choices( $element[ 0 ], app.dropdownField.config.args );
}
app.dropdownField.helpers.setInstance( $element, instance );
app.dropdownField.helpers.addPlaceholderChoice( $element, instance );
$element.closest( '.choices' ).toggleClass( 'wpforms-hidden', ! instance.config.choices.length );
},
/**
* Multiple option callback.
*
* @since 1.6.1
*
* @param {Object} event Event object.
*/
multiple( event ) {
const fieldId = $( this ).closest( '.wpforms-field-option-row-multiple' ).data().fieldId,
$primary = app.dropdownField.helpers.getPrimarySelector( fieldId ),
$optionChoicesItems = $( '#wpforms-field-option-row-' + fieldId + '-choices input.default' ),
$placeholder = $primary.find( '.placeholder' ),
isDynamicChoices = app.dropdownField.helpers.isDynamicChoices( fieldId ),
isMultiple = event.target.checked,
choicesType = isMultiple ? 'checkbox' : 'radio';
// Add/remove a `multiple` attribute.
$primary.prop( 'multiple', isMultiple );
// Change a `Choices` fields type:
// checkbox - needed for multiple selection
// radio - needed for single selection
$optionChoicesItems.prop( 'type', choicesType );
// Dynamic Choices doesn't have default choices (selected options) - make all as unselected.
if ( isDynamicChoices ) {
$primary.find( 'option:selected' ).prop( 'selected', false );
}
// Gets default choices.
const selectedChoices = $optionChoicesItems.filter( ':checked' );
if ( ! isMultiple && selectedChoices.length ) {
// Uncheck all choices.
$optionChoicesItems.prop( 'checked', false );
// For single selection, we can choose only one.
$( selectedChoices.get( 0 ) ).prop( 'checked', true );
}
// Toggle selection for a placeholder option based on a select type.
if ( $placeholder.length ) {
$placeholder.prop( 'selected', ! isMultiple );
}
// Update a primary field.
app.dropdownField.helpers.update( fieldId, isDynamicChoices );
},
/**
* Apply a style to