# mxchat-basic/2.0.4/js/chat-script.js

MxChat – AI Chatbot &amp; Content Generation for WordPress, version 2.0.4. 1,559 lines.

- Page: https://pluginprobe.com/plugins/mxchat-basic/2.0.4/code/js/chat-script.js
- Raw: https://pluginprobe.com/plugins/mxchat-basic/2.0.4/raw/js/chat-script.js
- Modified: 2025-02-17T17:03:00+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mxchat-basic/2.0.4/code/js/chat-script.js#L10-L20`.

````javascript
jQuery(document).ready(function($) {
//console.log('mxchatChat object:', mxchatChat);
//console.log('Link Target Toggle Value:', mxchatChat.link_target_toggle);
// Add these variables at the top of your chat-script.js file
    const toolbarIconColor = mxchatChat.toolbar_icon_color || '#212121';

    // Initialize color settings
    var userMessageBgColor = mxchatChat.user_message_bg_color;
    var userMessageFontColor = mxchatChat.user_message_font_color;
    var botMessageBgColor = mxchatChat.bot_message_bg_color;
    var botMessageFontColor = mxchatChat.bot_message_font_color;
    // Add live agent message colors
    var liveAgentMessageBgColor = mxchatChat.live_agent_message_bg_color;
    var liveAgentMessageFontColor = mxchatChat.live_agent_message_font_color;


    var linkTarget = mxchatChat.link_target_toggle === 'on' ? '_blank' : '_self';
    let lastSeenMessageId = '';
    let notificationCheckInterval;
    let notificationBadge;
    // Initialize session ID
    var sessionId = getChatSession();

    let pollingInterval; // Variable to store the interval ID
    let processedMessageIds = new Set(); // Add this at the top with your other variables
//console.log('Live Agent BG Color:', liveAgentMessageBgColor);
//console.log('Live Agent Font Color:', liveAgentMessageFontColor);
    let activePdfFile = null;
    let activeWordFile = null;


// Function to create and append notification badge
// Function to create and append notification badge
function createNotificationBadge() {
    console.log("Creating notification badge...");
    const chatButton = document.getElementById('floating-chatbot-button');
    console.log("Chat button found:", !!chatButton);
    
    if (!chatButton) return;

    // Remove any existing badge first
    const existingBadge = chatButton.querySelector('.chat-notification-badge');
    if (existingBadge) {
        console.log("Removing existing badge");
        existingBadge.remove();
    }

    notificationBadge = document.createElement('div');
    notificationBadge.className = 'chat-notification-badge';
    notificationBadge.style.cssText = `
        display: none;
        position: absolute;
        top: -5px;
        right: -5px;
        background-color: red;
        color: white;
        border-radius: 50%;
        padding: 4px 8px;
        font-size: 12px;
        font-weight: bold;
        z-index: 10001;
    `;
    chatButton.style.position = 'relative';
    chatButton.appendChild(notificationBadge);
    
    console.log("Notification badge created and appended:", {
        exists: !!notificationBadge,
        parent: notificationBadge?.parentNode?.id,
        display: notificationBadge?.style?.display
    });
}

// Function to check for new messages
function checkForNewMessages() {
    const sessionId = getChatSession();
    const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
    
    if (!chatPersistenceEnabled) return;

    $.ajax({
        url: mxchatChat.ajax_url,
        type: 'POST',
        data: {
            action: 'mxchat_check_new_messages',
            session_id: sessionId,
            last_seen_id: lastSeenMessageId,
            nonce: mxchatChat.nonce
        },
        success: function(response) {
            if (response.success && response.data.hasNewMessages) {
                showNotification();
            }
        }
    });
}

// Function to show notification
function showNotification() {
    const badge = document.getElementById('chat-notification-badge');
    if (badge && $('#floating-chatbot').hasClass('hidden')) {
        badge.style.display = 'block';
        badge.textContent = '1';
    }
}

function hideNotification() {
    const badge = document.getElementById('chat-notification-badge');
    if (badge) {
        badge.style.display = 'none';
    }
}
// Function to start notification checking
function startNotificationChecking() {
    const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
    if (!chatPersistenceEnabled) return;

    createNotificationBadge();
    notificationCheckInterval = setInterval(checkForNewMessages, 30000); // Check every 30 seconds
}

// Function to stop notification checking
function stopNotificationChecking() {
    if (notificationCheckInterval) {
        clearInterval(notificationCheckInterval);
    }
}




function getChatSession() {
    var sessionId = getCookie('mxchat_session_id');
    //console.log("Session ID retrieved from cookie: ", sessionId);

    if (!sessionId) {
        sessionId = generateSessionId();
        //console.log("Generated new session ID: ", sessionId);
        setChatSession(sessionId);
    }

    //console.log("Final session ID: ", sessionId);
    return sessionId;
}

function setChatSession(sessionId) {
    // Set the cookie with a 24-hour expiration (86400 seconds)
    document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
}

// Get cookie value by name
function getCookie(name) {
    let value = "; " + document.cookie;
    let parts = value.split("; " + name + "=");
    if (parts.length == 2) return parts.pop().split(";").shift();
}

// Generate a new session ID
function generateSessionId() {
    return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
}

// Function to send the message to the chatbot (backend)
function sendMessageToChatbot(message) {
    var sessionId = getChatSession(); // Reuse the session ID logic

    // Hide the popular questions section
    $('#mxchat-popular-questions').hide();

    // Show thinking indicator (no need to append the user's message again)
    appendThinkingMessage();
    scrollToBottom();

    //console.log("Sending message to chatbot:", message); // Log the message
    //console.log("Session ID:", sessionId); // Log the session ID

    // Call the chatbot using the same call logic as sendMessage
    callMxChat(message, function(response) {
        // ** Ensure temporary thinking message is removed before adding new response **
        $('.temporary-message').remove();

        // Replace thinking indicator with actual response
        replaceLastMessage("bot", response);
    });
}




function sendMessage() {
    var message = $('#chat-input').val(); // Get value from textarea
    if (message) {
        appendMessage("user", message); // Append user's message
        $('#chat-input').val(''); // Clear the textarea
        $('#chat-input').css('height', 'auto'); // Reset height after clearing content

        // Hide the popular questions section
        $('#mxchat-popular-questions').hide();

        // Show typing indicator
        appendThinkingMessage();
        scrollToBottom();

        callMxChat(message, function(response) {
            // Replace typing indicator with actual response
            replaceLastMessage("bot", response);
        });
    }
}



    // Function to append a thinking message with animation
    function appendThinkingMessage() {
        // Remove any existing thinking dots first
        $('.thinking-dots').remove();

        // Retrieve the bot message font color and background color
        var botMessageFontColor = mxchatChat.bot_message_font_color;
        var botMessageBgColor = mxchatChat.bot_message_bg_color;


        var thinkingHtml = '<div class="thinking-dots-container">' +
                           '<div class="thinking-dots">' +
                           '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
                           '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
                           '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
                           '</div>' +
                           '</div>';

        // Append the thinking dots to the chat container (or within the temporary message div)
        $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
        scrollToBottom();
    }

        // Trigger send button click when "Enter" key is pressed in the textarea
        $('#chat-input').keypress(function(e) {
            if (e.which == 13 && !e.shiftKey) { // Check if "Enter" is pressed without Shift
                e.preventDefault(); // Prevent default "Enter" behavior
                $('#send-button').click(); // Trigger send button click
            }
        });

    // Handle send button click
    $('#send-button').click(function() {
        sendMessage();
    });

    // Handle click on popular questions
    $('.mxchat-popular-question').on('click', function () {
        var question = $(this).text(); // Get the text of the clicked question

        // Append the question as if the user typed it
        appendMessage("user", question);

        // Send the question to the server (backend)
        sendMessageToChatbot(question);
    });


// Add this new function to handle markdown headers
function formatMarkdownHeaders(text) {
    // Handle h1 to h6 headers
    return text.replace(/^(#{1,6})\s(.+)$/gm, function(match, hashes, content) {
        const level = hashes.length;
        return `<h${level} class="chat-heading">${content}</h${level}>`;
    });
}

// Update the linkify function to handle both URLs and markdown
function linkify(inputText) {
    // First process markdown headers
    let processedText = formatMarkdownHeaders(inputText);
    
    // Then process links as before
    var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
    processedText = processedText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');

    // Replace standalone URLs not already in an <a> tag
    var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
    processedText = processedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');

    // Replace "www." prefixed URLs not already in an <a> tag
    var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
    processedText = processedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');

    return processedText;
}


function scrollElementToTop(element) {
    var chatBox = $('#chat-box');
    var elementTop = element.position().top + chatBox.scrollTop();
    chatBox.animate({ scrollTop: elementTop }, 500);
}


// Optimized scrollToBottom function for instant scrolling
function scrollToBottom(instant = false) {
    var chatBox = $('#chat-box');
    if (instant) {
        // Instantly set the scroll position to the bottom
        chatBox.scrollTop(chatBox.prop("scrollHeight"));
    } else {
        // Use requestAnimationFrame for smoother scrolling if needed
        let start = null;
        const scrollHeight = chatBox.prop("scrollHeight");
        const initialScroll = chatBox.scrollTop();
        const distance = scrollHeight - initialScroll;
        const duration = 500; // Duration in ms

        function smoothScroll(timestamp) {
            if (!start) start = timestamp;
            const progress = timestamp - start;
            const currentScroll = initialScroll + (distance * (progress / duration));
            chatBox.scrollTop(currentScroll);

            if (progress < duration) {
                requestAnimationFrame(smoothScroll);
            } else {
                chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
            }
        }

        requestAnimationFrame(smoothScroll);
    }
}


    // Function to format text with **bold** inside double asterisks
    function formatBoldText(text) {
        return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
    }

    // Function to convert newline characters to HTML line breaks and handle paragraph spacing
function convertNewlinesToBreaks(text) {
    // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
    const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
    
    // Wrap each paragraph in <p> tags
    return paragraphs
        .map(para => `<p>${para.trim()}</p>`)
        .join('');
}
    // Copy to clipboard function
    // Function to copy text to clipboard
    function copyToClipboard(text) {
        var tempInput = $('<input>');
        $('body').append(tempInput);
        tempInput.val(text).select();
        document.execCommand('copy');
        tempInput.remove();
    }


function updateChatModeIndicator(mode) {
    const indicator = document.getElementById('chat-mode-indicator');
    if (indicator) {
        indicator.textContent = mode === 'agent' ? 'Live Agent' : 'AI Agent';
    }

    // Start or stop polling based on mode
    if (mode === 'agent') {
        startPolling();
    } else {
        stopPolling();
    }
}

function callMxChat(message, callback) {
    $.ajax({
        url: mxchatChat.ajax_url,
        type: 'POST',
        dataType: 'json',
        data: {
            action: 'mxchat_handle_chat_request',
            message: message,
            session_id: getChatSession(),
            nonce: mxchatChat.nonce
        },
        success: function(response) {
            // Existing chat mode check
            if (response.chat_mode) {
                updateChatModeIndicator(response.chat_mode);
            }
            else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
                updateChatModeIndicator(response.fallbackResponse.chat_mode);
            }

            // Add PDF filename handling
            if (response.data && response.data.filename) {
                showActivePdf(response.data.filename);
                activePdfFile = response.data.filename;
            }


            // Check for live agent response
            if (response.success && response.data && response.data.status === 'waiting_for_agent') {
                updateChatModeIndicator('agent');
                return;
            }

            // Handle other responses
            let responseText = response.text || '';
            let responseHtml = response.html || '';
            let responseMessage = response.message || '';

            if (responseText === 'You are now chatting with the AI chatbot.') {
                updateChatModeIndicator('ai');
            }

            // Handle the message and show notification if chat is hidden
            if (responseText || responseHtml || responseMessage) {
                // Update the messages as before
                if (responseText && responseHtml) {
                    replaceLastMessage("bot", responseText, responseHtml);
                } else if (responseText) {
                    replaceLastMessage("bot", responseText);
                } else if (responseHtml) {
                    replaceLastMessage("bot", "", responseHtml);
                } else if (responseMessage) {
                    replaceLastMessage("bot", responseMessage);
                }

                // Check if chat is hidden and show notification
                if ($('#floating-chatbot').hasClass('hidden')) {
                    const badge = $('#chat-notification-badge');
                    if (badge.length) {
                        badge.show();
                    }
                }
            } else {
                console.error("Unexpected response format:", response);
                replaceLastMessage("bot", "I'm sorry, something went wrong.");
            }

            if (response.message_id) {
                lastSeenMessageId = response.message_id;
            }
        },
        error: function(xhr, status, error) {
            replaceLastMessage("bot", "An unexpected error occurred.");
        }
    });
}

function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
    //console.log("Appending message. Sender:", sender, "Content:", messageText);

    try {
        // Determine styles based on sender type
        let messageClass, bgColor, fontColor;

        if (sender === "user") {
            messageClass = "user-message";
            bgColor = userMessageBgColor;
            fontColor = userMessageFontColor;
            } else if (sender === "agent") {
                messageClass = "agent-message";
                bgColor = liveAgentMessageBgColor;
                fontColor = liveAgentMessageFontColor;
            } else {
            messageClass = "bot-message";
            bgColor = botMessageBgColor;
            fontColor = botMessageFontColor;
        }

    const messageDiv = $('<div>')
        .addClass(messageClass)
        .css({
            'background': bgColor,
            'color': fontColor,
        });

    // Add CSS for paragraphs
    messageDiv.css({
        'margin-bottom': '1em'
    });

        // Format and process the message content
        let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));

        // Add images if provided
        if (images && images.length > 0) {
            fullMessage += '<div class="image-gallery">';
            images.forEach(img => {
                fullMessage += `
                    <div style="margin-bottom: 10px;">
                        <strong>${img.title}</strong><br>
                        <a href="${img.image_url}" target="_blank">
                            <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
                        </a>
                    </div>`;
            });
            fullMessage += '</div>';
        }

        // Append HTML content if provided
        if (messageHtml) {
            fullMessage += '<br><br>' + messageHtml;
        }

        messageDiv.html(fullMessage);

        // Add a class for temporary messages if needed
        if (isTemporary) {
            messageDiv.addClass('temporary-message');
        }

        // Append the message to the chat box
        messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () {
            if (sender === "bot") {
                // After bot's message is displayed, scroll the last user message to the top
                const lastUserMessage = $('#chat-box').find('.user-message').last();
                if (lastUserMessage.length) {
                    scrollElementToTop(lastUserMessage);
                }
            }
        });

        if (messageText.id) {
                lastSeenMessageId = messageText.id;
                hideNotification();
            }
    } catch (error) {
        console.error("Error rendering message with images:", error);
    }
}


