/** * Contact Forms - Form Settings Editor * * Handles real-time preview updates and form settings saving. */ jQuery(function($){ 'use strict'; // ======================================================================== // Form Editor Page Initialization // ======================================================================== // Message override radio buttons — toggle WP editor visibility $('input[type=radio]').on('change', function() { var name = $(this).attr('name'); if ($(this).val() === '1') { $('#' + name + ' .defalut_message').hide(); $('#' + name + ' .wp-editor-wrap').show(); } else { if ($(this).val() !== '-1') { $('#' + name + ' .defalut_message').show(); } else { $('#' + name + ' .defalut_message').hide(); } $('#' + name + ' .wp-editor-wrap').hide(); } }); // Initialize message field visibility $.each(['success_message', 'error_message', 'admin_emails_message', 'confirmation_emails_message'], function(i, key) { var value = $('#accua_form_' + key + ' .accua_form_check_override:checked').val(); if (value !== undefined && value != 0) { if (value != -1) { $('#accua_form_' + key + ' .wp-editor-wrap').show(); } else { $('#accua_form_' + key + ' .wp-editor-wrap').hide(); } $('#accua_form_' + key + ' .defalut_message').hide(); } else { $('#accua_form_' + key + ' .wp-editor-wrap').hide(); $('#accua_form_' + key + ' .defalut_message').show(); } }); // Retention override toggle $('#accua_form_retention_override').on('change', function() { $('#accua_form_retention_fields').toggle(this.checked); }); // Token dialog if ($.fn.dialog) { $('#dialog_token').dialog({ dialogClass: 'wp-dialog', autoOpen: false, modal: true, show: 'blind', hide: 'blind' }); $('.token_link').on('click', function() { $('#dialog_token').dialog('open'); return false; }); $('#accua_token a').appendTo('.wp-media-buttons'); } // Preview area — resizable and auto-resize iframe on load var $previewWrapper = $('#accua_form_preview_area_wrapper'); var $previewIframe = $('#accua_form_preview_area'); if ($previewWrapper.length && $.fn.resizable) { $previewWrapper.resizable({ handles: 's' }); $previewIframe.css({ width: '100%', height: '100%' }); } if ($previewIframe.length) { $previewIframe.on('load', function() { try { var h = this.contentWindow.document.documentElement.scrollHeight + 200; this.style.height = h + 'px'; } catch (e) { /* cross-origin */ } }); } // ======================================================================== // Configuration // ======================================================================== var DEBOUNCE_DELAY = 100; // ms delay for debounced updates var PREVIEW_LOAD_DELAY = 300; // ms delay after iframe load before updating // All style fields that support override checkbox + value var ALL_STYLE_FIELDS = { // Form container styles form: [ 'style_margin', 'style_border_color', 'style_border_width', 'style_border_radius', 'style_background_color', 'style_padding', 'style_color', 'style_font_size' ], // Field styles field: [ 'style_field_spacing', 'style_field_border_color', 'style_field_border_width', 'style_field_border_radius', 'style_field_background_color', 'style_field_padding', 'style_field_color' ], // Submit button styles submit: [ 'style_submit_border_color', 'style_submit_border_width', 'style_submit_border_radius', 'style_submit_background_color', 'style_submit_padding', 'style_submit_color', 'style_submit_font_size' ] }; // Color fields that use wpColorPicker var COLOR_FIELDS = [ 'style_border_color', 'style_background_color', 'style_color', 'style_field_border_color', 'style_field_background_color', 'style_field_color', 'style_submit_border_color', 'style_submit_background_color', 'style_submit_color' ]; // ======================================================================== // State // ======================================================================== var previewReady = false; var $saveButton = $('.accua_form_save_settings_button'); // ======================================================================== // Utility Functions // ======================================================================== /** * Simple debounce function */ function debounce(func, wait) { var timeout; return function() { var context = this, args = arguments; clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context, args); }, wait); }; } /** * Get TinyMCE content or fallback to textarea value */ function getTinyMCEContent(name) { if ($('#' + name + ' .wp-editor-wrap').hasClass('tmce-active') && typeof tinyMCE !== 'undefined') { var editor = tinyMCE.get(name + '_textarea'); return editor ? editor.getContent() : $('#' + name + '_textarea').val(); } return $('#' + name + '_textarea').val(); } /** * Parse a numeric value and add px suffix if needed */ function parseStyleValue(value) { if (!value || value === '') return ''; value = String(value).trim(); // If it's a pure number, add px if (/^-?\d+(\.\d+)?$/.test(value)) { return value + 'px'; } return value; } // ======================================================================== // Preview Document Access // ======================================================================== /** * Safely get the preview iframe document */ function getPreviewDocument() { var iframe = document.getElementById('accua_form_preview_area'); if (!iframe) return null; try { var doc = iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document); return doc && doc.body ? doc : null; } catch (e) { return null; } } /** * Get the form element from preview */ function getPreviewForm() { var doc = getPreviewDocument(); return doc ? doc.querySelector('form.accua-form') : null; } // ======================================================================== // Style Value Retrieval // ======================================================================== /** * Get style value if the override checkbox is checked */ function getStyleValue(key) { var $container = $('#accua_form_' + key); var $checkbox = $container.find('.accua_form_check_override'); // Check if override is enabled if (!$checkbox.length || !$checkbox.is(':checked')) { return ''; } // For color pickers, get value from the hidden input (wpColorPicker syncs to it) var $valueInput = $container.find('.accua_form_value'); return $valueInput.val() || ''; } // ======================================================================== // Layout Preview // ======================================================================== /** * Reload preview iframe with new layout parameter. * Layout changes require HTML structure rebuild, not just CSS class toggling. * Different layouts (inline, toplabel, sidebyside) generate different HTML. */ function reloadPreviewWithLayout(layout) { var formId = $('#accua_form_save_settings_id').val(); var $iframe = $('#accua_form_preview_area'); var $wrapper = $('#accua_form_preview_area_wrapper'); previewReady = false; // Show loading state $wrapper.addClass('accua-form-preview-loading'); // Build preview URL with layout override parameter var previewUrl = 'admin-ajax.php?action=accua_forms_preview&fid=' + formId + '&_wpnonce=' + accua_forms_nonces.preview_nonce; // Always pass layout parameter - use 'default' when empty to signal global default should be used var layoutParam = (layout && layout !== '') ? layout : 'default'; previewUrl += '&preview_layout=' + encodeURIComponent(layoutParam); $iframe[0].src = previewUrl; } /** * Update preview layout class based on dropdown selection * NOTE: This only toggles CSS classes - used for CSS-only style changes. * For actual layout changes, use reloadPreviewWithLayout() instead. */ function updatePreviewLayout() { var form = getPreviewForm(); if (!form) return; var layout = $('#accua_form_layout .accua_form_value').val() || ''; // Remove all layout classes form.classList.remove( 'accua-form-view-standard', 'accua-form-view-sidebyside', 'accua-form-view-inlinelabel' ); // Add appropriate class based on selection switch (layout) { case 'toplabel': form.classList.add('accua-form-view-standard'); break; case 'inlinelabel': form.classList.add('accua-form-view-inlinelabel'); break; case 'sidebyside': form.classList.add('accua-form-view-sidebyside'); break; default: // Empty/default - use sidebyside as fallback for backwards compatibility form.classList.add('accua-form-view-sidebyside'); break; } } // ======================================================================== // Styles Preview // ======================================================================== /** * Build inline style string from style object */ function buildStyleString(styles) { var parts = []; for (var prop in styles) { if (styles.hasOwnProperty(prop) && styles[prop]) { parts.push(prop + ':' + styles[prop]); } } return parts.join(';'); } /** * Update all preview styles */ function updatePreviewStyles() { var form = getPreviewForm(); if (!form) return; var doc = getPreviewDocument(); if (!doc) return; // ---- Form Container Styles ---- var formStyles = {}; var margin = getStyleValue('style_margin'); if (margin) formStyles['margin'] = parseStyleValue(margin); var borderColor = getStyleValue('style_border_color'); if (borderColor) formStyles['border-color'] = borderColor; var borderWidth = getStyleValue('style_border_width'); if (borderWidth) { formStyles['border-width'] = parseStyleValue(borderWidth); formStyles['border-style'] = 'solid'; } var borderRadius = getStyleValue('style_border_radius'); if (borderRadius) formStyles['border-radius'] = parseStyleValue(borderRadius); var backgroundColor = getStyleValue('style_background_color'); if (backgroundColor) formStyles['background-color'] = backgroundColor; var padding = getStyleValue('style_padding'); if (padding) formStyles['padding'] = parseStyleValue(padding); var color = getStyleValue('style_color'); if (color) formStyles['color'] = color; var fontSize = getStyleValue('style_font_size'); if (fontSize) formStyles['font-size'] = parseStyleValue(fontSize); form.style.cssText = buildStyleString(formStyles); // ---- Field Styles ---- var fieldStyles = {}; var fieldBorderColor = getStyleValue('style_field_border_color'); if (fieldBorderColor) fieldStyles['border-color'] = fieldBorderColor; var fieldBorderWidth = getStyleValue('style_field_border_width'); if (fieldBorderWidth) { fieldStyles['border-width'] = parseStyleValue(fieldBorderWidth); fieldStyles['border-style'] = 'solid'; } var fieldBorderRadius = getStyleValue('style_field_border_radius'); if (fieldBorderRadius) fieldStyles['border-radius'] = parseStyleValue(fieldBorderRadius); var fieldBackgroundColor = getStyleValue('style_field_background_color'); if (fieldBackgroundColor) fieldStyles['background-color'] = fieldBackgroundColor; var fieldPadding = getStyleValue('style_field_padding'); if (fieldPadding) fieldStyles['padding'] = parseStyleValue(fieldPadding); var fieldColor = getStyleValue('style_field_color'); if (fieldColor) fieldStyles['color'] = fieldColor; var fieldStyleString = buildStyleString(fieldStyles); var fields = doc.querySelectorAll('.pfbc-textbox, .pfbc-textarea, .pfbc-select, input[type="text"], input[type="email"], input[type="date"], input[type="password"], textarea, select'); for (var i = 0; i < fields.length; i++) { fields[i].style.cssText = fieldStyleString; } // Field spacing (margin-bottom on pfbc-element) var fieldSpacing = getStyleValue('style_field_spacing'); if (fieldSpacing) { var elements = doc.querySelectorAll('.pfbc-element'); var spacingValue = parseStyleValue(fieldSpacing); for (var j = 0; j < elements.length; j++) { elements[j].style.marginBottom = spacingValue; } } // ---- Submit Button Styles ---- var submitStyles = {}; var submitBorderColor = getStyleValue('style_submit_border_color'); if (submitBorderColor) submitStyles['border-color'] = submitBorderColor; var submitBorderWidth = getStyleValue('style_submit_border_width'); if (submitBorderWidth) { submitStyles['border-width'] = parseStyleValue(submitBorderWidth); submitStyles['border-style'] = 'solid'; } var submitBorderRadius = getStyleValue('style_submit_border_radius'); if (submitBorderRadius) submitStyles['border-radius'] = parseStyleValue(submitBorderRadius); var submitBackgroundColor = getStyleValue('style_submit_background_color'); if (submitBackgroundColor) submitStyles['background-color'] = submitBackgroundColor; var submitPadding = getStyleValue('style_submit_padding'); if (submitPadding) submitStyles['padding'] = parseStyleValue(submitPadding); var submitColor = getStyleValue('style_submit_color'); if (submitColor) submitStyles['color'] = submitColor; var submitFontSize = getStyleValue('style_submit_font_size'); if (submitFontSize) submitStyles['font-size'] = parseStyleValue(submitFontSize); var submitButton = doc.querySelector('.pfbc-buttons button, .pfbc-buttons input[type="submit"], button[type="submit"], input[type="submit"]'); if (submitButton) { submitButton.style.cssText = buildStyleString(submitStyles); } } // Create debounced version for smoother UX var updatePreviewStylesDebounced = debounce(updatePreviewStyles, DEBOUNCE_DELAY); // ======================================================================== // Combined Preview Update // ======================================================================== /** * Update preview styles after iframe load. * Layout is NOT toggled here — the server-rendered iframe already has the * correct layout class and HTML structure. Layout changes go through * reloadPreviewWithLayout() which rebuilds the iframe entirely. */ function updateFullPreview() { if (!previewReady) return; updatePreviewStyles(); } // ======================================================================== // Color Picker Initialization // ======================================================================== /** * Close all open Iris color pickers * WordPress Iris adds .wp-picker-active class to open pickers */ function closeAllColorPickers() { $('.accua-style-content .wp-picker-container').each(function() { var $container = $(this); var $holder = $container.find('.wp-picker-holder'); var $iris = $holder.find('.iris-picker'); // Hide the Iris picker if ($iris.length) { $iris.hide(); } // Remove the active state $container.removeClass('wp-picker-active'); }); } /** * Initialize a color picker with change handler * Follows WordPress admin UI guidelines: * - Only one color picker open at a time * - Hex input always visible for keyboard accessibility */ function initColorPicker(fieldKey) { var $input = $('#accua_form_' + fieldKey + ' .accua_form_value'); if (!$input.length) return; $input.wpColorPicker({ change: function(event, ui) { // wpColorPicker change event - update preview updatePreviewStylesDebounced(); }, clear: function() { updatePreviewStylesDebounced(); } }); // Get the container elements after wpColorPicker initialization var $container = $input.closest('.wp-picker-container'); var $inputWrap = $container.find('.wp-picker-input-wrap'); var $hexInput = $container.find('.wp-color-picker'); var $colorButton = $container.find('.wp-color-result'); var $holder = $container.find('.wp-picker-holder'); // Always show the hex input field for accessibility (keyboard-accessible text input) // WordPress hides it by default and only shows when picker is open $inputWrap.css('display', ''); $hexInput.css('display', '').removeAttr('disabled'); // Close other pickers when this color button is clicked $colorButton.on('click', function(e) { // Check if this picker is about to open (will become active) var wasActive = $container.hasClass('wp-picker-active'); if (!wasActive) { // Close all other pickers before this one opens $('.accua-style-content .wp-picker-container').not($container).each(function() { var $other = $(this); var $otherIris = $other.find('.iris-picker'); if ($otherIris.length) { $otherIris.hide(); } $other.removeClass('wp-picker-active'); }); } }); // Also close other pickers when hex input is focused $hexInput.on('focus', function() { // Close all other pickers $('.accua-style-content .wp-picker-container').not($container).each(function() { var $other = $(this); var $otherIris = $other.find('.iris-picker'); if ($otherIris.length) { $otherIris.hide(); } $other.removeClass('wp-picker-active'); }); }); } // Initialize all color pickers $.each(COLOR_FIELDS, function(i, key) { initColorPicker(key); }); // Close all color pickers when clicking outside $(document).on('click', function(e) { var $target = $(e.target); // If click is not inside a color picker container, close all pickers if (!$target.closest('.wp-picker-container').length) { closeAllColorPickers(); } }); // ======================================================================== // Event Listeners - Style Fields // ======================================================================== /** * Attach event listeners for a style field */ function attachStyleFieldListeners(fieldKey, isColorField) { var $container = $('#accua_form_' + fieldKey); if (!$container.length) return; // Checkbox toggle - always triggers preview update $container.find('.accua_form_check_override').on('change', function() { updatePreviewStylesDebounced(); }); // Value input - for non-color fields if (!isColorField) { $container.find('.accua_form_value').on('input change', function() { updatePreviewStylesDebounced(); }); } } // Attach listeners to all form style fields $.each(ALL_STYLE_FIELDS.form, function(i, key) { attachStyleFieldListeners(key, COLOR_FIELDS.indexOf(key) !== -1); }); // Attach listeners to all field style fields $.each(ALL_STYLE_FIELDS.field, function(i, key) { attachStyleFieldListeners(key, COLOR_FIELDS.indexOf(key) !== -1); }); // Attach listeners to all submit style fields $.each(ALL_STYLE_FIELDS.submit, function(i, key) { attachStyleFieldListeners(key, COLOR_FIELDS.indexOf(key) !== -1); }); // ======================================================================== // Event Listeners - Layout // ======================================================================== $('#accua_form_layout .accua_form_value').on('change', function() { // Reload preview with new layout - layout changes require HTML rebuild // (different layouts generate different HTML structure, not just CSS) reloadPreviewWithLayout($(this).val()); }); // ======================================================================== // Preview Iframe Load Handler // ======================================================================== $('#accua_form_preview_area').on('load', function() { previewReady = true; // Remove loading state $('#accua_form_preview_area_wrapper').removeClass('accua-form-preview-loading'); // Small delay to ensure iframe content is fully rendered setTimeout(function() { updateFullPreview(); }, PREVIEW_LOAD_DELAY); }); // ======================================================================== // Save Handler // ======================================================================== $('.accua_form_save_settings_button').on('click', function(e) { e.preventDefault(); var $clickedButton = $(this); // Store original button text and add saving state with "Saving..." text var originalText = $clickedButton.val(); $saveButton.addClass('accua-saving').prop('disabled', true); $clickedButton.val(accua_forms_i18n.saving || 'Saving...').addClass('accua-saving-active'); var formId = $('#accua_form_save_settings_id').val(); // Build data object var data = { 'action': 'accua-save-form-settings', 'form-id': formId, '_nonce_edit_form': $('#_nonce_edit_form').val(), 'title': $('#title').val(), 'use_ajax': $('#accua_form_use_ajax .accua_form_value').is(':checked') ? 1 : 0 }; // Layout var layout = $('#accua_form_layout .accua_form_value').val(); if (layout === 'toplabel' || layout === 'sidebyside' || layout === 'inlinelabel') { data.layout = layout; } else { data.layout = ''; } // GADS conversion tracking var gads = $('#gads_conversion_code_input').val(); if (gads && gads !== 'undefined' && gads !== 'null') { data.gads_conversion_tracking_code = gads; } // Collect all override fields var overrideFields = [ 'success_message', 'error_message', 'emails_from_name', 'emails_from', 'admin_emails_to', 'emails_bcc', 'admin_emails_subject', 'admin_emails_message', 'confirmation_emails_subject', 'confirmation_emails_message' ]; // Add all style fields $.each(ALL_STYLE_FIELDS.form, function(i, key) { overrideFields.push(key); }); $.each(ALL_STYLE_FIELDS.field, function(i, key) { overrideFields.push(key); }); $.each(ALL_STYLE_FIELDS.submit, function(i, key) { overrideFields.push(key); }); $.each(overrideFields, function(i, key) { var $checkbox = $('#accua_form_' + key + ' .accua_form_check_override:checked'); var checkValue = $checkbox.val(); if (checkValue !== undefined && checkValue != 0) { if (checkValue == -1) { // "No message" option data[key] = ''; data[key + '_no_message'] = 1; } else { var $element = $('#accua_form_' + key + ' .accua_form_value'); if ($element.is('textarea')) { data[key] = getTinyMCEContent('accua_form_' + key); } else { data[key] = $element.val(); } } } }); // Trigger widget save buttons (saves individual field settings to draft) if (typeof accuaWidgets !== 'undefined' && accuaWidgets) { $('#widgets-right .button-primary.widget-control-save').click(); } // Data Retention fields data.submission_retention_override = $('#accua_form_retention_override').is(':checked') ? 1 : 0; data.submission_retention_value = $('#submission_retention_value').val(); data.submission_retention_unit = $('#submission_retention_unit').val(); data.submission_retention_mode = $('input[name="submission_retention_mode"]:checked').val(); // Save field order to draft if (typeof accuaWidgets !== 'undefined' && accuaWidgets.saveOrder) { accuaWidgets.saveOrder(); } // Step 1: Save settings to draft $.ajax({ url: ajaxurl, type: 'POST', data: data, success: function() { // Step 2: Publish draft to live database $.ajax({ url: ajaxurl, type: 'POST', data: { 'action': 'accua-publish-form-draft', 'form-id': formId, '_nonce_edit_form': $('#_nonce_edit_form').val() }, success: function() { // Reload preview to show saved state (include current layout) reloadPreviewWithLayout($('#accua_form_layout .accua_form_value').val()); // Update URL without reload try { if (history.pushState && window.location.search.indexOf('page=accua_forms_list') === -1) { history.pushState('', document.title, 'admin.php?page=accua_forms_list&fid=' + formId); window.onpopstate = function() { location.reload(); }; } } catch (e) {} // Restore button text immediately (was cleared to show spinner) $clickedButton.val(originalText); // Show success state on button briefly $saveButton.removeClass('accua-saving').addClass('accua-saved').prop('disabled', false); $clickedButton.removeClass('accua-saving-active').addClass('accua-saved-active'); // Remove success visual state after 1.5 seconds setTimeout(function() { $saveButton.removeClass('accua-saved'); $clickedButton.removeClass('accua-saved-active'); }, 1500); }, error: function() { // Show error state $saveButton.removeClass('accua-saving').prop('disabled', false); $clickedButton.removeClass('accua-saving-active').val(originalText).addClass('accua-error'); setTimeout(function() { $clickedButton.removeClass('accua-error'); }, 3000); } }); }, error: function() { // Show error state $saveButton.removeClass('accua-saving').prop('disabled', false); $clickedButton.removeClass('accua-saving-active').val(originalText).addClass('accua-error'); setTimeout(function() { $clickedButton.removeClass('accua-error'); }, 3000); } }); }); // ======================================================================== // Accessibility: Clickable Labels for Checkboxes // ======================================================================== /** * Toggle visibility of input field based on checkbox state. * Inputs are hidden when unchecked to save space. */ function toggleOptionFieldVisibility($checkbox) { var $container = $checkbox.closest('.accua-style-row, .label_container, .label_input, [id^="accua_form_"]'); var isChecked = $checkbox.is(':checked'); // Find the associated value input var $valueInput = $container.find('.accua_form_value').first(); var $colorPicker = $container.find('.wp-picker-container'); var $helpText = $container.find('.accua-style-help'); if ($colorPicker.length) { // For color pickers, toggle visibility if (isChecked) { $colorPicker.show(); $colorPicker.find('input').prop('disabled', false); } else { $colorPicker.hide(); $colorPicker.find('input').prop('disabled', true); } } else if ($valueInput.length) { // For regular text inputs - hide/show and toggle disabled if (isChecked) { $valueInput.show().prop('disabled', false); } else { $valueInput.hide().prop('disabled', true); } } // Toggle help text visibility if ($helpText.length) { if (isChecked) { $helpText.show(); } else { $helpText.hide(); } } } /** * Initialize option field visibility based on initial checkbox states. */ function initOptionFieldVisibility() { $('#accua_tab_customise input.accua_form_check_override[type="checkbox"]').each(function() { toggleOptionFieldVisibility($(this)); }); } /** * Bind change handlers on override checkboxes for toggling value field visibility. * Labels are native