// Simple debounce function implementation function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Helper function to open edit modal function mxchatOpenEditModal(intentId, phrases) { const modal = document.getElementById('mxchat-edit-modal'); if (!modal) return; // Get form fields const intentIdField = document.getElementById('edit_intent_id'); const phrasesField = document.getElementById('edit_phrases'); // Set values intentIdField.value = intentId; phrasesField.value = phrases; // Show modal with animation modal.style.display = 'flex'; requestAnimationFrame(() => { modal.classList.add('active'); }); // Set up close handlers const closeModal = () => { modal.classList.remove('active'); setTimeout(() => { modal.style.display = 'none'; }, 300); // Match the CSS transition time }; // Close button handler const closeBtn = modal.querySelector('.mxchat-modal-close'); if (closeBtn) { closeBtn.onclick = closeModal; } // Cancel button handler const cancelBtn = modal.querySelector('.mxchat-modal-cancel'); if (cancelBtn) { cancelBtn.onclick = closeModal; } // Click outside modal to close modal.onclick = (e) => { if (e.target === modal) { closeModal(); } }; // Focus the textarea phrasesField.focus(); } // Initialize event listeners document.addEventListener('DOMContentLoaded', () => { // Set up edit button handlers document.querySelectorAll('.mxchat-edit-button').forEach(button => { button.onclick = () => { const intentId = button.dataset.intentId; const phrases = button.dataset.phrases; mxchatOpenEditModal(intentId, phrases); }; }); }); jQuery(document).ready(function($) { // Ensure we have a debounce function (use lodash if available, otherwise use our implementation) const useDebounce = (window._ && window._.debounce) ? window._.debounce : debounce; // --- AJAX Auto-Save --- const $autosaveSections = $('.mxchat-autosave-section'); if ($autosaveSections.length) { // Handle real-time range slider value updates $autosaveSections.find('input[type="range"]').on('input', function() { const value = $(this).val(); $('#threshold_value').text(value); }); // Handle all input changes (including range slider) $autosaveSections.find('input, textarea, select').on('change', function() { const $field = $(this); const name = $field.attr('name'); let value; // Handle different input types if ($field.attr('type') === 'checkbox') { value = $field.is(':checked') ? 'on' : 'off'; } else { value = $field.val(); } // Create feedback container const feedbackContainer = $('
'); const spinner = $('
'); const successIcon = $('
'); // Position feedback container based on input type if ($field.closest('.toggle-switch').length) { $field.closest('td').append(feedbackContainer); } else if ($field.closest('.mxchat-toggle-switch').length) { $field.closest('.mxchat-toggle-container').append(feedbackContainer); } else if ($field.closest('.slider-container').length) { $field.closest('.slider-container').after(feedbackContainer); } else { $field.after(feedbackContainer); } feedbackContainer.append(spinner); // Determine which AJAX action and nonce to use: var ajaxAction, nonce; // Use the new AJAX action for submenu fields: if ( name.indexOf('mxchat_prompts_options') !== -1 || name === 'mxchat_auto_sync_posts' || name === 'mxchat_auto_sync_pages' ) { ajaxAction = 'mxchat_save_prompts_setting'; nonce = mxchatPromptsAdmin.prompts_setting_nonce; } else { // Otherwise, use the existing AJAX action. ajaxAction = 'mxchat_save_setting'; nonce = mxchatAdmin.setting_nonce; } // AJAX save request $.ajax({ url: (ajaxAction === 'mxchat_save_prompts_setting') ? mxchatPromptsAdmin.ajax_url : mxchatAdmin.ajax_url, type: 'POST', data: { action: ajaxAction, name: name, value: value, _ajax_nonce: nonce }, success: function(response) { if (response.success) { spinner.fadeOut(200, function() { feedbackContainer.append(successIcon); successIcon.fadeIn(200).delay(1000).fadeOut(200, function() { feedbackContainer.remove(); }); }); } else { alert('Error saving: ' + (response.data?.message || 'Unknown error')); if ($field.attr('type') === 'checkbox') { $field.prop('checked', !$field.is(':checked')); } feedbackContainer.remove(); } }, error: function() { alert('An error occurred while saving.'); if ($field.attr('type') === 'checkbox') { $field.prop('checked', !$field.is(':checked')); } feedbackContainer.remove(); } }); }); // Initialize color pickers with debouncing $autosaveSections.find('.my-color-field').each(function() { const $colorField = $(this); $(this).wpColorPicker({ change: useDebounce(function(event, ui) { // Safety check - ensure we have a valid field and value if (!$colorField || !$colorField.val()) { console.warn('Color picker not ready'); return; } const name = $colorField.attr('name'); const value = $colorField.val(); if (!name || !value) { console.warn('Missing required color picker values'); return; } // Create feedback container const feedbackContainer = $('
'); const spinner = $('
'); const successIcon = $('
'); // Position feedback container $colorField.closest('.wp-picker-container').after(feedbackContainer); feedbackContainer.append(spinner); // Determine AJAX action and nonce for color fields: var ajaxAction, nonce; if ( name.indexOf('mxchat_prompts_options') !== -1 || name === 'mxchat_auto_sync_posts' || name === 'mxchat_auto_sync_pages' ) { ajaxAction = 'mxchat_save_prompts_setting'; nonce = mxchatPromptsAdmin.prompts_setting_nonce; } else { ajaxAction = 'mxchat_save_setting'; nonce = mxchatAdmin.setting_nonce; } // AJAX save request $.ajax({ url: (ajaxAction === 'mxchat_save_prompts_setting') ? mxchatPromptsAdmin.ajax_url : mxchatAdmin.ajax_url, type: 'POST', data: { action: ajaxAction, name: name, value: value, _ajax_nonce: nonce }, success: function(response) { if (response.success) { spinner.fadeOut(200, function() { feedbackContainer.append(successIcon); successIcon.fadeIn(200).delay(1000).fadeOut(200, function() { feedbackContainer.remove(); }); }); } else { alert('Error saving: ' + (response.data?.message || 'Unknown error')); feedbackContainer.remove(); } }, error: function() { alert('An error occurred while saving.'); feedbackContainer.remove(); } }); }, 500) }); }); // Reinitialize color pickers when switching tabs $('.mxchat-nav-tab').on('click.mxchat', function() { setTimeout(function() { $('.my-color-field:visible').wpColorPicker('close'); }, 100); }); } // Initialize tabs system function initTabs() { // Remove any existing handlers first $('.mxchat-nav-tab').off('click.mxchat'); // Add new click handlers $('.mxchat-nav-tab').on('click.mxchat', function(e) { e.preventDefault(); e.stopPropagation(); var $this = $(this); // Get tab ID - try href first, fallback to data-tab, then to default var tabId = $this.attr('href'); if (tabId) { tabId = tabId.replace('#', ''); } else { tabId = $this.data('tab') || 'chatbot'; } // Safety check for empty tabId if (!tabId) { console.warn('No tab identifier found'); return; } // Update tabs $('.mxchat-nav-tab').removeClass('mxchat-nav-tab-active'); $this.addClass('mxchat-nav-tab-active'); // Update content areas - with safety check $('.mxchat-tab-content').removeClass('active').hide(); var $targetTab = $('#' + tabId); if ($targetTab.length) { $targetTab.addClass('active').show(); // Store active tab try { localStorage.setItem('mxchat_active_tab', tabId); } catch (e) { console.warn('LocalStorage not available:', e); } } else { console.warn('Tab content #' + tabId + ' not found'); } }); } // Initialize tabs and handle events initTabs(); $(document).on('widget-added widget-updated postbox-toggled', initTabs); // Activate initial tab try { var savedTab = localStorage.getItem('mxchat_active_tab'); if (savedTab && $('#' + savedTab).length > 0) { $('.mxchat-nav-tab[href="#' + savedTab + '"]').trigger('click.mxchat'); } else { $('.mxchat-nav-tab').first().trigger('click.mxchat'); } } catch (e) { $('.mxchat-nav-tab').first().trigger('click.mxchat'); } // Attach edit modal event handler $(document).on('click', '.mxchat-edit-button', function() { const intentId = $(this).data('intent-id'); const phrases = $(this).data('phrases'); mxchatOpenEditModal(intentId, phrases); }); // Toggle visibility handlers function toggleVisibility(selector) { $(selector).on('click', function() { var inputField = $(this).prev('input'); if (inputField.attr('type') === 'password') { inputField.attr('type', 'text'); $(this).text('Hide'); } else { inputField.attr('type', 'password'); $(this).text('Show'); } }); } // Initialize all toggle visibility buttons [ '#toggleApiKeyVisibility', '#toggleWooCommerceSecretVisibility', '#toggleLoopsApiKeyVisibility', '#toggleXaiApiKeyVisibility', '#toggleClaudeApiKeyVisibility', '#toggleBraveApiKeyVisibility', '#toggleWebhookUrlVisibility', '#toggleSecretKeyVisibility', '#toggleBotTokenVisibility', '#toggleDeepSeekApiKeyVisibility' ].forEach(toggleVisibility); // Add Intent Form Submission $('#mxchat-add-intent-form').on('submit', function(event) { $('#mxchat-intent-loading').show(); $('#mxchat-intent-loading-text').show(); $(this).find('button[type="submit"]').hide(); }); // Inline Edit Functionality $('.edit-button').on('click', function() { var row = $(this).closest('tr'); row.find('.content-view, .url-view').hide(); row.find('.content-edit, .url-edit').show(); row.find('.edit-button').hide(); row.find('.save-button').show(); }); // Save button handler $('.save-button').on('click', function() { var button = $(this); var row = button.closest('tr'); var id = button.data('id'); var newContent = row.find('.content-edit').val(); var newUrl = row.find('.url-edit').val(); button.prop('disabled', true); button.text('Saving...'); $.ajax({ url: mxchatAdmin.ajax_url, type: 'POST', data: { action: 'mxchat_save_inline_prompt', id: id, article_content: newContent, article_url: newUrl, _ajax_nonce: mxchatAdmin.inline_edit_nonce }, success: function(response) { button.prop('disabled', false); button.text('Save'); if (response.success) { row.find('.content-view').html(newContent.replace(/\n/g, "
")); if (newUrl) { row.find('.url-view').html('' + newUrl + ''); } else { row.find('.url-view').html('N/A'); } row.find('.content-edit, .url-edit').hide(); row.find('.content-view, .url-view').show(); row.find('.save-button').hide(); row.find('.edit-button').show(); } else { alert('Error saving content: ' + (response.data?.message || 'Unknown error')); } }, error: function() { button.prop('disabled', false); button.text('Save'); alert('An error occurred while saving.'); } }); }); // Activation handling const form = $('#mxchat-activation-form'); const spinner = $('#mxchat-activation-spinner'); const submitButton = $('#activate_license_button'); const licenseStatus = $('#mxchat-license-status'); if (form.length && licenseStatus.length && submitButton.length) { function handleActivationResponse(response) { spinner.hide(); if (response.success) { licenseStatus.text('Active'); licenseStatus.removeClass('inactive').addClass('active'); form.hide(); } else { licenseStatus.text('Inactive'); alert(response.data || 'Activation failed. Please check your input.'); submitButton.prop('disabled', false); } } form.on('submit', function(event) { event.preventDefault(); spinner.show(); submitButton.prop('disabled', true); var formData = { action: 'mxchat_activate_license', mxchat_pro_email: $('#mxchat_pro_email').val(), mxchat_activation_key: $('#mxchat_activation_key').val(), security: mxchatAdmin.license_nonce }; $.post(mxchatAdmin.ajax_url, formData, function(response) { handleActivationResponse(response); }).fail(function() { alert('Server error. Please try again.'); spinner.hide(); submitButton.prop('disabled', false); }); }); } // Questions handling $('.mxchat-add-question').on('click', function () { const container = $('#mxchat-additional-questions-container'); const questionCount = container.find('.mxchat-question-row').length + 4; const questionIndex = container.find('.mxchat-question-row').length; const newQuestion = `
`; container.append(newQuestion); }); $(document).on('click', '.mxchat-remove-question', function () { $(this).closest('.mxchat-question-row').remove(); saveQuestions(); }); $(document).on('change', '.mxchat-question-input', function() { saveQuestions(); }); function saveQuestions() { const questions = []; $('.mxchat-question-input').each(function() { const value = $(this).val().trim(); if (value) { questions.push(value); } }); const feedbackContainer = $('
'); const spinner = $('
'); const successIcon = $('
'); // Append feedback after the add button $('.mxchat-add-question').after(feedbackContainer); feedbackContainer.append(spinner); // Save via AJAX $.ajax({ url: mxchatAdmin.ajax_url, type: 'POST', data: { action: 'mxchat_save_setting', name: 'additional_popular_questions', value: JSON.stringify(questions), _ajax_nonce: mxchatAdmin.setting_nonce }, success: function(response) { if (response.success) { spinner.fadeOut(200, function() { feedbackContainer.append(successIcon); successIcon.fadeIn(200).delay(1000).fadeOut(200, function() { feedbackContainer.remove(); }); }); } else { alert('Error saving questions: ' + (response.data?.message || 'Unknown error')); feedbackContainer.remove(); } }, error: function() { alert('An error occurred while saving questions.'); feedbackContainer.remove(); } }); } // Live agent status handler const statusToggle = document.getElementById('live_agent_status'); const statusText = statusToggle?.parentElement.nextElementSibling?.querySelector('.status-text'); if (statusToggle && statusText) { statusToggle.addEventListener('change', function() { // Update display text statusText.textContent = this.checked ? 'Online' : 'Offline'; // Send the correct on/off value to the server if (window.mxchatSaveSetting) { window.mxchatSaveSetting('live_agent_status', this.checked ? 'on' : 'off'); } }); } });