function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
    var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
    var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();

    // Determine styles
    let bgColor, fontColor;
    if (sender === "user") {
        bgColor = userMessageBgColor;
        fontColor = userMessageFontColor;
    } else if (sender === "agent") {
        bgColor = liveAgentMessageBgColor;
        fontColor = liveAgentMessageFontColor;
    } else {
        bgColor = botMessageBgColor;
        fontColor = botMessageFontColor;
    }

    var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
    if (responseHtml) {
        fullMessage += '<br><br>' + responseHtml;
    }

    if (images.length > 0) {
        fullMessage += '<div class="image-gallery">';
        images.forEach(img => {
            fullMessage += `
                <div style="margin-bottom: 10px;">
                    <strong>${img.title}</strong><br>
                    <a href="${img.image_url}" target="_blank">
                        <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
                    </a>
                </div>`;
        });
        fullMessage += '</div>';
    }

    if (lastMessageDiv.length) {
        lastMessageDiv.fadeOut(200, function() {
            $(this)
                .html(fullMessage)
                .removeClass('bot-message user-message')
                .addClass(messageClass)
                .css({
                    'background-color': bgColor,
                    'color': fontColor,
                })
                .removeClass('temporary-message')
                .fadeIn(200, function() {
                    if (sender === "bot" || sender === "agent") {
                        const lastUserMessage = $('#chat-box').find('.user-message').last();
                        if (lastUserMessage.length) {
                            scrollElementToTop(lastUserMessage);
                        }
                        // Show notification if chat is hidden
                        if ($('#floating-chatbot').hasClass('hidden')) {
                            showNotification();
                        }
                    }
                });
        });
    } else {
        appendMessage(sender, responseText, responseHtml, images);
    }
}


