/* global wpuf_admin_script, ajaxurl, Swal, tinyMCE */ ;(function($) { 'use strict'; /** * Only proceed if current page is a form builder page */ if (!$('#wpuf-form-builder').length) { return; } if (!Array.prototype.hasOwnProperty('swap')) { Array.prototype.swap = function (from, to) { this.splice(to, 0, this.splice(from, 1)[0]); }; } // check if an element is visible in browser viewport function is_element_in_viewport (el) { if (typeof jQuery === "function" && el instanceof jQuery) { el = el[0]; } var rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */ rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */ ); } /** * Vuex Store data */ var wpuf_form_builder_store = new Vuex.Store({ state: { post: wpuf_form_builder.post, form_fields: wpuf_form_builder.form_fields, panel_sections: wpuf_form_builder.panel_sections, field_settings: wpuf_form_builder.field_settings, notifications: wpuf_form_builder.notifications, settings: wpuf_form_builder.form_settings, integrations: wpuf_form_builder.integrations || {}, current_panel: 'form-fields-v4-1', editing_field_id: 0, show_custom_field_tooltip: true, index_to_insert: 0, }, mutations: { set_form_fields: function (state, form_fields) { Vue.set(state, 'form_fields', form_fields); }, set_form_settings: function (state, value) { Vue.set(state, 'settings', value); }, // set the current panel set_current_panel: function (state, panel) { if ('field-options' !== state.current_panel && 'field-options' === panel && state.form_fields.length ) { state.editing_field_id = state.form_fields[0].id; } state.current_panel = panel; // reset editing field id if ('form-fields' === panel || 'form-fields-v4-1' === panel) { state.editing_field_id = 0; } }, // add show property to every panel section panel_add_show_prop: function (state) { state.panel_sections.map(function (section, index) { if (!section.hasOwnProperty('show')) { Vue.set(state.panel_sections[index], 'show', true); } }); }, // toggle panel sections panel_toggle: function (state, index) { state.panel_sections[index].show = !state.panel_sections[index].show; }, // open field settings panel open_field_settings: function ( state, field_id ) { var field = state.form_fields.filter( function ( item ) { return parseInt( field_id ) === parseInt( item.id ); } ); if ('field-options' === state.current_panel && field[0].id === state.editing_field_id) { return; } if (field.length) { state.editing_field_id = 0; state.current_panel = 'field-options'; setTimeout( function () { state.editing_field_id = field[0].id; }, 400 ); } }, update_editing_form_field: function (state, payload) { var i = 0; for (i = 0; i < state.form_fields.length; i++) { // check if the editing field exist in normal fields if (state.form_fields[i].id === parseInt(payload.editing_field_id)) { // Store original values before making changes var original_read_only = state.form_fields[i].read_only; var original_required = state.form_fields[i].required; if ( 'read_only' === payload.field_name && (payload.value === true || payload.value === 'yes') ) { // Only set required to 'no' if it's currently 'yes' (to avoid unnecessary updates) if (state.form_fields[i].required === 'yes') { Vue.set(state.form_fields[i], 'required', 'no'); } } if ( 'required' === payload.field_name && (payload.value === true || payload.value === 'yes') ) { // Only set read_only to empty string if it's currently true/yes (to avoid unnecessary updates) // Empty string is what the checkbox expects when unchecked (for is_single_opt checkboxes) if (state.form_fields[i].read_only === true || state.form_fields[i].read_only === 'yes') { // Use Vue.set to ensure reactivity, and set it before assigning required Vue.set(state.form_fields[i], 'read_only', ''); // Force object property access to ensure Vue processes the change // This creates a micro-delay that allows Vue's reactivity to propagate JSON.stringify({ r: state.form_fields[i].read_only }); } } // Prevent infinite loop: if this mutation was triggered by our programmatic change, // don't apply mutual exclusion again if ( 'required' === payload.field_name && original_required === 'no' && payload.value === 'yes' ) { // This is likely a user trying to set required back to 'yes' after we set it to 'no' // Check if read_only was just set if (original_read_only === true || original_read_only === 'yes') { Vue.set(state.form_fields[i], 'read_only', ''); // Force object property access to ensure Vue processes the change JSON.stringify({ r: state.form_fields[i].read_only }); } } if (payload.field_name === 'name' && ! state.form_fields[i].hasOwnProperty('is_new') ) { continue; } else { // Prevent setting required to 'yes' if read_only is true if (payload.field_name === 'required' && payload.value === 'yes') { if (state.form_fields[i].read_only === true || state.form_fields[i].read_only === 'yes') { continue; // Skip updating required if read_only is set } } state.form_fields[i][payload.field_name] = payload.value; } } // check if the editing field belongs to a column field if (state.form_fields[i].template === 'column_field') { var innerColumnFields = state.form_fields[i].inner_fields; for (const columnFields in innerColumnFields) { if (innerColumnFields.hasOwnProperty(columnFields)) { var columnFieldIndex = 0; while (columnFieldIndex < innerColumnFields[columnFields].length) { // don't modify existing meta key if (payload.field_name === 'name' && ! innerColumnFields[columnFields][columnFieldIndex].hasOwnProperty('is_new') ) { columnFieldIndex++; continue; } if (innerColumnFields[columnFields][columnFieldIndex].id === parseInt(payload.editing_field_id)) { innerColumnFields[columnFields][columnFieldIndex][payload.field_name] = payload.value; } columnFieldIndex++; } } } } // check if the editing field belongs to a repeat field if (state.form_fields[i].template === 'repeat_field') { var innerRepeatFields = state.form_fields[i].inner_fields; if (Array.isArray(innerRepeatFields)) { for (var repeatFieldIndex = 0; repeatFieldIndex < innerRepeatFields.length; repeatFieldIndex++) { // don't modify existing meta key if (payload.field_name === 'name' && !innerRepeatFields[repeatFieldIndex].hasOwnProperty('is_new')) { continue; } if (innerRepeatFields[repeatFieldIndex].id === parseInt(payload.editing_field_id)) { if ('read_only' === payload.field_name && payload.value) { innerRepeatFields[repeatFieldIndex]['required'] = 'no'; } if ('required' === payload.field_name && 'yes' === payload.value) { innerRepeatFields[repeatFieldIndex]['read_only'] = false; } innerRepeatFields[repeatFieldIndex][payload.field_name] = payload.value; } } } } } }, // add new form field element add_form_field_element: function (state, payload) { // Initialize icon properties for new fields to ensure Vue reactivity if (!payload.field.hasOwnProperty('show_icon')) { payload.field.show_icon = 'no'; } if (!payload.field.hasOwnProperty('field_icon')) { payload.field.field_icon = ''; } if (!payload.field.hasOwnProperty('icon_position')) { payload.field.icon_position = 'left_label'; } state.form_fields.splice(payload.toIndex, 0, payload.field); var sprintf = wp.i18n.sprintf; var __ = wp.i18n.__; // bring newly added element into viewport, do not show for reg form if ( window.location.search.substring(1).split('&').includes('page=wpuf-profile-forms') ) { return; } Vue.nextTick(function () { var el = $('#form-preview-stage .wpuf-form .field-items').eq(payload.toIndex); if ('yes' === payload.field.is_meta && state.show_custom_field_tooltip) { var image_one = wpuf_admin_script.asset_url + '/images/custom-fields/settings.png'; var image_two = wpuf_admin_script.asset_url + '/images/custom-fields/advance.png'; var html = '
'; html += '
'; html += sprintf( '

%s %s%s"

', __( 'Navigate through', 'wp-user-frontend' ), ajaxurl.replace('admin-ajax.php', '') + 'admin.php?page=wpuf-settings#wpuf_frontend_posting', __( 'WP-admin > WPUF > Settings > Frontend Posting', 'wp-user-frontend' ), __( '- there you have to check the checkbox: "Show custom field data in the post content area', 'wp-user-frontend' ) ); html += 'settings'; html += '
'; html += '
'; var fieldIdForLink = payload.field.id; html += sprintf( '

%s%s%s

', __( 'Edit the custom field inside the post form and on the right side you will see ', 'wp-user-frontend' ), fieldIdForLink, __( '"Advanced Options".', 'wp-user-frontend' ), __( ' Expand that, scroll down and you will see ', 'wp-user-frontend' ), fieldIdForLink, __( '"Show data on post"', 'wp-user-frontend' ), __( ' - set this yes.', 'wp-user-frontend' ) ); html += 'custom field data'; html += '
'; html += '
'; Swal.fire({ title: __( 'Do you want to show custom field data inside your post ?', 'wp-user-frontend' ), html: html, imageUrl: wpuf_form_builder.is_pro_active ? wpuf_form_builder.lock_icon : wpuf_form_builder.free_icon, showCancelButton: true, confirmButtonText: "Don't show again", cancelButtonText: 'Okay', customClass: { confirmButton: '!wpuf-bg-white !wpuf-text-black !wpuf-border !wpuf-border-solid !wpuf-border-gray-300 focus:!wpuf-shadow-none', cancelButton: '!wpuf-text-white', }, cancelButtonColor: '#059669', didOpen: (modal) => { $(modal).find('button.wpuf-swal-action-link[data-action="open-advanced-options"]').on('click', function(e) { e.preventDefault(); var fieldId = $(this).data('field-id'); Swal.close(); setTimeout(() => { wpuf_form_builder_store.commit('open_field_settings', fieldId); Vue.nextTick(() => { setTimeout(() => { // Single timeout after Vue.nextTick let advancedOptionsTargetText = ''; if (typeof wpuf_form_builder_mixins !== 'undefined' && typeof wpuf_form_builder_mixins(Vue.prototype).i18n !== 'undefined' && wpuf_form_builder_mixins(Vue.prototype).i18n.advanced_options) { advancedOptionsTargetText = wpuf_form_builder_mixins(Vue.prototype).i18n.advanced_options; } else { advancedOptionsTargetText = __('"Advanced Options".', 'wp-user-frontend'); } advancedOptionsTargetText = advancedOptionsTargetText.replace(/"/g, '').replace(/\.$/, "").trim().toLowerCase(); var $fieldOptionsMainContainer = $('div.wpuf-form-builder-field-options'); if (!$fieldOptionsMainContainer.length) { console.warn('WPUF Form Builder Debug: Field options main container "div.wpuf-form-builder-field-options" NOT FOUND after delay.'); return; } // Check if the loader/placeholder is still visible if ($fieldOptionsMainContainer.find('> div:first-child[class*="text-center"]').is(':visible') && $fieldOptionsMainContainer.find('.option-fields-section').length === 0) { console.warn('WPUF Form Builder Debug: Loader/placeholder seems to be still visible or settings sections not rendered in .wpuf-form-builder-field-options. Action aborted.'); return; } var $advancedOptionsToggle, $advancedOptionsContentDiv, $sectionToScroll; var $sections = $fieldOptionsMainContainer.find('.option-fields-section'); $sections.each(function() { var $parentSection = $(this); var $h3 = $parentSection.find('h3').first(); if (!$h3.length) { return true; } var rawH3Text = $h3.clone().children('i').remove().end().text(); var normalizedH3Text = rawH3Text.trim().toLowerCase().replace(/\.$/, ""); if (normalizedH3Text === advancedOptionsTargetText) { $advancedOptionsToggle = $h3; $advancedOptionsContentDiv = $parentSection.find('div.option-field-section-fields').first(); $sectionToScroll = $parentSection; return false; } }); if ($advancedOptionsToggle && $advancedOptionsToggle.length) { var isContentVisible = false; if ($advancedOptionsContentDiv && $advancedOptionsContentDiv.length) { isContentVisible = $advancedOptionsContentDiv.is(':visible'); } else { console.warn('WPUF Form Builder Debug: Advanced options content div (.option-field-section-fields) not found relative to matched h3.'); } if (!isContentVisible) { $advancedOptionsToggle.trigger('click'); setTimeout(function() { if ($advancedOptionsContentDiv && $advancedOptionsContentDiv.length) { } }, 150); } else { } if (!$sectionToScroll || !$sectionToScroll.length) { $sectionToScroll = $advancedOptionsToggle; } setTimeout(function() { var elementToScrollTo = $sectionToScroll.get(0); if (elementToScrollTo && typeof elementToScrollTo.scrollIntoView === 'function') { elementToScrollTo.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }); } }, 350); } else { console.warn('WPUF Form Builder Debug: Could not find "Advanced Options" h3 toggle based on text: "' + advancedOptionsTargetText + '".'); } }, 400); // Inner timeout duration (e.g., 400ms) }); }, 250); }); } }).then(function(result) { if (result.isConfirmed) { state.show_custom_field_tooltip = false; } }); } if (el && !is_element_in_viewport(el.get(0))) { $('#builder-stage section').scrollTo(el, 800, {offset: -50}); } }); }, // sorting inside stage swap_form_field_elements: function (state, payload) { state.form_fields.swap(payload.fromIndex, payload.toIndex); }, clone_form_field_element: function (state, payload) { var field = _.find(state.form_fields, function (item) { return parseInt(item.id) === parseInt(payload.field_id); }); var clone = $.extend(true, {}, field), index = parseInt(payload.index) + 1; let column_field = state.form_fields.find(function (field) { return field.id === payload.field_id && field.input_type === 'column_field'; }); if (column_field){ let columns = ['column-1','column-2','column-3']; columns.forEach(function (column) { let inner_field = clone.inner_fields[column]; if(inner_field.length){ inner_field.forEach(function (field) { field.id = Math.floor(Math.random() * (9999999999 - 999999 + 1)) + 999999; field.name = field.name + '_copy'; field.is_new = true; }); } }); } clone.id = payload.new_id; clone.name = clone.name + '_copy'; clone.is_new = true; state.form_fields.splice(index, 0, clone); }, // delete a field delete_form_field_element: function (state, index) { state.current_panel = 'form-fields-v4-1'; state.form_fields.splice(index, 1); state.editing_field_id = 0; }, // set fields for a panel section set_panel_section_fields: function (state, payload) { var section = _.find(state.panel_sections, function (item) { return item.id === payload.id; }); section.fields = payload.fields; }, // notifications addNotification: function(state, payload) { state.notifications.push(payload); }, deleteNotification: function(state, index) { state.notifications.splice(index, 1); }, cloneNotification: function(state, index) { var clone = $.extend(true, {}, state.notifications[index]); index = parseInt(index) + 1; state.notifications.splice(index, 0, clone); }, // update by it's property updateNotificationProperty: function(state, payload) { state.notifications[payload.index][payload.property] = payload.value; }, updateNotification: function(state, payload) { state.notifications[payload.index] = payload.value; }, // add new form field element to column field add_column_inner_field_element: function (state, payload) { var columnFieldIndex = state.form_fields.findIndex(field => field.id === payload.toWhichColumnField); if (state.form_fields[columnFieldIndex].inner_fields[payload.toWhichColumn] === undefined) { state.form_fields[columnFieldIndex].inner_fields[payload.toWhichColumn] = []; } else { state.form_fields[columnFieldIndex].inner_fields[payload.toWhichColumn].splice( payload.toIndex, 0, payload.field ); } }, // add new form field element to repeat field add_repeat_inner_field_element: function (state, payload) { var repeatFieldIndex = state.form_fields.findIndex(field => field.id === payload.toWhichRepeatField); if (repeatFieldIndex === -1) return; if (!state.form_fields[repeatFieldIndex].inner_fields) { Vue.set(state.form_fields[repeatFieldIndex], 'inner_fields', []); } state.form_fields[repeatFieldIndex].inner_fields.splice(payload.toIndex, 0, payload.field); }, // delete a repeat inner field element delete_repeat_inner_field_element: function (state, payload) { var repeatFieldIndex = state.form_fields.findIndex(field => field.id === payload.field_id); if (repeatFieldIndex === -1) return; state.current_panel = 'form-fields-v4-1'; state.form_fields[repeatFieldIndex].inner_fields.splice(payload.index, 1); }, // clone a repeat inner field element clone_repeat_inner_field_element: function (state, payload) { var repeatFieldIndex = state.form_fields.findIndex(field => field.id === payload.field_id); if (repeatFieldIndex === -1) return; var field = state.form_fields[repeatFieldIndex].inner_fields[payload.index]; var clone = $.extend(true, {}, field); var newIndex = parseInt(payload.index) + 1; clone.id = payload.new_id; clone.name = clone.name + '_copy'; clone.is_new = true; state.form_fields[repeatFieldIndex].inner_fields.splice(newIndex, 0, clone); }, move_column_inner_fields: function(state, payload) { var columnFieldIndex = state.form_fields.findIndex(field => field.id === payload.field_id), innerFields = payload.inner_fields, mergedFields = []; Object.keys(innerFields).forEach(function (column) { // clear column-1, column-2 and column-3 fields if move_to specified column-1 // add column-1, column-2 and column-3 fields to mergedFields, later mergedFields will move to column-1 field if (payload.move_to === "column-1") { innerFields[column].forEach(function(field){ mergedFields.push(field); }); // clear current column inner fields state.form_fields[columnFieldIndex].inner_fields[column].splice(0, innerFields[column].length); } // clear column-2 and column-3 fields if move_to specified column-2 // add column-2 and column-3 fields to mergedFields, later mergedFields will move to column-2 field if (payload.move_to === "column-2") { if ( column === "column-2" || column === "column-3" ) { innerFields[column].forEach(function(field){ mergedFields.push(field); }); // clear current column inner fields state.form_fields[columnFieldIndex].inner_fields[column].splice(0, innerFields[column].length); } } }); // move inner fields to specified column if (mergedFields.length !== 0) { mergedFields.forEach(function(field){ state.form_fields[columnFieldIndex].inner_fields[payload.move_to].splice(0, 0, field); }); } }, // sorting inside column field swap_column_field_elements: function (state, payload) { var columnFieldIndex = state.form_fields.findIndex(field => field.id === payload.field_id), fieldObj = state.form_fields[columnFieldIndex].inner_fields[payload.fromColumn][payload.fromIndex]; if( payload.fromColumn !== payload.toColumn) { // add the field object to the target column state.form_fields[columnFieldIndex].inner_fields[payload.toColumn].splice(payload.toIndex, 0, fieldObj); // remove the field index from the source column state.form_fields[columnFieldIndex].inner_fields[payload.fromColumn].splice(payload.fromIndex, 1); }else{ state.form_fields[columnFieldIndex].inner_fields[payload.toColumn].swap(payload.fromIndex, payload.toIndex); } }, // open field settings panel open_column_field_settings: function (state, payload) { var field = payload.column_field; if ('field-options' === state.current_panel && field.id === state.editing_field_id) { return; } if (field) { state.editing_field_id = 0; state.current_panel = 'field-options'; state.editing_field_type = 'column_field'; state.editing_column_field_id = payload.field_id; state.edting_field_column = payload.column; state.editing_inner_field_index = payload.index; setTimeout(function () { state.editing_field_id = field.id; }, 400); } }, // open repeat field settings panel open_repeat_field_settings: function (state, payload) { var field = payload.repeat_field; if ('field-options' === state.current_panel && field.id === state.editing_field_id) { return; } if (field) { state.editing_field_id = field.id; state.current_panel = 'field-options'; state.editing_field_type = 'repeat_field'; state.editing_repeat_field_id = payload.field_id; state.editing_inner_field_index = payload.index; setTimeout(function () { state.editing_field_id = field.id; }, 400); } }, clone_column_field_element: function (state, payload) { var columnFieldIndex = state.form_fields.findIndex(field => field.id === payload.field_id); var field = _.find(state.form_fields[columnFieldIndex].inner_fields[payload.toColumn], function (item) { return parseInt(item.id) === parseInt(payload.column_field_id); }); var clone = $.extend(true, {}, field), index = parseInt(payload.index) + 1; clone.id = payload.new_id; clone.name = clone.name + '_copy'; clone.is_new = true; state.form_fields[columnFieldIndex].inner_fields[payload.toColumn].splice(index, 0, clone); }, // delete a column field delete_column_field_element: function (state, payload) { var columnFieldIndex = state.form_fields.findIndex(field => field.id === payload.field_id); state.current_panel = 'form-fields-v4-1'; state.form_fields[columnFieldIndex].inner_fields[payload.fromColumn].splice(payload.index, 1); }, // update the panel sections set_panel_sections: function ( state, sections ) { state.panel_sections = sections; }, // set default panel sections set_default_panel_sections: function ( state ) { state.panel_sections = wpuf_form_builder.panel_sections; }, // update integration settings updateIntegration: function(state, payload) { Vue.set(state.integrations, payload.index, payload.value); } } }); /** * The main form builder vue instance */ new Vue({ el: '#wpuf-form-builder', mixins: wpuf_form_builder_mixins(wpuf_mixins.root), store: wpuf_form_builder_store, data: { is_form_saving: false, is_form_saved: false, is_form_switcher: false, post_title_editing: false, isDirty: false, shortcodeCopied: false, logoUrl: wpuf_form_builder.asset_url + '/images/wpuf-icon-circle.svg', settings_titles: wpuf_form_builder.settings_titles, settings_items: wpuf_form_builder.settings_items, active_tab: 'form-editor', form_settings: wpuf_form_builder.form_settings, active_settings_tab: Object.keys(wpuf_form_builder.settings_titles[Object.keys(wpuf_form_builder.settings_titles)[0]].sub_items)[0], active_settings_title: wpuf_form_builder.settings_titles[Object.keys(wpuf_form_builder.settings_titles)[0]].sub_items[Object.keys(wpuf_form_builder.settings_titles[Object.keys(wpuf_form_builder.settings_titles)[0]].sub_items)[0]].label, }, computed: { current_panel: function () { return this.$store.state.current_panel; }, post: function () { return this.$store.state.post; }, form_fields_count: function () { return this.$store.state.form_fields.length; }, form_fields: function () { return this.$store.state.form_fields; }, notifications: function() { return this.$store.state.notifications; }, settings: function() { return this.$store.state.settings; }, meta_field_key: function () { let meta_key = []; this.$store.state.form_fields.forEach(function (field) { if ( 'yes' === field.is_meta ) { meta_key.push(field.name); } }); return meta_key.map(function(name) { return '{' + name +'}'; }).join( ); }, settings_titles: function() { return this.$store.state.settings_titles; }, settings_items: function() { return this.$store.state.settings_items; }, section_exists: function() { return this.settings_items[this.active_settings_tab] && this.settings_items[this.active_settings_tab].section; } }, watch: { form_fields: { handler: function() { this.isDirty = true; }, deep: true }, active_settings_tab: { // attach selectize to the dropdowns after settings tab changes handler: function() { setTimeout(function() { $('.wpuf-settings-container select:not(.wpuf-no-selectize)').selectize({ plugins: ['remove_button'], }); }, 100); } } }, created: function () { this.$store.commit('panel_add_show_prop'); Vue.nextTick(function () { // selectize for all the dropdowns $('.wpuf-settings-container select:not(.wpuf-no-selectize)').selectize({ plugins: ['remove_button'], }); }); /** * This is the event hub we'll use in every * component to communicate between them */ wpuf_form_builder.event_hub = new Vue(); }, mounted: function () { // Ensure all fields have proper default values for icon settings when editing existing forms this.$store.state.form_fields.forEach(function(field) { // If show_icon is not set in the database, default it to 'no' if (field.show_icon === undefined || field.show_icon === null) { field.show_icon = 'no'; } // If field_icon is not set in the database, set it to default icon fas fa-0 if (field.field_icon === undefined || field.field_icon === null) { field.field_icon = 'fas fa-0'; } // If icon_position is not set, default it to 'left_label' if (field.icon_position === undefined || field.icon_position === null) { field.icon_position = 'left_label'; } }); // Check if there are hidden custom taxonomy fields and show warning if (wpuf_form_builder.has_hidden_taxonomies && !wpuf_form_builder.is_pro_active) { var self = this; // Check if warning has already been shown in this session // Temporarily disabled for testing - uncomment to enable session check // if (sessionStorage.getItem('wpuf_pro_taxonomy_warning_shown')) { // return; // } setTimeout(function() { var __ = (wp && wp.i18n && wp.i18n.__) ? wp.i18n.__ : function(text) { return text; }; if (typeof Swal !== 'undefined') { Swal.fire({ title: '', html: '
' + '
' + '
' + '' + __('Pro upgrade notification icon', 'wp-user-frontend') + '' + '
' + '

' + __('Pro Fields Hidden', 'wp-user-frontend') + '

' + '

' + __('This form includes custom taxonomy fields from third-party plugins. These are Pro-only and are hidden in both the builder and frontend until WPUF Pro is activated.', 'wp-user-frontend') + '

' + '
' + '
' + '' + __('Event Pro Field preview', 'wp-user-frontend') + '' + '
' + '
', icon: false, showCancelButton: false, showConfirmButton: true, confirmButtonColor: '#059669', confirmButtonText: __('Okay', 'wp-user-frontend'), customClass: { popup: 'wpuf-pro-taxonomy-warning', confirmButton: 'wpuf-btn-primary', icon: 'wpuf-warning-icon' }, width: '1038px', height: '512px', padding: '36px', imageAlt: __('Pro upgrade notification', 'wp-user-frontend') }); // Mark warning as shown in this session sessionStorage.setItem('wpuf_pro_taxonomy_warning_shown', 'true'); } else { // Fallback to alert if SweetAlert is not available alert(__('This form includes custom taxonomy fields from third-party plugins. These are Pro-only and are hidden in both the builder and frontend until WPUF Pro is activated.', 'wp-user-frontend')); // Mark warning as shown in this session sessionStorage.setItem('wpuf_pro_taxonomy_warning_shown', 'true'); } }, 500); // Small delay to ensure page is fully loaded } // primary nav tabs and their contents this.bind_tab_on_click($('#wpuf-form-builder > fieldset > .nav-tab-wrapper > a'), '#wpuf-form-builder'); // secondary settings tabs and their contents var settings_tabs = $('#wpuf-form-builder-settings .nav-tab'), settings_tab_contents = $('#wpuf-form-builder-settings .tab-contents .group'); settings_tabs.first().addClass('nav-tab-active'); settings_tab_contents.first().addClass('active'); this.bind_tab_on_click(settings_tabs, '#wpuf-form-builder-settings'); var clipboard = new window.Clipboard('.form-id'); $(".form-id").tooltip(); var self = this; clipboard.on('success', function(e) { // Show copied tooltip $(e.trigger) .attr('data-original-title', 'Copied!') .tooltip('show'); // Reset the copied tooltip setTimeout(function() { $(e.trigger).tooltip('hide') .attr('data-original-title', self.i18n.copy_shortcode); }, 1000); e.clearSelection(); }); window.onbeforeunload = function () { if ( self.isDirty ) { return self.i18n.unsaved_changes; } }; var mail_shortcodes = new window.Clipboard('.wpuf-long-help span[data-clipboard-text]'); mail_shortcodes.on('success', function(e) { // Show copied tooltip $(e.trigger) .attr('data-original-title', 'Copied!') .tooltip('show'); // Reset the copied tooltip setTimeout(function() { $(e.trigger).tooltip('hide') .attr('data-original-title', self.i18n.copy_shortcode); }, 1000); e.clearSelection(); }); // Mutual exclusivity between Enable Payments and Enable Pricing Fields Payment $(document).on('change', '#payment_options', function() { if ($(this).is(':checked')) { // When Enable Payments is turned ON, turn OFF Enable Pricing Fields Payment $('#enable_pricing_payment').prop('checked', false).trigger('change'); } }); $(document).on('change', '#enable_pricing_payment', function() { if ($(this).is(':checked')) { // When Enable Pricing Fields Payment is turned ON, turn OFF Enable Payments $('#payment_options').prop('checked', false).trigger('change'); } }); }, methods: { // tabs and their contents bind_tab_on_click: function (tabs, scope) { tabs.on('click', function (e) { e.preventDefault(); var button = $(this), tab_contents = $(scope + ' > fieldset > .tab-contents'), group_id = button.attr('href'); button.addClass('nav-tab-active').siblings('.nav-tab-active').removeClass('nav-tab-active'); tab_contents.children().removeClass('active'); $(group_id).addClass('active'); }); }, setActiveSettingsTab: function (e) { this.active_settings_tab = $(e.target).attr('href'); }, // switch form switch_form: function () { this.is_form_switcher = (this.is_form_switcher) ? false : true; }, // set current sidebar panel set_current_panel: function (panel) { if (panel === 'form-fields-v4-1') { this.$store.state.panel_sections = wpuf_form_builder.panel_sections; } this.$store.commit('set_current_panel', panel); }, // save form builder data save_form_builder: function () { var self = this; if (_.isFunction(this.validate_form_before_submit) && !this.validate_form_before_submit()) { this.warn({ title: 'Post Form Validation Error!', html: this.validation_error_msg, reverseButtons: true, customClass: { cancelButton: '!wpuf-bg-white !wpuf-text-black !wpuf-border !wpuf-border-solid !wpuf-border-gray-300 focus:!wpuf-shadow-none', confirmButton: '!wpuf-text-white !wpuf-bg-primary', }, }); return; } self.is_form_saving = true; var form_id = $('#wpuf-form-builder [name="wpuf_form_id"]').val(); if ( typeof tinyMCE !== 'undefined' && window.location.search.substring(1).split('&').includes('page=wpuf-profile-forms') ) { var parentWrap = $('#wp-wpuf_verification_body_' + form_id + '-wrap'); if ( ! parentWrap.hasClass('tmce-active') ) { $('#wpuf_verification_body_' + form_id + '-tmce').click(); // bring user to the visual editor $('#wpuf_welcome_email_body_' + form_id + '-tmce').click(); } $('textarea[name="wpuf_settings[notification][verification_body]"]').val(tinyMCE.get('wpuf_verification_body_' + form_id).getContent()); $('textarea[name="wpuf_settings[notification][welcome_email_body]"]').val(tinyMCE.get('wpuf_welcome_email_body_' + form_id).getContent()); } wp.ajax.send('wpuf_form_builder_save_form', { data: { form_data: $('#wpuf-form-builder').serialize(), form_fields: JSON.stringify(self.form_fields), notifications: JSON.stringify(self.notifications) }, success: function (response) { if (response.form_fields) { self.$store.commit('set_form_fields', response.form_fields); } if (response.form_settings) { self.$store.commit('set_form_settings', response.form_settings); } self.is_form_saving = false; self.is_form_saved = true; // Check if currently on field-options panel and switch to form-fields after save if (self.$store.state.current_panel === 'field-options') { self.$store.commit('set_current_panel', 'form-fields-v4-1'); } setTimeout(function(){ self.isDirty = false; }, 500); toastr.success(self.i18n.saved_form_data); }, error: function (response) { self.is_form_saving = false; // Handle server-side validation errors // WordPress AJAX sends errors in response.data if (response && response.data) { self.warn({ title: 'Validation Error', html: response.data, reverseButtons: true, customClass: { cancelButton: '!wpuf-bg-white !wpuf-text-black !wpuf-border !wpuf-border-solid !wpuf-border-gray-300 focus:!wpuf-shadow-none', confirmButton: '!wpuf-text-white !wpuf-bg-primary', }, }); } } }); }, // settings field classes to add similar field classes setting_class_names: function(field_type) { switch (field_type) { case 'upload_btn': return 'file-selector wpuf-rounded-[6px] wpuf-btn-secondary'; case 'radio': return '!wpuf-mt-0 !wpuf-mr-2 wpuf-radio !wpuf-shadow-none checked:!wpuf-shadow-none focus:checked:!wpuf-shadow-primary !wpuf-border-gray-300 checked:!wpuf-border-primary checked:!wpuf-bg-primary before:checked:!wpuf-bg-white hover:checked:!wpuf-bg-primary focus:!wpuf-ring-transparent focus:checked:!wpuf-ring-transparent hover:checked:!wpuf-ring-transparent focus:checked:!wpuf-bg-primary focus:checked:!wpuf-shadow-none focus:wpuf-shadow-primary'; case 'checkbox': return '!wpuf-mt-0 !wpuf-mr-2 wpuf-h-4 wpuf-w-4 !wpuf-shadow-none checked:!wpuf-shadow-none focus:checked:!wpuf-shadow-primary focus:checked:!wpuf-shadow-none !wpuf-border-gray-300 checked:!wpuf-border-primary checked:!wpuf-bg-primary before:checked:!wpuf-bg-white hover:checked:!wpuf-bg-primary focus:!wpuf-ring-transparent focus:checked:!wpuf-ring-transparent hover:checked:!wpuf-ring-transparent focus:checked:!wpuf-bg-primary focus:wpuf-shadow-primary checked:focus:!wpuf-bg-primary checked:hover:wpuf-bg-primary checked:!wpuf-bg-primary before:!wpuf-content-none wpuf-rounded'; case 'dropdown': return 'wpuf-block wpuf-w-full wpuf-min-w-full wpuf-text-gray-700 wpuf-font-normal !wpuf-shadow-sm wpuf-border !wpuf-border-gray-300 !wpuf-rounded-[6px] focus:!wpuf-ring-transparent focus:checked:!wpuf-ring-transparent hover:checked:!wpuf-ring-transparent hover:!wpuf-text-gray-700 !wpuf-text-base !leading-6'; default: return 'wpuf-block wpuf-min-w-full wpuf-my-0 wpuf-mb-0 !wpuf-leading-none !wpuf-py-[10px] !wpuf-px-[14px] wpuf-text-gray-700 !wpuf-shadow-sm placeholder:wpuf-text-gray-400 wpuf-border !wpuf-border-gray-300 !wpuf-rounded-[6px] wpuf-max-w-full focus:!wpuf-ring-transparent'; } }, switch_settings_menu: function(menu, submenu) { this.active_settings_tab = submenu; if (submenu === 'modules') { this.active_settings_title = 'Modules'; } else { this.active_settings_title = this.settings_titles[menu].sub_items[submenu].label; } }, switch_form_settings_pic_radio_item: function ( key, value ) { this.form_settings[key] = value; }, // Show warning dialog using SweetAlert2 warn: function (options) { Swal.fire(options); } } }); var SettingsTab = { init: function() { $(function() { $('.datepicker').datetimepicker(); $('.wpuf-ms-color').wpColorPicker(); }); }, }; // on DOM ready $(function() { // resizeBuilderContainer(); // // $("#collapse-menu").click(function () { // resizeBuilderContainer(); // }); // Commented out - not currently used // function resizeBuilderContainer() { // if ($(document.body).hasClass('folded')) { // $("#wpuf-form-builder").css("width", "calc(100% - 80px)"); // } else { // $("#wpuf-form-builder").css("width", "calc(100% - 200px)"); // } // } SettingsTab.init(); const dependencies = { // Fields and their show/hide conditions fields: { message: { type: 'textarea', dependsOn: [{ field: 'redirect_to', value: 'same' }], }, page_id: { type: 'select', dependsOn: [{ field: 'redirect_to', value: 'page' }], }, url: { type: 'text', dependsOn: [{ field: 'redirect_to', value: 'url' }], }, update_message: { type: 'textarea', dependsOn: [{ field: 'edit_redirect_to', value: 'same' }], }, edit_page_id: { type: 'select', dependsOn: [{ field: 'edit_redirect_to', value: 'page' }], }, edit_url: { type: 'text', dependsOn: [{ field: 'edit_redirect_to', value: 'url' }], }, guest_details: { type: 'checkbox', dependsOn: [{ field: 'post_permission', value: 'guest_post' }] }, guest_email_verify: { type: 'text', dependsOn: [ { field: 'post_permission', value: 'guest_post' }, { field: 'guest_details', value: true } ] }, name_label: { type: 'text', dependsOn: [ { field: 'post_permission', value: 'guest_post' }, { field: 'guest_details', value: true } ] }, email_label: { type: 'text', dependsOn: [ { field: 'post_permission', value: 'guest_post' }, { field: 'guest_details', value: true } ] }, roles: { type: 'select', dependsOn: [{ field: 'post_permission', value: 'role_base' }] }, message_restrict: { type: 'textarea', dependsOn: [{ field: 'post_permission', value: 'role_base' }] }, choose_payment_option: { type: 'select', dependsOn: [{ field: 'payment_options', value: true }] }, fallback_ppp_enable: { type: 'checkbox', dependsOn: [ { field: 'payment_options', value: true }, { field: 'choose_payment_option', value: 'force_pack_purchase' } ] }, fallback_ppp_cost: { type: 'number', dependsOn: [ { field: 'payment_options', value: true }, { field: 'choose_payment_option', value: 'force_pack_purchase' }, { field: 'fallback_ppp_enable', value: true } ] }, pay_per_post_cost: { type: 'number', dependsOn: [ { field: 'payment_options', value: true }, { field: 'choose_payment_option', value: 'enable_pay_per_post' } ] }, ppp_payment_success_page: { type: 'select', dependsOn: [ { field: 'payment_options', value: true }, { field: 'choose_payment_option', value: 'enable_pay_per_post' } ] }, new_to: { type: 'text', dependsOn: [{ field: 'new', value: true }] }, new_subject: { type: 'text', dependsOn: [{ field: 'new', value: true }] }, new_body: { type: 'textarea', dependsOn: [{ field: 'new', value: true }] }, schedule_start: { type: 'text', dependsOn: [{ field: 'schedule_form', value: true }] }, form_pending_message: { type: 'textarea', dependsOn: [{ field: 'schedule_form', value: true }] }, form_expired_message: { type: 'textarea', dependsOn: [{ field: 'schedule_form', value: true }] }, limit_number: { type: 'number', dependsOn: [{ field: 'limit_entries', value: true }] }, limit_message: { type: 'textarea', dependsOn: [{ field: 'limit_entries', value: true }] }, n8n_webhook_url: { type: 'text', dependsOn: [{ field: 'enable_n8n', value: true }], } } }; // load only for post form settings if (wpuf_form_builder.form_type === 'wpuf_forms') { new FormDependencyHandler(dependencies); } // initially show the first tab(General) on first page load show_settings_for('general'); function show_settings_for(settings) { $('.wpuf-settings-body').each(function() { if ($(this).data('settings-body') === settings) { $(this).fadeIn(); } else { $(this).fadeOut(); } }); } $('ul.wpuf-sidebar-menu li').each(function() { $(this).on('click', function() { show_settings_for($(this).data('settings')); }); }); $('#modules-menu').on('click', function() { show_settings_for('modules'); }); $('.wpuf-pic-radio img').dblclick(function() { $( this ).siblings( 'input[type="radio"]' ).prop( 'checked', false ); }); }); class FormDependencyHandler { constructor(dependencies) { this.dependencies = dependencies; this.init(); } init() { // Get all fields that have dependencies const fieldsWithDependencies = Object.keys(this.dependencies.fields); // Initially hide all dependent fields fieldsWithDependencies.forEach(fieldId => { this.hideField(fieldId); }); // Add change event listeners to all form fields that others depend on const uniqueControlFields = this.getUniqueControlFields(); uniqueControlFields.forEach(fieldId => { this.attachFieldListener(fieldId); }); // Initial check for all fields this.checkAllDependencies(); } getUniqueControlFields() { // Get unique list of fields that control other fields const controlFields = new Set(); Object.values(this.dependencies.fields).forEach(field => { field.dependsOn.forEach(dependency => { controlFields.add(dependency.field); }); }); return Array.from(controlFields); } attachFieldListener(fieldId) { const field = $(`#${fieldId}`); if (field.length === 0) { return; } const fieldType = field.attr('type') || field.prop('tagName').toLowerCase(); if (fieldType === 'checkbox' || fieldType === 'select') { field.on('change', () => this.checkAllDependencies()); } else if (fieldType === 'radio') { const fieldName = field.attr('name'); const radioFields = $(`input[name="${fieldName}"]`); radioFields.each(function(_, radio) { $(radio).on('change', function() { this.checkAllDependencies(); }.bind(this)); }.bind(this)); } } checkAllDependencies() { Object.keys(this.dependencies.fields).forEach(fieldId => { this.checkFieldDependencies(fieldId); }); } checkFieldDependencies(fieldId) { const fieldConfig = this.dependencies.fields[fieldId]; const shouldShow = this.shouldFieldBeVisible(fieldConfig.dependsOn); if (shouldShow) { this.showField(fieldId); this.attachSelectize(fieldId); } else { this.hideField(fieldId); } } shouldFieldBeVisible(dependencies) { // All conditions must be met (AND logic) return dependencies.every(dep => { const controlField = $(`#${dep.field}`); if (controlField.length === 0) { return false; } const fieldType = controlField.attr('type') || controlField.prop('tagName').toLowerCase(); if (fieldType === 'checkbox') { // For checkboxes, check if the field is checked return controlField.is(':checked') === dep.value; } else if (fieldType === 'radio') { // For radio groups, find the checked radio with the same name const radioName = controlField.attr('name'); const checkedRadio = $(`input[name="${radioName}"]:checked`); if (checkedRadio.length === 0) { return dep.value === false || dep.value === ''; } return checkedRadio.val() === dep.value; } else if (fieldType === 'select') { // For selects, compare the selected value return controlField.val() === dep.value; } else { // For text inputs and other field types return controlField.val() === dep.value; } }); } showField(fieldId) { $(`#${fieldId}`).closest('.wpuf-input-container').fadeIn(200); } hideField(fieldId) { $(`#${fieldId}`).closest('.wpuf-input-container').fadeOut(200); } // if it is a select field, then attach selectize like below attachSelectize(fieldId) { if ($(`#${fieldId}`).is('select')) { $(`#${fieldId}`).selectize({ plugins: ['remove_button'], }); } } } window.FormDependencyHandler = FormDependencyHandler; // Mobile view menu toggle $('#wpuf-form-builder').on('click', '#wpuf-toggle-field-options, #wpuf-toggle-show-form, .field-buttons .fa-pencil, .ui-draggable-handle', function() { $('#wpuf-toggle-field-options').toggleClass('hide'); $('#wpuf-toggle-show-form').toggleClass('show'); $('#builder-form-fields').toggleClass('show'); }); // Initialize default categories on page load and bind change event $(document).ready(function() { var $postTypeSelect = $('select#post_type'); // Load initial categories if post type select exists if ($postTypeSelect.length) { populate_default_categories($postTypeSelect[0], true); } // Bind change event $postTypeSelect.on('change', function() { populate_default_categories(this, false); }); }); function populate_default_categories(obj) { // isInitialLoad parameter removed - not used var post_type = $(obj).val(); // Don't proceed if no post type is selected if (!post_type) { return; } // Get form ID from the form var form_id = $('input[name="wpuf_form_id"]').val() || 0; wp.ajax.send('wpuf_form_setting_post', { data: { post_type: post_type, form_id: form_id, wpuf_form_builder_setting_nonce: wpuf_form_builder.nonce }, success: function (response) { // Remove all existing taxonomy containers $('.taxonomy-container, .wpuf_settings_taxonomy, .wpuf-input-container:has(select[name*="default_"])').remove(); // Find the container to append new content after var $container = $(obj).closest('.wpuf-input-container'); if ($container.length && response.data) { // Append the new taxonomy fields $container.after(response.data); // Initialize selectize for all new taxonomy selects $('.tax-list-selector:not(.selectized)').each(function() { var $select = $(this); // The select options are already set with selected attributes from PHP // so we don't need to manually set values here // Initialize selectize $select.selectize({ plugins: ['remove_button'], }); }); } }, error: function () { // Error handler - currently no specific error handling } }); } })(jQuery);