function startPolling() {
    // Clear any existing interval first
    stopPolling();
    // Start new polling interval
    pollingInterval = setInterval(checkForAgentMessages, 5000);
    //console.log("Started agent message polling");
}

function stopPolling() {
    if (pollingInterval) {
        clearInterval(pollingInterval);
        pollingInterval = null;
        //console.log("Stopped agent message polling");
    }
}


// Update your checkForAgentMessages function
function checkForAgentMessages() {
    const sessionId = getChatSession();
    $.ajax({
        url: mxchatChat.ajax_url,
        type: 'POST',
        dataType: 'json',
        data: {
            action: 'mxchat_fetch_new_messages',
            session_id: sessionId,
            last_seen_id: lastSeenMessageId,
            nonce: mxchatChat.nonce
        },
        success: function (response) {
            if (response.success && response.data?.new_messages) {
                let hasNewMessage = false;
                
                response.data.new_messages.forEach(function (message) {
                    if (message.role === "agent" && !processedMessageIds.has(message.id)) {
                        hasNewMessage = true;
                        replaceLastMessage("agent", message.content);
                        lastSeenMessageId = message.id;
                        processedMessageIds.add(message.id);
                    }
                });

                if (hasNewMessage && $('#floating-chatbot').hasClass('hidden')) {
                    showNotification();
                }
                
                scrollToBottom(true);
            }
        },
        error: function (xhr, status, error) {
            console.error("Polling error:", xhr, status, error);
        }
    });
}
function loadChatHistory() {
    var sessionId = getChatSession();
    var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';

    if (chatPersistenceEnabled && sessionId) {
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            dataType: 'json',
            data: {
                action: 'mxchat_fetch_conversation_history',
                session_id: sessionId
            },
            success: function(response) {
                if (response.success && response.data && Array.isArray(response.data.conversation)) {


                    var $chatBox = $('#chat-box');
                    var $fragment = $(document.createDocumentFragment());
                    let highestMessageId = lastSeenMessageId;

                    if (response.data.chat_mode) {
                        updateChatModeIndicator(response.data.chat_mode);
                    }

                    $.each(response.data.conversation, function(index, message) {
                        // Skip agent messages if persistence is off
                        if (!chatPersistenceEnabled && message.role === 'agent') {
                            return;
                        }

                        var messageClass, messageBgColor, messageFontColor;

                        switch (message.role) {
                            case 'user':
                                messageClass = 'user-message';
                                messageBgColor = userMessageBgColor;
                                messageFontColor = userMessageFontColor;
                                break;
                            case 'agent':
                                messageClass = 'agent-message';
                                messageBgColor = liveAgentMessageBgColor;
                                messageFontColor = liveAgentMessageFontColor;
                                break;
                            default:
                                messageClass = 'bot-message';
                                messageBgColor = botMessageBgColor;
                                messageFontColor = botMessageFontColor;
                                break;
                        }

                        var messageElement = $('<div>').addClass(messageClass)
                            .css({
                                'background': messageBgColor,
                                'color': messageFontColor
                            });

                        var content = message.content;
                        content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
                        content = decodeHTMLEntities(content);

                        if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
                            messageElement.html(content);
                        } else {
                            var formattedContent = linkify(
                                formatBoldText(
                                    convertNewlinesToBreaks(formatCodeBlocks(content))
                                )
                            );
                            messageElement.html(formattedContent);
                        }

                        $fragment.append(messageElement);

                            // In loadChatHistory, change this part:
                            if (message.id) {
                                highestMessageId = Math.max(highestMessageId, message.id);
                                processedMessageIds.add(message.id); // Add all message IDs to processed set
                            }
                    });

                    $chatBox.append($fragment);
                    scrollToBottom(true);

                    if (response.data.conversation.length > 0) {
                        $('#mxchat-popular-questions').hide();
                    }

                    // Update lastSeenMessageId after history loads
                    lastSeenMessageId = highestMessageId;

                    // Only update chat mode if persistence is enabled
                    if (chatPersistenceEnabled && response.data.conversation.length > 0) {
                        var lastMessage = response.data.conversation[response.data.conversation.length - 1];
                        if (lastMessage.role === 'agent') {
                            updateChatModeIndicator('agent');
                        }
                    }
                } else {
                    console.warn("No conversation history found.");
                }
            },
            error: function(xhr, status, error) {
                console.error("Error loading chat history:", status, error);
                appendMessage("bot", "Unable to load chat history.");
            }
        });
    } else {
        console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
    }
}

// Function to decode HTML entities
function decodeHTMLEntities(text) {
    var textArea = document.createElement('textarea');
    textArea.innerHTML = text;
    return textArea.value;
}


// Update formatCodeBlocks function
function formatCodeBlocks(text) {
    // First handle raw PHP tags
    text = text.replace(/(<\?php[\s\S]*?\?>)/g, (match) => {
        return `<pre><code class="language-php">${escapeHtml(match)}</code></pre>`;
    });

    // Then handle code blocks with backticks
    text = text.replace(/```php5?\n([\s\S]+?)```/gi, (match, code) => {
        return `<pre><code class="language-php">${escapeHtml(code)}</code></pre>`;
    });

    return text;
}

// Update escapeHtml function to preserve existing code blocks
function escapeHtml(unsafe) {
    // First check if it's already a code block
    if (unsafe.includes('<pre><code') || unsafe.includes('</code></pre>')) {
        return unsafe;
    }
    
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}
// Utility function to escape HTML
function escapeHtml(unsafe) {
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}




// Function to convert newlines, skipping preformatted text
function convertNewlinesToBreaks(text) {
    // Split while preserving code blocks
    return text.split(/(<pre\b[^>]*>[\s\S]*?<\/pre>)/g).map(part => {
        if (part.startsWith('<pre')) return part;
        return part.replace(/(^|[^>])\n/g, '$1<br>');
    }).join('');
}




    // Helper function to check if a string is an image HTML
    function isImageHtml(str) {
        return str.startsWith('<img') && str.endsWith('>');
    }

    // Function to remove thinking dots
    function removeThinkingDots() {
        $('.thinking-dots').closest('.temporary-message').remove();
    }

    function isMobile() {
        // This can be a simple check, or more sophisticated detection of mobile devices
        return window.innerWidth <= 768; // Example threshold for mobile devices
    }

    function disableScroll() {
        if (isMobile()) {
            $('body').css('overflow', 'hidden');
        }
    }

    function enableScroll() {
        if (isMobile()) {
            $('body').css('overflow', '');
        }
    }

// Pre-chat dismissal check function (wrapped in a function for reuse)
    function checkPreChatDismissal() {
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            data: {
                action: 'mxchat_check_pre_chat_message_status',
                _ajax_nonce: mxchatChat.nonce
            },
            success: function(response) {
                if (response.success && !response.data.dismissed) {
                    $('#pre-chat-message').fadeIn(250);
                } else {
                    $('#pre-chat-message').hide();
                }
            },
            error: function() {
                console.error('Failed to check pre-chat message dismissal status.');
            }
        });
    }
    
   // Function to show the chatbot widget
function showChatWidget() {
    // First ensure display is set
    $('#floating-chatbot-button').css('display', 'flex');
    // Then handle the fade
    $('#floating-chatbot-button').fadeTo(500, 1);
    // Force visibility
    $('#floating-chatbot-button').removeClass('hidden');
    //console.log('Showing widget');
}

// Function to hide the chatbot widget
function hideChatWidget() {
    $('#floating-chatbot-button').css('display', 'none');
    $('#floating-chatbot-button').addClass('hidden');
    //console.log('Hiding widget');
}

function initializeChatVisibility() {
    //console.log('Initializing chat visibility');
    const complianzEnabled = mxchatChat.complianz_toggle === 'on' || 
                            mxchatChat.complianz_toggle === '1' || 
                            mxchatChat.complianz_toggle === 1;

    if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
        // Initial check
        checkConsentAndShowChat();

        // Listen for consent changes
        $(document).on('cmplz_status_change', function(event) {
            //console.log('Status change detected');
            checkConsentAndShowChat();
        });
    } else {
        // If Complianz is not enabled, always show
        $('#floating-chatbot-button')
            .css('display', 'flex')
            .removeClass('hidden no-consent')
            .fadeTo(500, 1);
            
        // Also check pre-chat message when Complianz is not enabled
        checkPreChatDismissal();
    }
}



function checkConsentAndShowChat() {
    var consentStatus = cmplz_has_consent('marketing');
    var consentType = complianz.consenttype;
    
    //console.log('Checking consent:', {status: consentStatus,type: consentType});

    let $widget = $('#floating-chatbot-button');
    let $chatbot = $('#floating-chatbot');
    let $preChat = $('#pre-chat-message');
    
    if (consentStatus === true) {
        //console.log('Consent granted - showing widget');
        $widget
            .removeClass('no-consent')
            .css('display', 'flex')
            .removeClass('hidden')
            .fadeTo(500, 1);
        $chatbot.removeClass('no-consent');
        
        // Show pre-chat message if not dismissed
        checkPreChatDismissal();
    } else {
        //console.log('No consent - hiding widget');
        $widget
            .addClass('no-consent')
            .fadeTo(500, 0, function() {
                $(this)
                    .css('display', 'none')
                    .addClass('hidden');
            });
        $chatbot.addClass('no-consent');
        
        // Hide pre-chat message when no consent
        $preChat.hide();
    }
}

    // Function to dismiss pre-chat message for 24 hours
    function handlePreChatDismissal() {
        $('#pre-chat-message').fadeOut(200);
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            data: {
                action: 'mxchat_dismiss_pre_chat_message',
                _ajax_nonce: mxchatChat.nonce
            },
            success: function() {
                $('#pre-chat-message').hide();
            },
            error: function() {
                console.error('Failed to dismiss pre-chat message.');
            }
        });
    }

    // Handle pre-chat message dismissal on button click
    $(document).on('click', '.close-pre-chat-message', function(e) {
        e.stopPropagation();
        handlePreChatDismissal();
    });

    // Toggle chatbot visibility on floating button click
    $(document).on('click', '#floating-chatbot-button', function() {
        var chatbot = $('#floating-chatbot');
        if (chatbot.hasClass('hidden')) {
            chatbot.removeClass('hidden').addClass('visible');
            $(this).addClass('hidden');
            $('#chat-notification-badge').hide(); // Hide notification when opening chat
            disableScroll();
            $('#pre-chat-message').fadeOut(250);
        } else {
            chatbot.removeClass('visible').addClass('hidden');
            $(this).removeClass('hidden');
            enableScroll();
            checkPreChatDismissal();
        }
    });

    $(document).on('click', '#exit-chat-button', function() {
        $('#floating-chatbot').addClass('hidden').removeClass('visible');
        $('#floating-chatbot-button').removeClass('hidden');
        enableScroll();
    });

    // Close pre-chat message on click
    $(document).on('click', '.close-pre-chat-message', function(e) {
        e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
        $('#pre-chat-message').fadeOut(200, function() {
            $(this).remove();
        });
    });

    // Open chatbot when pre-chat message is clicked
    $(document).on('click', '#pre-chat-message', function() {
        var chatbot = $('#floating-chatbot');
        if (chatbot.hasClass('hidden')) {
            chatbot.removeClass('hidden').addClass('visible');
            $('#floating-chatbot-button').addClass('hidden');
            $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
            disableScroll(); // Disable scroll when chatbot opens
        }
    });

    // If the chatbot is initially hidden, ensure the button is visible
    if ($('#floating-chatbot').hasClass('hidden')) {
        $('#floating-chatbot-button').removeClass('hidden');
    }

    function setFullHeight() {
        var vh = $(window).innerHeight() * 0.01;
        $(':root').css('--vh', vh + 'px');
    }

    // Set the height when the page loads


    // Set the height on resize and orientation change events
    $(window).on('resize orientationchange', function() {
        setFullHeight();
    });


    // Now handle the close button to dismiss the pre-chat message for 24 hours
    var closeButton = document.querySelector('.close-pre-chat-message');
    if (closeButton) {
        closeButton.addEventListener('click', function() {
            $('#pre-chat-message').fadeOut(200); // Hide the message

            // Send an AJAX request to set the transient flag for 24 hours
            $.ajax({
                url: mxchatChat.ajax_url,
                type: 'POST',
                data: {
                    action: 'mxchat_dismiss_pre_chat_message',
                    _ajax_nonce: mxchatChat.nonce
                },
                success: function() {
                    //console.log('Pre-chat message dismissed for 24 hours.');

                    // Ensure the message is hidden after dismissal
                    $('#pre-chat-message').hide();
                },
                error: function() {
                    //console.error('Failed to dismiss pre-chat message.');
                }
            });
        });
    }




// Event listener for Add to Cart button
$(document).on('click', '.mxchat-add-to-cart-button', function() {
    var productId = $(this).data('product-id');
    // Add a special prefix to indicate this is from button
    appendMessage("user", "add to cart");
    sendMessageToChatbot("!addtocart");  // Special command to indicate button click
});


if (document.getElementById('pdf-upload-btn')) {
    document.getElementById('pdf-upload-btn').addEventListener('click', function() {
        document.getElementById('pdf-upload').click();
    });
}

if (document.getElementById('word-upload-btn')) {
    document.getElementById('word-upload-btn').addEventListener('click', function() {
        document.getElementById('word-upload').click();
    });
}

// PDF file input change handler
document.getElementById('pdf-upload').addEventListener('change', async function(e) {
    const file = e.target.files[0];

    if (!file || file.type !== 'application/pdf') {
        alert('Please select a valid PDF file.');
        return;
    }

    if (!sessionId) {
        console.error('No session ID found');
        alert('Error: No session ID found');
        return;
    }

    if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
        console.error('mxchatChat not properly configured:', mxchatChat);
        alert('Error: Ajax configuration missing');
        return;
    }

    // Disable buttons and show loading state
    const uploadBtn = document.getElementById('pdf-upload-btn');
    const sendBtn = document.getElementById('send-button');
    const originalBtnContent = uploadBtn.innerHTML;

    try {
        const formData = new FormData();
        formData.append('action', 'mxchat_upload_pdf');
        formData.append('pdf_file', file);
        formData.append('session_id', sessionId);
        formData.append('nonce', mxchatChat.nonce);

        uploadBtn.disabled = true;
        sendBtn.disabled = true;
        uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
            <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
        </svg>`;

        const response = await fetch(mxchatChat.ajax_url, {
            method: 'POST',
            body: formData
        });

        const data = await response.json();

        if (data.success) {
            // Hide popular questions if they exist
            const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
            if (popularQuestionsContainer) {
                popularQuestionsContainer.style.display = 'none';
            }

            // Show the active PDF name
            showActivePdf(data.data.filename);
            
            appendMessage('bot', data.data.message);
            scrollToBottom();
            activePdfFile = data.data.filename;
        } else {
            console.error('Upload failed:', data.data);
            alert('Failed to upload PDF. Please try again.');
        }
    } catch (error) {
        console.error('Upload error:', error);
        alert('Error uploading file. Please try again.');
    } finally {
        uploadBtn.disabled = false;
        sendBtn.disabled = false;
        uploadBtn.innerHTML = originalBtnContent;
        this.value = ''; // Reset file input
    }
});

// Word file input change handler
document.getElementById('word-upload').addEventListener('change', async function(e) {
    const file = e.target.files[0];

    if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
        alert('Please select a valid Word document (.docx).');
        return;
    }

    if (!sessionId) {
        console.error('No session ID found');
        alert('Error: No session ID found');
        return;
    }

    // Disable buttons and show loading state
    const uploadBtn = document.getElementById('word-upload-btn');
    const sendBtn = document.getElementById('send-button');
    const originalBtnContent = uploadBtn.innerHTML;

    try {
        const formData = new FormData();
        formData.append('action', 'mxchat_upload_word');
        formData.append('word_file', file);
        formData.append('session_id', sessionId);
        formData.append('nonce', mxchatChat.nonce);

        uploadBtn.disabled = true;
        sendBtn.disabled = true;
        uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
            <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
        </svg>`;

        const response = await fetch(mxchatChat.ajax_url, {
            method: 'POST',
            body: formData
        });

        const data = await response.json();

        if (data.success) {
            // Hide popular questions if they exist
            const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
            if (popularQuestionsContainer) {
                popularQuestionsContainer.style.display = 'none';
            }

            // Show the active Word document name
            showActiveWord(data.data.filename);
            
            appendMessage('bot', data.data.message);
            scrollToBottom();
            activeWordFile = data.data.filename;
        } else {
            console.error('Upload failed:', data.data);
            alert('Failed to upload Word document. Please try again.');
        }
    } catch (error) {
        console.error('Upload error:', error);
        alert('Error uploading file. Please try again.');
    } finally {
        uploadBtn.disabled = false;
        sendBtn.disabled = false;
        uploadBtn.innerHTML = originalBtnContent;
        this.value = ''; // Reset file input
    }
});

// Function to show active PDF name in toolbar
function showActivePdf(filename) {
    const container = document.getElementById('active-pdf-container');
    const nameElement = document.getElementById('active-pdf-name');
    
    if (!container || !nameElement) {
        console.error('PDF container elements not found');
        return;
    }

    nameElement.textContent = filename;
    container.style.display = 'flex';
}

// Function to show active Word document name in toolbar
function showActiveWord(filename) {
    const container = document.getElementById('active-word-container');
    const nameElement = document.getElementById('active-word-name');
    
    if (!container || !nameElement) {
        console.error('Word document container elements not found');
        return;
    }

    nameElement.textContent = filename;
    container.style.display = 'flex';
}

// Function to remove active PDF
function removeActivePdf() {
    const container = document.getElementById('active-pdf-container');
    const nameElement = document.getElementById('active-pdf-name');
    
    if (!container || !nameElement || !activePdfFile) return;

    fetch(mxchatChat.ajax_url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            'action': 'mxchat_remove_pdf',
            'session_id': sessionId,
            'nonce': mxchatChat.nonce
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            container.style.display = 'none';
            nameElement.textContent = '';
            activePdfFile = null;
            appendMessage('bot', 'PDF removed.');
        }
    })
    .catch(error => {
        console.error('Error removing PDF:', error);
    });
}

// Function to remove active Word document
function removeActiveWord() {
    const container = document.getElementById('active-word-container');
    const nameElement = document.getElementById('active-word-name');
    
    if (!container || !nameElement || !activeWordFile) return;

    fetch(mxchatChat.ajax_url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            'action': 'mxchat_remove_word',
            'session_id': sessionId,
            'nonce': mxchatChat.nonce
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            container.style.display = 'none';
            nameElement.textContent = '';
            activeWordFile = null;
            appendMessage('bot', 'Word document removed.');
        }
    })
    .catch(error => {
        console.error('Error removing Word document:', error);
    });
}

// Add remove button click handlers
document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
    e.preventDefault();
    e.stopPropagation();
    removeActivePdf();
});

document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
    e.preventDefault();
    e.stopPropagation();
    removeActiveWord();
});

// Check initial document status
function checkInitialDocumentStatus() {
    if (!sessionId) return;

    // Check PDF status
    fetch(mxchatChat.ajax_url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            'action': 'mxchat_check_pdf_status',
            'session_id': sessionId,
            'nonce': mxchatChat.nonce
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.success && data.data.filename) {
            showActivePdf(data.data.filename);
            activePdfFile = data.data.filename;
        }
    })
    .catch(error => {
        console.error('Error checking PDF status:', error);
    });

    // Check Word document status
    fetch(mxchatChat.ajax_url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            'action': 'mxchat_check_word_status',
            'session_id': sessionId,
            'nonce': mxchatChat.nonce
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.success && data.data.filename) {
            showActiveWord(data.data.filename);
            activeWordFile = data.data.filename;
        }
    })
    .catch(error => {
        console.error('Error checking Word document status:', error);
    });
}

// Apply toolbar settings
if (mxchatChat.chat_toolbar_toggle === 'on') {
    $('.chat-toolbar').show();
} else {
    $('.chat-toolbar').hide();
}

// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
    checkInitialDocumentStatus();
});

// Style all toolbar elements
const toolbarElements = [
    '#mxchat-chatbot .toolbar-btn svg',
    '#mxchat-chatbot .active-pdf-name',
    '#mxchat-chatbot .active-word-name',
    '#mxchat-chatbot .remove-pdf-btn svg',
    '#mxchat-chatbot .remove-word-btn svg'
];
$(toolbarElements.join(', ')).css({
    'fill': toolbarIconColor,
    'color': toolbarIconColor
});



// Ensure essential elements are defined
const emailForm = document.getElementById('email-collection-form');
const emailBlocker = document.getElementById('email-blocker');
const chatbotWrapper = document.getElementById('chat-container');

if (emailForm && emailBlocker && chatbotWrapper) {
    // Check if email exists for the current session
    function checkSessionAndEmail() {
    const sessionId = getChatSession();
    //console.log("[DEBUG JS] checkSessionAndEmail -> sessionId:", sessionId);

    fetch(mxchatChat.ajax_url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            action: 'mxchat_check_email_provided',
            session_id: sessionId,
            nonce: mxchatChat.nonce,
        }),
    })
    .then((response) => response.json())
    .then((data) => {
        //console.log("[DEBUG JS] mxchat_check_email_provided response:", data);
        
        if (data.success) {
            if (data.data.logged_in) {
                //console.log("[DEBUG JS] User is logged in. Hiding email form.");
                emailBlocker.style.display = 'none';
                chatbotWrapper.style.display = 'flex';
            } else if (data.data.email) {
                //console.log("[DEBUG JS] Email found for session. Hiding email form.");
                emailBlocker.style.display = 'none';
                chatbotWrapper.style.display = 'flex';
            } else {
                //console.log("[DEBUG JS] No email provided. Showing email form.");
                emailBlocker.style.display = 'flex';
                chatbotWrapper.style.display = 'none';
            }
        } else {
            //console.log("[DEBUG JS] Error or no data received. Showing email form.");
            emailBlocker.style.display = 'flex';
            chatbotWrapper.style.display = 'none';
        }
    })
    .catch((error) => {
       // console.error("[DEBUG JS] Fetch error -> forcing email form visible:", error);
        emailBlocker.style.display = 'flex';
        chatbotWrapper.style.display = 'none';
    });
}



    // Handle email form submission
    emailForm.addEventListener('submit', function (event) {
        event.preventDefault();
        const userEmail = document.getElementById('user-email').value;
        const sessionId = getChatSession();

        if (userEmail) {
            fetch(mxchatChat.ajax_url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({
                    action: 'mxchat_handle_save_email_and_response',
                    email: userEmail,
                    session_id: sessionId,
                    nonce: mxchatChat.nonce,
                }),
            })
                .then((response) => response.json())
                .then((data) => {
                    //console.log('Backend response:', data);
                    if (data.success) {
                        //console.log('Email saved successfully:', userEmail);
                        emailBlocker.style.display = 'none';
                        chatbotWrapper.style.display = 'flex';

                        // Optionally handle bot response
                        if (data.message) {
                            appendMessage('bot', data.message);
                            scrollToBottom();
                        }
                    } else {
                        console.error('Error saving email:', data.message || 'Unknown error');
                    }
                })
                .catch((error) => {
                    console.error('AJAX error:', error);
                });
        }
    });

    // Check session and email status on page load
    checkSessionAndEmail();
} else {
    console.error('Essential elements for email handling are missing.');
}


// Initialize when document is ready
$(document).ready(function() {
        setFullHeight();
    initializeChatVisibility();
        loadChatHistory();

});

});

// Event listener for copy button
document.addEventListener("click", (e) => {
    if (e.target.classList.contains("mxchat-copy-button")) {
        const copyButton = e.target;
        const codeBlock = copyButton
            .closest(".mxchat-code-block-container")
            .querySelector(".mxchat-code-block code");

        if (codeBlock) {
            // Preserve formatting using innerText
            navigator.clipboard.writeText(codeBlock.innerText).then(() => {
                copyButton.textContent = "Copied!";
                copyButton.setAttribute("aria-label", "Copied to clipboard");

                setTimeout(() => {
                    copyButton.textContent = "Copy";
                    copyButton.setAttribute("aria-label", "Copy to clipboard");
                }, 2000);
            });
        }
    }
});




````
